# Model: Payment

**Table**: `clinic_payments` (new)

## Purpose

Records the payment transaction for one booking. **Deliberately a new, standalone table** — the
existing `App\Models\Payment\PaymentTransaction` is hard-coupled to `SalesOrder` (a required
`sales_order_id` FK, and `markPaid()`/`markFailed()` hard-code updates to `$this->order`), so
reusing it would mean either forcing a fake `SalesOrder` per booking or refactoring
`PaymentTransaction` into a polymorphic `payable`. Both were rejected (confirmed with the user)
in favor of this isolated table — it still reuses the *generic* parts
(`App\Services\Payment\PaymentGatewayManager` and the gateway adapters configured via
`App\Models\Payment\PaymentGateway`), just not the `SalesOrder`-specific transaction record.

## Fields

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `booking_id` | bigint | `foreignId('booking_id')->unique()->constrained('clinic_bookings')->cascadeOnDelete()` | 1:1 |
| `gateway_code` | string | | which `PaymentGateway.code` was used (stripe/paymob/paypal), same field name as `PaymentTransaction.gateway_code` for consistency |
| `amount` | decimal(10,2) | | copied from `Booking.price` |
| `currency` | string | default from config | matches `PaymentTransaction.currency` |
| `provider_transaction_id` | string | nullable | gateway's own reference |
| `provider_reference` | string | nullable | |
| `status` | string | default `'pending'` | `pending` / `paid` / `failed` |
| `request_payload` | json | nullable | |
| `response_payload` | json | nullable | |
| `webhook_payload` | json | nullable | |
| `failure_message` | string | nullable | |
| `paid_at` | datetime | nullable | |
| `failed_at` | datetime | nullable | |
| `created_at` / `updated_at` | timestamp | | |

Field names deliberately mirror `PaymentTransaction`'s (`app/Models/Payment/PaymentTransaction.php:11-25`)
so the same adapter interface (`handleReturn()`, `handleWebhook()`) can populate either model with
no adapter-side changes — only the controller/model glue differs.

## Relationships

| Relation | Type | Target |
|---|---|---|
| `booking()` | `belongsTo` | `Booking` |

## Behavior (mirrors `PaymentTransaction`'s methods, adapted)

- `markPaid()` — sets `status='paid'`, `paid_at=now()`, and updates the related `Booking.status`
  to `confirmed` (instead of `PaymentTransaction`'s hard-coded `$this->order()->update([...])`).
- `markFailed()` — sets `status='failed'`, `failed_at=now()`; `Booking` stays `pending` so the
  patient can retry payment.

## Flow

See `flows/03-payment-flow.md` for the full `BookingPaymentController` → `PaymentGatewayManager`
→ adapter → return/webhook sequence.
