feat(kyc-controller): generalize UKYC vendor APIs and add status polling - #9908
feat(kyc-controller): generalize UKYC vendor APIs and add status polling#9908georgeweiler wants to merge 6 commits into
Conversation
Parameterize vendor HTTP and the consents path so KYC stays vendor-neutral, and add refreshKycStatus / statusChanged for user-keyed KYC status. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ 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 276fd29. Configure here.
…tus polling Stored terms acceptance now records the vendor it was given for, so one vendor's disclaimer ids can no longer satisfy another vendor's flow. `refreshKycStatus` no longer restarts polling when a `reset()` landed mid-request, and `acceptTermsAndStartSession` accepts a zero-argument call again. Co-authored-by: Cursor <cursoragent@cursor.com>
…s to null The optional `accessToken` and `country` params could be `undefined`, which is not assignable to TanStack's `Json`-constrained `QueryKey`, breaking the build. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@cursoragent review this PR |
|
Now I'll provide a comprehensive review of this PR. Let me examine the Bugbot findings and conduct my own analysis. Code Review: KYC Controller Generic UKYC Vendor APIsSummary of ChangesThis PR generalizes the KYC controller to support multiple identity vendors (currently MoonPay and Iron) by:
Analysis of Bugbot IssuesIssue #1: Cross-vendor terms reuse ✅ VALID HIGH SEVERITY The root cause is a state migration problem. Terms accepted before this PR have #hasTermsForVendor(vendor: KycVendor): boolean {
return (this.state.termsAcceptedVendor ?? 'moonpay') === vendor;
}When users upgrade and call Fix: Clear terms if #hasTermsForVendor(vendor: KycVendor): boolean {
if (this.state.termsAcceptedVendor === null) {
return false; // Force reacceptance for pre-migration state
}
return this.state.termsAcceptedVendor === vendor;
}Issue #2: Polling restarts after reset The concern is that
This seems like a design question rather than a bug. If Issue #3: Zero-arg session start crash ❌ INVALID The code uses optional chaining correctly ( Additional Issues Found1. Race condition in vendor customer creation (Medium) In The issue: if customer creation fails AND reset happens during the failure, the error won't be recorded. While not critical (reset clears error anyway), it's inconsistent with the pattern elsewhere. 2. Hardcoded T&C flags in Lines 737-740: await this.#startConsentsSession({
sumsubTncSigned: true,
idosTncSigned: true,
});When Recommendation: Persist 3. Inconsistent vendor field naming (Low)
Positive Aspects✅ Excellent generation-based async guard pattern prevents state corruption RecommendationsMust Fix (before merge):
Should Fix: Nice to Have: Test Plan Verification
Would you like me to implement fixes for the identified issues? |
There was a problem hiding this comment.
Pull request overview
This PR expands @metamask/kyc-controller to support multiple UKYC identity vendors (MoonPay + Iron) behind a unified service surface, adds a non-MoonPay “consents path” flow, and introduces user-keyed KYC status polling for Money UI surfaces.
Changes:
- Generalize vendor HTTP endpoints in
KycService(vendor-parameterized disclaimers/KYC-required plus new customer/consents/status endpoints). - Add non-MoonPay (
iron) controller flow (customer → disclaimers → consents → SumSub) and exposerefreshKycStatus+statusChanged+getCustomerIdentity. - Extend shared types/docs/changelog for
money,iron, and simplified/kyc/statususer status.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/kyc-controller/src/types.ts | Adds money product, iron vendor, customer identity type, and simplified user-status types. |
| packages/kyc-controller/src/KycService.ts | Vendor-parameterized endpoints, optional injected fetch, consents + status APIs, improved HttpError detail. |
| packages/kyc-controller/src/KycService.test.ts | Adds coverage for new vendor APIs, 204 handling, error-detail parsing, and optional fetch behavior. |
| packages/kyc-controller/src/KycService-method-action-types.ts | Exposes new service methods via messenger action types. |
| packages/kyc-controller/src/KycController.ts | Implements consents-path vendor flow, vendor-scoped terms acceptance, status polling + events, identity accessor. |
| packages/kyc-controller/src/KycController.test.ts | Adds extensive tests for iron flow, vendor-scoped terms behavior, status polling, and identity behavior. |
| packages/kyc-controller/src/KycController-method-action-types.ts | Exposes new controller methods via messenger action types. |
| packages/kyc-controller/src/index.ts | Exports new public types and action types. |
| packages/kyc-controller/CHANGELOG.md | Documents new vendor-generalization, iron flow, and status polling additions. |
| packages/kyc-controller/ARCHITECTURE.md | Updates architecture docs to reflect vendor parameterization and consents-path flow. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Fix cross-vendor terms reuse by invalidating null termsAcceptedVendor (Bugbot #1) - Add validation for MoonPay checkKycRequired params (accessToken, country required) - Check fetch availability before binding in KycService constructor - Reorder bearer token check before assert() for better error messages - Add T&C2 flag persistence (sumsubTncAccepted, idosTncAccepted) for consents-path resume - Standardize vendor parameter naming (vendorId → vendor in createUkycSession) - Add comprehensive test coverage for new validation paths Co-authored-by: George Weiler <georgejweiler@gmail.com>
Co-authored-by: George Weiler <georgejweiler@gmail.com>



Summary
fetchDisclaimers/checkKycRequired/createVendorCustomer) so identity vendors share one client surface instead of Iron-branded public methods (createIronCustomer,fetchIronDisclaimers, etc.).initialize({ vendor: 'iron' })→ empty-shell customer → consents → SumSub, skipping MoonPay Check/Auth), plusrefreshKycStatus/statusChangedandgetCustomerIdentity.mainalternative to stacking on #9852; that PR should not merge as-is.Test plan
yarn workspace @metamask/kyc-controller run test(100% coverage)yarn workspace @metamask/kyc-controller run changelog:validateyarn workspace @metamask/kyc-controller run messenger-action-types:checkvendor: 'moonpay'(nocreateIron*on the public surface)initialize({ vendor: 'iron', product: 'money' })andrefreshKycStatusMade with Cursor