47 lines
3.5 KiB
Markdown
47 lines
3.5 KiB
Markdown
# 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.
|