add refinement phases
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# Refinement Phase 8 — External rails go real (SMS → trust/identity → money)
|
||||
|
||||
> **Mission:** swap the in-process mocks for real vendors, in impact order. **`ISmsSender` is launch-critical —
|
||||
> OTP delivery is a log statement today, so no real user can ever log in.** After that: identity/KYC/geocoding/
|
||||
> object-storage, then the money rails (PSP, BNPL, payout, tax). Each swap is an *adapter behind an existing
|
||||
> seam*, not a redesign — the seam shapes (idempotency keys, server-side re-verify, upsert-first webhooks) are
|
||||
> already the handler behavior.
|
||||
>
|
||||
> **Track:** backend (integrations) · **Depends on:** [Phase 6](refinement-phase-6-money-correctness.md) (do 6.1
|
||||
> before real BNPL/manual refunds), [Phase 7](refinement-phase-7-unattended-ops.md) (scheduler for the
|
||||
> reconciliation/Moadian polls) · **Unlocks:** a real, transacting platform
|
||||
> **Before you start, read [../../phases/_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md).**
|
||||
|
||||
## 1. Context
|
||||
|
||||
This phase **is** the server audit's **post-phase-5 (trust rails)** + **post-phase-6 (money rails)**. Full
|
||||
per-seam "make it real" steps live in **[../server/post-phase-backend-plan.md](../server/post-phase-backend-plan.md)**
|
||||
§ post-phase-5/6, the topology in **[../server/runtime-services.md](../server/runtime-services.md)** § 8–16, and
|
||||
the exact swap recipe (package → options → implement → register) for every seam in
|
||||
**[../../shared-working-context/reports/mocks-registry.md](../../shared-working-context/reports/mocks-registry.md)**.
|
||||
|
||||
Every vendor dependency is a deterministic in-process mock today. This phase makes them real, one seam at a
|
||||
time, config-selected — handlers don't change.
|
||||
|
||||
## 2. Required reading
|
||||
|
||||
- **[../server/post-phase-backend-plan.md](../server/post-phase-backend-plan.md)** § post-phase-5 (5.1–5.6) and
|
||||
§ post-phase-6 (6.1–6.6). These are the spec.
|
||||
- **[../../shared-working-context/reports/mocks-registry.md](../../shared-working-context/reports/mocks-registry.md)**
|
||||
— the per-row make-it-real steps (packages, config keys, the exact methods to implement).
|
||||
- **[../server/runtime-services.md](../server/runtime-services.md)** § 8–16 — vendor defaults + deployment
|
||||
notes per rail.
|
||||
|
||||
## 3. Scope — swap the seams, in impact order
|
||||
|
||||
### 3.1 Trust & identity rails (post-phase-5)
|
||||
- **5.1 — real SMS gateway behind `ISmsSender` (LAUNCH-CRITICAL, do first).** Kavenegar/Ghasedak/SMS.ir;
|
||||
template/pattern OTP send; `Seams:Sms:{ApiKey,SenderLine,BaseUrl}`. This **replaces the Development OTP-in-logs
|
||||
bridge** from [Phase 0](refinement-phase-0-bring-up.md). Keep the per-phone resend window + `otp` rate policy.
|
||||
**No real user can log in until this ships.**
|
||||
- **5.2 — real Shahkar + e-KYC** (`IShahkarVerifier`, `IIdentityKycProvider`) via a Finnotech-class bridge; keep
|
||||
shared-SIM a handled failure; persist real vendor refs.
|
||||
- **5.3 — real استعلام شبا** (`IBankAccountOwnershipVerifier`) — the b13 first-payout gate; bundle with 5.2's
|
||||
vendor family.
|
||||
- **5.4 — real geocoder** (`IGeocoder`) behind Neshan (address coordinates / EVV distance). REQ-008's user pin
|
||||
reduces but doesn't remove the need.
|
||||
- **5.5 — real object storage** (`IObjectStorage`) — MinIO/S3/ArvanCloud with presigned PUT/GET; makes the b6
|
||||
signed-URL contract and REQ-006 avatars real (local disk + `file://` today).
|
||||
- **5.6 — document manual MoH/INO/eNamad** (`ICredentialVerifier`, `ILicenseVerificationService`) as the
|
||||
intended MVP state (no public B2B API exists) — mark the registry rows so they stop reading as debt.
|
||||
|
||||
### 3.2 Money rails (post-phase-6) — **do [Phase 6](refinement-phase-6-money-correctness.md) 6.1 first**
|
||||
- **6.1 — real PSP/IPG + webhook signatures + تسهیم** (`IPaymentProvider`, `IWebhookVerifier`,
|
||||
`ISettlementSplitProvider`) — ZarinPal/Sadad/Vandar/Jibit; mandatory server-side `verify` re-check; per-provider
|
||||
HMAC on the raw body; provider factory per gateway row; persist full responses. (High risk — Shaparak
|
||||
certification lead time.)
|
||||
- **6.2 — real BNPL adapters** (`IBnplProvider`/`IBnplProviderResolver`) — SnappPay/Digipay; Toman↔IRR only via
|
||||
`ICurrencyNormalizer`; per-contract commission from the settle response. (Do Phase 6 §6.1 revert-clearing
|
||||
first.) Also settle the **`balinyaar` in-house provider** decision from [Phase 3](refinement-phase-3-contract-batch.md) REQ-022.
|
||||
- **6.3 — real PAYA/SATNA payout rail** (`IBankTransferProvider`) + the async `submitted → paid/failed`
|
||||
reconciliation callback (the mock settles instantly). Wire the weekly trigger via
|
||||
[Phase 7](refinement-phase-7-unattended-ops.md).
|
||||
- **6.4 — remove `IPaymentCaptureSimulator`** (b10's real capture superseded it; move it into the test host or
|
||||
gate to Development/Testing).
|
||||
- **6.5 — real Moadian submission + reconciliation** (`IMoadianClient`) — enrollment + signing cert; the
|
||||
`pending → submitted → registered/failed` poll registered under [Phase 7](refinement-phase-7-unattended-ops.md).
|
||||
- **6.6 — decide the partner-center settlement rail** (resolver exists; no money path pays a center yet) — a
|
||||
product decision, then optionally a center-settlement ledger account + payout reusing the b13 machinery.
|
||||
|
||||
## 4. Mocks & seams
|
||||
|
||||
This phase retires the vendor mocks one row at a time (each config-selected, so a partial rollout is fine —
|
||||
e.g. real SMS + real geocoder while payments stay mocked in a pre-launch environment). The manual MoH/INO/eNamad
|
||||
seams (5.6) **stay** as the intended MVP mechanism.
|
||||
|
||||
## 5. Critical rules
|
||||
|
||||
- **5.1 SMS is the gate to a first real session** — sequence it first; nothing else matters if no one can log
|
||||
in. Once it ships, **the OTP must never be logged** (remove the Development log/echo bridge).
|
||||
- **Never trust a payment callback alone** — the mandatory server-side `verify` re-check + per-provider HMAC are
|
||||
non-negotiable on the real PSP swap.
|
||||
- **Money-rail swaps are irreversible-transfer territory** — the DB uniques (`UNIQUE(booking_id)` payout link,
|
||||
filtered payment uniques), forward-only status machines, and idempotency keys are the backstops; keep them.
|
||||
- **Do [Phase 6](refinement-phase-6-money-correctness.md) 6.1 before real BNPL/manual refunds** or real money
|
||||
strands ledger state.
|
||||
- **Currency conversion happens only in the adapter** via `ICurrencyNormalizer` — never internally.
|
||||
|
||||
## 6. Definition of Done
|
||||
|
||||
- [ ] Real SMS delivers OTPs to a real handset; the Development OTP-in-logs/echo bridge is removed; a real user
|
||||
can complete login end-to-end.
|
||||
- [ ] Each swapped seam is config-selected, persists real vendor responses, and its handler is unchanged;
|
||||
`dotnet build`/`dotnet test` green.
|
||||
- [ ] Money rails: a real card capture confirms via server-side verify + signature; a real payout reconciles
|
||||
`submitted → paid`; an invoice registers with Moadian; the BNPL revert-clearing (Phase 6) completes.
|
||||
- [ ] `IPaymentCaptureSimulator` is out of the production registration.
|
||||
- [ ] The registry rows for swapped seams are 🟢; manual-review seams (5.6) are marked "manual = intended MVP".
|
||||
|
||||
## 7. How to test
|
||||
|
||||
- Request an OTP → receive a real SMS → log in (no code in the logs).
|
||||
- (Sandbox) run a real card payment → server-side verify + signature check pass → booking converts; a signed
|
||||
webhook with a bad signature is rejected.
|
||||
- Run a payout batch against the real rail → it moves to `submitted`, then the async callback flips it to
|
||||
`paid`; an invoice reaches `registered` at Moadian.
|
||||
|
||||
## 8. Hand off & document
|
||||
|
||||
- Update `server/CLAUDE.md` (seam registrations), the mocks-registry (rows → 🟢), and
|
||||
[../server/runtime-services.md](../server/runtime-services.md) (which services are now live). Record vendor
|
||||
accounts/config keys in the deploy docs (never the secrets). Save a memory note per rail that goes live.
|
||||
Reference in New Issue
Block a user