# Hardening Phase 2 — Money-path mock integrity (no fake success on real data)

> Sever the dangling cross-imports the phase-4 de-mock left behind — the refunds and BNPL mocks still
> read retired in-memory stores keyed by REAL server ids — split the seams whose real halves are
> already live (verification nurse flow, nurse earnings), and stop discarding the delivered
> `bookingId`. After this phase, nothing in the money path can show a fake success against real data.
> **Track:** frontend · **Depends on:** Phase 0 · **Unlocks:** Phase 5
> **Before you start, read [_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md).**

## 1. Context — where this sits

Fixes **H-06 … H-10** from [issues.md](issues.md). Refinement-phase-4 flipped 14 domains to real but
left 7 mocked; two of the mocked ones (`refunds`, `bnpl`) still hard-import the **mock modules** of
now-real sibling domains (`bookings`, `bookingRequests`), whose fixture stores no longer correspond to
anything — so a real customer's cancel 404s and the BNPL wizard either 404s or fabricates a client-side
"booking" while the real request expires unpaid. Meanwhile two seams hold real, shipped backend halves
hostage to a single flag (`verification` nurse flow — which silently breaks nurse discoverability
because the real search gate needs the real `is_verified`; `payouts` nurse reads — REQ-025 is live).

**What already exists (do not rebuild):** the per-domain seam pattern (`apis/index.ts` selects
mock/real), all the real `clientApi.ts` implementations named below, the b6 verification endpoints, the
REQ-025 nurse payout endpoints (`NursePayoutsController.cs:33-48`), and the REQ-017 `bookingId` on
`BookingRequestDto`.

## 2. Required reading (do this first)

- [issues.md](issues.md) H-06/H-07/H-08/H-09/H-10 — full evidence; don't re-audit.
- `client/CLAUDE.md` → "The services/{domain} reference pattern" + the de-mock status block.
- The files cited per finding (refunds/bnpl/bookings/bookingRequests mockApis + constants, checkout
  pages, `PublishGate.tsx`, `MyServicesList.tsx`, payment `clientApi.ts`/`invalidations.ts`,
  payouts/verification services).
- `dev/shared-working-context/reports/mocks-registry.md` — you will update the affected rows.

## 3. Scope — build this

1. **H-06 — refunds mock must see real bookings.** While `USE_REFUNDS_MOCK=true` (its REQs land in
   Phase 4), rewire `services/refunds/apis/mockApi.ts` to resolve the booking through the **selected**
   `bookingsApi` (the seam, real today) instead of importing `mockGetBookingForRefund` from the
   bookings mock module. The mock computes the policy preview locally off the real booking's
   status/sessions/amounts and simulates the cancel/refund state machine on top. A real booking id
   must produce a coherent preview + cancel flow (mock-side state), never a 404.
2. **H-07 — BNPL must not fake success.** Two parts:
   - Derive the checkout BNPL CTA visibility instead of the bare `BNPL_ENABLED=true`
     (`services/payment/constants.ts:23`, used at `checkout/page.tsx:214`): hide the branch whenever
     `USE_BNPL_MOCK` is true while `bookingRequests`/`payment` are real (mixed state = the dangerous
     combination). An honest absent button beats a fabricated booking.
   - For the dev/demo path that remains reachable (both mocked, e.g. Testing), rewire
     `services/bnpl/apis/mockApi.ts:5-8` to go through the **selected** `bookingRequestsApi`/
     `bookingsApi` seams, never the raw mock modules.
3. **H-08 — split the verification seam; make the nurse trust flow real.**
   - Split `VerificationApi` selection per half: nurse-facing methods (status, start, identity,
     Shahkar, bank, document upload, credentials, trust badge) go **real** now — the clientApi is
     already 1:1 per the mocks registry; admin-facing methods (queue, case, decideStep, signed doc
     URL) stay mocked until REQ-034 (Phase 4). Follow whatever per-method selection shape is cleanest
     under the existing seam pattern (two flags, or a composed api object) and record it in the
     registry.
   - Wire `PublishGate.tsx` to the real status (its CTA currently calls nothing — `:59-68`), and gate
     the variant-activation affordance in `MyServicesList.tsx` on real verification status with an
     honest explainer, so a nurse can no longer "publish" into invisibility. Remove/dev-gate the
     `__mockApproveAll` simulator button accordingly.
4. **H-09 — split the payouts seam.** Nurse reads (`earnings_balance`, `earnings`, `{id}` detail,
   history) go real (`payouts/apis/clientApi.ts:163-187` already implements them); admin batch methods
   stay mocked until REQ-036. Same split mechanics as verification.
5. **H-10 — stop nulling `bookingId`.** `services/payment/apis/clientApi.ts:55-75`: read `bookingId`
   off the wire (drop the `Omit`/`bookingId: null` and the stale REQ-017-pending comments in
   `clientApi.ts` and `types.ts:98-110`); confirm `invalidations.ts` now receives it and the
   confirmation page renders the booking + invoice deep links.

## 4. Mocks & seams in this phase

Touches the `refunds`, `bnpl`, `verification`, `payouts` seams (splits/rewires; flags flipped only for
the halves whose backend is live). Update each row in
`dev/shared-working-context/reports/mocks-registry.md` — including correcting the stale "reads the
shared f7 store" descriptions.

## 5. Critical rules you must not get wrong

- **A mock may only reach sibling data through the selected seam** (`services/{domain}/apis/index.ts`),
  never by importing a sibling's `mockApi.ts` — that's the root cause you're eradicating. Grep for
  remaining cross-mock imports before finishing.
- Money is served IRR digit-strings, BigInt-safe — the refunds mock's local preview must reuse the
  existing money utils and reconcile (refund + fee = captured), never float math.
- Verified-only search, two-stage disclosure, and "client never computes what the server owns"
  invariants all still hold; the verification split must not let the client write `is_verified`.
- A 409 on the money path is benign convergence, never a toast (existing rule).
- Keep i18n complete for any new copy (both message files); `npm run check` owns the gate.

## 6. Definition of Done

- Real journey (seeded world, real server): create booking request → accept → pay → **cancel** →
  policy preview renders with reconciling numbers (no 404).
- BNPL CTA absent on real checkout while the domain is mocked; the D1-D5 wizard is unreachable with a
  real request id (and works fully in the all-mock dev mode).
- Fresh nurse: real verification wizard drives the real b6 endpoints; variant activation is gated
  until the real status approves; after approval + activation the nurse appears in real search.
- Nurse earnings screen shows real amounts from the live endpoints.
- Payment confirmation shows working "view booking" + "download invoice" deep links after a real
  capture.
- `npm run check` + `npm run test:ci` green; no `services/*/apis/mockApi` imports across domain
  boundaries (grep-proof in the report).

## 7. How to test (human)

1. Full customer journey per §6 against the RUNBOOK setup; screenshot the cancel preview.
2. `grep -r "apis/mockApi" client/src/services --include=*.ts | grep -v "own domain"` → empty.
3. Nurse persona 09120000003 (unverified): services page shows the gated state; run the real
   verification flow; confirm search visibility flips after approval.

## 8. Hand off & document

- Tick H-06…H-10 in [issues.md](issues.md) with commit hashes.
- Update `client/CLAUDE.md` de-mock status block + `mocks-registry.md` rows.
- Write `dev/shared-working-context/reports/hardening-phase-2-report.md`; file new REQs if a split
  exposed a missing server read (append to `for-backend.md`, Phase 4 picks them up).
