3.5 KiB
3.5 KiB
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_etafor 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.jsonrefreshed. - All money is IRR integer, on the wire as a digit-string.
expected_customer_refund_etais 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). Seereports/mocks-registry.md. - BNPL revert runs against
IBnplProvider— a thin local stub now; b12 owns the real seam and its adapter. Thebnpl_revertledger legs are already identical to the card path. - "Was the nurse paid?" is derived from the booking's dispute-window close (
INursePayoutStatus) until b13 shipsnurse_payouts; clawback recovery/netting is b13 (this phase only opens thependingreceivable + supports write-off). ticketsarrives in b15:refunds.ticket_idis 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 clearsrefund_payable ↔ escrow_heldfor aprocessingrefund.
Notes for the next backend phases
- b12 (BNPL): owns the real
IBnplProvider; the refundbnpl_revertpath already calls it through the seam. - b13 (payouts): implement the real
INursePayoutStatus(nurse_payout_booking_links) and netpendingnurse_clawbacksout of a payout batch (setrecovered_in_payout_id/original_payout_id, postDEBIT nurse_payable / CREDIT nurse_clawback_receivable). - b15 (tickets/partner centers): wire the real
refunds.ticket_idFK + fliprefund_ticket_requiredon; wireinvoices.partner_center_id+nurse_clawbackspayout FKs.