# Handoff — after backend-phase-11 (Refunds, clawbacks & invoices) **The reversal leg of the payments arc is live.** Money can now flow *backwards*: an admin reverses a captured booking payment across both fee legs, the balanced reversal posts to the append-only ledger, and the flow forks on whether the nurse was already paid (clean `nurse_payable` reversal vs a first-class `nurse_clawbacks` receivable). The minimal commission **invoice** (VAT on the commission line, sequential number, mocked مودیان) ships too. ## What the frontend (f10-b11) can now build - **Admin refund console** — create a refund (`POST admin_refunds`: full/partial by percentage or explicit legs), the refund worklist (`GET admin_refunds` — channel, decomposed legs, status, ETA, policy snapshot), and the clawback write-off (`POST admin_clawbacks/{id}/write_off`). - **Admin invoice issuance** — `POST admin_invoices` (sequential number, VAT on commission, مودیان pending). - **Customer cancellation/refund-status view** — `GET refunds/{id}/status` (tenancy-scoped; status, channel, amount, **`expected_customer_refund_eta`** for the BNPL 7–10-business-day window, masked reference). This is the **only** customer-visible refund surface — refunds are admin-initiated, there is no self-service initiation. - **Customer/admin invoice view** — `GET invoices/{booking_id}` (number, gross, commission, VAT, مودیان status, PDF URL when present). ## Live endpoints / contract - Contract: **`dev/contracts/domains/refunds-invoices.md`** (enums, DTO shapes, IRR digit-strings, masked references, failure codes, side-effects). Machine schema: `dev/contracts/openapi/swagger.v1.json` **refreshed**. - All money is IRR integer, on the wire as a **digit-string**. `expected_customer_refund_eta` is a **date**. - Admin endpoints are behind the admin policy and **rate-limited** (sensitive). Refunds are **never** customer self-service. ## What is mocked / waiting - **سامانه مودیان** is mocked behind **`IMoadianClient`** (pending/no-ref by default; a config switch forces a registered 22-digit ref). See `reports/mocks-registry.md`. - **BNPL revert** runs against **`IBnplProvider`** — a **thin local stub** now; **b12 owns the real seam** and its adapter. The `bnpl_revert` ledger legs are already identical to the card path. - **"Was the nurse paid?"** is derived from the booking's dispute-window close (`INursePayoutStatus`) until **b13** ships `nurse_payouts`; **clawback recovery/netting is b13** (this phase only opens the `pending` receivable + supports write-off). - **`tickets`** arrives in **b15**: `refunds.ticket_id` is a nullable column (no FK yet); the "ticket required" rule is config-gated off (`refund_ticket_required = false`). - **Deferred crons** (thin/manual today): the مودیان reconciliation job (`pending → registered`) and the BNPL-revert reconciliation job that clears `refund_payable ↔ escrow_held` for a `processing` refund. ## Notes for the next backend phases - **b12 (BNPL):** owns the real `IBnplProvider`; the refund `bnpl_revert` path already calls it through the seam. - **b13 (payouts):** implement the real `INursePayoutStatus` (`nurse_payout_booking_links`) and net `pending` `nurse_clawbacks` out of a payout batch (set `recovered_in_payout_id` / `original_payout_id`, post `DEBIT nurse_payable / CREDIT nurse_clawback_receivable`). - **b15 (tickets/partner centers):** wire the real `refunds.ticket_id` FK + flip `refund_ticket_required` on; wire `invoices.partner_center_id` + `nurse_clawbacks` payout FKs.