# Hardening Phase 4 — Contract completion batch (close the Tier B/C REQs)

> Deliver the remaining open contract requests so the last seven mocked client domains have a real
> backend to flip to: customer refunds, BNPL reads, review eligibility, family-record access, admin
> console deltas, the admin halves of verification/refunds/payouts, and the partner portal's identity
> signal + self-scoped reads. The sequel to refinement-phase-3 (which delivered Tier A).
> **Track:** backend · **Depends on:** Phase 1 (RBAC — the admin endpoints here must be gated by the
> now-working policy) · **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-15, H-16a** from [issues.md](issues.md). The canonical spec for every item is the REQ entry
in [for-backend.md](../../shared-working-context/frontend/requests/for-backend.md) plus the Tier B/C
sections of [refinement-phase-3](../refinement/refinement-phase-3-contract-batch.md) — this file
sequences and annotates; it does not restate shapes. **Re-check each REQ's `Status:` line before
building — some were partially delivered** (REQ-025 nurse reads are live; REQ-027's GET/PUT
care_record exists; REQ-017/my_review landed earlier; Phase 1 may have delivered REQ-031; Phase 2 may
have appended new REQs — pick those up too).

**What already exists (do not rebuild):** the b11 refund engine (fee-leg decomposition, clawback
fork), the b12 BNPL machine, the b14 review/moderation + care-record entities, the b13 payout engine +
`nurse_payouts` reads, the b6 verification pipeline + signed-URL storage seam, partner centers with
kebab-case admin routes, and the refinement-phase-3 house pattern for contract deltas
(migration + swagger regen + tests per REQ).

## 2. Required reading (do this first)

- `for-backend.md` — every REQ still `open`; the proposed shapes there are the contract.
- [refinement-phase-3-contract-batch.md](../refinement/refinement-phase-3-contract-batch.md) §Tier B/C
  + its "how to test" — mirror its delivery pattern.
- `server/CLAUDE.md`, `server/CONVENTIONS.md`; the relevant `product/` docs per area (refund policy,
  BNPL, verification, partners) — business rules are decisions, not guesses.
- Phase 2's report + any REQs it filed.

## 3. Scope — build this (one commit-sized slice per bullet, in this order)

1. **REQ-019/020/021 — customer refunds** (unblocks `refunds`): `POST bookings/{id}/cancel`
   (customer-initiated, tenancy-checked), `GET bookings/{id}/cancellation_policy` preview — and
   **define the canonical `cancellation_policy_code` set** (the client invented
   `free_24h`/`partial_under_24h`/`customer_no_show`; decide with `product/` and publish the enum),
   `GET refunds/by_booking/{id}` with fee-leg decomposition. Reuse the b11 engine; a customer cancel
   is never a self-issued refund — it routes through the existing policy/approval model.
2. **REQ-022/023/024 — BNPL reads** (unblocks `bnpl`): `checkout_bnpl/options/{requestId}`,
   `checkout_bnpl/schedule`, eligibility accepting the D3 KYC fields, wallet installments
   (provider-reported — D5 is NOT a Balinyaar ledger), customer-facing `bookingId` on settle.
3. **REQ-026 — reviews**: `bookings/{id}/review_eligibility` + confirm/finish `my_review` (verify
   what already shipped) + the masked-author decision.
4. **REQ-027 leftovers — family care record**: the `record_access` check endpoint; confirm the
   product decision recorded for the family-owned record (the GET/PUT already exist with `long` ids —
   the id-type reconciliation is client-side, Phase 5). Structured `taskResults` on visit notes if the
   REQ still asks.
5. **REQ-029/030 — admin console deltas**: config `updatedAt`/`updatedBy`; audit filters
   (`actor_id`/`action`/`from`/`to`). (REQ-031 should be done by Phase 1 — verify, don't duplicate.)
6. **REQ-034/035/036 — the admin halves** (unblock `verification`/`refunds`/`payouts` admin UIs):
   verification queue + case + per-step decide + on-demand signed doc URL; refund preview/initiate/
   approve/reject (ticket-linked, reusing b11); payout batch preview/run (idempotency-keyed)/retry/
   record-transfer-reference (reusing b13). All gated with the Phase-1 policy + fine-grained roles
   (finance vs moderation vs support scopes as seeded).
7. **REQ-032/033/038 — partner portal**: `administersPartnerCenterId` on `MeResult` + a seeded demo
   partner admin; `centers/me`, `centers/me/nurses`, `centers/me/bookings`, `centers/me/settlement`
   (identity-derived tenancy — resolve the center from the caller, never from a client-sent id);
   invoice `totalIrr`; activate/suspend toggle if still open.

## 4. Mocks & seams in this phase

No new vendor seams. Everything reuses existing engines behind existing seams. Update
`mocks-registry.md` rows only where a "Make it real →" step is now shorter.

## 5. Critical rules you must not get wrong

- **Additive only** — no breaking DTO/route changes; the 14 already-real client domains must keep
  working untouched.
- Money invariants: refund Σ ≤ captured (409 otherwise), fee-leg decomposition, pre/post-payout fork
  via `INursePayoutStatus`, BNPL net-of-fee model, payout invariant-to-method. All exist — wire, don't
  re-derive.
- Tenancy on every new customer/nurse/partner read (owner-or-staff, 404-not-403); partner reads are
  identity-derived (`/me`-style), never id-parameterized.
- Admin endpoints: `[Authorize(ConstantPolicies.DynamicPermission)]` + correct fine-grained scope —
  Phase 1's tests must still pass.
- Signed URLs short-lived, fetched on demand (the b6/f15 contract). Sequential invoice numbering and
  Moadian states untouched.
- Per-REQ: migration if schema changes, swagger regenerated into `dev/contracts/openapi/`, tests per
  the refinement-3 pattern, `Status:` updated in `for-backend.md` in the same change.

## 6. Definition of Done

- Every REQ listed above is either delivered (status updated, tested) or explicitly re-deferred with
  a reason recorded in `for-backend.md` — no silent skips.
- `dotnet build Baya.sln` 0 new warnings · `dotnet test Baya.sln` green.
- Swagger regenerated; the refinement-3 §7 smoke calls for Tier B/C pass against the seeded world
  (customer cancel creates a refund; `centers/me` resolves the caller's center; admin
  queues/previews return their shapes under the right roles).

## 7. How to test (human)

Per tier, via Swagger against the demo world: cancel a paid booking as its customer (policy preview →
cancel → refund visible via by_booking); fetch BNPL options for an accepted request; open the
verification queue as super_admin and decide a step; preview + run a payout batch as finance; log in
as the seeded partner admin → `centers/me` chain returns their center only.

## 8. Hand off & document

- Tick H-15/H-16a in [issues.md](issues.md) (commit hashes per REQ group is fine).
- Update `for-backend.md` statuses, `mocks-registry.md`, `server/CLAUDE.md` if the project map moved.
- Write `dev/shared-working-context/reports/hardening-phase-4-report.md` + a handoff note listing
  exactly which flags Phase 5 may now flip.
