@metamask-previews/kyc-controller 0.0.0-preview-8bfa290fb → 0.2.0-preview-0a30e47

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 (203) hide show
  1. package/CHANGELOG.md +30 -49
  2. package/dist/{KycController-method-action-types.d.mts → KycController-method-action-types.d.ts} +35 -7
  3. package/dist/KycController-method-action-types.d.ts.map +1 -0
  4. package/dist/{KycController-method-action-types.mjs → KycController-method-action-types.js} +1 -1
  5. package/dist/KycController-method-action-types.js.map +1 -0
  6. package/dist/{KycController.d.mts → KycController.d.ts} +87 -45
  7. package/dist/KycController.d.ts.map +1 -0
  8. package/dist/KycController.js +1991 -0
  9. package/dist/KycController.js.map +1 -0
  10. package/dist/{KycService-method-action-types.d.mts → KycService-method-action-types.d.ts} +16 -16
  11. package/dist/KycService-method-action-types.d.ts.map +1 -0
  12. package/dist/{KycService-method-action-types.mjs → KycService-method-action-types.js} +1 -1
  13. package/dist/KycService-method-action-types.js.map +1 -0
  14. package/dist/{KycService.d.mts → KycService.d.ts} +23 -23
  15. package/dist/KycService.d.ts.map +1 -0
  16. package/dist/{KycService.mjs → KycService.js} +194 -185
  17. package/dist/KycService.js.map +1 -0
  18. package/dist/{countryCodes.d.cts → countryCodes.d.ts} +1 -1
  19. package/dist/countryCodes.d.ts.map +1 -0
  20. package/dist/{countryCodes.mjs → countryCodes.js} +1 -1
  21. package/dist/countryCodes.js.map +1 -0
  22. package/dist/{crypto.d.mts → crypto.d.ts} +1 -1
  23. package/dist/crypto.d.ts.map +1 -0
  24. package/dist/{crypto.mjs → crypto.js} +7 -7
  25. package/dist/crypto.js.map +1 -0
  26. package/dist/{encoding.d.mts → encoding.d.ts} +1 -1
  27. package/dist/encoding.d.ts.map +1 -0
  28. package/dist/{encoding.mjs → encoding.js} +2 -2
  29. package/dist/encoding.js.map +1 -0
  30. package/dist/index.d.ts +19 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +12 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/{logger.d.cts → logger.d.ts} +2 -3
  35. package/dist/logger.d.ts.map +1 -0
  36. package/dist/{logger.mjs → logger.js} +2 -2
  37. package/dist/logger.js.map +1 -0
  38. package/dist/{selectors.d.mts → selectors.d.ts} +4 -4
  39. package/dist/selectors.d.ts.map +1 -0
  40. package/dist/{selectors.mjs → selectors.js} +2 -2
  41. package/dist/selectors.js.map +1 -0
  42. package/dist/{types.d.cts → types.d.ts} +72 -13
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/{types.mjs → types.js} +1 -1
  45. package/dist/types.js.map +1 -0
  46. package/dist/ukyc/{constants.d.mts → constants.d.ts} +9 -9
  47. package/dist/ukyc/constants.d.ts.map +1 -0
  48. package/dist/ukyc/{constants.mjs → constants.js} +1 -1
  49. package/dist/ukyc/constants.js.map +1 -0
  50. package/dist/ukyc/{deriveClientMaterial.d.cts → deriveClientMaterial.d.ts} +1 -1
  51. package/dist/ukyc/deriveClientMaterial.d.ts.map +1 -0
  52. package/dist/ukyc/{deriveClientMaterial.mjs → deriveClientMaterial.js} +7 -7
  53. package/dist/ukyc/deriveClientMaterial.js.map +1 -0
  54. package/dist/ukyc/{jwtChain.d.mts → jwtChain.d.ts} +1 -1
  55. package/dist/ukyc/jwtChain.d.ts.map +1 -0
  56. package/dist/ukyc/{jwtChain.mjs → jwtChain.js} +4 -4
  57. package/dist/ukyc/jwtChain.js.map +1 -0
  58. package/dist/ukyc/{localUserSecret.d.mts → localUserSecret.d.ts} +1 -1
  59. package/dist/ukyc/localUserSecret.d.ts.map +1 -0
  60. package/dist/ukyc/{localUserSecret.mjs → localUserSecret.js} +4 -4
  61. package/dist/ukyc/localUserSecret.js.map +1 -0
  62. package/dist/ukyc/{storageAccessToken.d.mts → storageAccessToken.d.ts} +2 -2
  63. package/dist/ukyc/storageAccessToken.d.ts.map +1 -0
  64. package/dist/ukyc/{storageAccessToken.mjs → storageAccessToken.js} +5 -5
  65. package/dist/ukyc/storageAccessToken.js.map +1 -0
  66. package/dist/ukyc/{testToken.d.mts → testToken.d.ts} +2 -2
  67. package/dist/ukyc/testToken.d.ts.map +1 -0
  68. package/dist/ukyc/{testToken.mjs → testToken.js} +5 -5
  69. package/dist/ukyc/testToken.js.map +1 -0
  70. package/dist/ukyc/{wrapEncryptionKey.d.mts → wrapEncryptionKey.d.ts} +1 -1
  71. package/dist/ukyc/wrapEncryptionKey.d.ts.map +1 -0
  72. package/dist/ukyc/{wrapEncryptionKey.mjs → wrapEncryptionKey.js} +3 -4
  73. package/dist/ukyc/wrapEncryptionKey.js.map +1 -0
  74. package/dist/ukyc/{wrappedRelayPayload.d.mts → wrappedRelayPayload.d.ts} +3 -3
  75. package/dist/ukyc/wrappedRelayPayload.d.ts.map +1 -0
  76. package/dist/ukyc/{wrappedRelayPayload.mjs → wrappedRelayPayload.js} +2 -2
  77. package/dist/ukyc/wrappedRelayPayload.js.map +1 -0
  78. package/dist/vendorDisclaimerAcceptance.d.ts +39 -0
  79. package/dist/vendorDisclaimerAcceptance.d.ts.map +1 -0
  80. package/dist/vendorDisclaimerAcceptance.js +67 -0
  81. package/dist/vendorDisclaimerAcceptance.js.map +1 -0
  82. package/dist/vendors/MoonPayFrameHandler.d.ts +74 -0
  83. package/dist/vendors/MoonPayFrameHandler.d.ts.map +1 -0
  84. package/dist/vendors/MoonPayFrameHandler.js +218 -0
  85. package/dist/vendors/MoonPayFrameHandler.js.map +1 -0
  86. package/package.json +19 -23
  87. package/dist/KycController-method-action-types.cjs +0 -7
  88. package/dist/KycController-method-action-types.cjs.map +0 -1
  89. package/dist/KycController-method-action-types.d.cts +0 -239
  90. package/dist/KycController-method-action-types.d.cts.map +0 -1
  91. package/dist/KycController-method-action-types.d.mts.map +0 -1
  92. package/dist/KycController-method-action-types.mjs.map +0 -1
  93. package/dist/KycController.cjs +0 -1882
  94. package/dist/KycController.cjs.map +0 -1
  95. package/dist/KycController.d.cts +0 -395
  96. package/dist/KycController.d.cts.map +0 -1
  97. package/dist/KycController.d.mts.map +0 -1
  98. package/dist/KycController.mjs +0 -1877
  99. package/dist/KycController.mjs.map +0 -1
  100. package/dist/KycService-method-action-types.cjs +0 -7
  101. package/dist/KycService-method-action-types.cjs.map +0 -1
  102. package/dist/KycService-method-action-types.d.cts +0 -220
  103. package/dist/KycService-method-action-types.d.cts.map +0 -1
  104. package/dist/KycService-method-action-types.d.mts.map +0 -1
  105. package/dist/KycService-method-action-types.mjs.map +0 -1
  106. package/dist/KycService.cjs +0 -637
  107. package/dist/KycService.cjs.map +0 -1
  108. package/dist/KycService.d.cts +0 -552
  109. package/dist/KycService.d.cts.map +0 -1
  110. package/dist/KycService.d.mts.map +0 -1
  111. package/dist/KycService.mjs.map +0 -1
  112. package/dist/countryCodes.cjs +0 -274
  113. package/dist/countryCodes.cjs.map +0 -1
  114. package/dist/countryCodes.d.cts.map +0 -1
  115. package/dist/countryCodes.d.mts +0 -18
  116. package/dist/countryCodes.d.mts.map +0 -1
  117. package/dist/countryCodes.mjs.map +0 -1
  118. package/dist/crypto.cjs +0 -154
  119. package/dist/crypto.cjs.map +0 -1
  120. package/dist/crypto.d.cts +0 -84
  121. package/dist/crypto.d.cts.map +0 -1
  122. package/dist/crypto.d.mts.map +0 -1
  123. package/dist/crypto.mjs.map +0 -1
  124. package/dist/encoding.cjs +0 -40
  125. package/dist/encoding.cjs.map +0 -1
  126. package/dist/encoding.d.cts +0 -24
  127. package/dist/encoding.d.cts.map +0 -1
  128. package/dist/encoding.d.mts.map +0 -1
  129. package/dist/encoding.mjs.map +0 -1
  130. package/dist/index.cjs +0 -37
  131. package/dist/index.cjs.map +0 -1
  132. package/dist/index.d.cts +0 -19
  133. package/dist/index.d.cts.map +0 -1
  134. package/dist/index.d.mts +0 -19
  135. package/dist/index.d.mts.map +0 -1
  136. package/dist/index.mjs +0 -12
  137. package/dist/index.mjs.map +0 -1
  138. package/dist/logger.cjs +0 -9
  139. package/dist/logger.cjs.map +0 -1
  140. package/dist/logger.d.cts.map +0 -1
  141. package/dist/logger.d.mts +0 -6
  142. package/dist/logger.d.mts.map +0 -1
  143. package/dist/logger.mjs.map +0 -1
  144. package/dist/selectors.cjs +0 -30
  145. package/dist/selectors.cjs.map +0 -1
  146. package/dist/selectors.d.cts +0 -24
  147. package/dist/selectors.d.cts.map +0 -1
  148. package/dist/selectors.d.mts.map +0 -1
  149. package/dist/selectors.mjs.map +0 -1
  150. package/dist/types.cjs +0 -10
  151. package/dist/types.cjs.map +0 -1
  152. package/dist/types.d.cts.map +0 -1
  153. package/dist/types.d.mts +0 -217
  154. package/dist/types.d.mts.map +0 -1
  155. package/dist/types.mjs.map +0 -1
  156. package/dist/ukyc/constants.cjs +0 -75
  157. package/dist/ukyc/constants.cjs.map +0 -1
  158. package/dist/ukyc/constants.d.cts +0 -69
  159. package/dist/ukyc/constants.d.cts.map +0 -1
  160. package/dist/ukyc/constants.d.mts.map +0 -1
  161. package/dist/ukyc/constants.mjs.map +0 -1
  162. package/dist/ukyc/deriveClientMaterial.cjs +0 -65
  163. package/dist/ukyc/deriveClientMaterial.cjs.map +0 -1
  164. package/dist/ukyc/deriveClientMaterial.d.cts.map +0 -1
  165. package/dist/ukyc/deriveClientMaterial.d.mts +0 -54
  166. package/dist/ukyc/deriveClientMaterial.d.mts.map +0 -1
  167. package/dist/ukyc/deriveClientMaterial.mjs.map +0 -1
  168. package/dist/ukyc/jwtChain.cjs +0 -54
  169. package/dist/ukyc/jwtChain.cjs.map +0 -1
  170. package/dist/ukyc/jwtChain.d.cts +0 -40
  171. package/dist/ukyc/jwtChain.d.cts.map +0 -1
  172. package/dist/ukyc/jwtChain.d.mts.map +0 -1
  173. package/dist/ukyc/jwtChain.mjs.map +0 -1
  174. package/dist/ukyc/localUserSecret.cjs +0 -98
  175. package/dist/ukyc/localUserSecret.cjs.map +0 -1
  176. package/dist/ukyc/localUserSecret.d.cts +0 -65
  177. package/dist/ukyc/localUserSecret.d.cts.map +0 -1
  178. package/dist/ukyc/localUserSecret.d.mts.map +0 -1
  179. package/dist/ukyc/localUserSecret.mjs.map +0 -1
  180. package/dist/ukyc/storageAccessToken.cjs +0 -139
  181. package/dist/ukyc/storageAccessToken.cjs.map +0 -1
  182. package/dist/ukyc/storageAccessToken.d.cts +0 -99
  183. package/dist/ukyc/storageAccessToken.d.cts.map +0 -1
  184. package/dist/ukyc/storageAccessToken.d.mts.map +0 -1
  185. package/dist/ukyc/storageAccessToken.mjs.map +0 -1
  186. package/dist/ukyc/testToken.cjs +0 -61
  187. package/dist/ukyc/testToken.cjs.map +0 -1
  188. package/dist/ukyc/testToken.d.cts +0 -50
  189. package/dist/ukyc/testToken.d.cts.map +0 -1
  190. package/dist/ukyc/testToken.d.mts.map +0 -1
  191. package/dist/ukyc/testToken.mjs.map +0 -1
  192. package/dist/ukyc/wrapEncryptionKey.cjs +0 -31
  193. package/dist/ukyc/wrapEncryptionKey.cjs.map +0 -1
  194. package/dist/ukyc/wrapEncryptionKey.d.cts +0 -36
  195. package/dist/ukyc/wrapEncryptionKey.d.cts.map +0 -1
  196. package/dist/ukyc/wrapEncryptionKey.d.mts.map +0 -1
  197. package/dist/ukyc/wrapEncryptionKey.mjs.map +0 -1
  198. package/dist/ukyc/wrappedRelayPayload.cjs +0 -32
  199. package/dist/ukyc/wrappedRelayPayload.cjs.map +0 -1
  200. package/dist/ukyc/wrappedRelayPayload.d.cts +0 -35
  201. package/dist/ukyc/wrappedRelayPayload.d.cts.map +0 -1
  202. package/dist/ukyc/wrappedRelayPayload.d.mts.map +0 -1
  203. package/dist/ukyc/wrappedRelayPayload.mjs.map +0 -1
@@ -1,1882 +0,0 @@
1
- "use strict";
2
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
- if (kind === "m") throw new TypeError("Private method is not writable");
4
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
- };
8
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
- };
13
- var _KycController_instances, _KycController_sumsubLauncher, _KycController_moonpayFrameKeypair, _KycController_authClientToken, _KycController_generation, _KycController_sessionStatusPollIntervalMs, _KycController_pollTimer, _KycController_pollToken, _KycController_userStatusPollIntervalMs, _KycController_userStatusPollTimer, _KycController_userStatusPolling, _KycController_userStatusPollToken, _KycController_localUserSecretStore, _KycController_startConsentsSession, _KycController_recordSessionDisclaimers, _KycController_createSession, _KycController_clearAcceptedTerms, _KycController_clearMoonPaySession, _KycController_dropTermsUnlessForVendor, _KycController_hasTermsForVendor, _KycController_handleCheckOutcome, _KycController_handleAuthOutcome, _KycController_continueAfterAuthentication, _KycController_requireTermsReacceptance, _KycController_buildUkycSessionVendorFields, _KycController_createUkycSession, _KycController_fetchAndApplyUserStatus, _KycController_applyUserStatus, _KycController_ensureUserStatusPolling, _KycController_stopUserStatusPolling, _KycController_startSessionStatusPolling, _KycController_pollSessionStatusOnce, _KycController_stopPolling, _KycController_cancelPendingSession, _KycController_updateIfCurrent, _KycController_applyUpdate, _KycController_assertAttestedServerPublicKey, _KycController_fail;
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.KycController = exports.getDefaultKycControllerState = exports.controllerName = void 0;
16
- const base_controller_1 = require("@metamask/base-controller");
17
- const utils_1 = require("@metamask/utils");
18
- const ed25519_1 = require("@noble/curves/ed25519");
19
- const crypto_js_1 = require("./crypto.cjs");
20
- const encoding_js_1 = require("./encoding.cjs");
21
- const logger_js_1 = require("./logger.cjs");
22
- const deriveClientMaterial_js_1 = require("./ukyc/deriveClientMaterial.cjs");
23
- const jwtChain_js_1 = require("./ukyc/jwtChain.cjs");
24
- const localUserSecret_js_1 = require("./ukyc/localUserSecret.cjs");
25
- const storageAccessToken_js_1 = require("./ukyc/storageAccessToken.cjs");
26
- const wrapEncryptionKey_js_1 = require("./ukyc/wrapEncryptionKey.cjs");
27
- // === GENERAL ===
28
- exports.controllerName = 'KycController';
29
- const FRAMES_BASE_URL = 'https://blocks.moonpay.com/platform/v1';
30
- const CHANNEL_CHECK = 'ch_1';
31
- const CHANNEL_AUTH = 'ch_2';
32
- const CHANNEL_RESET = 'ch_reset';
33
- // Placeholder credentials for the SumSub sub-flow. These are demo values that
34
- // must be replaced with real UKYC-issued material before production use.
35
- const MOCK_JWT_TOKEN = 'mock-jwt-token';
36
- // Lifetime of the read-only `ukyc_capability_token` minted when creating a
37
- // UKYC session. The storage-and-auth spec requires the token's `expires_at` to
38
- // cover the KYC session's expected lifetime — including the provider journey —
39
- // rather than a fixed short window, so this is a session-scoped window.
40
- const UKYC_CAPABILITY_TOKEN_TTL_MS = 4 * 60 * 60 * 1000;
41
- // The SumSub SDK status that signals the applicant finished the flow
42
- // successfully. Any other resolution (abandonment, failure, or a non-success
43
- // outcome) must not be recorded as `complete`.
44
- const SUMSUB_COMPLETED_STATUS = 'Completed';
45
- // Phases that represent an active vendor-session flow (tokens issued and/or
46
- // Check/Auth frames in progress). A repeat `initialize` while in one of these
47
- // must not restart the session and disrupt the in-flight flow.
48
- const IN_PROGRESS_PHASES = [
49
- 'session',
50
- 'check',
51
- 'auth',
52
- 'form',
53
- 'submit',
54
- ];
55
- // How often to poll the UKYC session status after the SumSub SDK completes,
56
- // until a terminal status is reached. Overridable via the constructor.
57
- const DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS = 15000;
58
- // UKYC status values. `kycStatus` (the relay-side decision) and `finalStatus`
59
- // (the vendor-side outcome) draw from the same vocabulary, so they are defined
60
- // once here and composed into the sets/checks below rather than repeated as
61
- // literals.
62
- const KYC_STATUSES = {
63
- approved: 'approved',
64
- completed: 'completed',
65
- rejected: 'rejected',
66
- failed: 'failed',
67
- blocked: 'blocked',
68
- pending: 'pending',
69
- };
70
- // `finalStatus` values that end the polling loop. Anything else (e.g.
71
- // `KYC_STATUSES.pending`) keeps polling.
72
- const TERMINAL_SESSION_STATUSES = new Set([
73
- KYC_STATUSES.approved,
74
- KYC_STATUSES.completed,
75
- KYC_STATUSES.rejected,
76
- KYC_STATUSES.failed,
77
- KYC_STATUSES.blocked,
78
- ]);
79
- // Terminal `finalStatus` values that represent a successful verification. Any
80
- // other terminal status resolves the sub-flow to `failed`.
81
- const SUCCESSFUL_SESSION_STATUSES = new Set([
82
- KYC_STATUSES.approved,
83
- KYC_STATUSES.completed,
84
- ]);
85
- // Session creation can report that the applicant is already approved on the
86
- // relay (`kycStatus === KYC_STATUSES.approved`) while the vendor is still
87
- // finalizing its decision (`finalStatus === KYC_STATUSES.pending`, a
88
- // non-terminal status). In that case there is nothing left for the applicant
89
- // to do, so the sub-flow stops before launching the SDK and surfaces this
90
- // message.
91
- const VENDOR_PROCESSING_MESSAGE = 'Your KYC has been submitted and is being processed by the vendor.';
92
- // UKYC / relay error indicating the applicant already finished KYC. Mapped to
93
- // the simplified `completed` user status for the Money toast surface.
94
- const SESSION_NOT_IN_VALID_STATE = 'session_not_in_valid_state';
95
- // How often to refresh the user-keyed `GET /kyc/status` while the simplified
96
- // status is still `pending`. Overridable via the constructor.
97
- const DEFAULT_USER_STATUS_POLL_INTERVAL_MS = 15000;
98
- const kycControllerMetadata = {
99
- phase: {
100
- includeInDebugSnapshot: true,
101
- includeInStateLogs: true,
102
- persist: false,
103
- usedInUi: true,
104
- },
105
- statusMessage: {
106
- includeInDebugSnapshot: true,
107
- includeInStateLogs: true,
108
- persist: false,
109
- usedInUi: true,
110
- },
111
- error: {
112
- includeInDebugSnapshot: true,
113
- includeInStateLogs: true,
114
- persist: false,
115
- usedInUi: true,
116
- },
117
- email: {
118
- includeInDebugSnapshot: false,
119
- includeInStateLogs: false,
120
- persist: false,
121
- usedInUi: false,
122
- },
123
- termsAcceptedAt: {
124
- includeInDebugSnapshot: true,
125
- includeInStateLogs: true,
126
- persist: true,
127
- usedInUi: false,
128
- },
129
- acceptedDisclaimerIds: {
130
- includeInDebugSnapshot: true,
131
- includeInStateLogs: true,
132
- persist: true,
133
- usedInUi: false,
134
- },
135
- termsAcceptedVendor: {
136
- includeInDebugSnapshot: true,
137
- includeInStateLogs: true,
138
- persist: true,
139
- usedInUi: false,
140
- },
141
- sumsubTncAccepted: {
142
- includeInDebugSnapshot: true,
143
- includeInStateLogs: true,
144
- persist: true,
145
- usedInUi: false,
146
- },
147
- idosTncAccepted: {
148
- includeInDebugSnapshot: true,
149
- includeInStateLogs: true,
150
- persist: true,
151
- usedInUi: false,
152
- },
153
- credentialReusabilityConsentGiven: {
154
- includeInDebugSnapshot: true,
155
- includeInStateLogs: true,
156
- persist: false,
157
- usedInUi: false,
158
- },
159
- disclaimers: {
160
- includeInDebugSnapshot: false,
161
- includeInStateLogs: false,
162
- persist: false,
163
- usedInUi: true,
164
- },
165
- disclaimersError: {
166
- includeInDebugSnapshot: true,
167
- includeInStateLogs: true,
168
- persist: false,
169
- usedInUi: true,
170
- },
171
- sessionDisclaimers: {
172
- includeInDebugSnapshot: false,
173
- includeInStateLogs: false,
174
- persist: false,
175
- usedInUi: true,
176
- },
177
- geoCountry: {
178
- includeInDebugSnapshot: true,
179
- includeInStateLogs: true,
180
- persist: false,
181
- usedInUi: true,
182
- },
183
- sessionToken: {
184
- includeInDebugSnapshot: false,
185
- includeInStateLogs: false,
186
- persist: false,
187
- usedInUi: false,
188
- },
189
- accessToken: {
190
- includeInDebugSnapshot: false,
191
- includeInStateLogs: false,
192
- persist: false,
193
- usedInUi: false,
194
- },
195
- moonpayCustomerId: {
196
- includeInDebugSnapshot: false,
197
- includeInStateLogs: false,
198
- persist: false,
199
- usedInUi: false,
200
- },
201
- activeVendor: {
202
- includeInDebugSnapshot: true,
203
- includeInStateLogs: true,
204
- persist: false,
205
- usedInUi: true,
206
- },
207
- activeProduct: {
208
- includeInDebugSnapshot: true,
209
- includeInStateLogs: true,
210
- persist: false,
211
- usedInUi: true,
212
- },
213
- kycRequiredByProduct: {
214
- includeInDebugSnapshot: true,
215
- includeInStateLogs: true,
216
- persist: true,
217
- usedInUi: true,
218
- },
219
- lastCheckedAt: {
220
- includeInDebugSnapshot: true,
221
- includeInStateLogs: true,
222
- persist: true,
223
- usedInUi: false,
224
- },
225
- userStatus: {
226
- includeInDebugSnapshot: true,
227
- includeInStateLogs: true,
228
- persist: true,
229
- usedInUi: true,
230
- },
231
- userStatusSumsubSessionId: {
232
- includeInDebugSnapshot: false,
233
- includeInStateLogs: false,
234
- persist: true,
235
- usedInUi: true,
236
- },
237
- userStatusErrorCode: {
238
- includeInDebugSnapshot: true,
239
- includeInStateLogs: true,
240
- persist: true,
241
- usedInUi: true,
242
- },
243
- sumsub: {
244
- includeInDebugSnapshot: false,
245
- includeInStateLogs: false,
246
- persist: false,
247
- usedInUi: true,
248
- },
249
- };
250
- /**
251
- * Constructs the default {@link KycController} state.
252
- *
253
- * @returns The default state.
254
- */
255
- function getDefaultKycControllerState() {
256
- return {
257
- phase: 'idle',
258
- statusMessage: '',
259
- error: null,
260
- email: null,
261
- termsAcceptedAt: null,
262
- acceptedDisclaimerIds: [],
263
- termsAcceptedVendor: null,
264
- sumsubTncAccepted: null,
265
- idosTncAccepted: null,
266
- credentialReusabilityConsentGiven: null,
267
- disclaimers: [],
268
- disclaimersError: null,
269
- sessionDisclaimers: null,
270
- geoCountry: null,
271
- sessionToken: null,
272
- accessToken: null,
273
- moonpayCustomerId: null,
274
- activeVendor: 'moonpay',
275
- activeProduct: null,
276
- kycRequiredByProduct: {},
277
- lastCheckedAt: null,
278
- userStatus: null,
279
- userStatusSumsubSessionId: null,
280
- userStatusErrorCode: null,
281
- sumsub: {
282
- status: 'idle',
283
- result: null,
284
- sessionId: null,
285
- applicantAccessToken: null,
286
- sessionStatus: null,
287
- },
288
- };
289
- }
290
- exports.getDefaultKycControllerState = getDefaultKycControllerState;
291
- /**
292
- * Whether an error indicates the applicant already finished KYC — the UKYC /
293
- * relay `session_not_in_valid_state` signal — which the controller maps to the
294
- * simplified `completed` user status.
295
- *
296
- * @param error - The caught error.
297
- * @returns `true` when the error carries the `session_not_in_valid_state`
298
- * marker.
299
- */
300
- function isSessionAlreadyCompletedError(error) {
301
- return String(error).includes(SESSION_NOT_IN_VALID_STATE);
302
- }
303
- /**
304
- * Whether recording session disclaimers failed because those document
305
- * versions were already consented for the session (`409 Conflict`).
306
- *
307
- * @param error - The caught error.
308
- * @returns `true` when the error is an HTTP 409.
309
- */
310
- function isConsentConflictError(error) {
311
- return (typeof error === 'object' &&
312
- error !== null &&
313
- typeof error.httpStatus === 'number' &&
314
- error.httpStatus === 409);
315
- }
316
- /**
317
- * Maps a session-disclaimer catalog into the `{ key, version }` records the
318
- * record-consents API expects, or an empty list when the user declined that
319
- * category.
320
- *
321
- * @param documents - Catalog documents for one consent category.
322
- * @param accepted - Whether the user accepted that category.
323
- * @returns Consent records, or `[]` when not accepted.
324
- */
325
- function consentRecordsFromCatalog(documents, accepted) {
326
- if (!accepted) {
327
- return [];
328
- }
329
- return documents
330
- .filter((document) => !document.consented)
331
- .map(({ key, version }) => ({ key, version }));
332
- }
333
- /**
334
- * Whether an accepted T&C2 category has no catalog documents. An empty list
335
- * would otherwise skip the POST and count as success.
336
- *
337
- * @param documents - Catalog documents for one consent category.
338
- * @param accepted - Whether the user accepted that category.
339
- * @returns `true` when the user accepted and the catalog is empty.
340
- */
341
- function isAcceptedCategoryEmpty(documents, accepted) {
342
- return accepted && documents.length === 0;
343
- }
344
- /**
345
- * Whether an accepted category is still missing consent after a 409 re-GET:
346
- * empty catalog or any document still unconsented.
347
- *
348
- * @param documents - Latest catalog documents for one consent category.
349
- * @param accepted - Whether the user accepted that category.
350
- * @returns `true` when accepted documents are not fully consented.
351
- */
352
- function acceptedCategoryStillMissing(documents, accepted) {
353
- return (accepted &&
354
- (documents.length === 0 ||
355
- documents.some((document) => !document.consented)));
356
- }
357
- /**
358
- * Vendors other than MoonPay skip Check/Auth frames and use the empty-shell
359
- * customer + consents path instead.
360
- *
361
- * @param vendor - The identity vendor for the current flow.
362
- * @returns `true` when the vendor uses the consents session path.
363
- */
364
- function usesConsentsFlow(vendor) {
365
- return vendor !== 'moonpay';
366
- }
367
- // === MESSENGER ===
368
- const MESSENGER_EXPOSED_METHODS = [
369
- 'initialize',
370
- 'loadDisclaimers',
371
- 'acceptTermsAndStartSession',
372
- 'createVendorCustomer',
373
- 'clearSavedTerms',
374
- 'handleFrameMessage',
375
- 'buildCheckFrameUrl',
376
- 'buildAuthFrameUrl',
377
- 'buildResetFrameUrl',
378
- 'checkKycRequired',
379
- 'getKycStatus',
380
- 'getCustomerIdentity',
381
- 'refreshKycStatus',
382
- 'startSumSub',
383
- 'getSessionStatus',
384
- 'reset',
385
- 'clearState',
386
- ];
387
- // === CONTROLLER DEFINITION ===
388
- /**
389
- * `KycController` orchestrates the vendor-backed KYC / identity-verification
390
- * flow (MoonPay identity + SumSub documents) behind a vendor-neutral, per
391
- * product surface used by ramps and card. It owns all state, HTTP
392
- * orchestration (via `KycService`), crypto, and the frame message protocol;
393
- * platform-specific presentation (WebView/iframe, SumSub SDK) is injected.
394
- */
395
- class KycController extends base_controller_1.BaseController {
396
- /**
397
- * Constructs a new {@link KycController}.
398
- *
399
- * @param options - The constructor options.
400
- * @param options.messenger - The messenger suited for this controller.
401
- * @param options.state - Partial initial state; merged over defaults.
402
- * @param options.sumsubLauncher - The platform SumSub launcher adapter.
403
- * @param options.sessionStatusPollIntervalMs - How often to poll the UKYC
404
- * session status after the SumSub SDK completes.
405
- * @param options.userStatusPollIntervalMs - How often to refresh the
406
- * user-keyed KYC status while it is still `pending`.
407
- */
408
- constructor({ messenger, state, sumsubLauncher, sessionStatusPollIntervalMs = DEFAULT_SESSION_STATUS_POLL_INTERVAL_MS, userStatusPollIntervalMs = DEFAULT_USER_STATUS_POLL_INTERVAL_MS, }) {
409
- super({
410
- messenger,
411
- metadata: kycControllerMetadata,
412
- name: exports.controllerName,
413
- state: { ...getDefaultKycControllerState(), ...state },
414
- });
415
- _KycController_instances.add(this);
416
- _KycController_sumsubLauncher.set(this, void 0);
417
- /** MoonPay Check/Auth frame X25519 keypair (never persisted). */
418
- _KycController_moonpayFrameKeypair.set(this, null);
419
- /** Auth-frame client token, kept out of state. */
420
- _KycController_authClientToken.set(this, null);
421
- /**
422
- * Monotonic flow generation. Incremented by {@link reset} and
423
- * {@link clearState} so in-flight async work (e.g. the KYC-required check)
424
- * can detect that it was superseded and avoid writing stale results onto a
425
- * reset controller.
426
- */
427
- _KycController_generation.set(this, 0);
428
- /** Interval, in milliseconds, between session-status polls. */
429
- _KycController_sessionStatusPollIntervalMs.set(this, void 0);
430
- /** Handle for the scheduled next session-status poll, or `null`. */
431
- _KycController_pollTimer.set(this, null);
432
- /**
433
- * Monotonic polling token. Bumped by {@link #stopPolling} (called on reset, a
434
- * new sub-flow, and once a terminal status is reached) so an in-flight poll
435
- * `tick` can detect it was superseded and neither write state nor schedule a
436
- * follow-up. This closes the gap where clearing the timer alone would still
437
- * let an already-awaiting request finish and reschedule.
438
- */
439
- _KycController_pollToken.set(this, 0);
440
- /** Interval, in milliseconds, between user-keyed status polls. */
441
- _KycController_userStatusPollIntervalMs.set(this, void 0);
442
- /** Handle for the scheduled next user-status poll, or `null`. */
443
- _KycController_userStatusPollTimer.set(this, null);
444
- /**
445
- * Whether a user-status poll loop is currently active. Tracked separately
446
- * from {@link #userStatusPollTimer} because a scheduled tick clears the timer
447
- * handle before awaiting `fetchKycStatus`; relying on the handle alone would
448
- * let a concurrent {@link refreshKycStatus} start a second loop on the same
449
- * token during that in-flight window.
450
- */
451
- _KycController_userStatusPolling.set(this, false);
452
- /** Monotonic token for the user-status poll loop (see `#pollToken`). */
453
- _KycController_userStatusPollToken.set(this, 0);
454
- __classPrivateFieldSet(this, _KycController_sumsubLauncher, sumsubLauncher, "f");
455
- __classPrivateFieldSet(this, _KycController_sessionStatusPollIntervalMs, sessionStatusPollIntervalMs, "f");
456
- __classPrivateFieldSet(this, _KycController_userStatusPollIntervalMs, userStatusPollIntervalMs, "f");
457
- this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
458
- }
459
- /**
460
- * Resolves persisted terms + geolocation, and auto-creates a session when
461
- * terms are already accepted and an email is available.
462
- *
463
- * @param params - Optional parameters.
464
- * @param params.email - The account email to associate with the session.
465
- * @param params.product - The consuming feature the flow runs for. When
466
- * provided, the controller automatically runs the KYC-required check once
467
- * authentication completes (and chains into document verification when KYC
468
- * is required). When omitted, the flow stops at `form` and the consumer must
469
- * call `checkKycRequired` manually.
470
- * @param params.vendor - Identity vendor for this flow. Non-MoonPay vendors
471
- * skip Check/Auth frames and use the consents path. Defaults to `moonpay`.
472
- */
473
- async initialize(params) {
474
- // A repeat `initialize` while a session flow is already in progress must
475
- // not tear it down: creating a new vendor session clears the tokens and
476
- // forces `phase` back through `session`/`check`, breaking an in-flight
477
- // Check/Auth frame flow. Leave the active flow untouched and let the
478
- // consumer drive it (or call `reset` first to start over).
479
- const vendor = params?.vendor ?? 'moonpay';
480
- if (IN_PROGRESS_PHASES.includes(this.state.phase)) {
481
- if (vendor === 'moonpay' && !__classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f")) {
482
- __classPrivateFieldSet(this, _KycController_moonpayFrameKeypair, (0, crypto_js_1.generateKeyPair)(), "f");
483
- }
484
- return;
485
- }
486
- if (vendor === 'moonpay') {
487
- __classPrivateFieldSet(this, _KycController_moonpayFrameKeypair, (0, crypto_js_1.generateKeyPair)(), "f");
488
- }
489
- else {
490
- __classPrivateFieldSet(this, _KycController_moonpayFrameKeypair, null, "f");
491
- }
492
- // `initialize` starts a fresh flow, so `activeProduct` is always reset to
493
- // this call's product (or `null`). Otherwise a prior run's product could
494
- // linger and cause `#continueAfterAuthentication` to auto-run the check /
495
- // sub-flow when the caller intended the manual (product-less) flow.
496
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
497
- if (params?.email) {
498
- state.email = params.email;
499
- }
500
- state.activeVendor = vendor;
501
- // MoonPay Check/Auth artifacts must not survive a switch to another
502
- // vendor: leftover `sessionToken` would keep `buildCheckFrameUrl` alive,
503
- // leftover `accessToken` / `#authClientToken` would keep Auth / KYC
504
- // calls bound to MoonPay, and leftover `moonpayCustomerId` would make
505
- // `getCustomerIdentity` report a MoonPay id under the wrong vendor.
506
- if (vendor !== 'moonpay') {
507
- __classPrivateFieldSet(this, _KycController_authClientToken, null, "f");
508
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearMoonPaySession).call(this, state);
509
- }
510
- state.activeProduct = params?.product ?? null;
511
- });
512
- // Capture the flow generation so a `reset()` landing while the async
513
- // geolocation / session steps below are in flight cannot write results
514
- // onto an idle controller.
515
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
516
- // Resolve country for display; non-blocking.
517
- try {
518
- const country = await this.messenger.call('KycService:getGeoCountry');
519
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
520
- state.geoCountry = country;
521
- });
522
- }
523
- catch {
524
- // Ignore; disclaimers loading will surface a country error if needed.
525
- }
526
- // A `reset()` / `clearState()` that landed while the geolocation request
527
- // was in flight supersedes this flow. Stop here rather than driving the
528
- // controller on into `terms` (or a new session): the steps below write
529
- // unconditionally, and `loadDisclaimers` captures the post-reset
530
- // generation, so its own guard would not catch this.
531
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
532
- return;
533
- }
534
- if (usesConsentsFlow(vendor) && this.state.email) {
535
- try {
536
- await this.messenger.call('KycService:createVendorCustomer', {
537
- vendor,
538
- email: this.state.email,
539
- });
540
- }
541
- catch (error) {
542
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
543
- return;
544
- }
545
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Vendor customer creation failed: ${String(error)}`);
546
- return;
547
- }
548
- }
549
- // Drop another vendor's persisted acceptance only after this flow has
550
- // committed (customer creation succeeded, or there was none to wait for).
551
- // Clearing earlier would permanently lose ramps/card terms if Iron
552
- // customer creation failed or a reset landed while it was in flight.
553
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
554
- return;
555
- }
556
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_dropTermsUnlessForVendor).call(this, vendor);
557
- const hasTerms = Boolean(this.state.termsAcceptedAt) &&
558
- this.state.acceptedDisclaimerIds.length > 0;
559
- if (hasTerms && this.state.email) {
560
- if (usesConsentsFlow(vendor)) {
561
- // Consents-path vendors require T&C2 flags; if they weren't persisted
562
- // (i.e. null from pre-migration state), require reacceptance.
563
- const sumsubTncSigned = this.state.sumsubTncAccepted;
564
- const idosTncSigned = this.state.idosTncAccepted;
565
- if (sumsubTncSigned === null || idosTncSigned === null) {
566
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
567
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
568
- state.phase = 'terms';
569
- });
570
- await this.loadDisclaimers();
571
- return;
572
- }
573
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startConsentsSession).call(this, {
574
- sumsubTncSigned,
575
- idosTncSigned,
576
- credentialReusabilityConsentGiven: this.state.credentialReusabilityConsentGiven ?? false,
577
- });
578
- }
579
- else {
580
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
581
- }
582
- return;
583
- }
584
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
585
- state.phase = 'terms';
586
- });
587
- await this.loadDisclaimers();
588
- }
589
- /**
590
- * Creates (or resumes) an empty-shell customer for the given identity
591
- * vendor. Exposed so a consumer can ensure the customer exists before
592
- * showing T&C screens independently of {@link initialize}.
593
- *
594
- * A call while a session flow is already in progress is a no-op — matching
595
- * {@link initialize} — so a vendor switch cannot leave Check/Auth frames
596
- * attached to the wrong vendor. Call {@link reset} first to start over.
597
- *
598
- * @param params - The parameters.
599
- * @param params.vendor - Identity vendor for the customer.
600
- * @param params.email - Email for the vendor customer.
601
- */
602
- async createVendorCustomer(params) {
603
- if (IN_PROGRESS_PHASES.includes(this.state.phase)) {
604
- return;
605
- }
606
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
607
- state.email = params.email;
608
- // MoonPay Check/Auth artifacts must not survive a switch to another
609
- // vendor — see `initialize`. Terms for another vendor are dropped only
610
- // after this request succeeds.
611
- state.activeVendor = params.vendor;
612
- if (params.vendor !== 'moonpay') {
613
- __classPrivateFieldSet(this, _KycController_authClientToken, null, "f");
614
- __classPrivateFieldSet(this, _KycController_moonpayFrameKeypair, null, "f");
615
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearMoonPaySession).call(this, state);
616
- }
617
- });
618
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
619
- try {
620
- await this.messenger.call('KycService:createVendorCustomer', {
621
- vendor: params.vendor,
622
- email: params.email,
623
- });
624
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
625
- return;
626
- }
627
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_dropTermsUnlessForVendor).call(this, params.vendor);
628
- }
629
- catch (error) {
630
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
631
- return;
632
- }
633
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Vendor customer creation failed: ${String(error)}`);
634
- }
635
- }
636
- /**
637
- * Loads the disclaimers for the resolved (or provided) country.
638
- *
639
- * @param params - Optional parameters.
640
- * @param params.country - ISO 3166-1 alpha-3 country code override.
641
- */
642
- async loadDisclaimers(params) {
643
- // Capture the flow generation so a `reset()` landing while the geo /
644
- // disclaimers requests are in flight cannot write results onto an idle
645
- // controller.
646
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
647
- try {
648
- const country = params?.country ??
649
- this.state.geoCountry ??
650
- (await this.messenger.call('KycService:getGeoCountry'));
651
- if (country !== this.state.geoCountry) {
652
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
653
- state.geoCountry = country;
654
- });
655
- }
656
- const disclaimers = await this.messenger.call('KycService:fetchVendorDisclaimers', {
657
- vendor: this.state.activeVendor,
658
- country,
659
- });
660
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
661
- state.disclaimers = disclaimers;
662
- state.disclaimersError = null;
663
- });
664
- }
665
- catch (error) {
666
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
667
- state.disclaimersError = `Failed to load disclaimers: ${String(error)}`;
668
- });
669
- }
670
- }
671
- /**
672
- * Captures terms acceptance for the currently loaded disclaimers and creates
673
- * a session.
674
- *
675
- * @param params - The parameters.
676
- * @param params.email - The account email to associate with the session.
677
- * @param params.product - The consuming feature the flow runs for. See
678
- * {@link initialize} for how the product drives the automatic post
679
- * authentication continuation.
680
- * @param params.sumsubTncSigned - Whether Sumsub T&C were accepted (T&C2).
681
- * Required for every vendor so callers explicitly declare acceptance.
682
- * @param params.idosTncSigned - Whether idOS T&C were accepted (T&C2).
683
- * Required for every vendor so callers explicitly declare acceptance.
684
- * @param params.credentialReusabilityConsentGiven - Whether the customer
685
- * consented to reuse existing idOS credentials. Used when recording
686
- * session-scoped disclaimers on the consents path. Defaults to `false`.
687
- */
688
- async acceptTermsAndStartSession(params) {
689
- const sumsubTncSigned = params?.sumsubTncSigned;
690
- const idosTncSigned = params?.idosTncSigned;
691
- if (typeof sumsubTncSigned !== 'boolean' ||
692
- typeof idosTncSigned !== 'boolean') {
693
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing T&C2 acceptance flags.');
694
- return;
695
- }
696
- const credentialReusabilityConsentGiven = params.credentialReusabilityConsentGiven ?? false;
697
- const termsAcceptedAt = new Date().toISOString();
698
- const disclaimerIds = this.state.disclaimers.map((disclaimer) => disclaimer.id);
699
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
700
- if (params.email) {
701
- state.email = params.email;
702
- }
703
- if (params.product) {
704
- state.activeProduct = params.product;
705
- }
706
- state.termsAcceptedAt = termsAcceptedAt;
707
- state.acceptedDisclaimerIds = disclaimerIds;
708
- state.termsAcceptedVendor = state.activeVendor;
709
- state.sumsubTncAccepted = sumsubTncSigned;
710
- state.idosTncAccepted = idosTncSigned;
711
- state.credentialReusabilityConsentGiven =
712
- credentialReusabilityConsentGiven;
713
- });
714
- if (usesConsentsFlow(this.state.activeVendor)) {
715
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startConsentsSession).call(this, {
716
- sumsubTncSigned,
717
- idosTncSigned,
718
- credentialReusabilityConsentGiven,
719
- });
720
- return;
721
- }
722
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createSession).call(this);
723
- }
724
- /**
725
- * Clears the persisted terms acceptance.
726
- */
727
- clearSavedTerms() {
728
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
729
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
730
- });
731
- }
732
- /**
733
- * Handles a message posted by a Check/Auth frame and advances the flow.
734
- *
735
- * The transport-agnostic caller (WebView on mobile, iframe on web) forwards
736
- * the raw message and injects the returned `reply` back into the frame.
737
- *
738
- * @param params - The parameters.
739
- * @param params.message - The raw message posted by the frame.
740
- * @returns An object whose optional `reply` should be posted back.
741
- */
742
- async handleFrameMessage(params) {
743
- const payload = params.message;
744
- if (!payload) {
745
- return {};
746
- }
747
- if (payload.kind === 'handshake') {
748
- const channelId = payload.meta?.channelId;
749
- return { reply: { version: 2, meta: { channelId }, kind: 'ack' } };
750
- }
751
- if (payload.kind !== 'complete') {
752
- return {};
753
- }
754
- const channelId = payload.meta?.channelId;
755
- // Only honor a Check/Auth `complete` for the MoonPay frame the flow is
756
- // currently waiting on. This drops stale or duplicate messages — e.g. a
757
- // late post after `reset()` (phase `idle`), after the flow already
758
- // advanced past this frame, or after a vendor switch — so they cannot
759
- // resurrect tokens, rewind `phase`, or recapture `moonpayCustomerId` on a
760
- // controller that has moved on. Frame messages are external input and,
761
- // unlike the async steps, are not covered by the `#generation` guard.
762
- let expectedPhase = null;
763
- if (channelId === CHANNEL_CHECK) {
764
- expectedPhase = 'check';
765
- }
766
- else if (channelId === CHANNEL_AUTH) {
767
- expectedPhase = 'auth';
768
- }
769
- if (!expectedPhase ||
770
- this.state.phase !== expectedPhase ||
771
- this.state.activeVendor !== 'moonpay') {
772
- return {};
773
- }
774
- const status = payload.payload?.status;
775
- const credsEnvelope = payload.payload?.credentials;
776
- const customerId = payload.payload?.customer?.id ?? null;
777
- if (customerId) {
778
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
779
- state.moonpayCustomerId = customerId;
780
- });
781
- }
782
- if (!status) {
783
- return {};
784
- }
785
- let accessToken;
786
- let clientToken;
787
- if (credsEnvelope && __classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f")) {
788
- try {
789
- const { credentials } = (0, crypto_js_1.decryptCredentials)(credsEnvelope, __classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f").privateKey);
790
- accessToken = credentials.accessToken;
791
- clientToken = credentials.clientToken;
792
- }
793
- catch (error) {
794
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Failed to decrypt frame credentials: ${String(error)}`);
795
- return {};
796
- }
797
- }
798
- if (channelId === CHANNEL_CHECK) {
799
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_handleCheckOutcome).call(this, status, accessToken, clientToken);
800
- return {};
801
- }
802
- // channelId === CHANNEL_AUTH, guaranteed by the expectedPhase guard above.
803
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_handleAuthOutcome).call(this, status, accessToken);
804
- return {};
805
- }
806
- /**
807
- * Builds the Check-frame URL, or `null` when no session exists yet.
808
- *
809
- * @returns The Check-frame URL or `null`.
810
- */
811
- buildCheckFrameUrl() {
812
- if (this.state.activeVendor !== 'moonpay' ||
813
- !this.state.sessionToken ||
814
- !__classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f")) {
815
- return null;
816
- }
817
- const url = new URL(`${FRAMES_BASE_URL}/check-connection`);
818
- url.searchParams.set('sessionToken', this.state.sessionToken);
819
- url.searchParams.set('publicKey', __classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f").publicKeyHex);
820
- url.searchParams.set('channelId', CHANNEL_CHECK);
821
- url.searchParams.set('skipKyc', 'true');
822
- return url.toString();
823
- }
824
- /**
825
- * Builds the Auth-frame URL, or `null` when no client token is available.
826
- *
827
- * @returns The Auth-frame URL or `null`.
828
- */
829
- buildAuthFrameUrl() {
830
- if (this.state.activeVendor !== 'moonpay' ||
831
- !__classPrivateFieldGet(this, _KycController_authClientToken, "f") ||
832
- !__classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f")) {
833
- return null;
834
- }
835
- const url = new URL(`${FRAMES_BASE_URL}/auth`);
836
- url.searchParams.set('clientToken', __classPrivateFieldGet(this, _KycController_authClientToken, "f"));
837
- url.searchParams.set('publicKey', __classPrivateFieldGet(this, _KycController_moonpayFrameKeypair, "f").publicKeyHex);
838
- url.searchParams.set('channelId', CHANNEL_AUTH);
839
- return url.toString();
840
- }
841
- /**
842
- * Builds the Reset-frame URL.
843
- *
844
- * @returns The Reset-frame URL.
845
- */
846
- buildResetFrameUrl() {
847
- const url = new URL(`${FRAMES_BASE_URL}/reset`);
848
- url.searchParams.set('channelId', CHANNEL_RESET);
849
- return url.toString();
850
- }
851
- /**
852
- * Checks whether KYC is required for a product and caches the result.
853
- *
854
- * @param params - The parameters.
855
- * @param params.product - The consuming feature.
856
- * @param params.country - Optional alpha-3 country override.
857
- * @returns Whether KYC is required.
858
- */
859
- async checkKycRequired(params) {
860
- const { accessToken } = this.state;
861
- if (!accessToken) {
862
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing accessToken — repeat the authentication step.');
863
- return false;
864
- }
865
- const country = params.country ?? this.state.geoCountry;
866
- if (!country) {
867
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing country for KYC-required check.');
868
- return false;
869
- }
870
- // Capture the flow generation so we can detect a `reset()` that happens
871
- // while the HTTP call is in flight and avoid writing stale results.
872
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
873
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
874
- state.phase = 'submit';
875
- state.statusMessage = 'Checking KYC status...';
876
- });
877
- try {
878
- const { kycRequired } = await this.messenger.call('KycService:checkKycRequired', { accessToken, country, capabilities: [{ product: params.product }] });
879
- // The flow was reset while the check was in flight; discard the result
880
- // rather than resurrecting a done/cached state on an idle controller.
881
- const applied = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
882
- state.kycRequiredByProduct[params.product] = kycRequired;
883
- state.lastCheckedAt = new Date().toISOString();
884
- state.phase = 'done';
885
- state.statusMessage = 'KYC check complete.';
886
- });
887
- if (!applied) {
888
- return false;
889
- }
890
- return kycRequired;
891
- }
892
- catch (error) {
893
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
894
- return false;
895
- }
896
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `KYC check failed: ${String(error)}`);
897
- return false;
898
- }
899
- }
900
- /**
901
- * Reads the cached "is KYC required" result for a product.
902
- *
903
- * @param params - The parameters.
904
- * @param params.product - The consuming feature.
905
- * @returns The cached value, or `undefined` if not yet checked.
906
- */
907
- getKycStatus(params) {
908
- return this.state.kycRequiredByProduct[params.product];
909
- }
910
- /**
911
- * Returns the vendor-scoped identity for the currently authenticated
912
- * customer, or `null` when the flow has not yet captured a vendor customer
913
- * id (before authentication or after {@link reset}), or when a MoonPay id
914
- * is present under a different `activeVendor`.
915
- *
916
- * Exposed so consumers (e.g. ramps autoramp creation) can attach the vendor
917
- * customer id to downstream calls without reading the full KYC state, which
918
- * also holds session/access tokens. The id is session-scoped and never
919
- * persisted.
920
- *
921
- * @returns The current {@link KycCustomerIdentity}, or `null`.
922
- */
923
- getCustomerIdentity() {
924
- const { moonpayCustomerId, activeVendor } = this.state;
925
- // `moonpayCustomerId` is issued only by MoonPay Check/Auth frames. Never
926
- // pair it with another vendor, even if a switch left the fields out of
927
- // sync, so consumers cannot attach a MoonPay id to an Iron (or other)
928
- // downstream call.
929
- if (!moonpayCustomerId || activeVendor !== 'moonpay') {
930
- return null;
931
- }
932
- return { vendor: 'moonpay', id: moonpayCustomerId };
933
- }
934
- /**
935
- * Runs the SumSub document-verification sub-flow end to end:
936
- *
937
- * 1. creates a UKYC session, receiving per-secret encryption schemas;
938
- * 2. verifies the `encryptionDataKey` schema's `jwtChain` against the
939
- * idOS enclave JWKS and the `ukycCapabilityToken` schema's `jwtChain` against
940
- * the idOS relay JWKS, then confirms each attested session server public
941
- * key;
942
- * 3. derives the `data_encryption_key` from the wallet's UKYC
943
- * `local_user_secret` and wraps it for the session server;
944
- * 4. mints a client-signed, read-only `ukyc_capability_token`, wraps it the
945
- * same way as the encryption key, and submits both via authorizations;
946
- * 5. fetches the SumSub applicant access token; and
947
- * 6. presents the SDK via the injected launcher.
948
- *
949
- * If a UKYC session already exists (the consents path creates it before
950
- * recording session disclaimers), steps 1–4 are skipped.
951
- *
952
- * If authorizations report the applicant is already approved on the relay
953
- * while the vendor is still finalizing (`kycStatus: approved`,
954
- * `finalStatus: pending`), the sub-flow stops at step 4 with a
955
- * `vendorProcessing` status and a message rather than launching the SDK.
956
- *
957
- * @param params - Optional parameters.
958
- * @param params.locale - BCP-47 locale for the SDK UI.
959
- * @param params.debug - Enables SDK debug logging.
960
- * @returns The SDK result.
961
- */
962
- async startSumSub(params) {
963
- // A new sub-flow supersedes any polling still running from a prior run.
964
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
965
- if (!__classPrivateFieldGet(this, _KycController_sumsubLauncher, "f").isAvailable()) {
966
- const error = 'SumSub SDK is not available in this runtime.';
967
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
968
- state.sumsub.status = 'failed';
969
- state.sumsub.result = { error };
970
- });
971
- throw new Error(error);
972
- }
973
- // Capture the flow generation so each async step can detect a `reset()`
974
- // that lands mid-flight and avoid writing stale sub-flow state (or, worse,
975
- // presenting the SDK) on a controller that is now idle.
976
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
977
- try {
978
- if (!this.state.sumsub.sessionId) {
979
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
980
- state.sumsub.status = 'creatingSession';
981
- state.sumsub.result = null;
982
- state.sumsub.sessionStatus = null;
983
- });
984
- const created = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createUkycSession).call(this, generation);
985
- if (!created) {
986
- return {};
987
- }
988
- // A user who already finished the journey can return to a session the
989
- // relay has already approved (`kycStatus`) while the vendor is still
990
- // finalizing its own decision (`finalStatus`). There is nothing left to
991
- // verify, so stop here and surface a message rather than launching the
992
- // SDK again.
993
- if (created.vendorProcessing) {
994
- return {
995
- kycStatus: created.kycStatus,
996
- finalStatus: created.finalStatus,
997
- };
998
- }
999
- }
1000
- // Empty string is a valid "no id to poll" session id used by tests and
1001
- // must not be coalesced away as missing.
1002
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1003
- const sessionId = this.state.sumsub.sessionId || '';
1004
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1005
- state.sumsub.status = 'fetchingToken';
1006
- state.sumsub.sessionId = sessionId;
1007
- });
1008
- const { applicantAccessToken } = await this.messenger.call('KycService:createJourney', sessionId);
1009
- // A reset() may have landed while the session/token was being prepared.
1010
- // Gate the `launching` write and the decision to open the SDK behind a
1011
- // single generation check: `#updateIfCurrent` only writes when still
1012
- // current and reports whether it did. Since there is no `await` between
1013
- // this check and `launch` below, a successful result guarantees the SDK
1014
- // is never presented on a flow that a concurrent reset() returned to idle.
1015
- const stillCurrent = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1016
- state.sumsub.status = 'launching';
1017
- state.sumsub.applicantAccessToken = applicantAccessToken;
1018
- });
1019
- if (!stillCurrent) {
1020
- return {};
1021
- }
1022
- // Track whether the SDK ever reported a successful completion. A resolved
1023
- // `launch` alone does not imply success — the applicant may have
1024
- // abandoned the flow or the SDK may have reported a non-success outcome.
1025
- let reachedCompletion = false;
1026
- const result = await __classPrivateFieldGet(this, _KycController_sumsubLauncher, "f").launch({
1027
- applicantAccessToken,
1028
- onTokenExpiration: async () => {
1029
- // A reset() may have superseded this flow while the SDK stayed open.
1030
- // Refuse to refresh against the now-stale UKYC session rather than
1031
- // silently keeping an orphaned SDK alive.
1032
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1033
- throw new Error('KYC flow was reset; SumSub session is no longer active.');
1034
- }
1035
- const refreshed = await this.messenger.call('KycService:createJourney', sessionId);
1036
- return refreshed.applicantAccessToken;
1037
- },
1038
- onStatusChange: (_prev, next) => {
1039
- if (next === SUMSUB_COMPLETED_STATUS) {
1040
- reachedCompletion = true;
1041
- }
1042
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1043
- state.sumsub.status =
1044
- next === SUMSUB_COMPLETED_STATUS ? 'complete' : 'inProgress';
1045
- });
1046
- },
1047
- locale: params?.locale ?? 'en',
1048
- debug: params?.debug ?? false,
1049
- });
1050
- // A resolved `launch` alone is not the final outcome: only a SDK-reported
1051
- // completion is worth polling for a verification decision. Anything else
1052
- // (abandonment, non-success) is `failed` and must not be polled.
1053
- const applied = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1054
- state.sumsub.status = reachedCompletion ? 'polling' : 'failed';
1055
- state.sumsub.result = result;
1056
- });
1057
- // Once the SDK completes, the authoritative verification decision comes
1058
- // from the UKYC backend, not the SDK result. Poll the session status
1059
- // until it reaches a terminal decision. Guard on `applied` so a `reset()`
1060
- // that landed during `launch` cannot start polling on an idle flow.
1061
- if (applied && reachedCompletion) {
1062
- if (sessionId) {
1063
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_startSessionStatusPolling).call(this, sessionId);
1064
- }
1065
- else {
1066
- // No session id to poll against; fall back to treating the SDK
1067
- // completion as the final outcome.
1068
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1069
- state.sumsub.status = 'complete';
1070
- });
1071
- }
1072
- }
1073
- return result;
1074
- }
1075
- catch (error) {
1076
- // Applicant already finished KYC — treat as completed for Money toast.
1077
- if (isSessionAlreadyCompletedError(error)) {
1078
- // A reset() may have landed while `launch` was in flight; forcing
1079
- // `completed` (and publishing `statusChanged`) on an idle controller
1080
- // would resurrect a flow the consumer already tore down.
1081
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1082
- return { alreadyCompleted: true };
1083
- }
1084
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, {
1085
- status: 'completed',
1086
- sumsubSessionId: null,
1087
- errorCode: null,
1088
- });
1089
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1090
- state.sumsub.status = 'complete';
1091
- state.sumsub.result = { alreadyCompleted: true };
1092
- state.statusMessage = 'KYC already completed.';
1093
- state.phase = 'done';
1094
- state.error = null;
1095
- });
1096
- return { alreadyCompleted: true };
1097
- }
1098
- const result = { error: String(error) };
1099
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1100
- state.sumsub.status = 'failed';
1101
- state.sumsub.result = result;
1102
- });
1103
- return result;
1104
- }
1105
- }
1106
- /**
1107
- * Refreshes the user-keyed simplified KYC status from `GET /kyc/status`,
1108
- * stores it on state, publishes {@link KycControllerStatusChangedEvent}, and
1109
- * schedules short-interval polling while the status is `pending`.
1110
- *
1111
- * @returns The latest status payload.
1112
- */
1113
- async refreshKycStatus() {
1114
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1115
- const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
1116
- // A `reset()` landing while the request was in flight already stopped
1117
- // polling and left the flow idle, and the payload above is the pre-reset
1118
- // cached status. Starting a loop from it would poll — and publish
1119
- // `statusChanged` — on a torn-down flow.
1120
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1121
- return payload;
1122
- }
1123
- if (payload.status === 'pending') {
1124
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_ensureUserStatusPolling).call(this);
1125
- }
1126
- else {
1127
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
1128
- }
1129
- return payload;
1130
- }
1131
- /**
1132
- * Fetches the current UKYC session status for the active sub-flow and records
1133
- * it on state. Useful for a one-off refresh outside the automatic polling
1134
- * loop that {@link startSumSub} runs.
1135
- *
1136
- * @returns The fetched session status.
1137
- * @throws If there is no active SumSub session to query.
1138
- */
1139
- async getSessionStatus() {
1140
- const { sessionId } = this.state.sumsub;
1141
- if (!sessionId) {
1142
- throw new Error('Cannot fetch session status: no active SumSub session.');
1143
- }
1144
- // Capture the flow generation so a `reset()` landing while the request is
1145
- // in flight cannot write the result onto an idle controller.
1146
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1147
- const sessionStatus = await this.messenger.call('KycService:getSessionStatus', { sessionId });
1148
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1149
- state.sumsub.sessionStatus = sessionStatus;
1150
- });
1151
- return sessionStatus;
1152
- }
1153
- /**
1154
- * Resets the flow to idle, clearing session tokens and sub-flow state while
1155
- * preserving persisted terms acceptance and the per-product cache.
1156
- */
1157
- reset() {
1158
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_cancelPendingSession).call(this);
1159
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1160
- state.phase = 'idle';
1161
- state.statusMessage = '';
1162
- state.error = null;
1163
- state.disclaimers = [];
1164
- state.disclaimersError = null;
1165
- state.sessionDisclaimers = null;
1166
- state.credentialReusabilityConsentGiven = null;
1167
- state.sessionToken = null;
1168
- state.accessToken = null;
1169
- state.moonpayCustomerId = null;
1170
- state.activeVendor = 'moonpay';
1171
- state.activeProduct = null;
1172
- state.sumsub = {
1173
- status: 'idle',
1174
- result: null,
1175
- sessionId: null,
1176
- applicantAccessToken: null,
1177
- sessionStatus: null,
1178
- };
1179
- });
1180
- }
1181
- /**
1182
- * Restores the controller to its default state, discarding everything
1183
- * {@link reset} deliberately keeps: the session email, the persisted terms
1184
- * acceptance, the per-product KYC-required cache and the user-keyed status.
1185
- *
1186
- * Intended for a full wallet reset, where no trace of the previous
1187
- * customer may survive into the next wallet.
1188
- */
1189
- clearState() {
1190
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_cancelPendingSession).call(this);
1191
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1192
- Object.assign(state, getDefaultKycControllerState());
1193
- });
1194
- }
1195
- }
1196
- exports.KycController = KycController;
1197
- _KycController_sumsubLauncher = new WeakMap(), _KycController_moonpayFrameKeypair = 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_userStatusPolling = new WeakMap(), _KycController_userStatusPollToken = new WeakMap(), _KycController_instances = new WeakSet(), _KycController_localUserSecretStore = function _KycController_localUserSecretStore() {
1198
- return {
1199
- get: async (path, entropySourceId) => this.messenger.call('UserStorageController:performGetStorage', path, entropySourceId),
1200
- set: async (path, value, entropySourceId) => this.messenger.call('UserStorageController:performSetStorage', path, value, entropySourceId),
1201
- };
1202
- }, _KycController_startConsentsSession =
1203
- /**
1204
- * Consents-path vendors: record vendor T&Cs, create a UKYC session,
1205
- * record session-scoped idOS / KYC-provider disclaimers, then launch
1206
- * SumSub — skipping MoonPay Check/Auth frames.
1207
- *
1208
- * @param consents - T&C2 flags mapped onto the session disclaimer catalog.
1209
- * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1210
- * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1211
- * @param consents.credentialReusabilityConsentGiven - Whether credential
1212
- * reuse was accepted.
1213
- */
1214
- async function _KycController_startConsentsSession(consents) {
1215
- const { email, acceptedDisclaimerIds } = this.state;
1216
- if (!email) {
1217
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing email for consents session.');
1218
- return;
1219
- }
1220
- if (acceptedDisclaimerIds.length === 0) {
1221
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing disclaimer acceptance.');
1222
- return;
1223
- }
1224
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1225
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1226
- state.error = null;
1227
- state.phase = 'session';
1228
- state.statusMessage = 'Submitting consents...';
1229
- state.sumsub.status = 'creatingSession';
1230
- state.sumsub.result = null;
1231
- state.sumsub.sessionStatus = null;
1232
- // Consents-path vendors have no MoonPay session/access tokens.
1233
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearMoonPaySession).call(this, state);
1234
- });
1235
- try {
1236
- await this.messenger.call('KycService:submitVendorDisclaimers', {
1237
- vendor: this.state.activeVendor,
1238
- disclaimerIds: acceptedDisclaimerIds,
1239
- });
1240
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1241
- return;
1242
- }
1243
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1244
- state.statusMessage = 'Creating session...';
1245
- });
1246
- const created = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_createUkycSession).call(this, generation);
1247
- if (!created || __classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1248
- return;
1249
- }
1250
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_recordSessionDisclaimers).call(this, created.sessionId, consents, generation);
1251
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1252
- return;
1253
- }
1254
- if (created.vendorProcessing) {
1255
- try {
1256
- await this.refreshKycStatus();
1257
- }
1258
- catch (statusError) {
1259
- (0, logger_js_1.controllerLog)('KYC status refresh failed:', statusError);
1260
- }
1261
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1262
- state.phase = 'done';
1263
- state.statusMessage = VENDOR_PROCESSING_MESSAGE;
1264
- });
1265
- return;
1266
- }
1267
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1268
- state.phase = 'submit';
1269
- state.statusMessage = 'Starting document verification...';
1270
- });
1271
- const sumsubResult = await this.startSumSub();
1272
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1273
- return;
1274
- }
1275
- // `startSumSub` records `sumsub.status = 'failed'` for thrown steps,
1276
- // an SDK close without Completed, *and* a terminal UKYC rejection
1277
- // after the SDK reported Completed. Only rewind when there is no
1278
- // session-status decision yet (abandonment / thrown step). A
1279
- // Completed-then-rejected poll writes `sessionStatus` and is a
1280
- // finished flow: refresh user status and land on `done`.
1281
- if (this.state.sumsub.status === 'failed' &&
1282
- this.state.sumsub.sessionStatus === null) {
1283
- const sumsubError = sumsubResult?.error;
1284
- throw new Error(typeof sumsubError === 'string'
1285
- ? sumsubError
1286
- : 'SumSub verification did not complete.');
1287
- }
1288
- // After SumSub, refresh user-keyed status for the Money toast and start
1289
- // polling while still pending. Soft-fail: toast refresh must not rewind
1290
- // the consent / SumSub outcome.
1291
- try {
1292
- await this.refreshKycStatus();
1293
- }
1294
- catch (statusError) {
1295
- (0, logger_js_1.controllerLog)('KYC status refresh failed:', statusError);
1296
- }
1297
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1298
- if (state.phase !== 'error' && state.phase !== 'done') {
1299
- state.phase = 'done';
1300
- state.statusMessage = 'KYC submitted.';
1301
- }
1302
- });
1303
- }
1304
- catch (error) {
1305
- if (isSessionAlreadyCompletedError(error)) {
1306
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1307
- return;
1308
- }
1309
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, {
1310
- status: 'completed',
1311
- sumsubSessionId: null,
1312
- errorCode: null,
1313
- });
1314
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1315
- state.sumsub.status = 'complete';
1316
- state.sumsub.result = { alreadyCompleted: true };
1317
- state.statusMessage = 'KYC already completed.';
1318
- state.phase = 'done';
1319
- state.error = null;
1320
- });
1321
- return;
1322
- }
1323
- (0, logger_js_1.controllerLog)('Consents session failed:', error);
1324
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1325
- return;
1326
- }
1327
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1328
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1329
- state.activeProduct = null;
1330
- state.sessionDisclaimers = null;
1331
- // Session create ran before recording disclaimers. Drop the leftover
1332
- // UKYC session so a later `startSumSub` cannot skip consent recording.
1333
- state.sumsub = { ...getDefaultKycControllerState().sumsub };
1334
- state.error = `Consents session failed: ${String(error)}`;
1335
- state.statusMessage =
1336
- 'Consent / verification failed — accept the terms to try again.';
1337
- state.phase = 'terms';
1338
- });
1339
- await this.loadDisclaimers();
1340
- }
1341
- }, _KycController_recordSessionDisclaimers =
1342
- /**
1343
- * Fetches the session-scoped disclaimer catalog and records consents
1344
- * derived from the T&C2 flags. Already-consented catalog rows are omitted
1345
- * from the POST. A 409 is re-checked with a GET: continue only when every
1346
- * accepted document is now consented, otherwise fail closed.
1347
- *
1348
- * @param sessionId - The UKYC session id.
1349
- * @param consents - T&C2 flags mapped onto catalog documents.
1350
- * @param consents.sumsubTncSigned - Whether Sumsub T&C were accepted.
1351
- * @param consents.idosTncSigned - Whether idOS T&C were accepted.
1352
- * @param consents.credentialReusabilityConsentGiven - Whether credential
1353
- * reuse was accepted.
1354
- * @param generation - Flow generation captured by the caller.
1355
- */
1356
- async function _KycController_recordSessionDisclaimers(sessionId, consents, generation) {
1357
- const catalog = await this.messenger.call('KycService:fetchSessionDisclaimers', { sessionId });
1358
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1359
- return;
1360
- }
1361
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1362
- state.sessionDisclaimers = catalog;
1363
- state.statusMessage = 'Submitting consents...';
1364
- });
1365
- if (isAcceptedCategoryEmpty(catalog.idOS, consents.idosTncSigned) ||
1366
- isAcceptedCategoryEmpty(catalog.kycProvider, consents.sumsubTncSigned)) {
1367
- throw new Error('Session disclaimer catalog is missing documents for an accepted category.');
1368
- }
1369
- const idOS = consentRecordsFromCatalog(catalog.idOS, consents.idosTncSigned);
1370
- const kycProvider = consentRecordsFromCatalog(catalog.kycProvider, consents.sumsubTncSigned);
1371
- const reuseUnchanged = catalog.credentialReusabilityConsentGiven ===
1372
- consents.credentialReusabilityConsentGiven;
1373
- if (idOS.length === 0 && kycProvider.length === 0 && reuseUnchanged) {
1374
- return;
1375
- }
1376
- try {
1377
- const recorded = await this.messenger.call('KycService:submitSessionDisclaimers', {
1378
- sessionId,
1379
- idOS,
1380
- kycProvider,
1381
- credentialReusabilityConsentGiven: consents.credentialReusabilityConsentGiven,
1382
- });
1383
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1384
- state.sessionDisclaimers = recorded;
1385
- });
1386
- }
1387
- catch (error) {
1388
- if (!isConsentConflictError(error)) {
1389
- throw error;
1390
- }
1391
- // 409 means some document version was already recorded. Re-fetch and
1392
- // continue only when every document the user accepted is now consented;
1393
- // otherwise fail closed so a version bump cannot skip new docs.
1394
- const latest = await this.messenger.call('KycService:fetchSessionDisclaimers', { sessionId });
1395
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1396
- return;
1397
- }
1398
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1399
- state.sessionDisclaimers = latest;
1400
- });
1401
- const stillMissingIdos = acceptedCategoryStillMissing(latest.idOS, consents.idosTncSigned);
1402
- const stillMissingProvider = acceptedCategoryStillMissing(latest.kycProvider, consents.sumsubTncSigned);
1403
- const stillMissingReuse = consents.credentialReusabilityConsentGiven &&
1404
- !latest.credentialReusabilityConsentGiven;
1405
- if (stillMissingIdos || stillMissingProvider || stillMissingReuse) {
1406
- throw error;
1407
- }
1408
- }
1409
- }, _KycController_createSession =
1410
- /**
1411
- * Creates a vendor session from the currently stored terms + email.
1412
- */
1413
- async function _KycController_createSession() {
1414
- const { email, termsAcceptedAt, acceptedDisclaimerIds } = this.state;
1415
- if (!email) {
1416
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing email for session creation.');
1417
- return;
1418
- }
1419
- if (!termsAcceptedAt || acceptedDisclaimerIds.length === 0) {
1420
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, 'Missing terms acceptance for session creation.');
1421
- return;
1422
- }
1423
- // A new session invalidates any authentication carried over from a prior
1424
- // session. Clear the stale session token, access token, and auth-frame
1425
- // client token so `buildCheckFrameUrl` cannot return a URL bound to an old
1426
- // (or, on failure, invalid) session token, `buildAuthFrameUrl` cannot
1427
- // return a URL tied to an old client token, and `checkKycRequired` cannot
1428
- // run with an access token from an earlier authentication. The Check/Auth
1429
- // frames re-populate these for the new session. Because `sessionToken` is
1430
- // cleared here and only re-set on success, a failed creation leaves it
1431
- // `null` rather than resurrecting the previous session.
1432
- // Capture the flow generation so a `reset()` landing while the create
1433
- // request is in flight cannot resurrect a session (success) or overwrite
1434
- // the now-idle controller (failure). The synchronous update below runs
1435
- // before any `await`, so it needs no guard.
1436
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1437
- __classPrivateFieldSet(this, _KycController_authClientToken, null, "f");
1438
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1439
- state.error = null;
1440
- state.phase = 'session';
1441
- state.statusMessage = 'Creating session...';
1442
- state.sessionToken = null;
1443
- state.accessToken = null;
1444
- });
1445
- try {
1446
- const { sessionToken } = await this.messenger.call('KycService:createSession', { email, termsAcceptedAt, disclaimerIds: acceptedDisclaimerIds });
1447
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1448
- state.sessionToken = sessionToken;
1449
- state.phase = 'check';
1450
- state.statusMessage = 'Authenticating via Check frame...';
1451
- });
1452
- }
1453
- catch (error) {
1454
- (0, logger_js_1.controllerLog)('Session creation failed:', error);
1455
- // A reset() superseded this flow while the request was in flight; leave
1456
- // the idle controller alone rather than forcing it back to `terms`.
1457
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1458
- return;
1459
- }
1460
- // Invalidate the stored acceptance so the customer can retry. Also clear
1461
- // `activeProduct` so a later `acceptTermsAndStartSession` that omits a
1462
- // product cannot auto-run the KYC check / SumSub chain for this failed
1463
- // flow's product — matching how `initialize` starts from a clean product.
1464
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1465
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1466
- state.activeProduct = null;
1467
- state.error = `Session creation failed: ${String(error)}`;
1468
- state.statusMessage =
1469
- 'Session creation failed — accept the terms to try again.';
1470
- state.phase = 'terms';
1471
- });
1472
- await this.loadDisclaimers();
1473
- }
1474
- }, _KycController_clearAcceptedTerms = function _KycController_clearAcceptedTerms(state) {
1475
- state.termsAcceptedAt = null;
1476
- state.acceptedDisclaimerIds = [];
1477
- state.termsAcceptedVendor = null;
1478
- state.sumsubTncAccepted = null;
1479
- state.idosTncAccepted = null;
1480
- state.credentialReusabilityConsentGiven = null;
1481
- }, _KycController_clearMoonPaySession = function _KycController_clearMoonPaySession(state) {
1482
- state.moonpayCustomerId = null;
1483
- state.sessionToken = null;
1484
- state.accessToken = null;
1485
- }, _KycController_dropTermsUnlessForVendor = function _KycController_dropTermsUnlessForVendor(vendor) {
1486
- if (__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_hasTermsForVendor).call(this, vendor)) {
1487
- return;
1488
- }
1489
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1490
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1491
- });
1492
- }, _KycController_hasTermsForVendor = function _KycController_hasTermsForVendor(vendor) {
1493
- if (this.state.termsAcceptedVendor === null) {
1494
- return false;
1495
- }
1496
- return this.state.termsAcceptedVendor === vendor;
1497
- }, _KycController_handleCheckOutcome =
1498
- /**
1499
- * Applies a Check-frame outcome.
1500
- *
1501
- * @param status - The frame status.
1502
- * @param accessToken - The decrypted access token, if any.
1503
- * @param clientToken - The decrypted client token, if any.
1504
- */
1505
- async function _KycController_handleCheckOutcome(status, accessToken, clientToken) {
1506
- if (status === 'active' && accessToken) {
1507
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1508
- state.accessToken = accessToken;
1509
- state.phase = 'form';
1510
- state.statusMessage = 'Already authenticated. Review to submit.';
1511
- });
1512
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_continueAfterAuthentication).call(this);
1513
- return;
1514
- }
1515
- if (status === 'connectionRequired' && clientToken) {
1516
- __classPrivateFieldSet(this, _KycController_authClientToken, clientToken, "f");
1517
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1518
- state.phase = 'auth';
1519
- state.statusMessage = 'Verify your email via OTP in the Auth frame.';
1520
- });
1521
- return;
1522
- }
1523
- if (status === 'termsAcceptanceRequired') {
1524
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_requireTermsReacceptance).call(this);
1525
- return;
1526
- }
1527
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Check frame returned status: ${status}`);
1528
- }, _KycController_handleAuthOutcome =
1529
- /**
1530
- * Applies an Auth-frame outcome.
1531
- *
1532
- * @param status - The frame status.
1533
- * @param accessToken - The decrypted access token, if any.
1534
- */
1535
- async function _KycController_handleAuthOutcome(status, accessToken) {
1536
- if (status === 'active' && accessToken) {
1537
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1538
- state.accessToken = accessToken;
1539
- state.phase = 'form';
1540
- state.statusMessage = 'Authenticated. Review to submit.';
1541
- });
1542
- await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_continueAfterAuthentication).call(this);
1543
- return;
1544
- }
1545
- if (status === 'termsAcceptanceRequired') {
1546
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_requireTermsReacceptance).call(this);
1547
- return;
1548
- }
1549
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fail).call(this, `Auth frame returned status: ${status}`);
1550
- }, _KycController_continueAfterAuthentication =
1551
- /**
1552
- * Continues the flow once authentication has completed (phase `form`).
1553
- *
1554
- * When the flow is scoped to a product (see {@link initialize}), the
1555
- * KYC-required check runs automatically, and — when KYC is required — the
1556
- * document-verification sub-flow is launched. When no product is set, this is
1557
- * a no-op and the flow stays at `form` for the consumer to drive manually.
1558
- *
1559
- * Errors are already recorded on state by `checkKycRequired` (`error`
1560
- * phase) and `startSumSub` (`sumsub.status = 'failed'`); this method swallows
1561
- * them so it can be awaited safely from the frame-message handler.
1562
- */
1563
- async function _KycController_continueAfterAuthentication() {
1564
- const product = this.state.activeProduct;
1565
- if (!product) {
1566
- return;
1567
- }
1568
- // Re-entry protection lives at the frame boundary: `handleFrameMessage`
1569
- // only honors a Check/Auth `complete` while `phase` matches and
1570
- // `activeVendor` is MoonPay, and both outcome handlers move `phase` to
1571
- // `form` before awaiting this method. A duplicate, late, or cross-vendor
1572
- // `complete` therefore lands after the phase moved on (or on the wrong
1573
- // vendor) and is dropped before it can start a second continuation. Any
1574
- // writes here are additionally guarded by `#generation` (see
1575
- // `checkKycRequired` / `startSumSub`) so a `reset()` mid-continuation
1576
- // cannot corrupt state.
1577
- const kycRequired = await this.checkKycRequired({ product });
1578
- if (!kycRequired) {
1579
- return;
1580
- }
1581
- try {
1582
- await this.startSumSub();
1583
- }
1584
- catch {
1585
- // `startSumSub` already records `sumsub.status = 'failed'`; swallow the
1586
- // rethrown error (e.g. SDK unavailable) so the awaited continuation
1587
- // resolves cleanly rather than surfacing as an unhandled rejection.
1588
- }
1589
- }, _KycController_requireTermsReacceptance = function _KycController_requireTermsReacceptance() {
1590
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1591
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_clearAcceptedTerms).call(this, state);
1592
- state.phase = 'terms';
1593
- state.statusMessage =
1594
- 'The vendor updated its Terms of Use — please re-accept.';
1595
- });
1596
- }, _KycController_buildUkycSessionVendorFields = function _KycController_buildUkycSessionVendorFields() {
1597
- if (this.state.activeVendor === 'moonpay') {
1598
- return {
1599
- vendor: 'moonpay',
1600
- vendorMetadata: {
1601
- moonPayAccessToken: this.state.accessToken,
1602
- moonPayUserId: this.state.moonpayCustomerId,
1603
- },
1604
- };
1605
- }
1606
- return { vendor: this.state.activeVendor };
1607
- }, _KycController_createUkycSession =
1608
- /**
1609
- * Creates a UKYC session, wraps the `data_encryption_key` and
1610
- * `ukyc_capability_token` against the returned encryption schemas, and
1611
- * submits both via authorizations. Stores `sumsub.sessionId`. Returns `null`
1612
- * when a `reset()` superseded the flow.
1613
- *
1614
- * @param generation - Flow generation captured by the caller.
1615
- * @returns The created session, or `null` if superseded.
1616
- */
1617
- async function _KycController_createUkycSession(generation) {
1618
- const jwtToken = MOCK_JWT_TOKEN;
1619
- // Establish a per-session X25519 keypair used to seal both secrets. The
1620
- // private half stays on the device; the public half is registered on the
1621
- // session so the server can open later authorizations. Each encryption
1622
- // schema from session creation supplies the matching server public key.
1623
- const sessionClientPrivateKey = ed25519_1.x25519.utils.randomSecretKey();
1624
- const sessionClientPublicKey = (0, encoding_js_1.toBase64Url)(ed25519_1.x25519.getPublicKey(sessionClientPrivateKey));
1625
- // Residence is the ISO 3166-1 alpha-3 country already resolved for
1626
- // disclaimers / KYC-required; fetch it if this sub-flow started without
1627
- // that earlier step.
1628
- const residenceCountry = this.state.geoCountry ??
1629
- (await this.messenger.call('KycService:getGeoCountry'));
1630
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1631
- return null;
1632
- }
1633
- if (residenceCountry !== this.state.geoCountry) {
1634
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1635
- state.geoCountry = residenceCountry;
1636
- });
1637
- }
1638
- const { sessionId, encryptionDataKey, ukycCapabilityToken: capabilityTokenSchema, } = await this.messenger.call('KycService:createUkycSession', {
1639
- jwtToken,
1640
- sessionClientPublicKey,
1641
- residenceCountry,
1642
- ...__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_buildUkycSessionVendorFields).call(this),
1643
- });
1644
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1645
- return null;
1646
- }
1647
- // Verify each schema's jwtChain against the matching issuer JWKS, then
1648
- // confirm the returned server public key matches the value attested inside
1649
- // the verified JWT payload before trusting it for wrapping.
1650
- // `encryptionDataKey` is attested by the idOS enclave; `ukycCapabilityToken` by the
1651
- // idOS relay.
1652
- const [{ keys: idosEnclaveKeys }, { keys: idosRelayKeys }] = await Promise.all([
1653
- this.messenger.call('KycService:fetchIdosEnclaveJwks'),
1654
- this.messenger.call('KycService:fetchIdosRelayJwks'),
1655
- ]);
1656
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_assertAttestedServerPublicKey).call(this, idosEnclaveKeys, encryptionDataKey);
1657
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_assertAttestedServerPublicKey).call(this, idosRelayKeys, capabilityTokenSchema);
1658
- // Derive the data_encryption_key from the local_user_secret, mint a
1659
- // read-only capability token, and wrap both for the session server. Only
1660
- // the wrapped (encrypted) material ever leaves the device.
1661
- const localUserSecret = await (0, localUserSecret_js_1.getOrCreateLocalUserSecret)(__classPrivateFieldGet(this, _KycController_instances, "m", _KycController_localUserSecretStore).call(this));
1662
- const clientMaterial = (0, deriveClientMaterial_js_1.deriveClientMaterial)(localUserSecret);
1663
- const wrappedEncryptionDataKey = (0, wrapEncryptionKey_js_1.wrapEncryptionKey)(sessionClientPrivateKey, encryptionDataKey.serverPublicKey.x, clientMaterial.dataEncryptionKey);
1664
- // Only the client holds the signing key derived from `local_user_secret`,
1665
- // so only the client can mint the token; scoping it to `read` means it
1666
- // authorizes later storage reads without granting write or delete access.
1667
- const ukycCapabilityToken = (0, storageAccessToken_js_1.signStorageAccessToken)({
1668
- material: clientMaterial,
1669
- operations: ['read'],
1670
- expiresAt: new Date(Date.now() + UKYC_CAPABILITY_TOKEN_TTL_MS),
1671
- });
1672
- const wrappedUkycCapabilityToken = (0, wrapEncryptionKey_js_1.wrapEncryptionKey)(sessionClientPrivateKey, capabilityTokenSchema.serverPublicKey.x, (0, utils_1.stringToBytes)((0, storageAccessToken_js_1.encodeStorageAccessTokenForHeader)(ukycCapabilityToken)));
1673
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1674
- return null;
1675
- }
1676
- const { kycStatus, finalStatus } = await this.messenger.call('KycService:setAuthorizations', {
1677
- sessionId,
1678
- wrappedEncryptionDataKey,
1679
- wrappedUkycCapabilityToken,
1680
- });
1681
- const vendorProcessing = kycStatus === KYC_STATUSES.approved &&
1682
- finalStatus === KYC_STATUSES.pending;
1683
- const stillCurrent = __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_updateIfCurrent).call(this, generation, (state) => {
1684
- state.sumsub.sessionId = sessionId;
1685
- if (vendorProcessing) {
1686
- state.sumsub.status = 'vendorProcessing';
1687
- state.statusMessage = VENDOR_PROCESSING_MESSAGE;
1688
- }
1689
- });
1690
- if (!stillCurrent) {
1691
- return null;
1692
- }
1693
- return { sessionId, kycStatus, finalStatus, vendorProcessing };
1694
- }, _KycController_fetchAndApplyUserStatus =
1695
- /**
1696
- * Fetches `GET /kyc/status` and applies it to state without managing the
1697
- * poll loop (used by both {@link refreshKycStatus} and the poll tick).
1698
- *
1699
- * @returns The latest status payload.
1700
- */
1701
- async function _KycController_fetchAndApplyUserStatus() {
1702
- const generation = __classPrivateFieldGet(this, _KycController_generation, "f");
1703
- const response = await this.messenger.call('KycService:fetchKycStatus');
1704
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1705
- return {
1706
- status: this.state.userStatus ?? 'not-started',
1707
- sumsubSessionId: this.state.userStatusSumsubSessionId,
1708
- errorCode: this.state.userStatusErrorCode,
1709
- };
1710
- }
1711
- const payload = {
1712
- status: response.status,
1713
- sumsubSessionId: response.sumsubSessionId ?? null,
1714
- errorCode: response.errorCode ?? null,
1715
- };
1716
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUserStatus).call(this, payload);
1717
- return payload;
1718
- }, _KycController_applyUserStatus = function _KycController_applyUserStatus(payload) {
1719
- const previous = this.state.userStatus;
1720
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1721
- state.userStatus = payload.status;
1722
- state.userStatusSumsubSessionId = payload.sumsubSessionId;
1723
- state.userStatusErrorCode = payload.errorCode;
1724
- });
1725
- if (previous !== payload.status) {
1726
- this.messenger.publish(`${exports.controllerName}:statusChanged`, payload);
1727
- }
1728
- }, _KycController_ensureUserStatusPolling = function _KycController_ensureUserStatusPolling() {
1729
- if (__classPrivateFieldGet(this, _KycController_userStatusPolling, "f")) {
1730
- return;
1731
- }
1732
- __classPrivateFieldSet(this, _KycController_userStatusPolling, true, "f");
1733
- const token = __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f");
1734
- const tick = async () => {
1735
- try {
1736
- const payload = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_fetchAndApplyUserStatus).call(this);
1737
- // Race with `reset()` / `#stopUserStatusPolling` while the request was
1738
- // in flight — do not reschedule onto an idle controller.
1739
- /* istanbul ignore next */
1740
- if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1741
- return;
1742
- }
1743
- if (payload.status !== 'pending') {
1744
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
1745
- return;
1746
- }
1747
- }
1748
- catch {
1749
- // Keep polling on transient errors, unless the loop was superseded.
1750
- /* istanbul ignore next */
1751
- if (__classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") !== token) {
1752
- return;
1753
- }
1754
- }
1755
- __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1756
- __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1757
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1758
- tick();
1759
- }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1760
- // Allow the process to exit while a pending-status poll is scheduled.
1761
- // React Native / browser timers are numbers with no `unref`, hence the
1762
- // optional call.
1763
- __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1764
- };
1765
- __classPrivateFieldSet(this, _KycController_userStatusPollTimer, setTimeout(() => {
1766
- __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1767
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1768
- tick();
1769
- }, __classPrivateFieldGet(this, _KycController_userStatusPollIntervalMs, "f")), "f");
1770
- __classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f").unref?.();
1771
- }, _KycController_stopUserStatusPolling = function _KycController_stopUserStatusPolling() {
1772
- __classPrivateFieldSet(this, _KycController_userStatusPollToken, __classPrivateFieldGet(this, _KycController_userStatusPollToken, "f") + 1, "f");
1773
- __classPrivateFieldSet(this, _KycController_userStatusPolling, false, "f");
1774
- if (__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f") !== null) {
1775
- clearTimeout(__classPrivateFieldGet(this, _KycController_userStatusPollTimer, "f"));
1776
- __classPrivateFieldSet(this, _KycController_userStatusPollTimer, null, "f");
1777
- }
1778
- }, _KycController_startSessionStatusPolling =
1779
- /**
1780
- * Begins polling the UKYC session status until a terminal decision is
1781
- * reached. The first poll runs immediately (and is awaited by
1782
- * {@link startSumSub}); subsequent polls are scheduled every
1783
- * `#sessionStatusPollIntervalMs`.
1784
- *
1785
- * @param sessionId - The UKYC session id to poll.
1786
- * @returns A promise that resolves once the first poll settles.
1787
- */
1788
- async function _KycController_startSessionStatusPolling(sessionId) {
1789
- // Supersede any prior loop and claim a fresh token for this one. Because
1790
- // `#stopPolling` bumps the token, any in-flight poll from a previous loop
1791
- // sees a mismatch and neither writes state nor reschedules.
1792
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
1793
- const token = __classPrivateFieldGet(this, _KycController_pollToken, "f");
1794
- const tick = async () => {
1795
- const shouldStop = await __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_pollSessionStatusOnce).call(this, sessionId, token);
1796
- if (shouldStop) {
1797
- return;
1798
- }
1799
- __classPrivateFieldSet(this, _KycController_pollTimer, setTimeout(() => {
1800
- __classPrivateFieldSet(this, _KycController_pollTimer, null, "f");
1801
- // `tick` swallows its own errors (see `#pollSessionStatusOnce`) and
1802
- // therefore never rejects, so this fire-and-forget scheduled poll
1803
- // cannot surface as an unhandled rejection.
1804
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1805
- tick();
1806
- }, __classPrivateFieldGet(this, _KycController_sessionStatusPollIntervalMs, "f")), "f");
1807
- };
1808
- await tick();
1809
- }, _KycController_pollSessionStatusOnce =
1810
- /**
1811
- * Performs a single session-status poll: fetches the status, records it, and
1812
- * resolves the sub-flow when the status is terminal.
1813
- *
1814
- * Transient errors are swallowed so the loop keeps polling; the last good
1815
- * `sessionStatus` is deliberately preserved rather than being overwritten
1816
- * with the error.
1817
- *
1818
- * @param sessionId - The UKYC session id to poll.
1819
- * @param token - The polling token captured when the loop started.
1820
- * @returns `true` when the loop should stop (terminal status or superseded
1821
- * by a reset / new sub-flow), `false` when it should keep polling.
1822
- */
1823
- async function _KycController_pollSessionStatusOnce(sessionId, token) {
1824
- try {
1825
- const sessionStatus = await this.messenger.call('KycService:getSessionStatus', { sessionId });
1826
- // Superseded while the request was in flight — drop the result.
1827
- if (__classPrivateFieldGet(this, _KycController_pollToken, "f") !== token) {
1828
- return true;
1829
- }
1830
- const isTerminal = TERMINAL_SESSION_STATUSES.has(sessionStatus.finalStatus);
1831
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1832
- state.sumsub.sessionStatus = sessionStatus;
1833
- if (isTerminal) {
1834
- state.sumsub.status = SUCCESSFUL_SESSION_STATUSES.has(sessionStatus.finalStatus)
1835
- ? 'complete'
1836
- : 'failed';
1837
- }
1838
- });
1839
- if (isTerminal) {
1840
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
1841
- }
1842
- return isTerminal;
1843
- }
1844
- catch {
1845
- // Keep polling on transient errors, preserving the last good status.
1846
- // Stop only when a reset / new sub-flow superseded this loop.
1847
- return __classPrivateFieldGet(this, _KycController_pollToken, "f") !== token;
1848
- }
1849
- }, _KycController_stopPolling = function _KycController_stopPolling() {
1850
- __classPrivateFieldSet(this, _KycController_pollToken, __classPrivateFieldGet(this, _KycController_pollToken, "f") + 1, "f");
1851
- if (__classPrivateFieldGet(this, _KycController_pollTimer, "f") !== null) {
1852
- clearTimeout(__classPrivateFieldGet(this, _KycController_pollTimer, "f"));
1853
- __classPrivateFieldSet(this, _KycController_pollTimer, null, "f");
1854
- }
1855
- }, _KycController_cancelPendingSession = function _KycController_cancelPendingSession() {
1856
- __classPrivateFieldSet(this, _KycController_authClientToken, null, "f");
1857
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopPolling).call(this);
1858
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_stopUserStatusPolling).call(this);
1859
- __classPrivateFieldSet(this, _KycController_generation, __classPrivateFieldGet(this, _KycController_generation, "f") + 1, "f");
1860
- }, _KycController_updateIfCurrent = function _KycController_updateIfCurrent(generation, updater) {
1861
- if (__classPrivateFieldGet(this, _KycController_generation, "f") !== generation) {
1862
- return false;
1863
- }
1864
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, updater);
1865
- return true;
1866
- }, _KycController_applyUpdate = function _KycController_applyUpdate(updater) {
1867
- this.update((state) => {
1868
- // @ts-expect-error Avoid "type instantiation is excessively deep".
1869
- updater(state);
1870
- });
1871
- }, _KycController_assertAttestedServerPublicKey = function _KycController_assertAttestedServerPublicKey(keys, schema) {
1872
- const jwtChainPayload = (0, jwtChain_js_1.verifyJwtChain)(keys, schema.jwtChain);
1873
- if (jwtChainPayload.sessionServerPublicKeyX !== schema.serverPublicKey.x) {
1874
- throw new Error('sessionServerPublicKey does not match the verified jwtChain payload (sessionServerPublicKeyX).');
1875
- }
1876
- }, _KycController_fail = function _KycController_fail(message) {
1877
- __classPrivateFieldGet(this, _KycController_instances, "m", _KycController_applyUpdate).call(this, (state) => {
1878
- state.error = message;
1879
- state.phase = 'error';
1880
- });
1881
- };
1882
- //# sourceMappingURL=KycController.cjs.map