Phase 6 — Idenfy KYC integration #7

Open
opened 2026-05-21 12:09:01 +00:00 by mik-tf · 0 comments
Owner

Phase 6 — Idenfy KYC integration

Tracks the s2-008 work item: lift the production-validated Idenfy KYC integration from the freezone backend into hero_onboarding, gate Stripe/ClickPesa top-up on KYC completion, and add an admin view.

Scope

  1. Schema — new crates/hero_onboarding_schema/schemas/onboarding/kyc.oschema (lifted from the consolidated freezone.oschema, trimmed to hero_onboarding scope — no resident_id, no public_key, since hero_onboarding has no DigitalResident or per-user keypair). Adds KycStatus = "pending" | "in_progress" | "completed" | "failed" | "expired" enum + KycSession rootobject. Modifies user.oschema to add kyc_status: KycStatus and kyc_verified_at?: otime.

  2. Backend — new crates/hero_onboarding_server/src/kyc.rs (KycProvider async trait + IdenfyProvider impl). Translation of freezone providers/kyc.rs: ureq sync → reqwest async, preserves HMAC-SHA256 webhook signature verification, preserves client_id timestamp-suffix trick (allows re-verification after KYC reset; webhook parser strips the suffix to recover the original client_id), preserves demo + dev_mode escape hatches.

  3. Server routes — gate /payment/intent on User.kyc_status == Completed (renders error page with link to /kyc/start if blocked). Adds GET /kyc/start, GET /kyc/return, POST /kyc/reset, POST /webhooks/idenfy, GET /admin/list-kyc-sessions. Idenfy webhook updates User.kyc_status + User.kyc_verified_at + KycSession.status on overall == "approved" | "suspected".

  4. Admin — new /kyc view in hero_onboarding_admin reading /admin/list-users + /admin/list-kyc-sessions.

  5. CLI — extend FORWARDED_ENV in hero_onboarding with IDENFY_API_KEY, IDENFY_API_SECRET, IDENFY_API_URL, IDENFY_WEBHOOK_SECRET, IDENFY_DEV_MODE.

  6. Smokescripts/smoke_kyc.sh runs against IDENFY_DEV_MODE=true (sidesteps live Idenfy sandbox dependency): payment_intent before KYC → blocked, /kyc/start → KycSession persisted, HMAC-signed mock callback → User.kyc_status = Completed, payment_intent after KYC → 302 to Stripe, admin-gating on /admin/list-kyc-sessions.

KYC-gating order (D-15 candidate)

Decision: gate-at-paid-resource (recommended posture per prompt2.md §3). KYC fires when the user clicks Top Up, not at first login. Rationale:

  • Keeps the demo's first-touch friction-free — users can browse the dashboard, log in via mycelium, see their (zero) balance without an identity check.
  • Mirrors the freezone wallet flow: anonymous account creation, KYC at the point of first spend.
  • Lighter migration cost: pre-existing User rows default to kyc_status: Pending; no flag-day re-verification.

Revisability cost if revisited later (gate-at-first-login):

  • ~10 LOC in the login flow to redirect to /kyc/start after challenge verification
  • Migration of pre-existing User rows from PendingRequired (if we add that state) is in-place oschema edit
  • The data-model change is just the gate location; User.kyc_status already exists

Schema-file-shape diff (vs meta-issue claim)

The meta-issue claimed kyc.oschema exists as a standalone file in the freezone backend. It doesn't — the freezone schema is consolidated into a single freezone.oschema file. The KycStatus + KycSession type definitions are verbatim-copyable; the file layout differs. hero_onboarding follows a per-domain layout (one rootobject per file like billing.oschema, user.oschema, etc.), so this Phase 6 ships kyc.oschema as a fresh file containing only KYC-related types.

Trimmed-out fields vs freezone:

  • resident_id — hero_onboarding has no DigitalResident
  • public_key — hero_onboarding has no per-user keypair
  • idenfy_scan_refidenfy_scan_ref? (optional, kept)
  • updated_at: otimelast_run_at: otime (per s2-007 Phase B finding 2 — updated_at: otime silently collides with autogen updated_at: u64)

Acceptance gate

  • cargo check --workspace
  • cargo test (+~3 new: schema CRUD auto for KycSession + 1 webhook parser unit test + 1 client_id-suffix-strip unit test)
  • lab build --release --install --workspace VICTORY 3/3
  • lab infocheck 3/3 clean
  • cargo fmt --check clean
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • scripts/smoke_kyc.sh GREEN

Parent: lhumina_code/hero_onboarding#1

# Phase 6 — Idenfy KYC integration Tracks the s2-008 work item: lift the production-validated Idenfy KYC integration from the freezone backend into `hero_onboarding`, gate Stripe/ClickPesa top-up on KYC completion, and add an admin view. ## Scope 1. **Schema** — new `crates/hero_onboarding_schema/schemas/onboarding/kyc.oschema` (lifted from the consolidated `freezone.oschema`, trimmed to hero_onboarding scope — no `resident_id`, no `public_key`, since hero_onboarding has no DigitalResident or per-user keypair). Adds `KycStatus = "pending" | "in_progress" | "completed" | "failed" | "expired"` enum + `KycSession` rootobject. Modifies `user.oschema` to add `kyc_status: KycStatus` and `kyc_verified_at?: otime`. 2. **Backend** — new `crates/hero_onboarding_server/src/kyc.rs` (`KycProvider` async trait + `IdenfyProvider` impl). Translation of freezone `providers/kyc.rs`: ureq sync → reqwest async, preserves HMAC-SHA256 webhook signature verification, preserves `client_id` timestamp-suffix trick (allows re-verification after KYC reset; webhook parser strips the suffix to recover the original `client_id`), preserves `demo` + `dev_mode` escape hatches. 3. **Server routes** — gate `/payment/intent` on `User.kyc_status == Completed` (renders error page with link to `/kyc/start` if blocked). Adds `GET /kyc/start`, `GET /kyc/return`, `POST /kyc/reset`, `POST /webhooks/idenfy`, `GET /admin/list-kyc-sessions`. Idenfy webhook updates `User.kyc_status` + `User.kyc_verified_at` + `KycSession.status` on `overall == "approved" | "suspected"`. 4. **Admin** — new `/kyc` view in `hero_onboarding_admin` reading `/admin/list-users` + `/admin/list-kyc-sessions`. 5. **CLI** — extend `FORWARDED_ENV` in `hero_onboarding` with `IDENFY_API_KEY`, `IDENFY_API_SECRET`, `IDENFY_API_URL`, `IDENFY_WEBHOOK_SECRET`, `IDENFY_DEV_MODE`. 6. **Smoke** — `scripts/smoke_kyc.sh` runs against `IDENFY_DEV_MODE=true` (sidesteps live Idenfy sandbox dependency): payment_intent before KYC → blocked, /kyc/start → KycSession persisted, HMAC-signed mock callback → User.kyc_status = Completed, payment_intent after KYC → 302 to Stripe, admin-gating on /admin/list-kyc-sessions. ## KYC-gating order (D-15 candidate) **Decision: gate-at-paid-resource** (recommended posture per `prompt2.md` §3). KYC fires when the user clicks Top Up, not at first login. Rationale: - Keeps the demo's first-touch friction-free — users can browse the dashboard, log in via mycelium, see their (zero) balance without an identity check. - Mirrors the freezone wallet flow: anonymous account creation, KYC at the point of first spend. - Lighter migration cost: pre-existing User rows default to `kyc_status: Pending`; no flag-day re-verification. Revisability cost if revisited later (gate-at-first-login): - ~10 LOC in the login flow to redirect to `/kyc/start` after challenge verification - Migration of pre-existing User rows from `Pending` → `Required` (if we add that state) is in-place oschema edit - The data-model change is just the gate location; `User.kyc_status` already exists ## Schema-file-shape diff (vs meta-issue claim) The meta-issue claimed `kyc.oschema` exists as a standalone file in the freezone backend. **It doesn't** — the freezone schema is consolidated into a single `freezone.oschema` file. The KycStatus + KycSession **type definitions** are verbatim-copyable; the **file layout** differs. hero_onboarding follows a per-domain layout (one rootobject per file like `billing.oschema`, `user.oschema`, etc.), so this Phase 6 ships `kyc.oschema` as a fresh file containing only KYC-related types. Trimmed-out fields vs freezone: - `resident_id` — hero_onboarding has no DigitalResident - `public_key` — hero_onboarding has no per-user keypair - `idenfy_scan_ref` → `idenfy_scan_ref?` (optional, kept) - `updated_at: otime` → `last_run_at: otime` (per s2-007 Phase B finding 2 — `updated_at: otime` silently collides with autogen `updated_at: u64`) ## Acceptance gate - `cargo check --workspace` - `cargo test` (+~3 new: schema CRUD auto for KycSession + 1 webhook parser unit test + 1 client_id-suffix-strip unit test) - `lab build --release --install --workspace` VICTORY 3/3 - `lab infocheck` 3/3 clean - `cargo fmt --check` clean - `cargo clippy --workspace --all-targets -- -D warnings` clean - `scripts/smoke_kyc.sh` GREEN Parent: lhumina_code/hero_onboarding#1
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_onboarding#7
No description provided.