# 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.