diff --git a/app/Http/Controllers/WalletBalanceController.php b/app/Http/Controllers/WalletBalanceController.php new file mode 100644 index 0000000..29d4267 --- /dev/null +++ b/app/Http/Controllers/WalletBalanceController.php @@ -0,0 +1,40 @@ +user()->public_id; + + $minor = Cache::remember("wallet_balance:{$publicId}", now()->addSeconds(30), function () use ($billing, $publicId) { + try { + return $billing->balanceMinor($publicId); + } catch (\Throwable) { + return null; + } + }); + + if ($minor === null) { + return response()->json(['available' => false]); + } + + $currency = (string) config('billing.currency', 'GHS'); + + return response()->json([ + 'available' => true, + 'balance_minor' => $minor, + 'currency' => $currency, + 'formatted' => $currency.' '.number_format($minor / 100, 2), + ]); + } +} diff --git a/config/billing.php b/config/billing.php index d3290ad..48be649 100644 --- a/config/billing.php +++ b/config/billing.php @@ -4,6 +4,6 @@ return [ 'api_url' => env('BILLING_API_URL', 'https://ladill.com/api/billing'), 'api_key' => env('BILLING_API_KEY_CARE'), 'service' => 'care', - 'wallet_balance_route' => 'care.wallet', + 'wallet_balance_route' => 'wallet.balance', 'currency' => 'GHS', ]; diff --git a/resources/js/app.js b/resources/js/app.js index 4ae877d..384888f 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -129,6 +129,11 @@ Alpine.data('afia', (config = {}) => ({ Alpine.data('walletWidget', (config = {}) => ({ display: '…', async load() { + if (! config.url) { + this.display = 'View wallet'; + + return; + } try { const res = await fetch(config.url, { headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }); const data = await res.json(); diff --git a/routes/web.php b/routes/web.php index e296d6c..43499d4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ group(function () { Route::post('/notifications/{id}/read', [NotificationController::class, 'markAsRead'])->name('notifications.mark-read'); Route::post('/notifications/mark-all-read', [NotificationController::class, 'markAllAsRead'])->name('notifications.mark-all-read'); + Route::get('/wallet/balance', [WalletBalanceController::class, 'balance'])->name('wallet.balance'); + Route::get('/onboarding', [OnboardingController::class, 'show'])->name('care.onboarding.show'); Route::post('/onboarding', [OnboardingController::class, 'store'])->name('care.onboarding.store'); diff --git a/tests/Feature/WalletBalanceTest.php b/tests/Feature/WalletBalanceTest.php new file mode 100644 index 0000000..ced23a6 --- /dev/null +++ b/tests/Feature/WalletBalanceTest.php @@ -0,0 +1,78 @@ +withoutMiddleware(EnsurePlatformSession::class); + config([ + 'billing.api_url' => 'https://billing.test', + 'billing.api_key' => 'test-care-key', + 'billing.currency' => 'GHS', + ]); + } + + public function test_wallet_balance_endpoint_returns_formatted_json(): void + { + Http::fake([ + 'billing.test/balance*' => Http::response(['balance_minor' => 12550]), + ]); + + $user = User::create([ + 'public_id' => 'care-wallet-user', + 'name' => 'Wallet User', + 'email' => 'wallet@example.com', + ]); + + $this->actingAs($user) + ->getJson(route('wallet.balance')) + ->assertOk() + ->assertJson([ + 'available' => true, + 'balance_minor' => 12550, + 'currency' => 'GHS', + 'formatted' => 'GHS 125.50', + ]); + + Http::assertSent(function ($request) { + return str_contains($request->url(), '/balance') + && $request['user'] === 'care-wallet-user'; + }); + } + + public function test_wallet_balance_endpoint_marks_unavailable_when_billing_fails(): void + { + Http::fake([ + 'billing.test/balance*' => Http::response(['error' => 'unauthorized'], 401), + ]); + + $user = User::create([ + 'public_id' => 'care-wallet-user-2', + 'name' => 'Wallet User', + 'email' => 'wallet2@example.com', + ]); + + $this->actingAs($user) + ->getJson(route('wallet.balance')) + ->assertOk() + ->assertJson(['available' => false]); + } + + public function test_wallet_balance_route_is_not_the_account_redirect(): void + { + $this->assertSame('wallet.balance', config('billing.wallet_balance_route')); + $this->assertTrue(\Illuminate\Support\Facades\Route::has('wallet.balance')); + $this->assertNotSame(route('care.wallet'), route('wallet.balance')); + } +}