@flopay/react 1.2.1 → 1.2.4

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.mjs CHANGED
@@ -686,6 +686,7 @@ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
686
686
  var DEFAULT_BUTTON_HEIGHT = 45;
687
687
  function DirectPayPalButton({
688
688
  sessionId,
689
+ nonce,
689
690
  billingApiUrl,
690
691
  email,
691
692
  clientId,
@@ -723,6 +724,7 @@ function DirectPayPalButton({
723
724
  const runBeforeButtonClickRef = useRef2(runBeforeButtonClick);
724
725
  const sessionRef = useRef2(session);
725
726
  const emailRef = useRef2(email);
727
+ const nonceRef = useRef2(nonce);
726
728
  const beforeClickRef = useRef2(null);
727
729
  useEffect3(() => {
728
730
  onTokenizedBodyRef.current = onTokenizedBody;
@@ -751,6 +753,9 @@ function DirectPayPalButton({
751
753
  useEffect3(() => {
752
754
  emailRef.current = email;
753
755
  }, [email]);
756
+ useEffect3(() => {
757
+ nonceRef.current = nonce;
758
+ }, [nonce]);
754
759
  useEffect3(() => {
755
760
  onLoadStateChangeRef.current?.(ready && !failed);
756
761
  }, [ready, failed]);
@@ -906,6 +911,7 @@ function DirectPayPalButton({
906
911
  effectiveUserId,
907
912
  {
908
913
  sessionId: effectiveSessionId,
914
+ nonce: nonceRef.current,
909
915
  tokenizedData: body,
910
916
  accountData: {
911
917
  userId: effectiveUserId,
@@ -940,9 +946,12 @@ function DirectPayPalButton({
940
946
  const prepared = beforeClickRef.current;
941
947
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
942
948
  const effectiveEmail = prepared?.accountPatch?.email ?? emailRef.current;
949
+ const intentHeaders = { "Content-Type": "application/json" };
950
+ const currentNonce = nonceRef.current;
951
+ if (currentNonce) intentHeaders["x-checkout-session-token"] = currentNonce;
943
952
  const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
944
953
  method: "POST",
945
- headers: { "Content-Type": "application/json" },
954
+ headers: intentHeaders,
946
955
  body: JSON.stringify({
947
956
  sessionId: effectiveSessionId,
948
957
  email: effectiveEmail,
@@ -1235,6 +1244,7 @@ import { FloPayError as FloPayError3, resolveTheme } from "@flopay/shared";
1235
1244
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1236
1245
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1237
1246
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1247
+ var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
1238
1248
  var FLOPAY_KEYFRAMES = `
1239
1249
  .paypal-buttons { margin: 0 !important; vertical-align: top !important; }
1240
1250
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
@@ -1371,6 +1381,7 @@ var SplitCardForm = forwardRef(
1371
1381
  );
1372
1382
  function PayPalButtonInner({
1373
1383
  sessionId,
1384
+ nonce,
1374
1385
  email,
1375
1386
  billingApiUrl,
1376
1387
  onTokenizedBody,
@@ -1400,6 +1411,25 @@ function PayPalButtonInner({
1400
1411
  const redirectStatus = params.get("redirect_status");
1401
1412
  if (!paymentIntentId || !clientSecret) return;
1402
1413
  paypalResumeAttempted.current = true;
1414
+ let persistedOverrides;
1415
+ try {
1416
+ const raw = localStorage.getItem(PAYPAL_RESUME_KEY);
1417
+ if (raw) {
1418
+ const parsed = JSON.parse(raw);
1419
+ if (parsed.clientSecret === clientSecret) {
1420
+ persistedOverrides = {
1421
+ ...parsed.accountPatch ? { accountPatch: parsed.accountPatch } : {},
1422
+ ...parsed.sessionId ? { sessionId: parsed.sessionId } : {},
1423
+ ...parsed.nonce ? { nonce: parsed.nonce } : {}
1424
+ };
1425
+ }
1426
+ }
1427
+ } catch {
1428
+ }
1429
+ try {
1430
+ localStorage.removeItem(PAYPAL_RESUME_KEY);
1431
+ } catch {
1432
+ }
1403
1433
  const cleanedUrl = new URL(window.location.href);
1404
1434
  cleanedUrl.searchParams.delete("payment_intent");
1405
1435
  cleanedUrl.searchParams.delete("payment_intent_client_secret");
@@ -1426,7 +1456,7 @@ function PayPalButtonInner({
1426
1456
  type: "card",
1427
1457
  threeDSecureActionResultTokenId: paymentIntent.id,
1428
1458
  isPaypal: true
1429
- });
1459
+ }, persistedOverrides);
1430
1460
  } else {
1431
1461
  const message = "PayPal payment was not completed. Please try again.";
1432
1462
  onErrorChange?.(message);
@@ -1452,7 +1482,8 @@ function PayPalButtonInner({
1452
1482
  }
1453
1483
  beforeClickRef.current = {
1454
1484
  accountPatch: beforeClick.accountPatch,
1455
- sessionId: beforeClick.sessionId
1485
+ sessionId: beforeClick.sessionId,
1486
+ nonce: beforeClick.nonce
1456
1487
  };
1457
1488
  onButtonClick?.("paypal");
1458
1489
  event.resolve();
@@ -1469,10 +1500,12 @@ function PayPalButtonInner({
1469
1500
  }
1470
1501
  prepared = {
1471
1502
  accountPatch: beforeClick.accountPatch,
1472
- sessionId: beforeClick.sessionId
1503
+ sessionId: beforeClick.sessionId,
1504
+ nonce: beforeClick.nonce
1473
1505
  };
1474
1506
  }
1475
1507
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1508
+ const effectiveNonce = prepared?.nonce ?? nonce;
1476
1509
  const effectiveEmail = prepared?.accountPatch?.email ?? email;
1477
1510
  try {
1478
1511
  setSubmitting(true);
@@ -1488,9 +1521,11 @@ function PayPalButtonInner({
1488
1521
  event.paymentFailed({ reason: "fail", message });
1489
1522
  return;
1490
1523
  }
1524
+ const intentHeaders = { "Content-Type": "application/json" };
1525
+ if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
1491
1526
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1492
1527
  method: "POST",
1493
- headers: { "Content-Type": "application/json" },
1528
+ headers: intentHeaders,
1494
1529
  body: JSON.stringify({
1495
1530
  sessionId: effectiveSessionId,
1496
1531
  email: effectiveEmail,
@@ -1519,12 +1554,29 @@ function PayPalButtonInner({
1519
1554
  if (paymentMethod?.id) {
1520
1555
  confirmParams["payment_method"] = paymentMethod.id;
1521
1556
  }
1557
+ if (typeof window !== "undefined") {
1558
+ try {
1559
+ localStorage.setItem(PAYPAL_RESUME_KEY, JSON.stringify({
1560
+ clientSecret: intentClientSecret,
1561
+ sessionId: effectiveSessionId,
1562
+ nonce: effectiveNonce,
1563
+ accountPatch: prepared?.accountPatch
1564
+ }));
1565
+ } catch {
1566
+ }
1567
+ }
1522
1568
  const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
1523
1569
  clientSecret: intentClientSecret,
1524
1570
  confirmParams,
1525
1571
  redirect: "if_required"
1526
1572
  });
1527
1573
  if (confirmError) {
1574
+ if (typeof window !== "undefined") {
1575
+ try {
1576
+ localStorage.removeItem(PAYPAL_RESUME_KEY);
1577
+ } catch {
1578
+ }
1579
+ }
1528
1580
  const message = confirmError.message ?? "PayPal payment failed.";
1529
1581
  onErrorChange?.(message);
1530
1582
  onDecline?.(buildDeclineEvent("paypal", message, {
@@ -1532,6 +1584,12 @@ function PayPalButtonInner({
1532
1584
  }));
1533
1585
  return;
1534
1586
  }
1587
+ if (typeof window !== "undefined") {
1588
+ try {
1589
+ localStorage.removeItem(PAYPAL_RESUME_KEY);
1590
+ } catch {
1591
+ }
1592
+ }
1535
1593
  const confirmedPmId = typeof paymentIntent?.payment_method === "string" ? paymentIntent.payment_method : paymentIntent?.payment_method?.id;
1536
1594
  onTokenizedBody({
1537
1595
  id: confirmedPmId ?? paymentIntent?.id,
@@ -1540,7 +1598,8 @@ function PayPalButtonInner({
1540
1598
  isPaypal: true
1541
1599
  }, {
1542
1600
  accountPatch: prepared?.accountPatch,
1543
- sessionId: effectiveSessionId
1601
+ sessionId: effectiveSessionId,
1602
+ nonce: effectiveNonce
1544
1603
  });
1545
1604
  return;
1546
1605
  } catch (err) {
@@ -1548,7 +1607,7 @@ function PayPalButtonInner({
1548
1607
  } finally {
1549
1608
  setSubmitting(false);
1550
1609
  }
1551
- }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
1610
+ }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
1552
1611
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1553
1612
  /* @__PURE__ */ jsx6(
1554
1613
  ExpressCheckoutReadySwap,
@@ -1588,6 +1647,7 @@ function PayPalButtonInner({
1588
1647
  }
1589
1648
  function WalletButtonInner({
1590
1649
  sessionId,
1650
+ nonce,
1591
1651
  email,
1592
1652
  billingApiUrl,
1593
1653
  expressMethods,
@@ -1624,11 +1684,13 @@ function WalletButtonInner({
1624
1684
  }
1625
1685
  prepared = {
1626
1686
  accountPatch: beforeClick.accountPatch,
1627
- sessionId: beforeClick.sessionId
1687
+ sessionId: beforeClick.sessionId,
1688
+ nonce: beforeClick.nonce
1628
1689
  };
1629
1690
  }
1630
1691
  const method = buttonMethod;
1631
1692
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1693
+ const effectiveNonce = prepared?.nonce ?? nonce;
1632
1694
  const effectiveEmail = prepared?.accountPatch?.email ?? email;
1633
1695
  try {
1634
1696
  setSubmitting(true);
@@ -1646,9 +1708,11 @@ function WalletButtonInner({
1646
1708
  if (!effectiveSessionId || !effectiveEmail) {
1647
1709
  throw new Error("Missing sessionId or email for wallet payment");
1648
1710
  }
1711
+ const intentHeaders = { "Content-Type": "application/json" };
1712
+ if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
1649
1713
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1650
1714
  method: "POST",
1651
- headers: { "Content-Type": "application/json" },
1715
+ headers: intentHeaders,
1652
1716
  body: JSON.stringify({
1653
1717
  sessionId: effectiveSessionId,
1654
1718
  email: effectiveEmail,
@@ -1687,7 +1751,8 @@ function WalletButtonInner({
1687
1751
  threeDSecureActionResultTokenId: paymentIntent?.id
1688
1752
  }, {
1689
1753
  accountPatch: prepared?.accountPatch,
1690
- sessionId: effectiveSessionId
1754
+ sessionId: effectiveSessionId,
1755
+ nonce: effectiveNonce
1691
1756
  });
1692
1757
  } catch (err) {
1693
1758
  onErrorChange?.(err instanceof Error ? err.message : "Wallet payment failed. Please try again.");
@@ -1695,7 +1760,7 @@ function WalletButtonInner({
1695
1760
  setSubmitting(false);
1696
1761
  }
1697
1762
  },
1698
- [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]
1763
+ [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]
1699
1764
  );
1700
1765
  const expressMethodMap = useMemo3(() => {
1701
1766
  const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
@@ -1741,7 +1806,8 @@ function WalletButtonInner({
1741
1806
  }
1742
1807
  beforeClickRef.current = {
1743
1808
  accountPatch: beforeClick.accountPatch,
1744
- sessionId: beforeClick.sessionId
1809
+ sessionId: beforeClick.sessionId,
1810
+ nonce: beforeClick.nonce
1745
1811
  };
1746
1812
  onButtonClick?.(lastWalletMethodRef.current);
1747
1813
  event.resolve();
@@ -1896,6 +1962,7 @@ function StripeMethodButton({
1896
1962
  function StripeMethodInlineForm({
1897
1963
  method,
1898
1964
  sessionId,
1965
+ nonce,
1899
1966
  email,
1900
1967
  billingName,
1901
1968
  billingApiUrl,
@@ -1929,6 +1996,7 @@ function StripeMethodInlineForm({
1929
1996
  }
1930
1997
  onButtonClick?.("card");
1931
1998
  const effectiveSessionId = beforeClick.sessionId ?? sessionId;
1999
+ const effectiveNonce = beforeClick.nonce ?? nonce;
1932
2000
  const effectiveEmail = beforeClick.accountPatch?.email ?? email;
1933
2001
  try {
1934
2002
  onErrorChange?.(null);
@@ -1946,9 +2014,11 @@ function StripeMethodInlineForm({
1946
2014
  if (!effectiveSessionId || !effectiveEmail) {
1947
2015
  throw new Error("Missing sessionId or email for payment.");
1948
2016
  }
2017
+ const intentHeaders = { "Content-Type": "application/json" };
2018
+ if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
1949
2019
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1950
2020
  method: "POST",
1951
- headers: { "Content-Type": "application/json" },
2021
+ headers: intentHeaders,
1952
2022
  body: JSON.stringify({
1953
2023
  sessionId: effectiveSessionId,
1954
2024
  email: effectiveEmail,
@@ -1968,7 +2038,10 @@ function StripeMethodInlineForm({
1968
2038
  if (typeof window !== "undefined") {
1969
2039
  try {
1970
2040
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
2041
+ clientSecret: intentClientSecret,
1971
2042
  sessionId: effectiveSessionId,
2043
+ nonce: effectiveNonce,
2044
+ accountPatch: beforeClick.accountPatch,
1972
2045
  paymentMethodId: paymentMethod.id,
1973
2046
  paymentMethodType: paymentMethod.type ?? method,
1974
2047
  gateway: "stripe"
@@ -2025,7 +2098,8 @@ function StripeMethodInlineForm({
2025
2098
  paymentMethodType: paymentMethod.type ?? method
2026
2099
  }, {
2027
2100
  accountPatch: beforeClick.accountPatch,
2028
- sessionId: effectiveSessionId
2101
+ sessionId: effectiveSessionId,
2102
+ nonce: effectiveNonce
2029
2103
  });
2030
2104
  } catch (err) {
2031
2105
  onErrorChange?.(err instanceof Error ? err.message : "Payment failed. Please try again.");
@@ -2041,6 +2115,7 @@ function StripeMethodInlineForm({
2041
2115
  runBeforeButtonClick,
2042
2116
  onButtonClick,
2043
2117
  sessionId,
2118
+ nonce,
2044
2119
  email,
2045
2120
  baseUrl,
2046
2121
  method,
@@ -2101,6 +2176,7 @@ function StripeMethodInlineForm({
2101
2176
  }
2102
2177
  function StripePaymentElementInner({
2103
2178
  sessionId,
2179
+ nonce,
2104
2180
  email,
2105
2181
  billingName,
2106
2182
  billingApiUrl,
@@ -2146,15 +2222,18 @@ function StripePaymentElementInner({
2146
2222
  }
2147
2223
  onButtonClick?.("card");
2148
2224
  const effectiveSessionId = beforeClick.sessionId ?? sessionId;
2225
+ const effectiveNonce = beforeClick.nonce ?? nonce;
2149
2226
  const effectiveEmail = beforeClick.accountPatch?.email ?? email;
2150
2227
  try {
2151
2228
  onErrorChange?.(null);
2152
2229
  if (!effectiveSessionId || !effectiveEmail) {
2153
2230
  throw new Error("Missing sessionId or email for payment.");
2154
2231
  }
2232
+ const intentHeaders = { "Content-Type": "application/json" };
2233
+ if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
2155
2234
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2156
2235
  method: "POST",
2157
- headers: { "Content-Type": "application/json" },
2236
+ headers: intentHeaders,
2158
2237
  body: JSON.stringify({
2159
2238
  sessionId: effectiveSessionId,
2160
2239
  email: effectiveEmail,
@@ -2178,7 +2257,10 @@ function StripePaymentElementInner({
2178
2257
  if (typeof window !== "undefined") {
2179
2258
  try {
2180
2259
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
2260
+ clientSecret: intentClientSecret,
2181
2261
  sessionId: effectiveSessionId,
2262
+ nonce: effectiveNonce,
2263
+ accountPatch: beforeClick.accountPatch,
2182
2264
  paymentMethodType: method,
2183
2265
  gateway: "stripe"
2184
2266
  }));
@@ -2243,7 +2325,8 @@ function StripePaymentElementInner({
2243
2325
  paymentMethodType: method
2244
2326
  }, {
2245
2327
  accountPatch: beforeClick.accountPatch,
2246
- sessionId: effectiveSessionId
2328
+ sessionId: effectiveSessionId,
2329
+ nonce: effectiveNonce
2247
2330
  });
2248
2331
  } catch (err) {
2249
2332
  onErrorChange?.(err instanceof Error ? err.message : "Payment failed. Please try again.");
@@ -2257,6 +2340,7 @@ function StripePaymentElementInner({
2257
2340
  runBeforeButtonClick,
2258
2341
  onButtonClick,
2259
2342
  sessionId,
2343
+ nonce,
2260
2344
  email,
2261
2345
  baseUrl,
2262
2346
  billingName,
@@ -2348,6 +2432,7 @@ function StripePaymentElementInner({
2348
2432
  }
2349
2433
  function SplitCardFormInner({
2350
2434
  sessionId,
2435
+ nonce,
2351
2436
  billingApiUrl,
2352
2437
  email,
2353
2438
  userId,
@@ -2657,19 +2742,24 @@ function SplitCardFormInner({
2657
2742
  const applyInlineSessionPatch = useCallback(
2658
2743
  (patch, method) => {
2659
2744
  if (!checkout.applyInlineSessionPatch) {
2660
- return Promise.resolve({ error: null, sessionId });
2745
+ return Promise.resolve({ error: null, sessionId, nonce });
2661
2746
  }
2662
2747
  return checkout.applyInlineSessionPatch(patch).then((result) => ({
2663
2748
  error: null,
2664
- sessionId: result.sessionId || sessionId
2749
+ sessionId: result.sessionId || sessionId,
2750
+ // A freshly bootstrapped session carries its own nonce on
2751
+ // `session.clientSecret`. Pair it with the new sessionId so
2752
+ // downstream continuation calls don't send the new id with the
2753
+ // prior session-bound token.
2754
+ nonce: result.session?.clientSecret || nonce
2665
2755
  })).catch((err) => {
2666
2756
  const floPayErr = normalizeBeforeButtonClickError(method, err);
2667
2757
  updateError(floPayErr.message);
2668
2758
  onError?.(floPayErr);
2669
- return { error: floPayErr, sessionId };
2759
+ return { error: floPayErr, sessionId, nonce };
2670
2760
  });
2671
2761
  },
2672
- [checkout.applyInlineSessionPatch, onError, sessionId, updateError]
2762
+ [checkout.applyInlineSessionPatch, nonce, onError, sessionId, updateError]
2673
2763
  );
2674
2764
  const runBeforeButtonClick = useCallback(async (method) => {
2675
2765
  if (!onBeforeButtonClick) return { proceed: true };
@@ -2696,7 +2786,8 @@ function SplitCardFormInner({
2696
2786
  return {
2697
2787
  proceed: true,
2698
2788
  accountPatch: result.account,
2699
- sessionId: patchResult.sessionId
2789
+ sessionId: patchResult.sessionId,
2790
+ nonce: patchResult.nonce
2700
2791
  };
2701
2792
  }
2702
2793
  return { proceed: true };
@@ -2715,6 +2806,7 @@ function SplitCardFormInner({
2715
2806
  setOverlayStatus("processing");
2716
2807
  updateError(null);
2717
2808
  const effectiveSessionId = overrides?.sessionId ?? sessionId;
2809
+ const effectiveNonce = overrides?.nonce ?? nonce;
2718
2810
  const effectiveAccount = mergeAccountPatch(resolvedAccount, overrides?.accountPatch);
2719
2811
  const resolvedCompletionPaymentMethodId = overrides?.completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
2720
2812
  const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
@@ -2722,6 +2814,7 @@ function SplitCardFormInner({
2722
2814
  const api = new PaymentAPI2(baseUrl);
2723
2815
  const response = await api.processPayment(effectiveAccount.userId ?? "", {
2724
2816
  sessionId: effectiveSessionId,
2817
+ nonce: effectiveNonce,
2725
2818
  tokenizedData: requestTokenizedBody,
2726
2819
  accountData: {
2727
2820
  userId: effectiveAccount.userId ?? "",
@@ -2825,6 +2918,12 @@ function SplitCardFormInner({
2825
2918
  type: "card",
2826
2919
  threeDSecureActionResultTokenId: result.paymentIntentId
2827
2920
  }, {
2921
+ // Preserve the session/nonce/account overrides resolved
2922
+ // above so the retry talks to the same (possibly
2923
+ // freshly bootstrapped) session as the original attempt.
2924
+ accountPatch: overrides?.accountPatch,
2925
+ sessionId: effectiveSessionId,
2926
+ nonce: effectiveNonce,
2828
2927
  completionPaymentMethodId: resolvedCompletionPaymentMethodId
2829
2928
  });
2830
2929
  }
@@ -2908,7 +3007,7 @@ function SplitCardFormInner({
2908
3007
  processingRef.current = false;
2909
3008
  }
2910
3009
  },
2911
- [baseUrl, sessionId, resolvedAccount, fullName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
3010
+ [baseUrl, sessionId, nonce, resolvedAccount, fullName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
2912
3011
  );
2913
3012
  const dispatchTokenizedBody = useCallback(
2914
3013
  (tokenizedBody, overrides) => {
@@ -2966,7 +3065,17 @@ function SplitCardFormInner({
2966
3065
  }
2967
3066
  };
2968
3067
  const payload = readPayload(STRIPE_RESUME_KEY) ?? readPayload(LEGACY_WALLET_RESUME_KEY);
2969
- if (payload?.sessionId && payload.sessionId !== sessionId) return;
3068
+ let persistedOverrides;
3069
+ if (payload?.clientSecret) {
3070
+ if (payload.clientSecret !== clientSecret) return;
3071
+ persistedOverrides = {
3072
+ ...payload.accountPatch ? { accountPatch: payload.accountPatch } : {},
3073
+ ...payload.sessionId ? { sessionId: payload.sessionId } : {},
3074
+ ...payload.nonce ? { nonce: payload.nonce } : {}
3075
+ };
3076
+ } else if (payload?.sessionId && payload.sessionId !== sessionId) {
3077
+ return;
3078
+ }
2970
3079
  stripeResumeAttemptedRef.current = true;
2971
3080
  localStorage.removeItem(STRIPE_RESUME_KEY);
2972
3081
  localStorage.removeItem(LEGACY_WALLET_RESUME_KEY);
@@ -3013,7 +3122,7 @@ function SplitCardFormInner({
3013
3122
  threeDSecureActionResultTokenId: paymentIntent.id,
3014
3123
  gateway: payload?.gateway ?? "stripe",
3015
3124
  paymentMethodType
3016
- });
3125
+ }, persistedOverrides);
3017
3126
  })();
3018
3127
  }, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
3019
3128
  const handleSubmit = useCallback(
@@ -3084,9 +3193,11 @@ function SplitCardFormInner({
3084
3193
  if (!sessionId || !resolvedAccount.email) {
3085
3194
  throw new FloPayError3("Missing sessionId or email", "validation_error");
3086
3195
  }
3196
+ const intentHeaders = { "Content-Type": "application/json" };
3197
+ if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
3087
3198
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3088
3199
  method: "POST",
3089
- headers: { "Content-Type": "application/json" },
3200
+ headers: intentHeaders,
3090
3201
  body: JSON.stringify({
3091
3202
  sessionId,
3092
3203
  email: resolvedAccount.email,
@@ -3154,7 +3265,7 @@ function SplitCardFormInner({
3154
3265
  }
3155
3266
  }
3156
3267
  },
3157
- [flopay, elements, isSubmitting, sessionId, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3268
+ [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3158
3269
  );
3159
3270
  const isReady = flopay !== null && elements !== null;
3160
3271
  if (!isReady) {
@@ -3686,6 +3797,7 @@ function SplitCardFormInner({
3686
3797
  DirectPayPalButton,
3687
3798
  {
3688
3799
  sessionId,
3800
+ nonce,
3689
3801
  billingApiUrl: resolvedBillingApiUrl,
3690
3802
  email: resolvedAccount.email,
3691
3803
  clientId: directPaypal.clientId,
@@ -3711,6 +3823,7 @@ function SplitCardFormInner({
3711
3823
  PayPalButtonInner,
3712
3824
  {
3713
3825
  sessionId,
3826
+ nonce,
3714
3827
  email: resolvedAccount.email,
3715
3828
  billingApiUrl: resolvedBillingApiUrl,
3716
3829
  onTokenizedBody: dispatchTokenizedBody,
@@ -3727,6 +3840,7 @@ function SplitCardFormInner({
3727
3840
  WalletButtonInner,
3728
3841
  {
3729
3842
  sessionId,
3843
+ nonce,
3730
3844
  email: resolvedAccount.email,
3731
3845
  billingApiUrl: resolvedBillingApiUrl,
3732
3846
  expressMethods: walletExpressMethods,
@@ -3743,6 +3857,7 @@ function SplitCardFormInner({
3743
3857
  StripePaymentElementInner,
3744
3858
  {
3745
3859
  sessionId,
3860
+ nonce,
3746
3861
  email: resolvedAccount.email,
3747
3862
  billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
3748
3863
  billingApiUrl: resolvedBillingApiUrl,
@@ -3918,6 +4033,7 @@ function SplitCardFormInner({
3918
4033
  {
3919
4034
  method: expandedApmMethod,
3920
4035
  sessionId,
4036
+ nonce,
3921
4037
  email: resolvedAccount.email,
3922
4038
  billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
3923
4039
  billingApiUrl: resolvedBillingApiUrl,
@@ -4016,6 +4132,7 @@ function SplitCardFormInner({
4016
4132
  {
4017
4133
  method: expandedApmMethod,
4018
4134
  sessionId,
4135
+ nonce,
4019
4136
  email: resolvedAccount.email,
4020
4137
  billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
4021
4138
  billingApiUrl: resolvedBillingApiUrl,
@@ -4064,6 +4181,7 @@ function SplitCardFormInner({
4064
4181
  DirectPayPalButton,
4065
4182
  {
4066
4183
  sessionId,
4184
+ nonce,
4067
4185
  billingApiUrl: resolvedBillingApiUrl,
4068
4186
  email: resolvedAccount.email,
4069
4187
  clientId: directPaypal.clientId,
@@ -4089,6 +4207,7 @@ function SplitCardFormInner({
4089
4207
  PayPalButtonInner,
4090
4208
  {
4091
4209
  sessionId,
4210
+ nonce,
4092
4211
  email: resolvedAccount.email,
4093
4212
  billingApiUrl: resolvedBillingApiUrl,
4094
4213
  onTokenizedBody: dispatchTokenizedBody,
@@ -4105,6 +4224,7 @@ function SplitCardFormInner({
4105
4224
  WalletButtonInner,
4106
4225
  {
4107
4226
  sessionId,
4227
+ nonce,
4108
4228
  email: resolvedAccount.email,
4109
4229
  billingApiUrl: resolvedBillingApiUrl,
4110
4230
  expressMethods: walletExpressMethods,
@@ -4121,6 +4241,7 @@ function SplitCardFormInner({
4121
4241
  StripePaymentElementInner,
4122
4242
  {
4123
4243
  sessionId,
4244
+ nonce,
4124
4245
  email: resolvedAccount.email,
4125
4246
  billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
4126
4247
  billingApiUrl: resolvedBillingApiUrl,
@@ -4264,6 +4385,7 @@ function getRedirectTokenFromProcessResponse(json, options) {
4264
4385
  async function recover3DSRedirectResult({
4265
4386
  billingApiUrl,
4266
4387
  sessionId,
4388
+ nonce,
4267
4389
  responseJson
4268
4390
  }) {
4269
4391
  const directToken = getRedirectTokenFromProcessResponse(responseJson, {
@@ -4280,7 +4402,7 @@ async function recover3DSRedirectResult({
4280
4402
  }
4281
4403
  try {
4282
4404
  const api = new PaymentAPI3(billingApiUrl);
4283
- const unified = await api.getUnifiedCheckoutSession(sessionId);
4405
+ const unified = await api.getUnifiedCheckoutSession(sessionId, nonce);
4284
4406
  const refreshedToken = unified.data.stripe?.clientSecret;
4285
4407
  if (isStripePaymentIntentClientSecret(refreshedToken)) {
4286
4408
  return {
@@ -4310,6 +4432,7 @@ async function processSavedPaymentForMode({
4310
4432
  const api = new PaymentAPI3(baseUrl);
4311
4433
  const response = await retryOnceOnFetchFailure(() => api.processPayment(customerId, {
4312
4434
  sessionId: resolvedSessionId,
4435
+ nonce: session.clientSecret,
4313
4436
  tokenizedData,
4314
4437
  accountData: {
4315
4438
  userId: customerId,
@@ -4352,6 +4475,7 @@ async function processSavedPaymentForMode({
4352
4475
  const recoveredRedirect = await recover3DSRedirectResult({
4353
4476
  billingApiUrl: baseUrl,
4354
4477
  sessionId: resolvedSessionId,
4478
+ nonce: session.clientSecret,
4355
4479
  responseJson: json
4356
4480
  });
4357
4481
  if (recoveredRedirect) {
@@ -5546,6 +5670,7 @@ function FloPayCheckout({
5546
5670
  DirectPayPalButton,
5547
5671
  {
5548
5672
  sessionId: activeSessionId,
5673
+ nonce: session.clientSecret || void 0,
5549
5674
  billingApiUrl: resolvedBillingUrl,
5550
5675
  email: session.customer?.email,
5551
5676
  clientId: directPaypalConfig.clientId,
@@ -5631,6 +5756,7 @@ function FloPayCheckout({
5631
5756
  SessionInjector,
5632
5757
  {
5633
5758
  sessionId: activeSessionId,
5759
+ nonce: session?.clientSecret || void 0,
5634
5760
  billingApiUrl: resolvedBillingUrl,
5635
5761
  session,
5636
5762
  children
@@ -5640,6 +5766,7 @@ function FloPayCheckout({
5640
5766
  SplitCardForm,
5641
5767
  {
5642
5768
  sessionId: activeSessionId,
5769
+ nonce: session?.clientSecret || void 0,
5643
5770
  email: session?.customer?.email,
5644
5771
  userId: session?.customer?.id,
5645
5772
  firstName: session?.customer?.firstName,
@@ -5690,6 +5817,7 @@ function FloPayCheckout({
5690
5817
  }
5691
5818
  function SessionInjector({
5692
5819
  sessionId,
5820
+ nonce,
5693
5821
  billingApiUrl,
5694
5822
  session,
5695
5823
  children
@@ -5699,6 +5827,7 @@ function SessionInjector({
5699
5827
  const existing = child.props;
5700
5828
  const injected = {};
5701
5829
  if (!existing.sessionId) injected.sessionId = sessionId;
5830
+ if (!existing.nonce && nonce) injected.nonce = nonce;
5702
5831
  if (!existing.billingApiUrl) injected.billingApiUrl = billingApiUrl;
5703
5832
  if (session?.customer) {
5704
5833
  if (!existing.email) injected.email = session.customer.email;
@@ -5948,6 +6077,7 @@ var CheckoutForm = forwardRef2(
5948
6077
  );
5949
6078
  function CheckoutFormInner({
5950
6079
  sessionId,
6080
+ nonce,
5951
6081
  billingApiUrl,
5952
6082
  email,
5953
6083
  userId,
@@ -6002,6 +6132,7 @@ function CheckoutFormInner({
6002
6132
  const api = new PaymentAPI5(baseUrl);
6003
6133
  const response = await api.processPayment(userId ?? "", {
6004
6134
  sessionId,
6135
+ nonce,
6005
6136
  tokenizedData: requestTokenizedBody,
6006
6137
  accountData: {
6007
6138
  userId: userId ?? "",
@@ -6101,7 +6232,7 @@ function CheckoutFormInner({
6101
6232
  setProcessing(false);
6102
6233
  }
6103
6234
  },
6104
- [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
6235
+ [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
6105
6236
  );
6106
6237
  const dispatchTokenizedBody = useCallback3(
6107
6238
  (tokenizedBody) => {
@@ -6184,9 +6315,11 @@ function CheckoutFormInner({
6184
6315
  if (!sessionId || !email) {
6185
6316
  throw new FloPayError6("Missing sessionId or email", "validation_error");
6186
6317
  }
6318
+ const intentHeaders = { "Content-Type": "application/json" };
6319
+ if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
6187
6320
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
6188
6321
  method: "POST",
6189
- headers: { "Content-Type": "application/json" },
6322
+ headers: intentHeaders,
6190
6323
  body: JSON.stringify({
6191
6324
  sessionId,
6192
6325
  email,
@@ -6245,7 +6378,7 @@ function CheckoutFormInner({
6245
6378
  }
6246
6379
  }
6247
6380
  },
6248
- [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
6381
+ [flopay, elements, isSubmitting, sessionId, nonce, email, firstName, lastName, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
6249
6382
  );
6250
6383
  const isReady = flopay !== null && elements !== null;
6251
6384
  return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
@@ -6283,6 +6416,7 @@ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5
6283
6416
  import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6284
6417
  function PayPalButton({
6285
6418
  sessionId,
6419
+ nonce,
6286
6420
  billingApiUrl,
6287
6421
  email,
6288
6422
  userId,
@@ -6307,6 +6441,7 @@ function PayPalButton({
6307
6441
  const api = new PaymentAPI6(baseUrl);
6308
6442
  const response = await api.processPayment(userId ?? "", {
6309
6443
  sessionId,
6444
+ nonce,
6310
6445
  tokenizedData: tokenizedBody,
6311
6446
  accountData: {
6312
6447
  userId: userId ?? "",
@@ -6326,7 +6461,7 @@ function PayPalButton({
6326
6461
  onErrorChange?.(err instanceof Error ? err.message : "An unexpected error occurred");
6327
6462
  }
6328
6463
  },
6329
- [baseUrl, sessionId, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
6464
+ [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
6330
6465
  );
6331
6466
  const dispatchTokenizedBody = useCallback4(
6332
6467
  (body) => {
@@ -6397,6 +6532,7 @@ function PayPalButton({
6397
6532
  const result = await flopay.confirmPayPalPayment({
6398
6533
  billingApiUrl: baseUrl,
6399
6534
  sessionId,
6535
+ nonce,
6400
6536
  email,
6401
6537
  returnUrl: window.location.href
6402
6538
  });
@@ -6417,7 +6553,7 @@ function PayPalButton({
6417
6553
  } finally {
6418
6554
  setSubmitting(false);
6419
6555
  }
6420
- }, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
6556
+ }, [flopay, elements, sessionId, nonce, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
6421
6557
  if (!flopay || !elements) {
6422
6558
  return /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
6423
6559
  }