@metamask-previews/kyc-controller 0.0.0-preview-e57e5c3dc → 0.0.0-preview-c34a16a37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +1 -1
  3. package/dist/KycController-method-action-types.cjs.map +1 -1
  4. package/dist/KycController-method-action-types.d.cts +46 -1
  5. package/dist/KycController-method-action-types.d.cts.map +1 -1
  6. package/dist/KycController-method-action-types.d.mts +46 -1
  7. package/dist/KycController-method-action-types.d.mts.map +1 -1
  8. package/dist/KycController-method-action-types.mjs.map +1 -1
  9. package/dist/KycController.cjs +366 -10
  10. package/dist/KycController.cjs.map +1 -1
  11. package/dist/KycController.d.cts +85 -3
  12. package/dist/KycController.d.cts.map +1 -1
  13. package/dist/KycController.d.mts +85 -3
  14. package/dist/KycController.d.mts.map +1 -1
  15. package/dist/KycController.mjs +366 -10
  16. package/dist/KycController.mjs.map +1 -1
  17. package/dist/KycService-method-action-types.cjs.map +1 -1
  18. package/dist/KycService-method-action-types.d.cts +56 -1
  19. package/dist/KycService-method-action-types.d.cts.map +1 -1
  20. package/dist/KycService-method-action-types.d.mts +56 -1
  21. package/dist/KycService-method-action-types.d.mts.map +1 -1
  22. package/dist/KycService-method-action-types.mjs.map +1 -1
  23. package/dist/KycService.cjs +167 -10
  24. package/dist/KycService.cjs.map +1 -1
  25. package/dist/KycService.d.cts +75 -2
  26. package/dist/KycService.d.cts.map +1 -1
  27. package/dist/KycService.d.mts +75 -2
  28. package/dist/KycService.d.mts.map +1 -1
  29. package/dist/KycService.mjs +168 -11
  30. package/dist/KycService.mjs.map +1 -1
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +5 -5
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.mts +5 -5
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/types.cjs.map +1 -1
  38. package/dist/types.d.cts +48 -10
  39. package/dist/types.d.cts.map +1 -1
  40. package/dist/types.d.mts +48 -10
  41. package/dist/types.d.mts.map +1 -1
  42. package/dist/types.mjs.map +1 -1
  43. package/dist/ukyc/deriveClientMaterial.cjs +1 -1
  44. package/dist/ukyc/deriveClientMaterial.cjs.map +1 -1
  45. package/dist/ukyc/deriveClientMaterial.mjs +1 -1
  46. package/dist/ukyc/deriveClientMaterial.mjs.map +1 -1
  47. package/dist/ukyc/storageAccessToken.cjs +1 -1
  48. package/dist/ukyc/storageAccessToken.cjs.map +1 -1
  49. package/dist/ukyc/storageAccessToken.d.cts.map +1 -1
  50. package/dist/ukyc/storageAccessToken.d.mts.map +1 -1
  51. package/dist/ukyc/storageAccessToken.mjs +1 -1
  52. package/dist/ukyc/storageAccessToken.mjs.map +1 -1
  53. package/dist/ukyc/wrappedRelayPayload.cjs.map +1 -1
  54. package/dist/ukyc/wrappedRelayPayload.d.cts.map +1 -1
  55. package/dist/ukyc/wrappedRelayPayload.d.mts.map +1 -1
  56. package/dist/ukyc/wrappedRelayPayload.mjs.map +1 -1
  57. package/package.json +1 -1
@@ -9,12 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _KycController_instances, _KycController_sumsubLauncher, _KycController_keypair, _KycController_authClientToken, _KycController_generation, _KycController_sessionStatusPollIntervalMs, _KycController_pollTimer, _KycController_pollToken, _KycController_localUserSecretStore, _KycController_createSession, _KycController_clearAcceptedTerms, _KycController_handleCheckOutcome, _KycController_handleAuthOutcome, _KycController_continueAfterAuthentication, _KycController_requireTermsReacceptance, _KycController_startSessionStatusPolling, _KycController_pollSessionStatusOnce, _KycController_stopPolling, _KycController_updateIfCurrent, _KycController_applyUpdate, _KycController_fail;
12
+ var _KycController_instances, _KycController_sumsubLauncher, _KycController_keypair, _KycController_authClientToken, _KycController_generation, _KycController_sessionStatusPollIntervalMs, _KycController_pollTimer, _KycController_pollToken, _KycController_userStatusPollIntervalMs, _KycController_userStatusPollTimer, _KycController_userStatusPollToken, _KycController_localUserSecretStore, _KycController_startIronSession, _KycController_createSession, _KycController_clearAcceptedTerms, _KycController_handleCheckOutcome, _KycController_handleAuthOutcome, _KycController_continueAfterAuthentication, _KycController_requireTermsReacceptance, _KycController_fetchAndApplyUserStatus, _KycController_applyUserStatus, _KycController_ensureUserStatusPolling, _KycController_stopUserStatusPolling, _KycController_startSessionStatusPolling, _KycController_pollSessionStatusOnce, _KycController_stopPolling, _KycController_updateIfCurrent, _KycController_applyUpdate, _KycController_fail;
13
13
  import { BaseController } from "@metamask/base-controller";
14
14
  import { x25519 } from "@noble/curves/ed25519";
15
15
  import { decryptCredentials, generateKeyPair } from "./crypto.mjs";
16
- import { deriveClientMaterial } from "./ukyc/deriveClientMaterial.mjs";
17
16
  import { toBase64Url } from "./encoding.mjs";
17
+ import { deriveClientMaterial } from "./ukyc/deriveClientMaterial.mjs";
18
18
  import { verifyJwtChain } from "./ukyc/jwtChain.mjs";
19
19
  import { getOrCreateLocalUserSecret } from "./ukyc/localUserSecret.mjs";
20
20
  import { signStorageAccessToken } from "./ukyc/storageAccessToken.mjs";
@@ -84,6 +84,12 @@ const SUCCESSFUL_SESSION_STATUSES = new Set([
84
84
  // to do, so the sub-flow stops before launching the SDK and surfaces this
85
85
  // message.
86
86
  const VENDOR_PROCESSING_MESSAGE = 'Your KYC has been submitted and is being processed by the vendor.';
87
+ // UKYC / relay error indicating the applicant already finished KYC. Mapped to
88
+ // the simplified `completed` user status for the Money toast surface.
89
+ const SESSION_NOT_IN_VALID_STATE = 'session_not_in_valid_state';
90
+ // How often to refresh the user-keyed `GET /kyc/status` while the simplified
91
+ // status is still `pending`. Overridable via the constructor.
92
+ const DEFAULT_USER_STATUS_POLL_INTERVAL_MS = 15000;
87
93
  const kycControllerMetadata = {
88
94
  phase: {
89
95
  includeInDebugSnapshot: true,
@@ -157,6 +163,12 @@ const kycControllerMetadata = {
157
163
  persist: false,
158
164
  usedInUi: false,
159
165
  },
166
+ activeVendor: {
167
+ includeInDebugSnapshot: true,
168
+ includeInStateLogs: true,
169
+ persist: false,
170
+ usedInUi: true,
171
+ },
160
172
  activeProduct: {
161
173
  includeInDebugSnapshot: true,
162
174
  includeInStateLogs: true,
@@ -175,6 +187,24 @@ const kycControllerMetadata = {
175
187
  persist: true,
176
188
  usedInUi: false,
177
189
  },
190
+ userStatus: {
191
+ includeInDebugSnapshot: true,
192
+ includeInStateLogs: true,
193
+ persist: true,
194
+ usedInUi: true,
195
+ },
196
+ userStatusSumsubSessionId: {
197
+ includeInDebugSnapshot: false,
198
+ includeInStateLogs: false,
199
+ persist: true,
200
+ usedInUi: true,
201
+ },
202
+ userStatusErrorCode: {
203
+ includeInDebugSnapshot: true,
204
+ includeInStateLogs: true,
205
+ persist: true,
206
+ usedInUi: true,
207
+ },
178
208
  sumsub: {
179
209
  includeInDebugSnapshot: false,
180
210
  includeInStateLogs: false,
@@ -201,9 +231,13 @@ export function getDefaultKycControllerState() {
201
231
  sessionToken: null,
202
232
  accessToken: null,
203
233
  moonpayCustomerId: null,
234
+ activeVendor: 'moonpay',
204
235
  activeProduct: null,
205
236
  kycRequiredByProduct: {},
206
237
  lastCheckedAt: null,
238
+ userStatus: null,
239
+ userStatusSumsubSessionId: null,
240
+ userStatusErrorCode: null,
207
241
  sumsub: {
208
242
  status: 'idle',
209
243
  result: null,
@@ -218,6 +252,7 @@ const MESSENGER_EXPOSED_METHODS = [
218
252
  'initialize',
219
253
  'loadDisclaimers',
220
254
  'acceptTermsAndStartSession',
255
+ 'createIronCustomer',
221
256
  'clearSavedTerms',
222
257
  'handleFrameMessage',
223
258
  'buildCheckFrameUrl',
@@ -225,6 +260,8 @@ const MESSENGER_EXPOSED_METHODS = [
225
260
  'buildResetFrameUrl',
226
261
  'checkKycRequired',
227
262
  'getKycStatus',
263
+ 'getCustomerIdentity',
264
+ 'refreshKycStatus',
228
265
  'startSumSub',
229
266
  'getSessionStatus',
230
267
  'reset',
@@ -247,8 +284,10 @@ export class KycController extends BaseController {
247
284
  * @param options.sumsubLauncher - The platform SumSub launcher adapter.
248
285
  * @param options.sessionStatusPollIntervalMs - How often to poll the UKYC
249
286
  * session status after the SumSub SDK completes.
287
+ * @param options.userStatusPollIntervalMs - How often to refresh the
288
+ * user-keyed KYC status while it is still `pending`.
250
289
  */
251
- constructor({ messenger, state, sumsubLauncher, sessionStatusPollIntervalMs = DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS, }) {
290
+ constructor({ messenger, state, sumsubLauncher, sessionStatusPollIntervalMs = DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS, userStatusPollIntervalMs = DEFAULT_USER_STATUS_POLL_INTERVAL_MS, }) {
252
291
  super({
253
292
  messenger,
254
293
  metadata: kycControllerMetadata,
@@ -279,8 +318,15 @@ export class KycController extends BaseController {
279
318
  * let an already-awaiting request finish and reschedule.
280
319
  */
281
320
  _KycController_pollToken.set(this, 0);
321
+ /** Interval, in milliseconds, between user-keyed status polls. */
322
+ _KycController_userStatusPollIntervalMs.set(this, void 0);
323
+ /** Handle for the scheduled next user-status poll, or `null`. */
324
+ _KycController_userStatusPollTimer.set(this, null);
325
+ /** Monotonic token for the user-status poll loop (see `#pollToken`). */
326
+ _KycController_userStatusPollToken.set(this, 0);
282
327
  __classPrivateFieldSet(this, _KycController_sumsubLauncher, sumsubLauncher, "f");
283
328
  __classPrivateFieldSet(this, _KycController_sessionStatusPollIntervalMs, sessionStatusPollIntervalMs, "f");
329
+ __classPrivateFieldSet(this, _KycController_userStatusPollIntervalMs, userStatusPollIntervalMs, "f");
284
330
  __classPrivateFieldSet(this, _KycController_keypair, generateKeyPair(), "f");
285
331
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
286
332
  }
@@ -295,6 +341,8 @@ export class KycController extends BaseController {
295
341
  * authentication completes (and chains into document verification when KYC
296
342
  * is required). When omitted, the flow stops at `form` and the consumer must
297
343
  * call `checkKycRequired` manually.
344
+ * @param params.vendor - Identity vendor for this flow. Pass `iron` for the
345
+ * Money/VBA path (no MoonPay Check/Auth frames). Defaults to `moonpay`.
298
346
  */
299
347
  async initialize(params) {
300
348
  // A repeat `initialize` while a session flow is already in progress must
@@ -305,6 +353,7 @@ export class KycController extends BaseController {
305
353
  if (IN_PROGRESS_PHASES.includes(this.state.phase)) {
306
354
  return;
307
355
  }
356
+ const vendor = params?.vendor ?? 'moonpay';
308
357
  // `initialize` starts a fresh flow, so `activeProduct` is always reset to
309
358
  // this call's product (or `null`). Otherwise a prior run's product could
310
359
  // linger and cause `#continueAfterAuthentication` to auto-run the check /
@@ -313,6 +362,13 @@ export class KycController extends BaseController {
313
362
  if (params?.email) {
314
363
  state.email = params.email;
315
364
  }
365
+ state.activeVendor = vendor;
366
+ // `moonpayCustomerId` is only ever issued by the MoonPay Check / Auth
367
+ // frames. Leaving it set while the flow switches to another vendor would
368
+ // make `getCustomerIdentity` report a MoonPay id under the wrong vendor.
369
+ if (vendor !== 'moonpay') {
370
+ state.moonpayCustomerId = null;
371
+ }
316
372
  state.activeProduct = params?.product ?? null;
317
373
  });
318
374
  // Capture the flow generation so a `reset()` landing while the async
@@ -329,10 +385,36 @@ export class KycController extends BaseController {
329
385
  catch {
330
386
  // Ignore; disclaimers loading will surface a country error if needed.
331
387
  }
388
+ // Iron: create the empty-shell customer before T&C (offsite decision).
389
+ if (vendor === 'iron' && this.state.email) {
390
+ try {
391
+ await this.messenger.call('KycService:createIronCustomer', {
392
+ email: this.state.email,
393
+ });
394
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
395
+ return;
396
+ }
397
+ }
398
+ catch (error) {
399
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
400
+ return;
401
+ }
402
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Iron customer creation failed: ${String(error)}`);
403
+ return;
404
+ }
405
+ }
332
406
  const hasTerms = Boolean(this.state.termsAcceptedAt) &&
333
407
  this.state.acceptedDisclaimerIds.length > 0;
334
408
  if (hasTerms && this.state.email) {
335
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
409
+ if (vendor === 'iron') {
410
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startIronSession).call(this, {
411
+ sumsubTncSigned: true,
412
+ idosTncSigned: true,
413
+ });
414
+ }
415
+ else {
416
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
417
+ }
336
418
  return;
337
419
  }
338
420
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
@@ -340,6 +422,35 @@ export class KycController extends BaseController {
340
422
  });
341
423
  await this.loadDisclaimers();
342
424
  }
425
+ /**
426
+ * Creates (or resumes) an Iron empty-shell customer. Exposed so Money can
427
+ * ensure the customer exists before showing T&C screens independently of
428
+ * {@link initialize}.
429
+ *
430
+ * @param params - The parameters.
431
+ * @param params.email - Email for the Iron customer.
432
+ */
433
+ async createIronCustomer(params) {
434
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
435
+ state.email = params.email;
436
+ state.activeVendor = 'iron';
437
+ // See `initialize`: a MoonPay-issued customer id must not survive a
438
+ // switch to Iron, or `getCustomerIdentity` reports the wrong vendor.
439
+ state.moonpayCustomerId = null;
440
+ });
441
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
442
+ try {
443
+ await this.messenger.call('KycService:createIronCustomer', {
444
+ email: params.email,
445
+ });
446
+ }
447
+ catch (error) {
448
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
449
+ return;
450
+ }
451
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Iron customer creation failed: ${String(error)}`);
452
+ }
453
+ }
343
454
  /**
344
455
  * Loads the disclaimers for the resolved (or provided) country.
345
456
  *
@@ -360,7 +471,13 @@ export class KycController extends BaseController {
360
471
  state.geoCountry = country;
361
472
  });
362
473
  }
363
- const disclaimers = await this.messenger.call('KycService:fetchDisclaimers', { country });
474
+ const disclaimers = this.state.activeVendor === 'iron'
475
+ ? await this.messenger.call('KycService:fetchIronDisclaimers', {
476
+ country,
477
+ })
478
+ : await this.messenger.call('KycService:fetchDisclaimers', {
479
+ country,
480
+ });
364
481
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
365
482
  state.disclaimers = disclaimers;
366
483
  state.disclaimersError = null;
@@ -381,6 +498,10 @@ export class KycController extends BaseController {
381
498
  * @param params.product - The consuming feature the flow runs for. See
382
499
  * {@link initialize} for how the product drives the automatic post
383
500
  * authentication continuation.
501
+ * @param params.sumsubTncSigned - Iron path: whether Sumsub T&C were
502
+ * accepted (T&C2). Defaults to `true` when omitted.
503
+ * @param params.idosTncSigned - Iron path: whether idOS T&C were accepted
504
+ * (T&C2). Defaults to `true` when omitted.
384
505
  */
385
506
  async acceptTermsAndStartSession(params) {
386
507
  const termsAcceptedAt = new Date().toISOString();
@@ -395,6 +516,13 @@ export class KycController extends BaseController {
395
516
  state.termsAcceptedAt = termsAcceptedAt;
396
517
  state.acceptedDisclaimerIds = disclaimerIds;
397
518
  });
519
+ if (this.state.activeVendor === 'iron') {
520
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startIronSession).call(this, {
521
+ sumsubTncSigned: params?.sumsubTncSigned ?? true,
522
+ idosTncSigned: params?.idosTncSigned ?? true,
523
+ });
524
+ return;
525
+ }
398
526
  await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
399
527
  }
400
528
  /**
@@ -576,6 +704,25 @@ export class KycController extends BaseController {
576
704
  getKycStatus(params) {
577
705
  return this.state.kycRequiredByProduct[params.product];
578
706
  }
707
+ /**
708
+ * Returns the vendor-scoped identity for the currently authenticated
709
+ * customer, or `null` when the flow has not yet captured a vendor customer
710
+ * id (before authentication or after {@link reset}).
711
+ *
712
+ * Exposed so consumers (e.g. ramps autoramp creation) can attach the vendor
713
+ * customer id to downstream calls without reading the full KYC state, which
714
+ * also holds session/access tokens. The id is session-scoped and never
715
+ * persisted.
716
+ *
717
+ * @returns The current {@link KycCustomerIdentity}, or `null`.
718
+ */
719
+ getCustomerIdentity() {
720
+ const { moonpayCustomerId, activeVendor } = this.state;
721
+ if (!moonpayCustomerId) {
722
+ return null;
723
+ }
724
+ return { vendor: activeVendor, id: moonpayCustomerId };
725
+ }
579
726
  /**
580
727
  * Runs the SumSub document-verification sub-flow end to end:
581
728
  *
@@ -654,12 +801,18 @@ export class KycController extends BaseController {
654
801
  operations: ['read'],
655
802
  expiresAt: new Date(Date.now() + UKYC_CAPABILITY_TOKEN_TTL_MS),
656
803
  });
804
+ const isIron = this.state.activeVendor === 'iron';
657
805
  const { sessionId, kycStatus, finalStatus } = await this.messenger.call('KycService:createUkycSession', {
658
806
  jwtToken,
659
- vendorMetadata: {
660
- moonPayAccessToken: this.state.accessToken,
661
- moonPayUserId: this.state.moonpayCustomerId,
662
- },
807
+ vendorId: isIron ? 'iron' : 'moonpay',
808
+ ...(isIron
809
+ ? {}
810
+ : {
811
+ vendorMetadata: {
812
+ moonPayAccessToken: this.state.accessToken,
813
+ moonPayUserId: this.state.moonpayCustomerId,
814
+ },
815
+ }),
663
816
  wrappedEncryptionKey,
664
817
  ukycCapabilityToken,
665
818
  });
@@ -749,6 +902,28 @@ export class KycController extends BaseController {
749
902
  return result;
750
903
  }
751
904
  catch (error) {
905
+ // Applicant already finished KYC — treat as completed for Money toast.
906
+ if (String(error).includes(SESSION_NOT_IN_VALID_STATE)) {
907
+ // A reset() may have landed while `launch` was in flight; forcing
908
+ // `completed` (and publishing `statusChanged`) on an idle controller
909
+ // would resurrect a flow the consumer already tore down.
910
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
911
+ return { alreadyCompleted: true };
912
+ }
913
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, {
914
+ status: 'completed',
915
+ sumsubSessionId: null,
916
+ errorCode: null,
917
+ });
918
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
919
+ state.sumsub.status = 'complete';
920
+ state.sumsub.result = { alreadyCompleted: true };
921
+ state.statusMessage = 'KYC already completed.';
922
+ state.phase = 'done';
923
+ state.error = null;
924
+ });
925
+ return { alreadyCompleted: true };
926
+ }
752
927
  const result = { error: String(error) };
753
928
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
754
929
  state.sumsub.status = 'failed';
@@ -757,6 +932,23 @@ export class KycController extends BaseController {
757
932
  return result;
758
933
  }
759
934
  }
935
+ /**
936
+ * Refreshes the user-keyed simplified KYC status from `GET /kyc/status`,
937
+ * stores it on state, publishes {@link KycControllerStatusChangedEvent}, and
938
+ * schedules short-interval polling while the status is `pending`.
939
+ *
940
+ * @returns The latest status payload.
941
+ */
942
+ async refreshKycStatus() {
943
+ const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
944
+ if (payload.status === 'pending') {
945
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_ensureUserStatusPolling).call(this);
946
+ }
947
+ else {
948
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
949
+ }
950
+ return payload;
951
+ }
760
952
  /**
761
953
  * Fetches the current UKYC session status for the active sub-flow and records
762
954
  * it on state. Useful for a one-off refresh outside the automatic polling
@@ -788,6 +980,7 @@ export class KycController extends BaseController {
788
980
  // Stop any session-status polling so a late poll cannot write onto the
789
981
  // now-idle controller.
790
982
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
983
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
791
984
  // Invalidate any in-flight async work started before this reset so its
792
985
  // results are discarded rather than written onto the now-idle controller.
793
986
  __classPrivateFieldSet(this, _KycController_generation, __classPrivateFieldGet(this, _KycController_generation, "f") + 1, "f");
@@ -800,6 +993,7 @@ export class KycController extends BaseController {
800
993
  state.sessionToken = null;
801
994
  state.accessToken = null;
802
995
  state.moonpayCustomerId = null;
996
+ state.activeVendor = 'moonpay';
803
997
  state.activeProduct = null;
804
998
  state.sumsub = {
805
999
  status: 'idle',
@@ -811,11 +1005,91 @@ export class KycController extends BaseController {
811
1005
  });
812
1006
  }
813
1007
  }
814
- _KycController_sumsubLauncher = new WeakMap(), _KycController_keypair = new WeakMap(), _KycController_authClientToken = new WeakMap(), _KycController_generation = new WeakMap(), _KycController_sessionStatusPollIntervalMs = new WeakMap(), _KycController_pollTimer = new WeakMap(), _KycController_pollToken = new WeakMap(), _KycController_instances = new WeakSet(), _KycController_localUserSecretStore = function _KycController_localUserSecretStore() {
1008
+ _KycController_sumsubLauncher = new WeakMap(), _KycController_keypair = new WeakMap(), _KycController_authClientToken = new WeakMap(), _KycController_generation = new WeakMap(), _KycController_sessionStatusPollIntervalMs = new WeakMap(), _KycController_pollTimer = new WeakMap(), _KycController_pollToken = new WeakMap(), _KycController_userStatusPollIntervalMs = new WeakMap(), _KycController_userStatusPollTimer = new WeakMap(), _KycController_userStatusPollToken = new WeakMap(), _KycController_instances = new WeakSet(), _KycController_localUserSecretStore = function _KycController_localUserSecretStore() {
815
1009
  return {
816
1010
  get: async (path, entropySourceId) => this.messenger.call('UserStorageController:performGetStorage', path, entropySourceId),
817
1011
  set: async (path, value, entropySourceId) => this.messenger.call('UserStorageController:performSetStorage', path, value, entropySourceId),
818
1012
  };
1013
+ }, _KycController_startIronSession =
1014
+ /**
1015
+ * Iron-only path: post consents (Iron signings + Sumsub/idOS ack), then
1016
+ * launch SumSub — skipping MoonPay Check/Auth frames.
1017
+ *
1018
+ * @param consents - T&C2 boolean flags.
1019
+ * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1020
+ * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1021
+ */
1022
+ async function _KycController_startIronSession(consents) {
1023
+ const { email, acceptedDisclaimerIds } = this.state;
1024
+ if (!email) {
1025
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing email for Iron session.');
1026
+ return;
1027
+ }
1028
+ if (acceptedDisclaimerIds.length === 0) {
1029
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing Iron disclaimer acceptance.');
1030
+ return;
1031
+ }
1032
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1033
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1034
+ state.error = null;
1035
+ state.phase = 'session';
1036
+ state.statusMessage = 'Submitting consents...';
1037
+ // Iron has no MoonPay session/access tokens.
1038
+ state.sessionToken = null;
1039
+ state.accessToken = null;
1040
+ });
1041
+ try {
1042
+ await this.messenger.call('KycService:submitConsents', {
1043
+ ironDisclaimerIds: acceptedDisclaimerIds,
1044
+ sumsubTncSigned: consents.sumsubTncSigned,
1045
+ idosTncSigned: consents.idosTncSigned,
1046
+ });
1047
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1048
+ return;
1049
+ }
1050
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1051
+ state.phase = 'submit';
1052
+ state.statusMessage = 'Starting document verification...';
1053
+ });
1054
+ const sumsubResult = await this.startSumSub();
1055
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1056
+ return;
1057
+ }
1058
+ const sumsubError = sumsubResult?.error;
1059
+ if (typeof sumsubError === 'string') {
1060
+ throw new Error(sumsubError);
1061
+ }
1062
+ // After SumSub, refresh user-keyed status for the Money toast and start
1063
+ // polling while still pending. Soft-fail: toast refresh must not rewind
1064
+ // the consent / SumSub outcome.
1065
+ try {
1066
+ await this.refreshKycStatus();
1067
+ }
1068
+ catch (statusError) {
1069
+ console.error('KYC status refresh failed:', statusError);
1070
+ }
1071
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1072
+ if (state.phase !== 'error' && state.phase !== 'done') {
1073
+ state.phase = 'done';
1074
+ state.statusMessage = 'KYC submitted.';
1075
+ }
1076
+ });
1077
+ }
1078
+ catch (error) {
1079
+ console.error('Iron session failed:', error);
1080
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1081
+ return;
1082
+ }
1083
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1084
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1085
+ state.activeProduct = null;
1086
+ state.error = `Iron session failed: ${String(error)}`;
1087
+ state.statusMessage =
1088
+ 'Consent / verification failed — accept the terms to try again.';
1089
+ state.phase = 'terms';
1090
+ });
1091
+ await this.loadDisclaimers();
1092
+ }
819
1093
  }, _KycController_createSession =
820
1094
  /**
821
1095
  * Creates a vendor session from the currently stored terms + email.
@@ -981,6 +1255,88 @@ async function _KycController_continueAfterAuthentication() {
981
1255
  state.statusMessage =
982
1256
  'The vendor updated its Terms of Use — please re-accept.';
983
1257
  });
1258
+ }, _KycController_fetchAndApplyUserStatus =
1259
+ /**
1260
+ * Fetches `GET /kyc/status` and applies it to state without managing the
1261
+ * poll loop (used by both {@link refreshKycStatus} and the poll tick).
1262
+ *
1263
+ * @returns The latest status payload.
1264
+ */
1265
+ async function _KycController_fetchAndApplyUserStatus() {
1266
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1267
+ const response = await this.messenger.call('KycService:fetchKycStatus');
1268
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1269
+ return {
1270
+ status: this.state.userStatus ?? 'not-started',
1271
+ sumsubSessionId: this.state.userStatusSumsubSessionId,
1272
+ errorCode: this.state.userStatusErrorCode,
1273
+ };
1274
+ }
1275
+ const payload = {
1276
+ status: response.status,
1277
+ sumsubSessionId: response.sumsubSessionId ?? null,
1278
+ errorCode: response.errorCode ?? null,
1279
+ };
1280
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, payload);
1281
+ return payload;
1282
+ }, _KycController_applyUserStatus = function _KycController_applyUserStatus(payload) {
1283
+ const previous = this.state.userStatus;
1284
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1285
+ state.userStatus = payload.status;
1286
+ state.userStatusSumsubSessionId = payload.sumsubSessionId;
1287
+ state.userStatusErrorCode = payload.errorCode;
1288
+ });
1289
+ if (previous !== payload.status) {
1290
+ this.messenger.publish(`${controllerName}:statusChanged`, payload);
1291
+ }
1292
+ }, _KycController_ensureUserStatusPolling = function _KycController_ensureUserStatusPolling() {
1293
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f") !== null) {
1294
+ return;
1295
+ }
1296
+ const token = __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f");
1297
+ const tick = async () => {
1298
+ try {
1299
+ const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
1300
+ // Race with `reset()` / `#stopUserStatusPolling` while the request was
1301
+ // in flight — do not reschedule onto an idle controller.
1302
+ /* istanbul ignore next */
1303
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1304
+ return;
1305
+ }
1306
+ if (payload.status !== 'pending') {
1307
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
1308
+ return;
1309
+ }
1310
+ }
1311
+ catch {
1312
+ // Keep polling on transient errors, unless the loop was superseded.
1313
+ /* istanbul ignore next */
1314
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1315
+ return;
1316
+ }
1317
+ }
1318
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1319
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1320
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1321
+ tick();
1322
+ }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1323
+ // Allow the process to exit while a pending-status poll is scheduled.
1324
+ // React Native / browser timers are numbers with no `unref`, hence the
1325
+ // optional call.
1326
+ __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1327
+ };
1328
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1329
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1330
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1331
+ tick();
1332
+ }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1333
+ __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1334
+ }, _KycController_stopUserStatusPolling = function _KycController_stopUserStatusPolling() {
1335
+ __classPrivateFieldSet(this, _KycController_userStatusPollToken, __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") + 1, "f");
1336
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f") !== null) {
1337
+ clearTimeout(__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f"));
1338
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1339
+ }
984
1340
  }, _KycController_startSessionStatusPolling =
985
1341
  /**
986
1342
  * Begins polling the UKYC session status until a terminal decision is