Skip to content

feat(kyc-controller): add VBA onboarding KYC getters - #10279

Open
georgeweiler wants to merge 6 commits into
mainfrom
kyc-vba-api-stubs
Open

georgeweiler wants to merge 6 commits into
mainfrom
kyc-vba-api-stubs

Conversation

@georgeweiler

@georgeweiler georgeweiler commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds VBA / Pix onboarding support to KycController: the exported enums and getters Mobile routes on, plus account-backed terms steps and a hydrate refresh so each onboarding stage reflects the customer's account rather than only device-local state.

Enums + getters

  • export KycVendor, KycProvider, and KycStatus const objects (and matching types)
  • isCustomerCreated(vendor) → persisted vendorCustomerIds written on successful createVendorCustomer (survives reset(), cleared by clearState())
  • hasCompletedVendorTerms(vendor) → backend required-signings after refresh, falling back to local vendorDisclaimersAccepted
  • hasCompletedProviderTerms(sumsub) → persisted providerDisclaimersAccepted.sumsub
  • getKycStatus(vendor) overload → maps persisted userStatusKycStatus

Account-backed terms (server-side)

  • acceptVendorTerms() posts vendor signings to the account (submitVendorDisclaimers), then records local acceptance — never claims acceptance the account does not hold
  • acceptProviderTerms() creates (or reuses) the UKYC session and posts idOS/SumSub session consents (submitSessionDisclaimers) without launching SumSub, recording locally only after the account write succeeds
  • new KycService.fetchRequiredSignings (GET /vendors/{vendor}/customers/{id}/required-signings) backs vendor-terms completion

Hydrate + routing

  • refreshVbaOnboardingStatus() pulls required-signings and GET /kyc/status (each fetch soft-fails independently) so hydration reads the account
  • getKycStatus(vendor) treats backend pending as NOT_STARTED until a persisted sumSubSubmitted flag is set when SumSub actually completes — so "session created, no documents yet" routes to the SumSub screen, not KYC-pending
  • persist activeVendor so a resumed Iron flow keeps its vendor context across reloads (avoids sending empty MoonPay session metadata)

This unblocks ramps VBA hydration so Mobile can call hydrateVbaOnboarding and advance stages from the customer's real KYC progress.

Follow-ups for KYC owners

  • make getKycStatus(vendor) truly vendor-scoped if needed
  • confirm terminal-failureREJECTED vs a dedicated EDD stage
  • optional network-backed customer lookup for cross-device cold starts without a persisted id
  • backend finalStatus cannot distinguish "awaiting documents" from "under review", hence the device-persisted sumSubSubmitted gate — revisit if a session-level signal becomes available

Testing

  • yarn workspace @metamask/kyc-controller run messenger-action-types:generate
  • yarn workspace @metamask/kyc-controller run messenger-action-types:check
  • yarn workspace @metamask/kyc-controller exec jest --no-coverage src/KycController.test.ts src/KycService.test.ts (292 passing)
  • yarn eslint on changed KYC files
  • yarn workspace @metamask/kyc-controller run build
  • yarn workspace @metamask/kyc-controller run changelog:validate

🤖 Generated with Claude Code


Note

Medium Risk
Changes KYC onboarding persistence, terms acceptance order, and status routing for VBA flows; mistakes could mis-route users or claim terms the account does not hold, though the design fails closed on backend errors.

Overview
Adds VBA / Pix onboarding support to KycController: exported KycVendor, KycProvider, and KycStatus enums plus messenger getters Mobile can use to drive multi-step onboarding from persisted and refreshed account state.

New flow APIs split terms from the monolithic session path: acceptVendorTerms posts vendor signings to the account before recording locally; acceptProviderTerms creates or reuses a UKYC session, posts idOS/SumSub consents without launching SumSub, and lets startSumSub reuse that session (including after reload via persisted ukycSessionId). refreshVbaOnboardingStatus hydrates vendor-terms completion from fetchRequiredSignings and KYC status from GET /kyc/status, with independent soft-fail behavior.

Persistence and routing changes include vendorCustomerIds and activeVendor surviving cold starts and reset(), isCustomerCreated, vendor overload on getKycStatus with a sumSubSubmitted gate so backend pending before document capture maps to NOT_STARTED rather than KYC-pending, and hasCompletedVendorTerms / hasCompletedProviderTerms for stage checks.

Reviewed by Cursor Bugbot for commit 2e9e869. Bugbot is set up for automated code reviews on this repo. Configure here.

Expose KycVendor/KycProvider/KycStatus const objects and noop messenger
methods so ramps can hydrate VBA onboarding without local KYC contracts.

Co-authored-by: Cursor <cursoragent@cursor.com>
@georgeweiler
georgeweiler requested review from a team as code owners September 17, 2026 02:17
@georgeweiler
georgeweiler deployed to default-branch September 17, 2026 02:17 — with GitHub Actions Active
Co-authored-by: Cursor <cursoragent@cursor.com>
georgeweiler added a commit that referenced this pull request Sep 17, 2026
Drop local KycVendor/KycProvider/KycStatus stubs and action types in favor
of the package exports introduced in #10279.

Co-authored-by: Cursor <cursoragent@cursor.com>
georgeweiler and others added 2 commits September 16, 2026 20:25
Co-authored-by: Cursor <cursoragent@cursor.com>
Persist vendorCustomerIds from createVendorCustomer, and implement
isCustomerCreated / hasCompletedVendorTerms / hasCompletedProviderTerms /
getKycStatus(vendor) against real controller state so ramps hydration can
advance past EmailOtpRequired.

Co-authored-by: Cursor <cursoragent@cursor.com>
@georgeweiler georgeweiler changed the title feat(kyc-controller): add VBA onboarding stub getters feat(kyc-controller): add VBA onboarding KYC getters Sep 17, 2026
…ydrate

Turn the VBA onboarding terms steps into account-backed operations and make
hydration re-read status from the vendor account:

- acceptVendorTerms / acceptProviderTerms now persist to the account
  (submitVendorDisclaimers; UKYC session + submitSessionDisclaimers) before
  recording locally, so acceptance is never claimed without a backend write
- add KycService.fetchRequiredSignings (GET required-signings) and
  KycController.refreshVbaOnboardingStatus so hydrate reflects the account's
  vendor-terms and KYC status rather than only device-local state
- gate getKycStatus on a persisted sumSubSubmitted flag so the session-created
  'pending' routes to the SumSub screen until documents are actually submitted
- persist activeVendor so a resumed flow keeps its Iron vendor context across
  reloads (avoids sending empty MoonPay session metadata)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/kyc-controller/src/KycController.ts
Comment thread packages/kyc-controller/src/KycController.ts
* that signal's last-known value rather than throwing, so hydration can still
* resolve a stage from whatever is current.
*/
async refreshVbaOnboardingStatus(): Promise<void> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to something more generic and leave here? Or pull this logic into ramps controller?

- persist `ukycSessionId` so a reload reuses the consented UKYC session instead
  of `startSumSub` creating a fresh, unconsented one (Bugbot: provider session
  dropped after reload)
- `acceptVendorTerms` captures the flow generation and writes via
  `#updateIfCurrent`, so a concurrent `reset()` no longer lands acceptance on an
  idle controller (Bugbot: vendor terms write ignores reset)
- regenerate `KycController-method-action-types` for the updated method JSDoc
- use `expect(await ...)` instead of the restricted `.resolves` matcher
- apply oxfmt formatting

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2e9e869. Configure here.

this.#applyUpdate((state) => {
state.sumsub.sessionId = state.ukycSessionId;
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reload skips vendor-processing short-circuit

Medium Severity

Restoring sumsub.sessionId from persisted ukycSessionId makes startSumSub skip session creation, the only place vendorProcessing is detected. After a reload, an applicant the relay already approved while the vendor is still finalizing is sent into the SumSub SDK instead of stopping with the vendor-processing status.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2e9e869. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant