frontend phase 9
This commit is contained in:
@@ -12,6 +12,41 @@ for awareness.
|
||||
- **Requests filed:** frontend/requests/for-backend.md (yes/no)
|
||||
-->
|
||||
|
||||
## frontend-phase-9-b10 — Checkout, card payment & invoice — 2026-07-10
|
||||
- **Shipped:** the money moment — a **new `services/payment` domain** (types/keys/constants/
|
||||
apis[client+mock]/invalidations/5 hooks + barrel) and the customer checkout flow: **C6 خلاصه و پرداخت**
|
||||
`/bookings/checkout?request_id=` (acceptance badge, served **reconciling** service-cost/کارمزد/مالیات/مبلغ کل
|
||||
breakdown, verbatim **escrow notice**, payment-window countdown, «ادامه پرداخت ←» with an
|
||||
**idempotency-key-per-attempt**, disabled BNPL seam for f11), the **card states** (initiating → redirect →
|
||||
dev **mock-gateway harness** `/bookings/checkout/gateway` → return `/bookings/checkout/return` with a
|
||||
**backoff pending-callback poll** → succeeded/failed/expired), the **confirmation**
|
||||
`/bookings/checkout/confirmation` («مشاهده رزرو» + «دانلود فاکتور»), and the **invoice**
|
||||
`/bookings/[id]/invoice` (VAT-on-commission line, read-only مودیان state, pdfUrl download or print
|
||||
receipt). Three shared tested composites: `PriceBreakdown`, `EscrowNotice`, `PaymentStatusBadge`. New
|
||||
`payment` i18n namespace (53 keys, both locales).
|
||||
- **Load-bearing rules honored:** money = IRR digit-strings, **BigInt only** (integer parts-per-10000 rate
|
||||
math in the mock — zero floats); the breakdown **must reconcile** (`PriceBreakdown` dev-guards it; rows
|
||||
are served, VAT never derived client-side); **VAT on the commission only** (invoice line labelled
|
||||
accordingly); escrow copy **verbatim** in fa (pinned by test against fa.json), info tone never error;
|
||||
**409 = benign convergence** (re-reads the outcome, never an error toast); poll uses **geometric backoff,
|
||||
stops on terminal + bounded attempts**; success flips the booking **by cache invalidation** (exact keys,
|
||||
no refetch storm).
|
||||
- **Consumes:** dev/contracts/domains/payments.md (b10 — initiate route + `Idempotency-Key` header +
|
||||
`InitiatePaymentResult`; status enum `pending|succeeded|failed`) and the invoice slice of
|
||||
refunds-invoices.md (b11 — `GET invoices/{bookingId}`, `InvoiceDto`), casing verified against
|
||||
swagger.v1.json. **Not served by any contract:** a checkout summary, a client transaction read, and the
|
||||
converted request's booking id → REQ-016/017/018.
|
||||
- **Mocked client-side:** `services/payment` via `paymentMockApi` (**USE_PAYMENT_MOCK=true, primary**) —
|
||||
it is the missing **conversion trigger bridging the f7 ↔ f8 mock stores**: capture converts the request
|
||||
(`converted` + client-augmented `bookingId`), inserts a **confirmed** booking into the f8 store, and
|
||||
auto-issues the b11-shaped invoice, so C5 → C6 → gateway → confirmation → booking detail → invoice runs
|
||||
end-to-end in one session. The dev **mock-gateway page is a test harness, not a product feature**. Real
|
||||
`paymentClientApi`: initiate/invoice = published contract; summary targets the REQ-016 proposed slug;
|
||||
outcome maps `booking_requests/get` (REQ-017).
|
||||
- **Gate:** npm run check green · npm run test:ci green (204 tests, +9) · production build green.
|
||||
- **Requests filed:** frontend/requests/for-backend.md — yes (REQ-016 checkout summary, REQ-017 payment
|
||||
outcome + bookingId, REQ-018 customer invoice availability post-capture).
|
||||
|
||||
## frontend-phase-8-b9 — Booking detail, sessions & nurse EVV — 2026-07-10
|
||||
- **Shipped:** the post-payment engagement — a **new** `services/bookings` domain (the sibling of
|
||||
`bookingRequests`, NOT a rename): types/keys/constants/apis[client(1:1 b9)+mock+serverApi]/8 hooks +
|
||||
|
||||
@@ -214,3 +214,52 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
|
||||
a casing/int drift or a `false`-vs-`null` conflation would mislabel a visit. Low-risk (mock-primary now),
|
||||
but worth locking before f9/f13 consume the same shapes.
|
||||
- **Status:** open
|
||||
|
||||
## REQ-016 — Checkout summary for C6 (served gross/commission/VAT breakdown) — filed by frontend-phase-9-b10 — 2026-07-10
|
||||
- **Need:** A customer-facing read that serves the C6 «خلاصه و پرداخت» money rows for an
|
||||
`accepted_awaiting_payment` request: the three b10 amounts (`grossPriceIrr`,
|
||||
`balinyaarCommissionIrr`, `nursePayoutAmount`) **plus the display decomposition** — service cost,
|
||||
commission **net of VAT**, `vatIrr` + `vatRate` — and the nurse/variant/schedule mini-info +
|
||||
`paymentDeadlineAt`.
|
||||
- **Why:** The b8 `BookingRequestDto` is money-free by design and no checkout-summary endpoint exists,
|
||||
but C6 must show a breakdown that **reconciles to the rial** (service + commission + VAT = total) and
|
||||
the client is forbidden from deriving commission or tax itself (no float math, rates are server
|
||||
config). Today the whole C6 money surface is mocked (`services/payment` mock computes the split from
|
||||
the mock variant price at the configured 12% fee / 10% VAT).
|
||||
- **Proposed shape:** `GET api/v1/booking_requests/checkout_summary/{id}` (owner-scoped) →
|
||||
`{ bookingRequestId, requestStatus, nurseName, patientName, variantLabel, variantPriceUnit,
|
||||
sessionCount, requestedDate, requestedTimeStart, requestedTimeEnd, paymentDeadlineAt,
|
||||
serviceCostIrr, commissionIrr, vatIrr, vatRate, totalIrr, grossPriceIrr, balinyaarCommissionIrr,
|
||||
nursePayoutAmount }` — the client's real `paymentClientApi.getCheckoutSummary` already targets this
|
||||
slug and unwraps this exact shape (`client/src/services/payment/types.ts: CheckoutSummaryDto`).
|
||||
- **Status:** open
|
||||
|
||||
## REQ-017 — Client-readable payment outcome + `bookingId` on a converted request — filed by frontend-phase-9-b10 — 2026-07-10
|
||||
- **Need:** After the gateway redirect returns, the client needs to learn (a) the payment transaction's
|
||||
status (`pending|succeeded|failed`) and (b) **which booking** the capture created. Either a
|
||||
transaction read (e.g. `GET api/v1/payment_transactions/{id}` or
|
||||
`GET api/v1/bookings/{bookingRequestId}/payments/latest`, owner-scoped) or, minimally, a
|
||||
`bookingId` field on `BookingRequestDto` once `status = converted`.
|
||||
- **Why:** b10 confirms captures inside the PSP webhook (correct — the client is never trusted), so the
|
||||
frontend's pending-callback state can only poll `booking_requests/get/{id}` and map
|
||||
`converted → succeeded` / `payment_deadline_expired → failed`. That works, but it cannot distinguish a
|
||||
*declined* payment (still `accepted_awaiting_payment`, retry allowed) from a *slow* callback, and the
|
||||
confirmation screen cannot deep-link «مشاهده رزرو» or «دانلود فاکتور» because the converted request
|
||||
never reveals its booking id. The client DTO already carries a client-augmented
|
||||
`bookingId: number | null` (mock fills it; real path returns null and the UI falls back to the
|
||||
bookings list, hiding the invoice link).
|
||||
- **Proposed shape:** add `bookingId: long?` to `BookingRequestDto` (null until converted) **and/or**
|
||||
`GET api/v1/bookings/{bookingRequestId}/payments/latest` → `{ transactionId, status,
|
||||
gatewayReferenceCode, bookingId? }`.
|
||||
- **Status:** open
|
||||
|
||||
## REQ-018 — Customer invoice availability after capture (auto-issue or owner-issue) — filed by frontend-phase-9-b10 — 2026-07-10
|
||||
- **Need:** Make the b11 invoice reachable by the paying customer right after capture: auto-issue the
|
||||
commission invoice on card capture (idempotent per booking, as `POST admin_invoices` already is), or
|
||||
allow the owning customer to trigger the idempotent issue on first `GET api/v1/invoices/{bookingId}`.
|
||||
- **Why:** The f9 confirmation screen offers «دانلود فاکتور», but b11 issues invoices only via the
|
||||
admin-only `POST api/v1/admin_invoices`, so a customer's `GET invoices/{bookingId}` 404s until an
|
||||
admin acts. The UI handles the 404 as a "فاکتور هنوز صادر نشده است" state (and the mock auto-issues at
|
||||
capture to demo the full flow), but on the real rails every fresh payment would land on that empty
|
||||
state.
|
||||
- **Status:** open
|
||||
|
||||
Reference in New Issue
Block a user