@rhinestone/deposit-modal 0.0.0-dev-20261005152729 → 0.0.0-dev-20261006132523
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -8
- package/dist/{BankOnrampStep-5JZU2VV7.cjs → BankOnrampStep-BJVXBEV5.cjs} +79 -100
- package/dist/{BankOnrampStep-2MGAO3L2.mjs → BankOnrampStep-TOE3JOPY.mjs} +75 -96
- package/dist/{DepositModalReown-QEXHGPJ7.mjs → DepositModalReown-2MS6WDSA.mjs} +3 -3
- package/dist/{DepositModalReown-7ARAFK5D.cjs → DepositModalReown-DNFBKJDD.cjs} +16 -16
- package/dist/WithdrawModal-HJ7BBSUK.cjs +12 -0
- package/dist/{WithdrawModal-SIVVBR2V.mjs → WithdrawModal-SC2LBNOS.mjs} +3 -3
- package/dist/{bearer-transport-CVHNBgoV.d.cts → bearer-transport-DZyeDG64.d.cts} +5 -1
- package/dist/{bearer-transport-CVHNBgoV.d.ts → bearer-transport-DZyeDG64.d.ts} +5 -1
- package/dist/{chunk-ERIMNOQF.mjs → chunk-3R3A7OYL.mjs} +8 -3
- package/dist/{chunk-ZO745YKG.cjs → chunk-5ACTCWIE.cjs} +1 -1
- package/dist/{chunk-IE62LNIK.mjs → chunk-7I2SG3KU.mjs} +1 -1
- package/dist/{chunk-6OFZPWBL.cjs → chunk-AXCGY2TG.cjs} +2 -2
- package/dist/{chunk-4Y7VO7E6.cjs → chunk-BAVRVXLV.cjs} +5 -5
- package/dist/{chunk-CCDDZTYW.cjs → chunk-DSM4IHTM.cjs} +10 -5
- package/dist/{chunk-3N33UIXN.cjs → chunk-FEQQJCJR.cjs} +286 -281
- package/dist/{chunk-MPGJAZQS.cjs → chunk-FO6P7HJA.cjs} +48 -33
- package/dist/{chunk-UQ6JWKI6.mjs → chunk-GR23TV4U.mjs} +3 -3
- package/dist/{chunk-5ZQCRQ7R.cjs → chunk-GRUBTFN4.cjs} +58 -58
- package/dist/{chunk-STHQNQSI.mjs → chunk-HQDTWHSI.mjs} +1 -1
- package/dist/{chunk-RHMWW357.mjs → chunk-SD733OIA.mjs} +17 -2
- package/dist/{chunk-6KNIVGZM.mjs → chunk-T255BMBC.mjs} +1 -1
- package/dist/{chunk-2ITR5NSI.mjs → chunk-WOOAUDHW.mjs} +58 -53
- package/dist/compliance.cjs +4 -4
- package/dist/compliance.d.cts +2 -2
- package/dist/compliance.d.ts +2 -2
- package/dist/compliance.mjs +3 -3
- package/dist/constants.cjs +2 -2
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/deposit.cjs +4 -4
- package/dist/deposit.d.cts +3 -3
- package/dist/deposit.d.ts +3 -3
- package/dist/deposit.mjs +3 -3
- package/dist/embed.cjs +14 -14
- package/dist/embed.d.cts +2 -3
- package/dist/embed.d.ts +2 -3
- package/dist/embed.mjs +4 -4
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +4 -4
- package/dist/onramp.cjs +4 -4
- package/dist/onramp.d.cts +2 -2
- package/dist/onramp.d.ts +2 -2
- package/dist/onramp.mjs +3 -3
- package/dist/{types-Bb-X7UNj.d.ts → types-BxSrazbq.d.ts} +1 -4
- package/dist/{types-DwGSrnlX.d.cts → types-CixUBhrM.d.cts} +1 -4
- package/dist/withdraw.cjs +4 -4
- package/dist/withdraw.d.cts +3 -4
- package/dist/withdraw.d.ts +3 -4
- package/dist/withdraw.mjs +3 -3
- package/package.json +1 -1
- package/dist/WithdrawModal-BU5WWC45.cjs +0 -12
package/README.md
CHANGED
|
@@ -326,9 +326,12 @@ frame-src https://widget.swapped.com https://sandbox.swapped.com https://connect
|
|
|
326
326
|
|
|
327
327
|
### Bank transfer accounts (Noah)
|
|
328
328
|
|
|
329
|
-
> **Preview.** Your proxy must forward
|
|
330
|
-
>
|
|
331
|
-
>
|
|
329
|
+
> **Preview.** Your proxy must forward these on the customer bearer before you ship
|
|
330
|
+
> a modal with `bankOnramp`: `GET /compliance/status`, `POST /compliance/verification`,
|
|
331
|
+
> `GET /onramp/options`, `POST`/`GET /onramp/accounts`,
|
|
332
|
+
> `GET /onramp/accounts/:id[/details]` and `GET /onramp/orders[/:id]`. A missing one
|
|
333
|
+
> 404s at the proxy. [`deposit-widget-proxy`](https://github.com/rhinestonewtf/deposit-widget-proxy)
|
|
334
|
+
> forwards all of them.
|
|
332
335
|
>
|
|
333
336
|
> The prop is opt-in and inert unless you pass it. Treat it as unstable until
|
|
334
337
|
> this note is removed.
|
|
@@ -337,10 +340,18 @@ Pass `bankOnramp` to use reusable USD/EUR account details when Noah is provision
|
|
|
337
340
|
for your organization. Integrator KYB happens directly with Noah; Rhinestone
|
|
338
341
|
enables provisioning after Noah confirms it. Card and Apple Pay continue through Swapped. A customer who has
|
|
339
342
|
not been verified is taken through hosted verification first; an existing active
|
|
340
|
-
account is resumed; otherwise the
|
|
343
|
+
account is resumed; otherwise the account is configured once. There is no account
|
|
344
|
+
selection: the customer's one account opens directly.
|
|
345
|
+
|
|
346
|
+
Rhinestone chooses the currency from the residence the customer declares during
|
|
347
|
+
verification: EUR inside SEPA, USD elsewhere. It is not configurable, and it does
|
|
348
|
+
not follow the customer's location. The options are read again once the customer
|
|
349
|
+
is approved, because only then is the residence known.
|
|
341
350
|
|
|
342
351
|
The modal checks `GET /onramp/options?provider=noah` before customer verification. A 404
|
|
343
|
-
(provisioning disabled or an older proxy)
|
|
352
|
+
(provisioning disabled, a declared residence Noah doesn't serve, or an older proxy)
|
|
353
|
+
routes bank transfer through Swapped. When Swapped's regional list has no bank transfer
|
|
354
|
+
either, the customer returns to the Cash methods with a notice and the row hidden.
|
|
344
355
|
A successful response keeps Noah exclusive, even when no currencies are available.
|
|
345
356
|
Authentication and service errors show a retry instead of switching providers.
|
|
346
357
|
|
|
@@ -363,7 +374,6 @@ chain. No on-chain link is shown for a fiat-only or sub-fee transfer.
|
|
|
363
374
|
```tsx
|
|
364
375
|
<DepositModal
|
|
365
376
|
bankOnramp={{
|
|
366
|
-
currency: "USD", // Optional; omit to show every available currency.
|
|
367
377
|
supportUrl: "https://your-app.example/support", // Optional bank-transfer help.
|
|
368
378
|
getSession: (smartAccount) =>
|
|
369
379
|
authenticatedSessionClient.getCustomerSession({ smartAccount }),
|
|
@@ -487,8 +497,9 @@ const options = {
|
|
|
487
497
|
getSession: () => authenticatedSessionClient.getCustomerSession(),
|
|
488
498
|
};
|
|
489
499
|
const onramp = createOnrampService(options);
|
|
490
|
-
|
|
491
|
-
const {
|
|
500
|
+
// Exactly one option, by region: EUR inside SEPA, USD elsewhere.
|
|
501
|
+
const { options: available } = await onramp.getOptions();
|
|
502
|
+
const { account } = await onramp.createAccount({ currency: available[0].currency });
|
|
492
503
|
// Setup completes in this call and returns the account it configured.
|
|
493
504
|
// Show the customer their bank details through a separate, repeatable call:
|
|
494
505
|
const { bankDetails } = await onramp.getAccountDetails(account.id);
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
var _chunkG264OM23cjs = require('./chunk-G264OM23.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var _chunkAXCGY2TGcjs = require('./chunk-AXCGY2TG.cjs');
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
var
|
|
15
|
+
var _chunkBAVRVXLVcjs = require('./chunk-BAVRVXLV.cjs');
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
var
|
|
19
|
-
require('./chunk-
|
|
18
|
+
var _chunkDSM4IHTMcjs = require('./chunk-DSM4IHTM.cjs');
|
|
19
|
+
require('./chunk-5ACTCWIE.cjs');
|
|
20
20
|
|
|
21
21
|
// src/components/steps/BankOnrampStep.tsx
|
|
22
22
|
var _react = require('react');
|
|
@@ -128,25 +128,9 @@ var OPTIONS_ERROR = "Bank transfer options are unavailable. Try again.";
|
|
|
128
128
|
var VERIFICATION_POLL_MS = 5e3;
|
|
129
129
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
130
130
|
function BankOnrampStep({
|
|
131
|
-
unavailableFallback,
|
|
132
|
-
...props
|
|
133
|
-
}) {
|
|
134
|
-
const [unavailable, setUnavailable] = _react.useState.call(void 0, false);
|
|
135
|
-
const handleUnavailable = _react.useCallback.call(void 0, () => setUnavailable(true), []);
|
|
136
|
-
if (unavailable) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: unavailableFallback });
|
|
137
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
138
|
-
NoahOnrampStep,
|
|
139
|
-
{
|
|
140
|
-
...props,
|
|
141
|
-
onUnavailable: unavailableFallback ? handleUnavailable : void 0
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
function NoahOnrampStep({
|
|
146
131
|
backendUrl,
|
|
147
132
|
smartAccount,
|
|
148
133
|
bankOnramp,
|
|
149
|
-
onInternalBack,
|
|
150
134
|
onError,
|
|
151
135
|
openExternally,
|
|
152
136
|
onUnavailable,
|
|
@@ -161,7 +145,7 @@ function NoahOnrampStep({
|
|
|
161
145
|
[backendUrl, getSessionRef, smartAccount]
|
|
162
146
|
);
|
|
163
147
|
const service = _react.useMemo.call(void 0,
|
|
164
|
-
() =>
|
|
148
|
+
() => _chunkAXCGY2TGcjs.createOnrampService.call(void 0, sessionOptions),
|
|
165
149
|
[sessionOptions]
|
|
166
150
|
);
|
|
167
151
|
const [availableForHistory, setAvailableForHistory] = _react.useState.call(void 0, false);
|
|
@@ -169,12 +153,15 @@ function NoahOnrampStep({
|
|
|
169
153
|
if (availableForHistory) _optionalChain([onHistorySource, 'optionalCall', _ => _(service)]);
|
|
170
154
|
return () => _optionalChain([onHistorySource, 'optionalCall', _2 => _2(null)]);
|
|
171
155
|
}, [service, onHistorySource, availableForHistory]);
|
|
172
|
-
const [
|
|
173
|
-
const [accounts, setAccounts] = _react.useState.call(void 0, []);
|
|
156
|
+
const [noOptions, setNoOptions] = _react.useState.call(void 0, false);
|
|
174
157
|
const [loading, setLoading] = _react.useState.call(void 0, true);
|
|
175
|
-
const [selectedAccount, setSelectedAccount] = _react.useState.call(void 0,
|
|
158
|
+
const [selectedAccount, setSelectedAccount] = _react.useState.call(void 0,
|
|
159
|
+
null
|
|
160
|
+
);
|
|
176
161
|
const [hostedUrl, setHostedUrl] = _react.useState.call(void 0, null);
|
|
177
|
-
const [bankDetails, setBankDetails] = _react.useState.call(void 0,
|
|
162
|
+
const [bankDetails, setBankDetails] = _react.useState.call(void 0,
|
|
163
|
+
null
|
|
164
|
+
);
|
|
178
165
|
const [verificationExpired, setVerificationExpired] = _react.useState.call(void 0, false);
|
|
179
166
|
const [verificationExpiresAt, setVerificationExpiresAt] = _react.useState.call(void 0, null);
|
|
180
167
|
const [iframeLoaded, setIframeLoaded] = _react.useState.call(void 0, false);
|
|
@@ -182,19 +169,13 @@ function NoahOnrampStep({
|
|
|
182
169
|
const [error, setError] = _react.useState.call(void 0, null);
|
|
183
170
|
const [verifying, setVerifying] = _react.useState.call(void 0, null);
|
|
184
171
|
const requestGenerationRef = _react.useRef.call(void 0, 0);
|
|
172
|
+
const verificationResumedRef = _react.useRef.call(void 0, false);
|
|
185
173
|
const isCurrentRequest = _react.useCallback.call(void 0,
|
|
186
174
|
(generation) => requestGenerationRef.current === generation,
|
|
187
175
|
[]
|
|
188
176
|
);
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
if (current.status === "approved") return "approved";
|
|
192
|
-
if (current.status === "declined") return "declined";
|
|
193
|
-
const session = await _chunk4Y7VO7E6cjs.startComplianceVerification.call(void 0, sessionOptions, {
|
|
194
|
-
// Verification is per fiat currency. Without an explicit choice the US
|
|
195
|
-
// dollar journey is the one every configured country can complete.
|
|
196
|
-
currency: _nullishCoalesce(bankOnramp.currency, () => ( "USD"))
|
|
197
|
-
});
|
|
177
|
+
const startVerification = _react.useCallback.call(void 0, async () => {
|
|
178
|
+
const session = await _chunkBAVRVXLVcjs.startComplianceVerification.call(void 0, sessionOptions);
|
|
198
179
|
if (session.status === "failed") return "declined";
|
|
199
180
|
if (!session.nextAction) return "reviewing";
|
|
200
181
|
if (!isTrustedNoahHostedUrl(session.nextAction.url)) return "reviewing";
|
|
@@ -208,21 +189,27 @@ function NoahOnrampStep({
|
|
|
208
189
|
setVerificationExpired(false);
|
|
209
190
|
setIframeLoaded(false);
|
|
210
191
|
return "action_required";
|
|
211
|
-
}, [
|
|
192
|
+
}, [openExternally, sessionOptions]);
|
|
193
|
+
const ensureVerified = _react.useCallback.call(void 0, async () => {
|
|
194
|
+
const current = await _chunkBAVRVXLVcjs.getComplianceStatus.call(void 0, sessionOptions);
|
|
195
|
+
if (current.status === "approved") return "approved";
|
|
196
|
+
if (current.status === "declined") return "declined";
|
|
197
|
+
return startVerification();
|
|
198
|
+
}, [sessionOptions, startVerification]);
|
|
212
199
|
const loadAccounts = _react.useCallback.call(void 0, async () => {
|
|
213
200
|
const generation = ++requestGenerationRef.current;
|
|
214
201
|
setLoading(true);
|
|
215
202
|
setError(null);
|
|
203
|
+
setNoOptions(false);
|
|
216
204
|
try {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
}).catch((error2) => {
|
|
220
|
-
if (error2 instanceof _chunkCCDDZTYWcjs.ScopedRequestError && error2.status === 404 && onUnavailable) {
|
|
205
|
+
const discover = () => service.getOptions().catch((error2) => {
|
|
206
|
+
if (error2 instanceof _chunkDSM4IHTMcjs.ScopedRequestError && error2.status === 404 && onUnavailable) {
|
|
221
207
|
if (isCurrentRequest(generation)) onUnavailable();
|
|
222
208
|
return null;
|
|
223
209
|
}
|
|
224
210
|
throw error2;
|
|
225
211
|
});
|
|
212
|
+
const available = await discover();
|
|
226
213
|
if (!available || !isCurrentRequest(generation)) return;
|
|
227
214
|
setAvailableForHistory(true);
|
|
228
215
|
const verification = await ensureVerified();
|
|
@@ -233,10 +220,22 @@ function NoahOnrampStep({
|
|
|
233
220
|
}
|
|
234
221
|
setVerifying(null);
|
|
235
222
|
setExternalOpened(false);
|
|
236
|
-
const existing = await
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
const [current, existing] = await Promise.all([
|
|
224
|
+
discover(),
|
|
225
|
+
service.listAccounts()
|
|
226
|
+
]);
|
|
227
|
+
if (!current || !isCurrentRequest(generation)) return;
|
|
228
|
+
const [option] = current.options;
|
|
229
|
+
if (!option) {
|
|
230
|
+
setNoOptions(true);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
void selectOptionRef.current(
|
|
234
|
+
option,
|
|
235
|
+
existing.items.find(
|
|
236
|
+
(account) => account.status === "active" && account.currency === option.currency
|
|
237
|
+
)
|
|
238
|
+
);
|
|
240
239
|
} catch (e3) {
|
|
241
240
|
if (!isCurrentRequest(generation)) return;
|
|
242
241
|
setError(OPTIONS_ERROR);
|
|
@@ -244,14 +243,7 @@ function NoahOnrampStep({
|
|
|
244
243
|
} finally {
|
|
245
244
|
if (isCurrentRequest(generation)) setLoading(false);
|
|
246
245
|
}
|
|
247
|
-
}, [
|
|
248
|
-
bankOnramp.currency,
|
|
249
|
-
ensureVerified,
|
|
250
|
-
isCurrentRequest,
|
|
251
|
-
onError,
|
|
252
|
-
onUnavailable,
|
|
253
|
-
service
|
|
254
|
-
]);
|
|
246
|
+
}, [ensureVerified, isCurrentRequest, onError, onUnavailable, service]);
|
|
255
247
|
_react.useEffect.call(void 0, () => {
|
|
256
248
|
setSelectedAccount(null);
|
|
257
249
|
setBankDetails(null);
|
|
@@ -269,16 +261,22 @@ function NoahOnrampStep({
|
|
|
269
261
|
setError(null);
|
|
270
262
|
setBankDetails(null);
|
|
271
263
|
try {
|
|
272
|
-
const { bankDetails: details } = await service.getAccountDetails(
|
|
264
|
+
const { bankDetails: details } = await service.getAccountDetails(
|
|
265
|
+
account.id
|
|
266
|
+
);
|
|
273
267
|
if (!isCurrentRequest(generation)) return;
|
|
274
|
-
if (details.currency !== account.currency)
|
|
268
|
+
if (details.currency !== account.currency)
|
|
269
|
+
throw new Error("Bank currency mismatch");
|
|
275
270
|
setBankDetails(details);
|
|
276
271
|
} catch (error2) {
|
|
277
272
|
if (!isCurrentRequest(generation)) return;
|
|
278
|
-
const pending = error2 instanceof
|
|
273
|
+
const pending = error2 instanceof _chunkDSM4IHTMcjs.ScopedRequestError && error2.status === 409;
|
|
279
274
|
const message = pending ? PENDING_ERROR : SAFE_ERROR;
|
|
280
275
|
setError(message);
|
|
281
|
-
_optionalChain([onError, 'optionalCall', _4 => _4(
|
|
276
|
+
_optionalChain([onError, 'optionalCall', _4 => _4(
|
|
277
|
+
message,
|
|
278
|
+
pending ? "BANK_ONRAMP_DETAILS_PENDING" : "BANK_ONRAMP_DETAILS_FAILED"
|
|
279
|
+
)]);
|
|
282
280
|
} finally {
|
|
283
281
|
if (isCurrentRequest(generation)) setLoading(false);
|
|
284
282
|
}
|
|
@@ -304,27 +302,28 @@ function NoahOnrampStep({
|
|
|
304
302
|
if (!isCurrentRequest(generation)) return;
|
|
305
303
|
setSelectedAccount(nextAccount);
|
|
306
304
|
await openDetails(nextAccount);
|
|
307
|
-
} catch (
|
|
305
|
+
} catch (error2) {
|
|
308
306
|
if (!isCurrentRequest(generation)) return;
|
|
307
|
+
if (error2 instanceof _chunkDSM4IHTMcjs.ScopedRequestError && error2.status === 409 && error2.code === "VERIFICATION_REQUIRED" && !verificationResumedRef.current) {
|
|
308
|
+
verificationResumedRef.current = true;
|
|
309
|
+
try {
|
|
310
|
+
const verification = await startVerification();
|
|
311
|
+
if (!isCurrentRequest(generation)) return;
|
|
312
|
+
setVerifying(verification);
|
|
313
|
+
setLoading(false);
|
|
314
|
+
return;
|
|
315
|
+
} catch (e4) {
|
|
316
|
+
if (!isCurrentRequest(generation)) return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
309
319
|
setError(SAFE_ERROR);
|
|
310
320
|
_optionalChain([onError, 'optionalCall', _5 => _5(SAFE_ERROR, "BANK_ONRAMP_SETUP_FAILED")]);
|
|
311
321
|
setLoading(false);
|
|
312
322
|
}
|
|
313
323
|
},
|
|
314
|
-
[isCurrentRequest, onError, openDetails, service]
|
|
324
|
+
[isCurrentRequest, onError, openDetails, service, startVerification]
|
|
315
325
|
);
|
|
316
|
-
const
|
|
317
|
-
requestGenerationRef.current += 1;
|
|
318
|
-
setSelectedAccount(null);
|
|
319
|
-
setBankDetails(null);
|
|
320
|
-
setError(null);
|
|
321
|
-
setLoading(false);
|
|
322
|
-
}, []);
|
|
323
|
-
_react.useEffect.call(void 0, () => {
|
|
324
|
-
if (!onInternalBack) return;
|
|
325
|
-
onInternalBack(selectedAccount ? backToCurrencies : null);
|
|
326
|
-
return () => onInternalBack(null);
|
|
327
|
-
}, [backToCurrencies, onInternalBack, selectedAccount]);
|
|
326
|
+
const selectOptionRef = _chunkG264OM23cjs.useLatestRef.call(void 0, selectOption);
|
|
328
327
|
_react.useEffect.call(void 0, () => {
|
|
329
328
|
if (verifying !== "action_required" || !hostedUrl) return;
|
|
330
329
|
const remaining = hostedSessionRemainingMs(
|
|
@@ -341,7 +340,7 @@ function NoahOnrampStep({
|
|
|
341
340
|
const timer = setInterval(() => {
|
|
342
341
|
void (async () => {
|
|
343
342
|
try {
|
|
344
|
-
const status = await
|
|
343
|
+
const status = await _chunkBAVRVXLVcjs.getComplianceStatus.call(void 0, sessionOptions);
|
|
345
344
|
if (!active) return;
|
|
346
345
|
if (status.status === "approved") {
|
|
347
346
|
setHostedUrl(null);
|
|
@@ -371,7 +370,7 @@ function NoahOnrampStep({
|
|
|
371
370
|
{
|
|
372
371
|
icon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkG264OM23cjs.BankIcon, {}),
|
|
373
372
|
title: "Bank transfer",
|
|
374
|
-
subtitle:
|
|
373
|
+
subtitle: "Your bank transfer details"
|
|
375
374
|
}
|
|
376
375
|
),
|
|
377
376
|
loading && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rs-loading-state", "aria-live": "polite", children: [
|
|
@@ -406,7 +405,14 @@ function NoahOnrampStep({
|
|
|
406
405
|
) })
|
|
407
406
|
] }),
|
|
408
407
|
!loading && !error && verifying === "action_required" && !verificationExpired && hostedUrl && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rs-bank-onramp-hosted", children: [
|
|
409
|
-
!iframeLoaded && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
408
|
+
!iframeLoaded && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
409
|
+
"div",
|
|
410
|
+
{
|
|
411
|
+
className: "rs-bank-onramp-frame-loading",
|
|
412
|
+
"aria-live": "polite",
|
|
413
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkG264OM23cjs.Spinner, {})
|
|
414
|
+
}
|
|
415
|
+
),
|
|
410
416
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
411
417
|
"iframe",
|
|
412
418
|
{
|
|
@@ -419,34 +425,7 @@ function NoahOnrampStep({
|
|
|
419
425
|
}
|
|
420
426
|
)
|
|
421
427
|
] }),
|
|
422
|
-
!loading && !error && !verifying &&
|
|
423
|
-
options.map((option) => {
|
|
424
|
-
const account = accounts.find(
|
|
425
|
-
(candidate) => candidate.status === "active" && candidate.currency === option.currency
|
|
426
|
-
);
|
|
427
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
428
|
-
"button",
|
|
429
|
-
{
|
|
430
|
-
type: "button",
|
|
431
|
-
className: "rs-list-row",
|
|
432
|
-
onClick: () => void selectOption(option, account),
|
|
433
|
-
"aria-label": `${account ? "View" : "Set up"} ${option.currency} bank account`,
|
|
434
|
-
children: [
|
|
435
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "rs-list-row-leading", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkG264OM23cjs.BankIcon, {}) }),
|
|
436
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "rs-list-row-body", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "rs-list-row-text", children: [
|
|
437
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "rs-list-row-title", children: [
|
|
438
|
-
option.currency,
|
|
439
|
-
" account"
|
|
440
|
-
] }),
|
|
441
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "rs-list-row-subtitle", children: account ? "View bank details" : "Set up bank details" })
|
|
442
|
-
] }) })
|
|
443
|
-
]
|
|
444
|
-
},
|
|
445
|
-
option.currency
|
|
446
|
-
);
|
|
447
|
-
}),
|
|
448
|
-
options.length === 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "rs-loading-state", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "rs-loading-title", children: "No bank transfer options are available." }) })
|
|
449
|
-
] }),
|
|
428
|
+
!loading && !error && !verifying && noOptions && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "rs-loading-state", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "rs-loading-title", children: "No bank transfer options are available." }) }),
|
|
450
429
|
!loading && !error && !verifying && selectedAccount && bankDetails && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, BankDetailsView, { details: bankDetails }, selectedAccount.id) })
|
|
451
430
|
] }) });
|
|
452
431
|
}
|
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
} from "./chunk-YY4ZOAH5.mjs";
|
|
9
9
|
import {
|
|
10
10
|
createOnrampService
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-T255BMBC.mjs";
|
|
12
12
|
import {
|
|
13
13
|
getComplianceStatus,
|
|
14
14
|
startComplianceVerification
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GR23TV4U.mjs";
|
|
16
16
|
import {
|
|
17
17
|
ScopedRequestError
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-3R3A7OYL.mjs";
|
|
19
|
+
import "./chunk-HQDTWHSI.mjs";
|
|
20
20
|
|
|
21
21
|
// src/components/steps/BankOnrampStep.tsx
|
|
22
22
|
import { useCallback, useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
@@ -128,25 +128,9 @@ var OPTIONS_ERROR = "Bank transfer options are unavailable. Try again.";
|
|
|
128
128
|
var VERIFICATION_POLL_MS = 5e3;
|
|
129
129
|
var MAX_TIMEOUT_MS = 2147483647;
|
|
130
130
|
function BankOnrampStep({
|
|
131
|
-
unavailableFallback,
|
|
132
|
-
...props
|
|
133
|
-
}) {
|
|
134
|
-
const [unavailable, setUnavailable] = useState2(false);
|
|
135
|
-
const handleUnavailable = useCallback(() => setUnavailable(true), []);
|
|
136
|
-
if (unavailable) return /* @__PURE__ */ jsx2(Fragment, { children: unavailableFallback });
|
|
137
|
-
return /* @__PURE__ */ jsx2(
|
|
138
|
-
NoahOnrampStep,
|
|
139
|
-
{
|
|
140
|
-
...props,
|
|
141
|
-
onUnavailable: unavailableFallback ? handleUnavailable : void 0
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
function NoahOnrampStep({
|
|
146
131
|
backendUrl,
|
|
147
132
|
smartAccount,
|
|
148
133
|
bankOnramp,
|
|
149
|
-
onInternalBack,
|
|
150
134
|
onError,
|
|
151
135
|
openExternally,
|
|
152
136
|
onUnavailable,
|
|
@@ -169,12 +153,15 @@ function NoahOnrampStep({
|
|
|
169
153
|
if (availableForHistory) onHistorySource?.(service);
|
|
170
154
|
return () => onHistorySource?.(null);
|
|
171
155
|
}, [service, onHistorySource, availableForHistory]);
|
|
172
|
-
const [
|
|
173
|
-
const [accounts, setAccounts] = useState2([]);
|
|
156
|
+
const [noOptions, setNoOptions] = useState2(false);
|
|
174
157
|
const [loading, setLoading] = useState2(true);
|
|
175
|
-
const [selectedAccount, setSelectedAccount] = useState2(
|
|
158
|
+
const [selectedAccount, setSelectedAccount] = useState2(
|
|
159
|
+
null
|
|
160
|
+
);
|
|
176
161
|
const [hostedUrl, setHostedUrl] = useState2(null);
|
|
177
|
-
const [bankDetails, setBankDetails] = useState2(
|
|
162
|
+
const [bankDetails, setBankDetails] = useState2(
|
|
163
|
+
null
|
|
164
|
+
);
|
|
178
165
|
const [verificationExpired, setVerificationExpired] = useState2(false);
|
|
179
166
|
const [verificationExpiresAt, setVerificationExpiresAt] = useState2(null);
|
|
180
167
|
const [iframeLoaded, setIframeLoaded] = useState2(false);
|
|
@@ -182,19 +169,13 @@ function NoahOnrampStep({
|
|
|
182
169
|
const [error, setError] = useState2(null);
|
|
183
170
|
const [verifying, setVerifying] = useState2(null);
|
|
184
171
|
const requestGenerationRef = useRef(0);
|
|
172
|
+
const verificationResumedRef = useRef(false);
|
|
185
173
|
const isCurrentRequest = useCallback(
|
|
186
174
|
(generation) => requestGenerationRef.current === generation,
|
|
187
175
|
[]
|
|
188
176
|
);
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
if (current.status === "approved") return "approved";
|
|
192
|
-
if (current.status === "declined") return "declined";
|
|
193
|
-
const session = await startComplianceVerification(sessionOptions, {
|
|
194
|
-
// Verification is per fiat currency. Without an explicit choice the US
|
|
195
|
-
// dollar journey is the one every configured country can complete.
|
|
196
|
-
currency: bankOnramp.currency ?? "USD"
|
|
197
|
-
});
|
|
177
|
+
const startVerification = useCallback(async () => {
|
|
178
|
+
const session = await startComplianceVerification(sessionOptions);
|
|
198
179
|
if (session.status === "failed") return "declined";
|
|
199
180
|
if (!session.nextAction) return "reviewing";
|
|
200
181
|
if (!isTrustedNoahHostedUrl(session.nextAction.url)) return "reviewing";
|
|
@@ -208,21 +189,27 @@ function NoahOnrampStep({
|
|
|
208
189
|
setVerificationExpired(false);
|
|
209
190
|
setIframeLoaded(false);
|
|
210
191
|
return "action_required";
|
|
211
|
-
}, [
|
|
192
|
+
}, [openExternally, sessionOptions]);
|
|
193
|
+
const ensureVerified = useCallback(async () => {
|
|
194
|
+
const current = await getComplianceStatus(sessionOptions);
|
|
195
|
+
if (current.status === "approved") return "approved";
|
|
196
|
+
if (current.status === "declined") return "declined";
|
|
197
|
+
return startVerification();
|
|
198
|
+
}, [sessionOptions, startVerification]);
|
|
212
199
|
const loadAccounts = useCallback(async () => {
|
|
213
200
|
const generation = ++requestGenerationRef.current;
|
|
214
201
|
setLoading(true);
|
|
215
202
|
setError(null);
|
|
203
|
+
setNoOptions(false);
|
|
216
204
|
try {
|
|
217
|
-
const
|
|
218
|
-
...bankOnramp.currency ? { currency: bankOnramp.currency } : {}
|
|
219
|
-
}).catch((error2) => {
|
|
205
|
+
const discover = () => service.getOptions().catch((error2) => {
|
|
220
206
|
if (error2 instanceof ScopedRequestError && error2.status === 404 && onUnavailable) {
|
|
221
207
|
if (isCurrentRequest(generation)) onUnavailable();
|
|
222
208
|
return null;
|
|
223
209
|
}
|
|
224
210
|
throw error2;
|
|
225
211
|
});
|
|
212
|
+
const available = await discover();
|
|
226
213
|
if (!available || !isCurrentRequest(generation)) return;
|
|
227
214
|
setAvailableForHistory(true);
|
|
228
215
|
const verification = await ensureVerified();
|
|
@@ -233,10 +220,22 @@ function NoahOnrampStep({
|
|
|
233
220
|
}
|
|
234
221
|
setVerifying(null);
|
|
235
222
|
setExternalOpened(false);
|
|
236
|
-
const existing = await
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
const [current, existing] = await Promise.all([
|
|
224
|
+
discover(),
|
|
225
|
+
service.listAccounts()
|
|
226
|
+
]);
|
|
227
|
+
if (!current || !isCurrentRequest(generation)) return;
|
|
228
|
+
const [option] = current.options;
|
|
229
|
+
if (!option) {
|
|
230
|
+
setNoOptions(true);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
void selectOptionRef.current(
|
|
234
|
+
option,
|
|
235
|
+
existing.items.find(
|
|
236
|
+
(account) => account.status === "active" && account.currency === option.currency
|
|
237
|
+
)
|
|
238
|
+
);
|
|
240
239
|
} catch {
|
|
241
240
|
if (!isCurrentRequest(generation)) return;
|
|
242
241
|
setError(OPTIONS_ERROR);
|
|
@@ -244,14 +243,7 @@ function NoahOnrampStep({
|
|
|
244
243
|
} finally {
|
|
245
244
|
if (isCurrentRequest(generation)) setLoading(false);
|
|
246
245
|
}
|
|
247
|
-
}, [
|
|
248
|
-
bankOnramp.currency,
|
|
249
|
-
ensureVerified,
|
|
250
|
-
isCurrentRequest,
|
|
251
|
-
onError,
|
|
252
|
-
onUnavailable,
|
|
253
|
-
service
|
|
254
|
-
]);
|
|
246
|
+
}, [ensureVerified, isCurrentRequest, onError, onUnavailable, service]);
|
|
255
247
|
useEffect(() => {
|
|
256
248
|
setSelectedAccount(null);
|
|
257
249
|
setBankDetails(null);
|
|
@@ -269,16 +261,22 @@ function NoahOnrampStep({
|
|
|
269
261
|
setError(null);
|
|
270
262
|
setBankDetails(null);
|
|
271
263
|
try {
|
|
272
|
-
const { bankDetails: details } = await service.getAccountDetails(
|
|
264
|
+
const { bankDetails: details } = await service.getAccountDetails(
|
|
265
|
+
account.id
|
|
266
|
+
);
|
|
273
267
|
if (!isCurrentRequest(generation)) return;
|
|
274
|
-
if (details.currency !== account.currency)
|
|
268
|
+
if (details.currency !== account.currency)
|
|
269
|
+
throw new Error("Bank currency mismatch");
|
|
275
270
|
setBankDetails(details);
|
|
276
271
|
} catch (error2) {
|
|
277
272
|
if (!isCurrentRequest(generation)) return;
|
|
278
273
|
const pending = error2 instanceof ScopedRequestError && error2.status === 409;
|
|
279
274
|
const message = pending ? PENDING_ERROR : SAFE_ERROR;
|
|
280
275
|
setError(message);
|
|
281
|
-
onError?.(
|
|
276
|
+
onError?.(
|
|
277
|
+
message,
|
|
278
|
+
pending ? "BANK_ONRAMP_DETAILS_PENDING" : "BANK_ONRAMP_DETAILS_FAILED"
|
|
279
|
+
);
|
|
282
280
|
} finally {
|
|
283
281
|
if (isCurrentRequest(generation)) setLoading(false);
|
|
284
282
|
}
|
|
@@ -304,27 +302,28 @@ function NoahOnrampStep({
|
|
|
304
302
|
if (!isCurrentRequest(generation)) return;
|
|
305
303
|
setSelectedAccount(nextAccount);
|
|
306
304
|
await openDetails(nextAccount);
|
|
307
|
-
} catch {
|
|
305
|
+
} catch (error2) {
|
|
308
306
|
if (!isCurrentRequest(generation)) return;
|
|
307
|
+
if (error2 instanceof ScopedRequestError && error2.status === 409 && error2.code === "VERIFICATION_REQUIRED" && !verificationResumedRef.current) {
|
|
308
|
+
verificationResumedRef.current = true;
|
|
309
|
+
try {
|
|
310
|
+
const verification = await startVerification();
|
|
311
|
+
if (!isCurrentRequest(generation)) return;
|
|
312
|
+
setVerifying(verification);
|
|
313
|
+
setLoading(false);
|
|
314
|
+
return;
|
|
315
|
+
} catch {
|
|
316
|
+
if (!isCurrentRequest(generation)) return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
309
319
|
setError(SAFE_ERROR);
|
|
310
320
|
onError?.(SAFE_ERROR, "BANK_ONRAMP_SETUP_FAILED");
|
|
311
321
|
setLoading(false);
|
|
312
322
|
}
|
|
313
323
|
},
|
|
314
|
-
[isCurrentRequest, onError, openDetails, service]
|
|
324
|
+
[isCurrentRequest, onError, openDetails, service, startVerification]
|
|
315
325
|
);
|
|
316
|
-
const
|
|
317
|
-
requestGenerationRef.current += 1;
|
|
318
|
-
setSelectedAccount(null);
|
|
319
|
-
setBankDetails(null);
|
|
320
|
-
setError(null);
|
|
321
|
-
setLoading(false);
|
|
322
|
-
}, []);
|
|
323
|
-
useEffect(() => {
|
|
324
|
-
if (!onInternalBack) return;
|
|
325
|
-
onInternalBack(selectedAccount ? backToCurrencies : null);
|
|
326
|
-
return () => onInternalBack(null);
|
|
327
|
-
}, [backToCurrencies, onInternalBack, selectedAccount]);
|
|
326
|
+
const selectOptionRef = useLatestRef(selectOption);
|
|
328
327
|
useEffect(() => {
|
|
329
328
|
if (verifying !== "action_required" || !hostedUrl) return;
|
|
330
329
|
const remaining = hostedSessionRemainingMs(
|
|
@@ -371,7 +370,7 @@ function NoahOnrampStep({
|
|
|
371
370
|
{
|
|
372
371
|
icon: /* @__PURE__ */ jsx2(BankIcon, {}),
|
|
373
372
|
title: "Bank transfer",
|
|
374
|
-
subtitle:
|
|
373
|
+
subtitle: "Your bank transfer details"
|
|
375
374
|
}
|
|
376
375
|
),
|
|
377
376
|
loading && /* @__PURE__ */ jsxs2("div", { className: "rs-loading-state", "aria-live": "polite", children: [
|
|
@@ -406,7 +405,14 @@ function NoahOnrampStep({
|
|
|
406
405
|
) })
|
|
407
406
|
] }),
|
|
408
407
|
!loading && !error && verifying === "action_required" && !verificationExpired && hostedUrl && /* @__PURE__ */ jsxs2("div", { className: "rs-bank-onramp-hosted", children: [
|
|
409
|
-
!iframeLoaded && /* @__PURE__ */ jsx2(
|
|
408
|
+
!iframeLoaded && /* @__PURE__ */ jsx2(
|
|
409
|
+
"div",
|
|
410
|
+
{
|
|
411
|
+
className: "rs-bank-onramp-frame-loading",
|
|
412
|
+
"aria-live": "polite",
|
|
413
|
+
children: /* @__PURE__ */ jsx2(Spinner, {})
|
|
414
|
+
}
|
|
415
|
+
),
|
|
410
416
|
/* @__PURE__ */ jsx2(
|
|
411
417
|
"iframe",
|
|
412
418
|
{
|
|
@@ -419,34 +425,7 @@ function NoahOnrampStep({
|
|
|
419
425
|
}
|
|
420
426
|
)
|
|
421
427
|
] }),
|
|
422
|
-
!loading && !error && !verifying &&
|
|
423
|
-
options.map((option) => {
|
|
424
|
-
const account = accounts.find(
|
|
425
|
-
(candidate) => candidate.status === "active" && candidate.currency === option.currency
|
|
426
|
-
);
|
|
427
|
-
return /* @__PURE__ */ jsxs2(
|
|
428
|
-
"button",
|
|
429
|
-
{
|
|
430
|
-
type: "button",
|
|
431
|
-
className: "rs-list-row",
|
|
432
|
-
onClick: () => void selectOption(option, account),
|
|
433
|
-
"aria-label": `${account ? "View" : "Set up"} ${option.currency} bank account`,
|
|
434
|
-
children: [
|
|
435
|
-
/* @__PURE__ */ jsx2("span", { className: "rs-list-row-leading", children: /* @__PURE__ */ jsx2(BankIcon, {}) }),
|
|
436
|
-
/* @__PURE__ */ jsx2("span", { className: "rs-list-row-body", children: /* @__PURE__ */ jsxs2("span", { className: "rs-list-row-text", children: [
|
|
437
|
-
/* @__PURE__ */ jsxs2("span", { className: "rs-list-row-title", children: [
|
|
438
|
-
option.currency,
|
|
439
|
-
" account"
|
|
440
|
-
] }),
|
|
441
|
-
/* @__PURE__ */ jsx2("span", { className: "rs-list-row-subtitle", children: account ? "View bank details" : "Set up bank details" })
|
|
442
|
-
] }) })
|
|
443
|
-
]
|
|
444
|
-
},
|
|
445
|
-
option.currency
|
|
446
|
-
);
|
|
447
|
-
}),
|
|
448
|
-
options.length === 0 && /* @__PURE__ */ jsx2("div", { className: "rs-loading-state", children: /* @__PURE__ */ jsx2("div", { className: "rs-loading-title", children: "No bank transfer options are available." }) })
|
|
449
|
-
] }),
|
|
428
|
+
!loading && !error && !verifying && noOptions && /* @__PURE__ */ jsx2("div", { className: "rs-loading-state", children: /* @__PURE__ */ jsx2("div", { className: "rs-loading-title", children: "No bank transfer options are available." }) }),
|
|
450
429
|
!loading && !error && !verifying && selectedAccount && bankDetails && /* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsx2(BankDetailsView, { details: bankDetails }, selectedAccount.id) })
|
|
451
430
|
] }) });
|
|
452
431
|
}
|