@swype-org/react-sdk 0.1.79 → 0.1.81
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/dist/index.cjs +123 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +123 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -389,7 +389,8 @@ declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.E
|
|
|
389
389
|
* call (works in Chrome/Firefox with the iframe permissions policy). If
|
|
390
390
|
* that fails (Safari), we open a same-origin pop-up window on the Swype
|
|
391
391
|
* domain to perform the ceremony, then relay the result back via
|
|
392
|
-
* `
|
|
392
|
+
* `BroadcastChannel` (Safari strips `window.opener` from popups opened
|
|
393
|
+
* by cross-origin iframes, so `postMessage` via opener doesn't work).
|
|
393
394
|
*
|
|
394
395
|
* Passkey *assertion* (`navigator.credentials.get`) still delegates to
|
|
395
396
|
* the parent page via postMessage as before.
|
|
@@ -419,12 +420,19 @@ interface PasskeyPopupOptions {
|
|
|
419
420
|
userVerification?: string;
|
|
420
421
|
};
|
|
421
422
|
timeout?: number;
|
|
423
|
+
/** Populated by `createPasskeyViaPopup`; not set by callers. */
|
|
424
|
+
channelId?: string;
|
|
422
425
|
}
|
|
423
426
|
/**
|
|
424
427
|
* Opens a same-origin pop-up window on the Swype domain to perform
|
|
425
428
|
* passkey creation. Used as a fallback when Safari blocks
|
|
426
429
|
* `navigator.credentials.create()` inside a cross-origin iframe.
|
|
427
430
|
*
|
|
431
|
+
* Communication uses `BroadcastChannel` because Safari strips
|
|
432
|
+
* `window.opener` from popups opened by cross-origin iframes.
|
|
433
|
+
* Falls back to `window.postMessage` for browsers that preserve the
|
|
434
|
+
* opener reference.
|
|
435
|
+
*
|
|
428
436
|
* Must be called from a user-gesture handler (e.g. button click) to
|
|
429
437
|
* avoid the browser's pop-up blocker.
|
|
430
438
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -389,7 +389,8 @@ declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.E
|
|
|
389
389
|
* call (works in Chrome/Firefox with the iframe permissions policy). If
|
|
390
390
|
* that fails (Safari), we open a same-origin pop-up window on the Swype
|
|
391
391
|
* domain to perform the ceremony, then relay the result back via
|
|
392
|
-
* `
|
|
392
|
+
* `BroadcastChannel` (Safari strips `window.opener` from popups opened
|
|
393
|
+
* by cross-origin iframes, so `postMessage` via opener doesn't work).
|
|
393
394
|
*
|
|
394
395
|
* Passkey *assertion* (`navigator.credentials.get`) still delegates to
|
|
395
396
|
* the parent page via postMessage as before.
|
|
@@ -419,12 +420,19 @@ interface PasskeyPopupOptions {
|
|
|
419
420
|
userVerification?: string;
|
|
420
421
|
};
|
|
421
422
|
timeout?: number;
|
|
423
|
+
/** Populated by `createPasskeyViaPopup`; not set by callers. */
|
|
424
|
+
channelId?: string;
|
|
422
425
|
}
|
|
423
426
|
/**
|
|
424
427
|
* Opens a same-origin pop-up window on the Swype domain to perform
|
|
425
428
|
* passkey creation. Used as a fallback when Safari blocks
|
|
426
429
|
* `navigator.credentials.create()` inside a cross-origin iframe.
|
|
427
430
|
*
|
|
431
|
+
* Communication uses `BroadcastChannel` because Safari strips
|
|
432
|
+
* `window.opener` from popups opened by cross-origin iframes.
|
|
433
|
+
* Falls back to `window.postMessage` for browsers that preserve the
|
|
434
|
+
* opener reference.
|
|
435
|
+
*
|
|
428
436
|
* Must be called from a user-gesture handler (e.g. button click) to
|
|
429
437
|
* avoid the browser's pop-up blocker.
|
|
430
438
|
*/
|
package/dist/index.js
CHANGED
|
@@ -698,13 +698,17 @@ var POPUP_RESULT_TIMEOUT_MS = 12e4;
|
|
|
698
698
|
var POPUP_CLOSED_POLL_MS = 500;
|
|
699
699
|
function createPasskeyViaPopup(options) {
|
|
700
700
|
return new Promise((resolve, reject) => {
|
|
701
|
-
const
|
|
701
|
+
const channelId = `swype-pk-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
702
|
+
const payload = { ...options, channelId };
|
|
703
|
+
const encoded = btoa(JSON.stringify(payload));
|
|
702
704
|
const popupUrl = `${window.location.origin}/passkey-register#${encoded}`;
|
|
703
705
|
const popup = window.open(popupUrl, "swype-passkey");
|
|
704
706
|
if (!popup) {
|
|
705
707
|
reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
|
|
706
708
|
return;
|
|
707
709
|
}
|
|
710
|
+
let settled = false;
|
|
711
|
+
const channel = typeof BroadcastChannel !== "undefined" ? new BroadcastChannel(channelId) : null;
|
|
708
712
|
const timer = setTimeout(() => {
|
|
709
713
|
cleanup();
|
|
710
714
|
reject(new Error("Passkey creation timed out. Please try again."));
|
|
@@ -715,11 +719,11 @@ function createPasskeyViaPopup(options) {
|
|
|
715
719
|
reject(new Error("Passkey setup window was closed before completing."));
|
|
716
720
|
}
|
|
717
721
|
}, POPUP_CLOSED_POLL_MS);
|
|
718
|
-
|
|
719
|
-
if (
|
|
720
|
-
const data = event.data;
|
|
722
|
+
function handleResult(data) {
|
|
723
|
+
if (settled) return;
|
|
721
724
|
if (!data || typeof data !== "object") return;
|
|
722
725
|
if (data.type !== "swype:passkey-popup-result") return;
|
|
726
|
+
settled = true;
|
|
723
727
|
cleanup();
|
|
724
728
|
if (data.error) {
|
|
725
729
|
reject(new Error(data.error));
|
|
@@ -728,13 +732,21 @@ function createPasskeyViaPopup(options) {
|
|
|
728
732
|
} else {
|
|
729
733
|
reject(new Error("Invalid passkey popup response."));
|
|
730
734
|
}
|
|
735
|
+
}
|
|
736
|
+
if (channel) {
|
|
737
|
+
channel.onmessage = (event) => handleResult(event.data);
|
|
738
|
+
}
|
|
739
|
+
const postMessageHandler = (event) => {
|
|
740
|
+
if (event.source !== popup) return;
|
|
741
|
+
handleResult(event.data);
|
|
731
742
|
};
|
|
743
|
+
window.addEventListener("message", postMessageHandler);
|
|
732
744
|
function cleanup() {
|
|
733
745
|
clearTimeout(timer);
|
|
734
746
|
clearInterval(closedPoll);
|
|
735
|
-
window.removeEventListener("message",
|
|
747
|
+
window.removeEventListener("message", postMessageHandler);
|
|
748
|
+
channel?.close();
|
|
736
749
|
}
|
|
737
|
-
window.addEventListener("message", handler);
|
|
738
750
|
});
|
|
739
751
|
}
|
|
740
752
|
|
|
@@ -3244,12 +3256,15 @@ function DepositScreen({
|
|
|
3244
3256
|
onChangeSource,
|
|
3245
3257
|
onSwitchWallet,
|
|
3246
3258
|
onBack,
|
|
3247
|
-
onLogout
|
|
3259
|
+
onLogout,
|
|
3260
|
+
onIncreaseLimit,
|
|
3261
|
+
increasingLimit
|
|
3248
3262
|
}) {
|
|
3249
3263
|
const { tokens } = useSwypeConfig();
|
|
3250
3264
|
const amount = initialAmount;
|
|
3251
3265
|
const isLowBalance = availableBalance < MIN_DEPOSIT;
|
|
3252
|
-
const
|
|
3266
|
+
const exceedsLimit = amount > remainingLimit && !isLowBalance;
|
|
3267
|
+
const canDeposit = amount >= MIN_DEPOSIT && !exceedsLimit && !isLowBalance && !processing;
|
|
3253
3268
|
const headerTitle = merchantName ? `Deposit to ${merchantName}` : "Deposit";
|
|
3254
3269
|
if (isLowBalance) {
|
|
3255
3270
|
return /* @__PURE__ */ jsxs(
|
|
@@ -3307,11 +3322,22 @@ function DepositScreen({
|
|
|
3307
3322
|
ScreenLayout,
|
|
3308
3323
|
{
|
|
3309
3324
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3310
|
-
/* @__PURE__ */ jsxs(
|
|
3311
|
-
"
|
|
3312
|
-
|
|
3325
|
+
exceedsLimit && onIncreaseLimit ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3326
|
+
/* @__PURE__ */ jsx(PrimaryButton, { onClick: onIncreaseLimit, loading: increasingLimit, children: "Increase limit" }),
|
|
3327
|
+
/* @__PURE__ */ jsxs("p", { style: limitExceededHintStyle(tokens.warning), children: [
|
|
3328
|
+
"Your deposit of $",
|
|
3329
|
+
amount.toFixed(2),
|
|
3330
|
+
" exceeds your One-Tap limit of $",
|
|
3331
|
+
remainingLimit.toFixed(2),
|
|
3332
|
+
". Increase your limit to continue."
|
|
3333
|
+
] })
|
|
3334
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3335
|
+
/* @__PURE__ */ jsxs(PrimaryButton, { onClick: () => onDeposit(amount), disabled: !canDeposit, loading: processing, children: [
|
|
3336
|
+
"Deposit $",
|
|
3337
|
+
amount.toFixed(2)
|
|
3338
|
+
] }),
|
|
3339
|
+
/* @__PURE__ */ jsx("p", { style: noApprovalStyle(tokens.textMuted), children: "No approval needed \xB7 within your One-Tap limit" })
|
|
3313
3340
|
] }),
|
|
3314
|
-
/* @__PURE__ */ jsx("p", { style: noApprovalStyle(tokens.textMuted), children: "No approval needed \xB7 within your One-Tap limit" }),
|
|
3315
3341
|
/* @__PURE__ */ jsxs("p", { style: routeStyle(tokens.textMuted), children: [
|
|
3316
3342
|
"From ",
|
|
3317
3343
|
sourceName,
|
|
@@ -3459,6 +3485,13 @@ var noApprovalStyle = (color) => ({
|
|
|
3459
3485
|
color,
|
|
3460
3486
|
margin: "12px 0 2px"
|
|
3461
3487
|
});
|
|
3488
|
+
var limitExceededHintStyle = (color) => ({
|
|
3489
|
+
textAlign: "center",
|
|
3490
|
+
fontSize: "0.78rem",
|
|
3491
|
+
color,
|
|
3492
|
+
margin: "12px 0 2px",
|
|
3493
|
+
lineHeight: 1.5
|
|
3494
|
+
});
|
|
3462
3495
|
var routeStyle = (color) => ({
|
|
3463
3496
|
textAlign: "center",
|
|
3464
3497
|
fontSize: "0.75rem",
|
|
@@ -5207,6 +5240,81 @@ function SwypePaymentInner({
|
|
|
5207
5240
|
merchantAuthorization,
|
|
5208
5241
|
transfer
|
|
5209
5242
|
]);
|
|
5243
|
+
const [increasingLimit, setIncreasingLimit] = useState(false);
|
|
5244
|
+
const handleIncreaseLimit = useCallback(async () => {
|
|
5245
|
+
const parsedAmount = depositAmount ?? 5;
|
|
5246
|
+
if (!sourceId) {
|
|
5247
|
+
setError("No account or provider selected.");
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
if (!activeCredentialId) {
|
|
5251
|
+
setError("Create a passkey on this device before continuing.");
|
|
5252
|
+
setStep("create-passkey");
|
|
5253
|
+
return;
|
|
5254
|
+
}
|
|
5255
|
+
setError(null);
|
|
5256
|
+
setIncreasingLimit(true);
|
|
5257
|
+
try {
|
|
5258
|
+
const token = await getAccessToken();
|
|
5259
|
+
if (!token) throw new Error("Not authenticated");
|
|
5260
|
+
let effectiveSourceType = sourceType;
|
|
5261
|
+
let effectiveSourceId = sourceId;
|
|
5262
|
+
if (effectiveSourceType === "accountId") {
|
|
5263
|
+
const acct = accounts.find((a) => a.id === effectiveSourceId);
|
|
5264
|
+
const activeWallet = acct?.wallets.find((w) => w.status === "ACTIVE");
|
|
5265
|
+
if (activeWallet) {
|
|
5266
|
+
effectiveSourceType = "walletId";
|
|
5267
|
+
effectiveSourceId = activeWallet.id;
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
const t = await createTransfer(apiBaseUrl, token, {
|
|
5271
|
+
id: idempotencyKey,
|
|
5272
|
+
credentialId: activeCredentialId,
|
|
5273
|
+
merchantAuthorization,
|
|
5274
|
+
sourceType: effectiveSourceType,
|
|
5275
|
+
sourceId: effectiveSourceId,
|
|
5276
|
+
destination,
|
|
5277
|
+
amount: parsedAmount
|
|
5278
|
+
});
|
|
5279
|
+
setTransfer(t);
|
|
5280
|
+
if (t.authorizationSessions && t.authorizationSessions.length > 0) {
|
|
5281
|
+
const uri = t.authorizationSessions[0].uri;
|
|
5282
|
+
setMobileFlow(true);
|
|
5283
|
+
pollingTransferIdRef.current = t.id;
|
|
5284
|
+
mobileSetupFlowRef.current = true;
|
|
5285
|
+
handlingMobileReturnRef.current = false;
|
|
5286
|
+
polling.startPolling(t.id);
|
|
5287
|
+
setDeeplinkUri(uri);
|
|
5288
|
+
persistMobileFlowState({
|
|
5289
|
+
transferId: t.id,
|
|
5290
|
+
deeplinkUri: uri,
|
|
5291
|
+
providerId: selectedProviderId,
|
|
5292
|
+
isSetup: true
|
|
5293
|
+
});
|
|
5294
|
+
triggerDeeplink(uri);
|
|
5295
|
+
}
|
|
5296
|
+
} catch (err) {
|
|
5297
|
+
const msg = err instanceof Error ? err.message : "Failed to increase limit";
|
|
5298
|
+
setError(msg);
|
|
5299
|
+
onError?.(msg);
|
|
5300
|
+
} finally {
|
|
5301
|
+
setIncreasingLimit(false);
|
|
5302
|
+
}
|
|
5303
|
+
}, [
|
|
5304
|
+
depositAmount,
|
|
5305
|
+
sourceId,
|
|
5306
|
+
sourceType,
|
|
5307
|
+
activeCredentialId,
|
|
5308
|
+
apiBaseUrl,
|
|
5309
|
+
getAccessToken,
|
|
5310
|
+
accounts,
|
|
5311
|
+
polling,
|
|
5312
|
+
onError,
|
|
5313
|
+
idempotencyKey,
|
|
5314
|
+
merchantAuthorization,
|
|
5315
|
+
destination,
|
|
5316
|
+
selectedProviderId
|
|
5317
|
+
]);
|
|
5210
5318
|
const completePasskeyRegistration = useCallback(async (credentialId, publicKey) => {
|
|
5211
5319
|
const token = await getAccessToken();
|
|
5212
5320
|
if (!token) throw new Error("Not authenticated");
|
|
@@ -5468,7 +5576,9 @@ function SwypePaymentInner({
|
|
|
5468
5576
|
onChangeSource: () => setStep("wallet-picker"),
|
|
5469
5577
|
onSwitchWallet: () => setStep("wallet-picker"),
|
|
5470
5578
|
onBack: onBack ?? (() => handleLogout()),
|
|
5471
|
-
onLogout: handleLogout
|
|
5579
|
+
onLogout: handleLogout,
|
|
5580
|
+
onIncreaseLimit: handleIncreaseLimit,
|
|
5581
|
+
increasingLimit
|
|
5472
5582
|
}
|
|
5473
5583
|
);
|
|
5474
5584
|
}
|