@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.
- package/CHANGELOG.md +14 -2
- package/README.md +1 -1
- package/dist/KycController-method-action-types.cjs.map +1 -1
- package/dist/KycController-method-action-types.d.cts +46 -1
- package/dist/KycController-method-action-types.d.cts.map +1 -1
- package/dist/KycController-method-action-types.d.mts +46 -1
- package/dist/KycController-method-action-types.d.mts.map +1 -1
- package/dist/KycController-method-action-types.mjs.map +1 -1
- package/dist/KycController.cjs +366 -10
- package/dist/KycController.cjs.map +1 -1
- package/dist/KycController.d.cts +85 -3
- package/dist/KycController.d.cts.map +1 -1
- package/dist/KycController.d.mts +85 -3
- package/dist/KycController.d.mts.map +1 -1
- package/dist/KycController.mjs +366 -10
- package/dist/KycController.mjs.map +1 -1
- package/dist/KycService-method-action-types.cjs.map +1 -1
- package/dist/KycService-method-action-types.d.cts +56 -1
- package/dist/KycService-method-action-types.d.cts.map +1 -1
- package/dist/KycService-method-action-types.d.mts +56 -1
- package/dist/KycService-method-action-types.d.mts.map +1 -1
- package/dist/KycService-method-action-types.mjs.map +1 -1
- package/dist/KycService.cjs +167 -10
- package/dist/KycService.cjs.map +1 -1
- package/dist/KycService.d.cts +75 -2
- package/dist/KycService.d.cts.map +1 -1
- package/dist/KycService.d.mts +75 -2
- package/dist/KycService.d.mts.map +1 -1
- package/dist/KycService.mjs +168 -11
- package/dist/KycService.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +48 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +48 -10
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ukyc/deriveClientMaterial.cjs +1 -1
- package/dist/ukyc/deriveClientMaterial.cjs.map +1 -1
- package/dist/ukyc/deriveClientMaterial.mjs +1 -1
- package/dist/ukyc/deriveClientMaterial.mjs.map +1 -1
- package/dist/ukyc/storageAccessToken.cjs +1 -1
- package/dist/ukyc/storageAccessToken.cjs.map +1 -1
- package/dist/ukyc/storageAccessToken.d.cts.map +1 -1
- package/dist/ukyc/storageAccessToken.d.mts.map +1 -1
- package/dist/ukyc/storageAccessToken.mjs +1 -1
- package/dist/ukyc/storageAccessToken.mjs.map +1 -1
- package/dist/ukyc/wrappedRelayPayload.cjs.map +1 -1
- package/dist/ukyc/wrappedRelayPayload.d.cts.map +1 -1
- package/dist/ukyc/wrappedRelayPayload.d.mts.map +1 -1
- package/dist/ukyc/wrappedRelayPayload.mjs.map +1 -1
- package/package.json +1 -1
package/dist/KycController.mjs
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|