# Payments — MyFatoorah & Tap

A gateway-agnostic layer (`app/Payments`) that serves both billing contexts.
Both gateways use a **hosted redirect** flow and are confirmed **server-side**
(the gateway's own status API is the source of truth — webhook bodies are never
trusted blindly).

## Two contexts, two sets of keys

| Context | Who pays whom | Credentials | Entry point |
|---|---|---|---|
| **SaaS billing** | Tenant → you (plan fee) | Platform keys in `config/payments.php` (`.env`) | `Central\SubscriptionCheckoutController` |
| **POS / invoice** | A tenant's customer → that tenant | The tenant's OWN keys, stored per-tenant | `Tenant\CheckoutController` |

Never collect a tenant's sales into your platform account. Each tenant stores
their own MyFatoorah/Tap keys in the tenant `settings` table.

## Architecture

```
app/Payments/
  Contracts/PaymentGateway.php        createPayment() · verify() · verifyWebhookSignature()
  Data/{PaymentRequest,PaymentSession,PaymentResult}.php
  Gateways/MyFatoorahGateway.php      SendPayment -> InvoiceURL ; GetPaymentStatus
  Gateways/TapGateway.php             POST /v2/charges -> transaction.url ; GET /v2/charges/{id}
  PaymentGatewayManager.php           driver($name, $credentials = null)
  Support/Currency.php                KWD/BHD/OMR -> 3 decimal places
```

Adding a third gateway = one new class implementing `PaymentGateway` + a `match`
arm in the manager. Nothing else changes.

## .env (platform / SaaS billing keys)

```env
PAYMENTS_DEFAULT=myfatoorah
PAYMENTS_CURRENCY=KWD

MYFATOORAH_TOKEN=your_token
MYFATOORAH_BASE_URL=https://apitest.myfatoorah.com   # live: https://api.myfatoorah.com (region-specific)
MYFATOORAH_WEBHOOK_SECRET=

TAP_SECRET_KEY=sk_test_xxx
TAP_BASE_URL=https://api.tap.company
TAP_WEBHOOK_SECRET=
```

## Storing a tenant's own keys

Run inside that tenant's context (e.g. a settings screen, or tinker after
`tenancy()->initialize($tenant)`):

```php
use App\Models\Tenant\Setting;

Setting::put('gateway.tap', [
    'secret_key'     => 'sk_live_xxx',
    'base_url'       => 'https://api.tap.company',
    'webhook_secret' => 'xxx',
]);

Setting::put('gateway.myfatoorah', [
    'token'          => 'tenant_live_token',
    'base_url'       => 'https://api.myfatoorah.com',
    'webhook_secret' => 'xxx',
]);
```

## Collecting payment for a sale

```php
// POST {tenant-domain}/checkout/pay
//   sale_id=123 & gateway=tap   -> redirects the customer to the hosted page
```

On success `InvoicePaymentService` records the payment, updates the invoice
balance, and posts `Dr Bank = Cr Accounts Receivable`, clearing the receivable
that `SaleService` opened at sale time. Callback and webhook are **idempotent**.

## Gateway endpoints (for reference)

- **MyFatoorah:** `POST /v2/SendPayment` (Bearer token) returns `Data.InvoiceURL`
  + `Data.InvoiceId`. Customer returns with `?paymentId=...`. Confirm with
  `POST /v2/GetPaymentStatus` (`InvoiceStatus == "Paid"`).
- **Tap:** `POST /v2/charges` (Bearer secret key) returns `transaction.url` +
  `id` (`chg_...`). Customer returns with `?tap_id=chg_...`. Confirm with
  `GET /v2/charges/{id}` (`status == "CAPTURED"`).

## Required: exempt gateway POSTs from CSRF

Gateways POST to your webhook (and sometimes the callback). In Laravel 11,
in `bootstrap/app.php`:

```php
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'billing/callback/*', 'billing/webhook/*',
        'checkout/callback/*', 'checkout/webhook/*',
    ]);
})
```

## Security notes

- `verify()` (server-to-server) is authoritative everywhere. Signature checks
  (`verifyWebhookSignature`) are defence-in-depth and **off when no secret is set**.
- Confirm the exact webhook signature construction in each provider's dashboard;
  the field order can vary by account, so validate before relying on it.
- KWD/BHD/OMR are 3-decimal currencies — `Support\Currency` handles formatting.

## Screens (tenant UI)

Two Blade pages ship with this layer, both inside the tenant context:

- **Payment gateways** — `GET /settings/gateways`
  Each tenant enters their own MyFatoorah / Tap keys. Secrets are write-only
  (shown as "•••• stored", blank keeps the existing value); base URLs are
  editable; each card shows a Connected / Not connected status and a Disconnect
  action. Saving calls `Setting::put("gateway.<name>", [...])`.

- **Pay invoice** — `GET /checkout/{sale}/pay`
  A receipt-style summary with the amount due as the hero, plus a gateway
  picker. Submitting posts to `checkout.pay`, which redirects to the provider's
  hosted page. The return `callback` lands back on this page with the settled
  status. Gateways the tenant hasn't connected are shown disabled with a link
  to settings.

Both extend `resources/views/layouts/app.blade.php`. Reaching the pay page just
needs a sale id (e.g. link to `route('checkout.show', $sale->id)` from your
invoice list once you build it).
