@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
@@ -10,14 +10,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  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");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- 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;
13
+ 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;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.KycController = exports.getDefaultKycControllerState = exports.controllerName = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
17
17
  const ed25519_1 = require("@noble/curves/ed25519");
18
18
  const crypto_js_1 = require("./crypto.cjs");
19
- const deriveClientMaterial_js_1 = require("./ukyc/deriveClientMaterial.cjs");
20
19
  const encoding_js_1 = require("./encoding.cjs");
20
+ const deriveClientMaterial_js_1 = require("./ukyc/deriveClientMaterial.cjs");
21
21
  const jwtChain_js_1 = require("./ukyc/jwtChain.cjs");
22
22
  const localUserSecret_js_1 = require("./ukyc/localUserSecret.cjs");
23
23
  const storageAccessToken_js_1 = require("./ukyc/storageAccessToken.cjs");
@@ -87,6 +87,12 @@ const SUCCESSFUL_SESSION_STATUSES = new Set([
87
87
  // to do, so the sub-flow stops before launching the SDK and surfaces this
88
88
  // message.
89
89
  const VENDOR_PROCESSING_MESSAGE = 'Your KYC has been submitted and is being processed by the vendor.';
90
+ // UKYC / relay error indicating the applicant already finished KYC. Mapped to
91
+ // the simplified `completed` user status for the Money toast surface.
92
+ const SESSION_NOT_IN_VALID_STATE = 'session_not_in_valid_state';
93
+ // How often to refresh the user-keyed `GET /kyc/status` while the simplified
94
+ // status is still `pending`. Overridable via the constructor.
95
+ const DEFAULT_USER_STATUS_POLL_INTERVAL_MS = 15000;
90
96
  const kycControllerMetadata = {
91
97
  phase: {
92
98
  includeInDebugSnapshot: true,
@@ -160,6 +166,12 @@ const kycControllerMetadata = {
160
166
  persist: false,
161
167
  usedInUi: false,
162
168
  },
169
+ activeVendor: {
170
+ includeInDebugSnapshot: true,
171
+ includeInStateLogs: true,
172
+ persist: false,
173
+ usedInUi: true,
174
+ },
163
175
  activeProduct: {
164
176
  includeInDebugSnapshot: true,
165
177
  includeInStateLogs: true,
@@ -178,6 +190,24 @@ const kycControllerMetadata = {
178
190
  persist: true,
179
191
  usedInUi: false,
180
192
  },
193
+ userStatus: {
194
+ includeInDebugSnapshot: true,
195
+ includeInStateLogs: true,
196
+ persist: true,
197
+ usedInUi: true,
198
+ },
199
+ userStatusSumsubSessionId: {
200
+ includeInDebugSnapshot: false,
201
+ includeInStateLogs: false,
202
+ persist: true,
203
+ usedInUi: true,
204
+ },
205
+ userStatusErrorCode: {
206
+ includeInDebugSnapshot: true,
207
+ includeInStateLogs: true,
208
+ persist: true,
209
+ usedInUi: true,
210
+ },
181
211
  sumsub: {
182
212
  includeInDebugSnapshot: false,
183
213
  includeInStateLogs: false,
@@ -204,9 +234,13 @@ function getDefaultKycControllerState() {
204
234
  sessionToken: null,
205
235
  accessToken: null,
206
236
  moonpayCustomerId: null,
237
+ activeVendor: 'moonpay',
207
238
  activeProduct: null,
208
239
  kycRequiredByProduct: {},
209
240
  lastCheckedAt: null,
241
+ userStatus: null,
242
+ userStatusSumsubSessionId: null,
243
+ userStatusErrorCode: null,
210
244
  sumsub: {
211
245
  status: 'idle',
212
246
  result: null,
@@ -222,6 +256,7 @@ const MESSENGER_EXPOSED_METHODS = [
222
256
  'initialize',
223
257
  'loadDisclaimers',
224
258
  'acceptTermsAndStartSession',
259
+ 'createIronCustomer',
225
260
  'clearSavedTerms',
226
261
  'handleFrameMessage',
227
262
  'buildCheckFrameUrl',
@@ -229,6 +264,8 @@ const MESSENGER_EXPOSED_METHODS = [
229
264
  'buildResetFrameUrl',
230
265
  'checkKycRequired',
231
266
  'getKycStatus',
267
+ 'getCustomerIdentity',
268
+ 'refreshKycStatus',
232
269
  'startSumSub',
233
270
  'getSessionStatus',
234
271
  'reset',
@@ -251,8 +288,10 @@ class KycController extends base_controller_1.BaseController {
251
288
  * @param options.sumsubLauncher - The platform SumSub launcher adapter.
252
289
  * @param options.sessionStatusPollIntervalMs - How often to poll the UKYC
253
290
  * session status after the SumSub SDK completes.
291
+ * @param options.userStatusPollIntervalMs - How often to refresh the
292
+ * user-keyed KYC status while it is still `pending`.
254
293
  */
255
- constructor({ messenger, state, sumsubLauncher, sessionStatusPollIntervalMs = DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS, }) {
294
+ constructor({ messenger, state, sumsubLauncher, sessionStatusPollIntervalMs = DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS, userStatusPollIntervalMs = DEFAULT_USER_STATUS_POLL_INTERVAL_MS, }) {
256
295
  super({
257
296
  messenger,
258
297
  metadata: kycControllerMetadata,
@@ -283,8 +322,15 @@ class KycController extends base_controller_1.BaseController {
283
322
  * let an already-awaiting request finish and reschedule.
284
323
  */
285
324
  _KycController_pollToken.set(this, 0);
325
+ /** Interval, in milliseconds, between user-keyed status polls. */
326
+ _KycController_userStatusPollIntervalMs.set(this, void 0);
327
+ /** Handle for the scheduled next user-status poll, or `null`. */
328
+ _KycController_userStatusPollTimer.set(this, null);
329
+ /** Monotonic token for the user-status poll loop (see `#pollToken`). */
330
+ _KycController_userStatusPollToken.set(this, 0);
286
331
  __classPrivateFieldSet(this, _KycController_sumsubLauncher, sumsubLauncher, "f");
287
332
  __classPrivateFieldSet(this, _KycController_sessionStatusPollIntervalMs, sessionStatusPollIntervalMs, "f");
333
+ __classPrivateFieldSet(this, _KycController_userStatusPollIntervalMs, userStatusPollIntervalMs, "f");
288
334
  __classPrivateFieldSet(this, _KycController_keypair, (0, crypto_js_1.generateKeyPair)(), "f");
289
335
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
290
336
  }
@@ -299,6 +345,8 @@ class KycController extends base_controller_1.BaseController {
299
345
  * authentication completes (and chains into document verification when KYC
300
346
  * is required). When omitted, the flow stops at `form` and the consumer must
301
347
  * call `checkKycRequired` manually.
348
+ * @param params.vendor - Identity vendor for this flow. Pass `iron` for the
349
+ * Money/VBA path (no MoonPay Check/Auth frames). Defaults to `moonpay`.
302
350
  */
303
351
  async initialize(params) {
304
352
  // A repeat `initialize` while a session flow is already in progress must
@@ -309,6 +357,7 @@ class KycController extends base_controller_1.BaseController {
309
357
  if (IN_PROGRESS_PHASES.includes(this.state.phase)) {
310
358
  return;
311
359
  }
360
+ const vendor = params?.vendor ?? 'moonpay';
312
361
  // `initialize` starts a fresh flow, so `activeProduct` is always reset to
313
362
  // this call's product (or `null`). Otherwise a prior run's product could
314
363
  // linger and cause `#continueAfterAuthentication` to auto-run the check /
@@ -317,6 +366,13 @@ class KycController extends base_controller_1.BaseController {
317
366
  if (params?.email) {
318
367
  state.email = params.email;
319
368
  }
369
+ state.activeVendor = vendor;
370
+ // `moonpayCustomerId` is only ever issued by the MoonPay Check / Auth
371
+ // frames. Leaving it set while the flow switches to another vendor would
372
+ // make `getCustomerIdentity` report a MoonPay id under the wrong vendor.
373
+ if (vendor !== 'moonpay') {
374
+ state.moonpayCustomerId = null;
375
+ }
320
376
  state.activeProduct = params?.product ?? null;
321
377
  });
322
378
  // Capture the flow generation so a `reset()` landing while the async
@@ -333,10 +389,36 @@ class KycController extends base_controller_1.BaseController {
333
389
  catch {
334
390
  // Ignore; disclaimers loading will surface a country error if needed.
335
391
  }
392
+ // Iron: create the empty-shell customer before T&C (offsite decision).
393
+ if (vendor === 'iron' && this.state.email) {
394
+ try {
395
+ await this.messenger.call('KycService:createIronCustomer', {
396
+ email: this.state.email,
397
+ });
398
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
399
+ return;
400
+ }
401
+ }
402
+ catch (error) {
403
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
404
+ return;
405
+ }
406
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Iron customer creation failed: ${String(error)}`);
407
+ return;
408
+ }
409
+ }
336
410
  const hasTerms = Boolean(this.state.termsAcceptedAt) &&
337
411
  this.state.acceptedDisclaimerIds.length > 0;
338
412
  if (hasTerms && this.state.email) {
339
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
413
+ if (vendor === 'iron') {
414
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startIronSession).call(this, {
415
+ sumsubTncSigned: true,
416
+ idosTncSigned: true,
417
+ });
418
+ }
419
+ else {
420
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
421
+ }
340
422
  return;
341
423
  }
342
424
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
@@ -344,6 +426,35 @@ class KycController extends base_controller_1.BaseController {
344
426
  });
345
427
  await this.loadDisclaimers();
346
428
  }
429
+ /**
430
+ * Creates (or resumes) an Iron empty-shell customer. Exposed so Money can
431
+ * ensure the customer exists before showing T&C screens independently of
432
+ * {@link initialize}.
433
+ *
434
+ * @param params - The parameters.
435
+ * @param params.email - Email for the Iron customer.
436
+ */
437
+ async createIronCustomer(params) {
438
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
439
+ state.email = params.email;
440
+ state.activeVendor = 'iron';
441
+ // See `initialize`: a MoonPay-issued customer id must not survive a
442
+ // switch to Iron, or `getCustomerIdentity` reports the wrong vendor.
443
+ state.moonpayCustomerId = null;
444
+ });
445
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
446
+ try {
447
+ await this.messenger.call('KycService:createIronCustomer', {
448
+ email: params.email,
449
+ });
450
+ }
451
+ catch (error) {
452
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
453
+ return;
454
+ }
455
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Iron customer creation failed: ${String(error)}`);
456
+ }
457
+ }
347
458
  /**
348
459
  * Loads the disclaimers for the resolved (or provided) country.
349
460
  *
@@ -364,7 +475,13 @@ class KycController extends base_controller_1.BaseController {
364
475
  state.geoCountry = country;
365
476
  });
366
477
  }
367
- const disclaimers = await this.messenger.call('KycService:fetchDisclaimers', { country });
478
+ const disclaimers = this.state.activeVendor === 'iron'
479
+ ? await this.messenger.call('KycService:fetchIronDisclaimers', {
480
+ country,
481
+ })
482
+ : await this.messenger.call('KycService:fetchDisclaimers', {
483
+ country,
484
+ });
368
485
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
369
486
  state.disclaimers = disclaimers;
370
487
  state.disclaimersError = null;
@@ -385,6 +502,10 @@ class KycController extends base_controller_1.BaseController {
385
502
  * @param params.product - The consuming feature the flow runs for. See
386
503
  * {@link initialize} for how the product drives the automatic post
387
504
  * authentication continuation.
505
+ * @param params.sumsubTncSigned - Iron path: whether Sumsub T&C were
506
+ * accepted (T&C2). Defaults to `true` when omitted.
507
+ * @param params.idosTncSigned - Iron path: whether idOS T&C were accepted
508
+ * (T&C2). Defaults to `true` when omitted.
388
509
  */
389
510
  async acceptTermsAndStartSession(params) {
390
511
  const termsAcceptedAt = new Date().toISOString();
@@ -399,6 +520,13 @@ class KycController extends base_controller_1.BaseController {
399
520
  state.termsAcceptedAt = termsAcceptedAt;
400
521
  state.acceptedDisclaimerIds = disclaimerIds;
401
522
  });
523
+ if (this.state.activeVendor === 'iron') {
524
+ await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startIronSession).call(this, {
525
+ sumsubTncSigned: params?.sumsubTncSigned ?? true,
526
+ idosTncSigned: params?.idosTncSigned ?? true,
527
+ });
528
+ return;
529
+ }
402
530
  await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
403
531
  }
404
532
  /**
@@ -580,6 +708,25 @@ class KycController extends base_controller_1.BaseController {
580
708
  getKycStatus(params) {
581
709
  return this.state.kycRequiredByProduct[params.product];
582
710
  }
711
+ /**
712
+ * Returns the vendor-scoped identity for the currently authenticated
713
+ * customer, or `null` when the flow has not yet captured a vendor customer
714
+ * id (before authentication or after {@link reset}).
715
+ *
716
+ * Exposed so consumers (e.g. ramps autoramp creation) can attach the vendor
717
+ * customer id to downstream calls without reading the full KYC state, which
718
+ * also holds session/access tokens. The id is session-scoped and never
719
+ * persisted.
720
+ *
721
+ * @returns The current {@link KycCustomerIdentity}, or `null`.
722
+ */
723
+ getCustomerIdentity() {
724
+ const { moonpayCustomerId, activeVendor } = this.state;
725
+ if (!moonpayCustomerId) {
726
+ return null;
727
+ }
728
+ return { vendor: activeVendor, id: moonpayCustomerId };
729
+ }
583
730
  /**
584
731
  * Runs the SumSub document-verification sub-flow end to end:
585
732
  *
@@ -658,12 +805,18 @@ class KycController extends base_controller_1.BaseController {
658
805
  operations: ['read'],
659
806
  expiresAt: new Date(Date.now() + UKYC_CAPABILITY_TOKEN_TTL_MS),
660
807
  });
808
+ const isIron = this.state.activeVendor === 'iron';
661
809
  const { sessionId, kycStatus, finalStatus } = await this.messenger.call('KycService:createUkycSession', {
662
810
  jwtToken,
663
- vendorMetadata: {
664
- moonPayAccessToken: this.state.accessToken,
665
- moonPayUserId: this.state.moonpayCustomerId,
666
- },
811
+ vendorId: isIron ? 'iron' : 'moonpay',
812
+ ...(isIron
813
+ ? {}
814
+ : {
815
+ vendorMetadata: {
816
+ moonPayAccessToken: this.state.accessToken,
817
+ moonPayUserId: this.state.moonpayCustomerId,
818
+ },
819
+ }),
667
820
  wrappedEncryptionKey,
668
821
  ukycCapabilityToken,
669
822
  });
@@ -753,6 +906,28 @@ class KycController extends base_controller_1.BaseController {
753
906
  return result;
754
907
  }
755
908
  catch (error) {
909
+ // Applicant already finished KYC — treat as completed for Money toast.
910
+ if (String(error).includes(SESSION_NOT_IN_VALID_STATE)) {
911
+ // A reset() may have landed while `launch` was in flight; forcing
912
+ // `completed` (and publishing `statusChanged`) on an idle controller
913
+ // would resurrect a flow the consumer already tore down.
914
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
915
+ return { alreadyCompleted: true };
916
+ }
917
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, {
918
+ status: 'completed',
919
+ sumsubSessionId: null,
920
+ errorCode: null,
921
+ });
922
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
923
+ state.sumsub.status = 'complete';
924
+ state.sumsub.result = { alreadyCompleted: true };
925
+ state.statusMessage = 'KYC already completed.';
926
+ state.phase = 'done';
927
+ state.error = null;
928
+ });
929
+ return { alreadyCompleted: true };
930
+ }
756
931
  const result = { error: String(error) };
757
932
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
758
933
  state.sumsub.status = 'failed';
@@ -761,6 +936,23 @@ class KycController extends base_controller_1.BaseController {
761
936
  return result;
762
937
  }
763
938
  }
939
+ /**
940
+ * Refreshes the user-keyed simplified KYC status from `GET /kyc/status`,
941
+ * stores it on state, publishes {@link KycControllerStatusChangedEvent}, and
942
+ * schedules short-interval polling while the status is `pending`.
943
+ *
944
+ * @returns The latest status payload.
945
+ */
946
+ async refreshKycStatus() {
947
+ const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
948
+ if (payload.status === 'pending') {
949
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_ensureUserStatusPolling).call(this);
950
+ }
951
+ else {
952
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
953
+ }
954
+ return payload;
955
+ }
764
956
  /**
765
957
  * Fetches the current UKYC session status for the active sub-flow and records
766
958
  * it on state. Useful for a one-off refresh outside the automatic polling
@@ -792,6 +984,7 @@ class KycController extends base_controller_1.BaseController {
792
984
  // Stop any session-status polling so a late poll cannot write onto the
793
985
  // now-idle controller.
794
986
  __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
987
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
795
988
  // Invalidate any in-flight async work started before this reset so its
796
989
  // results are discarded rather than written onto the now-idle controller.
797
990
  __classPrivateFieldSet(this, _KycController_generation, __classPrivateFieldGet(this, _KycController_generation, "f") + 1, "f");
@@ -804,6 +997,7 @@ class KycController extends base_controller_1.BaseController {
804
997
  state.sessionToken = null;
805
998
  state.accessToken = null;
806
999
  state.moonpayCustomerId = null;
1000
+ state.activeVendor = 'moonpay';
807
1001
  state.activeProduct = null;
808
1002
  state.sumsub = {
809
1003
  status: 'idle',
@@ -816,11 +1010,91 @@ class KycController extends base_controller_1.BaseController {
816
1010
  }
817
1011
  }
818
1012
  exports.KycController = KycController;
819
- _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() {
1013
+ _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() {
820
1014
  return {
821
1015
  get: async (path, entropySourceId) => this.messenger.call('UserStorageController:performGetStorage', path, entropySourceId),
822
1016
  set: async (path, value, entropySourceId) => this.messenger.call('UserStorageController:performSetStorage', path, value, entropySourceId),
823
1017
  };
1018
+ }, _KycController_startIronSession =
1019
+ /**
1020
+ * Iron-only path: post consents (Iron signings + Sumsub/idOS ack), then
1021
+ * launch SumSub — skipping MoonPay Check/Auth frames.
1022
+ *
1023
+ * @param consents - T&C2 boolean flags.
1024
+ * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1025
+ * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1026
+ */
1027
+ async function _KycController_startIronSession(consents) {
1028
+ const { email, acceptedDisclaimerIds } = this.state;
1029
+ if (!email) {
1030
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing email for Iron session.');
1031
+ return;
1032
+ }
1033
+ if (acceptedDisclaimerIds.length === 0) {
1034
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing Iron disclaimer acceptance.');
1035
+ return;
1036
+ }
1037
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1038
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1039
+ state.error = null;
1040
+ state.phase = 'session';
1041
+ state.statusMessage = 'Submitting consents...';
1042
+ // Iron has no MoonPay session/access tokens.
1043
+ state.sessionToken = null;
1044
+ state.accessToken = null;
1045
+ });
1046
+ try {
1047
+ await this.messenger.call('KycService:submitConsents', {
1048
+ ironDisclaimerIds: acceptedDisclaimerIds,
1049
+ sumsubTncSigned: consents.sumsubTncSigned,
1050
+ idosTncSigned: consents.idosTncSigned,
1051
+ });
1052
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1053
+ return;
1054
+ }
1055
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1056
+ state.phase = 'submit';
1057
+ state.statusMessage = 'Starting document verification...';
1058
+ });
1059
+ const sumsubResult = await this.startSumSub();
1060
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1061
+ return;
1062
+ }
1063
+ const sumsubError = sumsubResult?.error;
1064
+ if (typeof sumsubError === 'string') {
1065
+ throw new Error(sumsubError);
1066
+ }
1067
+ // After SumSub, refresh user-keyed status for the Money toast and start
1068
+ // polling while still pending. Soft-fail: toast refresh must not rewind
1069
+ // the consent / SumSub outcome.
1070
+ try {
1071
+ await this.refreshKycStatus();
1072
+ }
1073
+ catch (statusError) {
1074
+ console.error('KYC status refresh failed:', statusError);
1075
+ }
1076
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1077
+ if (state.phase !== 'error' && state.phase !== 'done') {
1078
+ state.phase = 'done';
1079
+ state.statusMessage = 'KYC submitted.';
1080
+ }
1081
+ });
1082
+ }
1083
+ catch (error) {
1084
+ console.error('Iron session failed:', error);
1085
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1086
+ return;
1087
+ }
1088
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1089
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1090
+ state.activeProduct = null;
1091
+ state.error = `Iron session failed: ${String(error)}`;
1092
+ state.statusMessage =
1093
+ 'Consent / verification failed — accept the terms to try again.';
1094
+ state.phase = 'terms';
1095
+ });
1096
+ await this.loadDisclaimers();
1097
+ }
824
1098
  }, _KycController_createSession =
825
1099
  /**
826
1100
  * Creates a vendor session from the currently stored terms + email.
@@ -986,6 +1260,88 @@ async function _KycController_continueAfterAuthentication() {
986
1260
  state.statusMessage =
987
1261
  'The vendor updated its Terms of Use — please re-accept.';
988
1262
  });
1263
+ }, _KycController_fetchAndApplyUserStatus =
1264
+ /**
1265
+ * Fetches `GET /kyc/status` and applies it to state without managing the
1266
+ * poll loop (used by both {@link refreshKycStatus} and the poll tick).
1267
+ *
1268
+ * @returns The latest status payload.
1269
+ */
1270
+ async function _KycController_fetchAndApplyUserStatus() {
1271
+ const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1272
+ const response = await this.messenger.call('KycService:fetchKycStatus');
1273
+ if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1274
+ return {
1275
+ status: this.state.userStatus ?? 'not-started',
1276
+ sumsubSessionId: this.state.userStatusSumsubSessionId,
1277
+ errorCode: this.state.userStatusErrorCode,
1278
+ };
1279
+ }
1280
+ const payload = {
1281
+ status: response.status,
1282
+ sumsubSessionId: response.sumsubSessionId ?? null,
1283
+ errorCode: response.errorCode ?? null,
1284
+ };
1285
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, payload);
1286
+ return payload;
1287
+ }, _KycController_applyUserStatus = function _KycController_applyUserStatus(payload) {
1288
+ const previous = this.state.userStatus;
1289
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1290
+ state.userStatus = payload.status;
1291
+ state.userStatusSumsubSessionId = payload.sumsubSessionId;
1292
+ state.userStatusErrorCode = payload.errorCode;
1293
+ });
1294
+ if (previous !== payload.status) {
1295
+ this.messenger.publish(`${exports.controllerName}:statusChanged`, payload);
1296
+ }
1297
+ }, _KycController_ensureUserStatusPolling = function _KycController_ensureUserStatusPolling() {
1298
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f") !== null) {
1299
+ return;
1300
+ }
1301
+ const token = __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f");
1302
+ const tick = async () => {
1303
+ try {
1304
+ const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
1305
+ // Race with `reset()` / `#stopUserStatusPolling` while the request was
1306
+ // in flight — do not reschedule onto an idle controller.
1307
+ /* istanbul ignore next */
1308
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1309
+ return;
1310
+ }
1311
+ if (payload.status !== 'pending') {
1312
+ __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
1313
+ return;
1314
+ }
1315
+ }
1316
+ catch {
1317
+ // Keep polling on transient errors, unless the loop was superseded.
1318
+ /* istanbul ignore next */
1319
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1320
+ return;
1321
+ }
1322
+ }
1323
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1324
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1325
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1326
+ tick();
1327
+ }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1328
+ // Allow the process to exit while a pending-status poll is scheduled.
1329
+ // React Native / browser timers are numbers with no `unref`, hence the
1330
+ // optional call.
1331
+ __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1332
+ };
1333
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1334
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1335
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1336
+ tick();
1337
+ }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1338
+ __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1339
+ }, _KycController_stopUserStatusPolling = function _KycController_stopUserStatusPolling() {
1340
+ __classPrivateFieldSet(this, _KycController_userStatusPollToken, __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") + 1, "f");
1341
+ if (__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f") !== null) {
1342
+ clearTimeout(__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f"));
1343
+ __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1344
+ }
989
1345
  }, _KycController_startSessionStatusPolling =
990
1346
  /**
991
1347
  * Begins polling the UKYC session status until a terminal decision is