@flopay/react 0.4.3 → 0.4.5

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
@@ -91,9 +91,9 @@ function FloPayProvider({
91
91
  }
92
92
 
93
93
  // src/flopay-checkout.tsx
94
- import React5, { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
95
- import { loadFloPay, PaymentAPI } from "@flopay/js";
96
- import { SDK_VERSION, FloPayError as FloPayError3, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
94
+ import React6, { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
95
+ import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
96
+ import { SDK_VERSION, FloPayError as FloPayError4, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
97
97
 
98
98
  // src/card-button-content.tsx
99
99
  import "react";
@@ -255,6 +255,157 @@ function useBillingApiUrl() {
255
255
  return ctx.billingApiUrl || resolveBillingApiUrl2();
256
256
  }
257
257
 
258
+ // src/processing-overlay.tsx
259
+ import "react";
260
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
261
+ var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
262
+ var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
263
+ function ProcessingOverlay({
264
+ status,
265
+ errorMessage
266
+ }) {
267
+ return /* @__PURE__ */ jsx4(
268
+ "div",
269
+ {
270
+ "data-testid": "flopay-processing-overlay",
271
+ "data-status": status,
272
+ role: "dialog",
273
+ "aria-modal": "true",
274
+ style: {
275
+ position: "fixed",
276
+ inset: 0,
277
+ background: "rgba(0,0,0,0.35)",
278
+ display: "flex",
279
+ alignItems: "center",
280
+ justifyContent: "center",
281
+ zIndex: 1e3,
282
+ backdropFilter: "blur(2px)"
283
+ },
284
+ children: /* @__PURE__ */ jsxs2(
285
+ "div",
286
+ {
287
+ style: {
288
+ background: "white",
289
+ borderRadius: 12,
290
+ padding: "2rem 2.5rem",
291
+ textAlign: "center",
292
+ boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
293
+ minWidth: 240,
294
+ display: "flex",
295
+ flexDirection: "column",
296
+ alignItems: "center",
297
+ gap: 16
298
+ },
299
+ children: [
300
+ /* @__PURE__ */ jsxs2("div", { style: { width: 48, height: 48, position: "relative" }, children: [
301
+ status === "processing" && /* @__PURE__ */ jsxs2(
302
+ "svg",
303
+ {
304
+ width: "48",
305
+ height: "48",
306
+ viewBox: "0 0 24 24",
307
+ fill: "none",
308
+ xmlns: "http://www.w3.org/2000/svg",
309
+ style: { animation: "flopay-spin 0.8s linear infinite" },
310
+ children: [
311
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
312
+ /* @__PURE__ */ jsx4("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
313
+ ]
314
+ }
315
+ ),
316
+ status === "success" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
317
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
318
+ /* @__PURE__ */ jsx4(
319
+ "path",
320
+ {
321
+ d: "M7 12.5l3 3 7-7",
322
+ stroke: "white",
323
+ strokeWidth: "2.5",
324
+ strokeLinecap: "round",
325
+ strokeLinejoin: "round",
326
+ style: {
327
+ strokeDasharray: 20,
328
+ strokeDashoffset: 20,
329
+ animation: "flopay-draw 0.4s 0.15s ease forwards"
330
+ }
331
+ }
332
+ )
333
+ ] }) }),
334
+ status === "error" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
335
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
336
+ /* @__PURE__ */ jsx4(
337
+ "path",
338
+ {
339
+ d: "M8 8l8 8M16 8l-8 8",
340
+ stroke: "white",
341
+ strokeWidth: "2.5",
342
+ strokeLinecap: "round",
343
+ style: {
344
+ strokeDasharray: 12,
345
+ strokeDashoffset: 12,
346
+ animation: "flopay-draw 0.3s 0.1s ease forwards"
347
+ }
348
+ }
349
+ )
350
+ ] }) })
351
+ ] }),
352
+ /* @__PURE__ */ jsxs2(
353
+ "span",
354
+ {
355
+ style: {
356
+ fontSize: 14,
357
+ fontWeight: 600,
358
+ letterSpacing: "0.05em",
359
+ color: status === "success" ? "#16a34a" : status === "error" ? "#dc2626" : "#374151"
360
+ },
361
+ children: [
362
+ status === "processing" && "PROCESSING...",
363
+ status === "success" && "PAYMENT SUCCESSFUL",
364
+ status === "error" && "PAYMENT FAILED"
365
+ ]
366
+ }
367
+ ),
368
+ status === "success" && /* @__PURE__ */ jsx4(
369
+ "p",
370
+ {
371
+ style: {
372
+ fontSize: 13,
373
+ color: "#6b7280",
374
+ fontWeight: 400,
375
+ maxWidth: 260,
376
+ lineHeight: 1.4,
377
+ margin: 0
378
+ },
379
+ children: "You will be automatically redirected, do not close or navigate away from this window."
380
+ }
381
+ ),
382
+ status === "error" && errorMessage && /* @__PURE__ */ jsx4(
383
+ "p",
384
+ {
385
+ style: {
386
+ fontSize: 13,
387
+ color: "#6b7280",
388
+ fontWeight: 400,
389
+ maxWidth: 260,
390
+ lineHeight: 1.4,
391
+ margin: 0
392
+ },
393
+ children: errorMessage
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsx4("style", { children: `
397
+ @keyframes flopay-spin { to { transform: rotate(360deg); } }
398
+ @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
399
+ @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
400
+ @keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
401
+ ` })
402
+ ]
403
+ }
404
+ )
405
+ }
406
+ );
407
+ }
408
+
258
409
  // src/checkout-utils.ts
259
410
  import { FloPayError } from "@flopay/shared";
260
411
  function mergeInlineSessionPatches(base, patch) {
@@ -312,7 +463,7 @@ function buildSyntheticSession(params, checkoutModeOverride) {
312
463
  },
313
464
  successUrl: params.successUrl,
314
465
  cancelUrl: params.cancelUrl,
315
- checkoutMode: params.checkoutMode ?? checkoutModeOverride ?? "full",
466
+ checkoutMode: checkoutModeOverride ?? params.checkoutMode ?? "full",
316
467
  items: (params.items ?? []).map((item, idx) => ({
317
468
  uuid: `synthetic-item-${idx}`,
318
469
  checkoutSessionId: "",
@@ -353,10 +504,22 @@ function buildDeclineEvent(method, input, overrides) {
353
504
  ...declineCode ? { declineCode } : {}
354
505
  };
355
506
  }
507
+ function resolveTokenizedPaymentMethodId(tokenizedBody) {
508
+ const candidates = [
509
+ tokenizedBody?.id,
510
+ tokenizedBody?.originalPaymentMethodId
511
+ ];
512
+ for (const candidate of candidates) {
513
+ if (typeof candidate === "string" && candidate.startsWith("pm_")) {
514
+ return candidate;
515
+ }
516
+ }
517
+ return void 0;
518
+ }
356
519
 
357
520
  // src/split-card-form.tsx
358
521
  import { FloPayError as FloPayError2 } from "@flopay/shared";
359
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
522
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
360
523
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
361
524
  var FLOPAY_KEYFRAMES = `
362
525
  @keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
@@ -398,7 +561,7 @@ function normalizeBeforeButtonClickError(method, err) {
398
561
  );
399
562
  }
400
563
  function FloPayKeyframes() {
401
- return /* @__PURE__ */ jsx4("style", { children: FLOPAY_KEYFRAMES });
564
+ return /* @__PURE__ */ jsx5("style", { children: FLOPAY_KEYFRAMES });
402
565
  }
403
566
  function toCssSize(value) {
404
567
  if (typeof value === "number") return `${value}px`;
@@ -419,8 +582,8 @@ function ExpressCheckoutReadySwap({
419
582
  children
420
583
  }) {
421
584
  if (state === "unavailable") return null;
422
- return /* @__PURE__ */ jsxs2("div", { style: { position: "relative", minHeight: 44 }, children: [
423
- /* @__PURE__ */ jsx4(
585
+ return /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: 44 }, children: [
586
+ /* @__PURE__ */ jsx5(
424
587
  "div",
425
588
  {
426
589
  "data-testid": placeholderTestId,
@@ -439,7 +602,7 @@ function ExpressCheckoutReadySwap({
439
602
  }
440
603
  }
441
604
  ),
442
- /* @__PURE__ */ jsx4(
605
+ /* @__PURE__ */ jsx5(
443
606
  "div",
444
607
  {
445
608
  style: {
@@ -454,109 +617,9 @@ function ExpressCheckoutReadySwap({
454
617
  )
455
618
  ] });
456
619
  }
457
- function ProcessingOverlay({ status, errorMessage }) {
458
- return /* @__PURE__ */ jsx4("div", { style: {
459
- position: "fixed",
460
- inset: 0,
461
- background: "rgba(0,0,0,0.35)",
462
- display: "flex",
463
- alignItems: "center",
464
- justifyContent: "center",
465
- zIndex: 1e3,
466
- backdropFilter: "blur(2px)"
467
- }, children: /* @__PURE__ */ jsxs2("div", { style: {
468
- background: "white",
469
- borderRadius: 12,
470
- padding: "2rem 2.5rem",
471
- textAlign: "center",
472
- boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
473
- minWidth: 240,
474
- display: "flex",
475
- flexDirection: "column",
476
- alignItems: "center",
477
- gap: 16
478
- }, children: [
479
- /* @__PURE__ */ jsxs2("div", { style: { width: 48, height: 48, position: "relative" }, children: [
480
- status === "processing" && /* @__PURE__ */ jsxs2(
481
- "svg",
482
- {
483
- width: "48",
484
- height: "48",
485
- viewBox: "0 0 24 24",
486
- fill: "none",
487
- xmlns: "http://www.w3.org/2000/svg",
488
- style: { animation: "flopay-spin 0.8s linear infinite" },
489
- children: [
490
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
491
- /* @__PURE__ */ jsx4("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
492
- ]
493
- }
494
- ),
495
- status === "success" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
496
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
497
- /* @__PURE__ */ jsx4(
498
- "path",
499
- {
500
- d: "M7 12.5l3 3 7-7",
501
- stroke: "white",
502
- strokeWidth: "2.5",
503
- strokeLinecap: "round",
504
- strokeLinejoin: "round",
505
- style: { strokeDasharray: 20, strokeDashoffset: 20, animation: "flopay-draw 0.4s 0.15s ease forwards" }
506
- }
507
- )
508
- ] }) }),
509
- status === "error" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
510
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
511
- /* @__PURE__ */ jsx4(
512
- "path",
513
- {
514
- d: "M8 8l8 8M16 8l-8 8",
515
- stroke: "white",
516
- strokeWidth: "2.5",
517
- strokeLinecap: "round",
518
- style: { strokeDasharray: 12, strokeDashoffset: 12, animation: "flopay-draw 0.3s 0.1s ease forwards" }
519
- }
520
- )
521
- ] }) })
522
- ] }),
523
- /* @__PURE__ */ jsxs2("span", { style: {
524
- fontSize: 14,
525
- fontWeight: 600,
526
- letterSpacing: "0.05em",
527
- color: status === "success" ? "#16a34a" : status === "error" ? "#dc2626" : "#374151"
528
- }, children: [
529
- status === "processing" && "PROCESSING...",
530
- status === "success" && "PAYMENT SUCCESSFUL",
531
- status === "error" && "PAYMENT FAILED"
532
- ] }),
533
- status === "success" && /* @__PURE__ */ jsx4("p", { style: {
534
- fontSize: 13,
535
- color: "#6b7280",
536
- fontWeight: 400,
537
- maxWidth: 260,
538
- lineHeight: 1.4,
539
- margin: 0
540
- }, children: "You will be automatically redirected, do not close or navigate away from this window." }),
541
- status === "error" && errorMessage && /* @__PURE__ */ jsx4("p", { style: {
542
- fontSize: 13,
543
- color: "#6b7280",
544
- fontWeight: 400,
545
- maxWidth: 260,
546
- lineHeight: 1.4,
547
- margin: 0
548
- }, children: errorMessage }),
549
- /* @__PURE__ */ jsx4("style", { children: `
550
- @keyframes flopay-spin { to { transform: rotate(360deg); } }
551
- @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
552
- @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
553
- @keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
554
- ` })
555
- ] }) });
556
- }
557
620
  var SplitCardForm = forwardRef(
558
621
  function SplitCardForm2(props, ref) {
559
- return /* @__PURE__ */ jsx4(SplitCardFormInner, { ...props, innerRef: ref });
622
+ return /* @__PURE__ */ jsx5(SplitCardFormInner, { ...props, innerRef: ref });
560
623
  }
561
624
  );
562
625
  function PayPalButtonInner({
@@ -719,8 +782,8 @@ function PayPalButtonInner({
719
782
  setSubmitting(false);
720
783
  }
721
784
  }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]);
722
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
723
- /* @__PURE__ */ jsx4(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ jsx4(
785
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
786
+ /* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ jsx5(
724
787
  ExpressCheckoutElement,
725
788
  {
726
789
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -742,7 +805,7 @@ function PayPalButtonInner({
742
805
  }
743
806
  }
744
807
  ) }),
745
- submitting && /* @__PURE__ */ jsx4(ProcessingOverlay, { status: "processing" })
808
+ submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
746
809
  ] });
747
810
  }
748
811
  function WalletButtonInner({
@@ -845,8 +908,8 @@ function WalletButtonInner({
845
908
  },
846
909
  [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]
847
910
  );
848
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
849
- /* @__PURE__ */ jsx4(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ jsx4(
911
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
912
+ /* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ jsx5(
850
913
  ExpressCheckoutElement,
851
914
  {
852
915
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["applePay", "googlePay"])),
@@ -884,7 +947,7 @@ function WalletButtonInner({
884
947
  }
885
948
  }
886
949
  ) }),
887
- submitting && /* @__PURE__ */ jsx4(ProcessingOverlay, { status: "processing" })
950
+ submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
888
951
  ] });
889
952
  }
890
953
  function SplitCardFormInner({
@@ -917,6 +980,7 @@ function SplitCardFormInner({
917
980
  cardButtonContent,
918
981
  cardBackButtonContent,
919
982
  cardTitleContent,
983
+ showSecurityFooter = true,
920
984
  onButtonClick,
921
985
  onBeforeButtonClick,
922
986
  enableAVS = false,
@@ -1118,10 +1182,11 @@ function SplitCardFormInner({
1118
1182
  });
1119
1183
  if (response.ok) {
1120
1184
  setOverlayStatus("success");
1121
- await new Promise((r) => setTimeout(r, 1200));
1185
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
1122
1186
  onComplete?.({
1123
1187
  status: "succeeded",
1124
- paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
1188
+ paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
1189
+ paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
1125
1190
  });
1126
1191
  return;
1127
1192
  }
@@ -1151,7 +1216,8 @@ function SplitCardFormInner({
1151
1216
  await processPaymentInternal({
1152
1217
  id: result.paymentIntentId,
1153
1218
  type: "card",
1154
- threeDSecureActionResultTokenId: result.paymentIntentId
1219
+ threeDSecureActionResultTokenId: result.paymentIntentId,
1220
+ originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
1155
1221
  });
1156
1222
  }
1157
1223
  } finally {
@@ -1205,11 +1271,11 @@ function SplitCardFormInner({
1205
1271
  code: json?.code ?? json?.gatewayErrorCode,
1206
1272
  declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1207
1273
  });
1208
- await new Promise((r) => setTimeout(r, 1500));
1274
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
1209
1275
  } catch (err) {
1210
1276
  setOverlayStatus("error");
1211
1277
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
1212
- await new Promise((r) => setTimeout(r, 1500));
1278
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
1213
1279
  } finally {
1214
1280
  setProcessing(false);
1215
1281
  setOverlayStatus(null);
@@ -1333,19 +1399,20 @@ function SplitCardFormInner({
1333
1399
  updateError(confirmResult.error.message);
1334
1400
  onError?.(confirmResult.error);
1335
1401
  emitDecline("card", confirmResult.error);
1336
- await new Promise((r) => setTimeout(r, 1500));
1402
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
1337
1403
  return;
1338
1404
  }
1339
1405
  handedOff = isSelfContained;
1340
1406
  dispatchTokenizedBody({
1341
1407
  id: pmResult.paymentMethodId,
1342
1408
  type: "card",
1343
- threeDSecureActionResultTokenId: confirmResult.paymentIntentId
1409
+ threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
1410
+ originalPaymentMethodId: pmResult.paymentMethodId
1344
1411
  });
1345
1412
  } catch (err) {
1346
1413
  setOverlayStatus("error");
1347
1414
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
1348
- await new Promise((r) => setTimeout(r, 1500));
1415
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
1349
1416
  } finally {
1350
1417
  if (!handedOff) {
1351
1418
  setProcessing(false);
@@ -1357,7 +1424,7 @@ function SplitCardFormInner({
1357
1424
  );
1358
1425
  const isReady = flopay !== null && elements !== null;
1359
1426
  if (!isReady) {
1360
- return /* @__PURE__ */ jsx4("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
1427
+ return /* @__PURE__ */ jsx5("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
1361
1428
  }
1362
1429
  const isButtons = layout === "buttons";
1363
1430
  const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
@@ -1400,7 +1467,7 @@ function SplitCardFormInner({
1400
1467
  invalid: { color: "#ef4444" }
1401
1468
  }
1402
1469
  };
1403
- const cardFormBlock = /* @__PURE__ */ jsxs2("div", { style: {
1470
+ const cardFormBlock = /* @__PURE__ */ jsxs3("div", { style: {
1404
1471
  backgroundColor: cardBg,
1405
1472
  borderRadius: "8px",
1406
1473
  padding: isButtons ? "0" : "1rem",
@@ -1408,7 +1475,7 @@ function SplitCardFormInner({
1408
1475
  ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {},
1409
1476
  ...sharedInputPlaceholderVars
1410
1477
  }, children: [
1411
- /* @__PURE__ */ jsx4("style", { children: `
1478
+ /* @__PURE__ */ jsx5("style", { children: `
1412
1479
  .flopay-shared-input::placeholder {
1413
1480
  color: var(--flopay-input-placeholder-color);
1414
1481
  opacity: 1;
@@ -1417,12 +1484,12 @@ function SplitCardFormInner({
1417
1484
  font-weight: var(--flopay-input-font-weight);
1418
1485
  }
1419
1486
  ` }),
1420
- isButtons && showCardForm && /* @__PURE__ */ jsxs2("div", { style: {
1487
+ isButtons && showCardForm && /* @__PURE__ */ jsxs3("div", { style: {
1421
1488
  display: "flex",
1422
1489
  alignItems: "center",
1423
1490
  padding: "0.75rem 0 0.625rem"
1424
1491
  }, children: [
1425
- /* @__PURE__ */ jsxs2(
1492
+ /* @__PURE__ */ jsxs3(
1426
1493
  "button",
1427
1494
  {
1428
1495
  type: "button",
@@ -1444,7 +1511,7 @@ function SplitCardFormInner({
1444
1511
  },
1445
1512
  "aria-label": "Back to payment methods",
1446
1513
  children: [
1447
- /* @__PURE__ */ jsx4("span", { style: {
1514
+ /* @__PURE__ */ jsx5("span", { style: {
1448
1515
  display: "inline-flex",
1449
1516
  alignItems: "center",
1450
1517
  justifyContent: "center",
@@ -1454,12 +1521,12 @@ function SplitCardFormInner({
1454
1521
  backgroundColor: "#f3f4f6",
1455
1522
  transition: "background-color 0.15s",
1456
1523
  ...bStyles.backButtonIcon
1457
- }, children: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("path", { d: "M15 18l-6-6 6-6" }) }) }),
1458
- /* @__PURE__ */ jsx4(BackButtonContentSlot, { content: cardBackButtonContent })
1524
+ }, children: /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M15 18l-6-6 6-6" }) }) }),
1525
+ /* @__PURE__ */ jsx5(BackButtonContentSlot, { content: cardBackButtonContent })
1459
1526
  ]
1460
1527
  }
1461
1528
  ),
1462
- hideTitle ? /* @__PURE__ */ jsx4("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx4("div", { style: {
1529
+ hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
1463
1530
  flex: 1,
1464
1531
  textAlign: "center",
1465
1532
  fontWeight: 600,
@@ -1467,18 +1534,18 @@ function SplitCardFormInner({
1467
1534
  color: "#262833",
1468
1535
  paddingRight: 80,
1469
1536
  ...bStyles.title
1470
- }, children: /* @__PURE__ */ jsx4(TitleContentSlot, { content: cardTitleContent }) })
1537
+ }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
1471
1538
  ] }),
1472
- !isButtons && !hideTitle && /* @__PURE__ */ jsx4("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx4(TitleContentSlot, { content: cardTitleContent }) }),
1473
- /* @__PURE__ */ jsx4("div", { style: {
1539
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx5("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) }),
1540
+ /* @__PURE__ */ jsx5("div", { style: {
1474
1541
  backgroundColor: cardInputBg,
1475
1542
  border: `1px solid ${resolvedBorder}`,
1476
1543
  borderTopLeftRadius: "8px",
1477
1544
  borderTopRightRadius: "8px",
1478
1545
  padding: "10px"
1479
- }, children: /* @__PURE__ */ jsx4(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
1480
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex" }, children: [
1481
- /* @__PURE__ */ jsx4("div", { style: {
1546
+ }, children: /* @__PURE__ */ jsx5(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
1547
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
1548
+ /* @__PURE__ */ jsx5("div", { style: {
1482
1549
  flex: 1,
1483
1550
  backgroundColor: cardInputBg,
1484
1551
  border: `1px solid ${resolvedBorder}`,
@@ -1486,23 +1553,23 @@ function SplitCardFormInner({
1486
1553
  borderRight: "none",
1487
1554
  borderBottomLeftRadius: "8px",
1488
1555
  padding: "10px"
1489
- }, children: /* @__PURE__ */ jsx4(CardExpiryElement, { options: stripeElementStyle }) }),
1490
- /* @__PURE__ */ jsx4("div", { style: {
1556
+ }, children: /* @__PURE__ */ jsx5(CardExpiryElement, { options: stripeElementStyle }) }),
1557
+ /* @__PURE__ */ jsx5("div", { style: {
1491
1558
  flex: 1,
1492
1559
  backgroundColor: cardInputBg,
1493
1560
  border: `1px solid ${resolvedBorder}`,
1494
1561
  borderTop: "none",
1495
1562
  borderBottomRightRadius: "8px",
1496
1563
  padding: "10px"
1497
- }, children: /* @__PURE__ */ jsx4(CardCvcElement, { options: stripeElementStyle }) })
1564
+ }, children: /* @__PURE__ */ jsx5(CardCvcElement, { options: stripeElementStyle }) })
1498
1565
  ] }),
1499
- /* @__PURE__ */ jsx4("div", { style: {
1566
+ /* @__PURE__ */ jsx5("div", { style: {
1500
1567
  backgroundColor: cardInputBg,
1501
1568
  border: `1px solid ${resolvedBorder}`,
1502
1569
  borderRadius: "8px",
1503
1570
  marginTop: "0.5rem",
1504
1571
  padding: "10px"
1505
- }, children: /* @__PURE__ */ jsx4(
1572
+ }, children: /* @__PURE__ */ jsx5(
1506
1573
  "input",
1507
1574
  {
1508
1575
  className: "flopay-shared-input",
@@ -1522,20 +1589,20 @@ function SplitCardFormInner({
1522
1589
  }
1523
1590
  }
1524
1591
  ) }),
1525
- enableAVS && /* @__PURE__ */ jsxs2("div", { style: {
1592
+ enableAVS && /* @__PURE__ */ jsxs3("div", { style: {
1526
1593
  display: "flex",
1527
1594
  flexDirection: avsLayoutProp === "column" ? "column" : "row",
1528
1595
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
1529
1596
  marginTop: "0.5rem"
1530
1597
  }, children: [
1531
- /* @__PURE__ */ jsx4("div", { style: {
1598
+ /* @__PURE__ */ jsx5("div", { style: {
1532
1599
  flex: avsLayoutProp === "row" ? 1 : void 0,
1533
1600
  backgroundColor: cardInputBg,
1534
1601
  border: `1px solid ${resolvedBorder}`,
1535
1602
  padding: "10px",
1536
1603
  ...avsLayoutProp === "row" ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
1537
1604
  ...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
1538
- }, children: /* @__PURE__ */ jsx4(
1605
+ }, children: /* @__PURE__ */ jsx5(
1539
1606
  "select",
1540
1607
  {
1541
1608
  value: selectedCountry,
@@ -1556,21 +1623,21 @@ function SplitCardFormInner({
1556
1623
  cursor: "pointer",
1557
1624
  ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
1558
1625
  },
1559
- children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs2("option", { value: c.code, children: [
1626
+ children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs3("option", { value: c.code, children: [
1560
1627
  c.flag,
1561
1628
  " ",
1562
1629
  c.name
1563
1630
  ] }, c.code))
1564
1631
  }
1565
1632
  ) }),
1566
- /* @__PURE__ */ jsx4("div", { style: {
1633
+ /* @__PURE__ */ jsx5("div", { style: {
1567
1634
  flex: avsLayoutProp === "row" ? 1 : void 0,
1568
1635
  backgroundColor: cardInputBg,
1569
1636
  border: `1px solid ${resolvedBorder}`,
1570
1637
  padding: "10px",
1571
1638
  ...avsLayoutProp === "row" ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
1572
1639
  ...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
1573
- }, children: /* @__PURE__ */ jsx4(
1640
+ }, children: /* @__PURE__ */ jsx5(
1574
1641
  "input",
1575
1642
  {
1576
1643
  className: "flopay-shared-input",
@@ -1596,7 +1663,7 @@ function SplitCardFormInner({
1596
1663
  }
1597
1664
  ) })
1598
1665
  ] }),
1599
- displayError && /* @__PURE__ */ jsxs2("div", { role: "alert", "data-testid": "flopay-error", style: {
1666
+ displayError && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
1600
1667
  margin: "0.75rem 0",
1601
1668
  padding: "0.625rem 0.875rem",
1602
1669
  background: "#FEF2F2",
@@ -1610,10 +1677,10 @@ function SplitCardFormInner({
1610
1677
  gap: "0.5rem",
1611
1678
  ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
1612
1679
  }, children: [
1613
- /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx4("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1680
+ /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1614
1681
  displayError
1615
1682
  ] }),
1616
- children ?? /* @__PURE__ */ jsx4(
1683
+ children ?? /* @__PURE__ */ jsx5(
1617
1684
  "button",
1618
1685
  {
1619
1686
  type: "submit",
@@ -1636,7 +1703,7 @@ function SplitCardFormInner({
1636
1703
  children: isSubmitting ? "PROCESSING..." : submitLabel
1637
1704
  }
1638
1705
  ),
1639
- !isButtons && /* @__PURE__ */ jsx4("div", { style: {
1706
+ !isButtons && showSecurityFooter && /* @__PURE__ */ jsx5("div", { style: {
1640
1707
  backgroundColor: "#EFF9F0",
1641
1708
  borderRadius: "8px",
1642
1709
  padding: "0.75rem",
@@ -1653,11 +1720,11 @@ function SplitCardFormInner({
1653
1720
  const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
1654
1721
  const buttonsAnim = viewState === "expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
1655
1722
  const cardAnim = viewState === "expanding" ? `flopay-card-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-card-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : void 0;
1656
- return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1657
- /* @__PURE__ */ jsx4(FloPayKeyframes, {}),
1658
- overlayStatus && /* @__PURE__ */ jsx4(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1659
- /* @__PURE__ */ jsxs2("div", { style: { display: "grid" }, children: [
1660
- /* @__PURE__ */ jsxs2("div", { style: {
1723
+ return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1724
+ /* @__PURE__ */ jsx5(FloPayKeyframes, {}),
1725
+ overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1726
+ /* @__PURE__ */ jsxs3("div", { style: { display: "grid" }, children: [
1727
+ /* @__PURE__ */ jsxs3("div", { style: {
1661
1728
  gridArea: "1 / 1",
1662
1729
  display: "flex",
1663
1730
  flexDirection: "column",
@@ -1666,7 +1733,7 @@ function SplitCardFormInner({
1666
1733
  ...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
1667
1734
  ...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
1668
1735
  }, children: [
1669
- showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx4(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx4(
1736
+ showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
1670
1737
  PayPalButtonInner,
1671
1738
  {
1672
1739
  sessionId,
@@ -1680,7 +1747,7 @@ function SplitCardFormInner({
1680
1747
  runBeforeButtonClick
1681
1748
  }
1682
1749
  ) }),
1683
- showWallets && stripeInstance ? /* @__PURE__ */ jsx4(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx4(
1750
+ showWallets && stripeInstance ? /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
1684
1751
  WalletButtonInner,
1685
1752
  {
1686
1753
  sessionId,
@@ -1694,8 +1761,8 @@ function SplitCardFormInner({
1694
1761
  onDecline,
1695
1762
  runBeforeButtonClick
1696
1763
  }
1697
- ) }) : showWallets ? /* @__PURE__ */ jsx4("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1698
- /* @__PURE__ */ jsx4(
1764
+ ) }) : showWallets ? /* @__PURE__ */ jsx5("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1765
+ /* @__PURE__ */ jsx5(
1699
1766
  "button",
1700
1767
  {
1701
1768
  type: "button",
@@ -1733,10 +1800,10 @@ function SplitCardFormInner({
1733
1800
  onMouseUp: (e) => {
1734
1801
  e.currentTarget.style.transform = "scale(1)";
1735
1802
  },
1736
- children: /* @__PURE__ */ jsx4(CardButtonContentSlot, { content: cardButtonContent })
1803
+ children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
1737
1804
  }
1738
1805
  ),
1739
- displayError && viewState === "buttons" && /* @__PURE__ */ jsxs2("div", { role: "alert", "data-testid": "flopay-error", style: {
1806
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
1740
1807
  margin: "0.25rem 0",
1741
1808
  padding: "0.625rem 0.875rem",
1742
1809
  background: "#FEF2F2",
@@ -1750,11 +1817,11 @@ function SplitCardFormInner({
1750
1817
  gap: "0.5rem",
1751
1818
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
1752
1819
  }, children: [
1753
- /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx4("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1820
+ /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1754
1821
  displayError
1755
1822
  ] })
1756
1823
  ] }),
1757
- isCardView && /* @__PURE__ */ jsx4("div", { style: {
1824
+ isCardView && /* @__PURE__ */ jsx5("div", { style: {
1758
1825
  gridArea: "1 / 1",
1759
1826
  ...cardAnim ? { animation: cardAnim } : {},
1760
1827
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
@@ -1762,10 +1829,10 @@ function SplitCardFormInner({
1762
1829
  ] })
1763
1830
  ] });
1764
1831
  }
1765
- return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1766
- /* @__PURE__ */ jsx4(FloPayKeyframes, {}),
1767
- overlayStatus && /* @__PURE__ */ jsx4(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1768
- showWallets && stripeInstance && /* @__PURE__ */ jsx4(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx4(
1832
+ return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1833
+ /* @__PURE__ */ jsx5(FloPayKeyframes, {}),
1834
+ overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1835
+ showWallets && stripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
1769
1836
  WalletButtonInner,
1770
1837
  {
1771
1838
  sessionId,
@@ -1778,7 +1845,7 @@ function SplitCardFormInner({
1778
1845
  onDecline
1779
1846
  }
1780
1847
  ) }),
1781
- showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx4(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx4(
1848
+ showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
1782
1849
  PayPalButtonInner,
1783
1850
  {
1784
1851
  sessionId,
@@ -1790,7 +1857,7 @@ function SplitCardFormInner({
1790
1857
  onDecline
1791
1858
  }
1792
1859
  ) }),
1793
- (showWallets && stripeInstance || showPayPal && paypalStripeInstance) && /* @__PURE__ */ jsxs2("div", { style: {
1860
+ (showWallets && stripeInstance || showPayPal && paypalStripeInstance) && /* @__PURE__ */ jsxs3("div", { style: {
1794
1861
  display: "flex",
1795
1862
  alignItems: "center",
1796
1863
  gap: "0.75rem",
@@ -1798,17 +1865,503 @@ function SplitCardFormInner({
1798
1865
  color: "#999",
1799
1866
  fontSize: "0.85rem"
1800
1867
  }, children: [
1801
- /* @__PURE__ */ jsx4("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
1802
- /* @__PURE__ */ jsx4("span", { children: "or pay with card" }),
1803
- /* @__PURE__ */ jsx4("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
1868
+ /* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
1869
+ /* @__PURE__ */ jsx5("span", { children: "or pay with card" }),
1870
+ /* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
1804
1871
  ] }),
1805
1872
  cardFormBlock
1806
1873
  ] });
1807
1874
  }
1808
1875
 
1876
+ // src/saved-payment-flow.ts
1877
+ import { loadFloPay, PaymentAPI } from "@flopay/js";
1878
+ import { FloPayError as FloPayError3 } from "@flopay/shared";
1879
+ var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
1880
+ function resolveSavedPaymentReturnUrl(session) {
1881
+ if (typeof window !== "undefined" && window.location.href) {
1882
+ return window.location.href;
1883
+ }
1884
+ return session.successUrl || session.cancelUrl || void 0;
1885
+ }
1886
+ function isStripePaymentIntentClientSecret(value) {
1887
+ return typeof value === "string" && value.startsWith("pi_") && value.includes("_secret_");
1888
+ }
1889
+ async function retryOnceOnFetchFailure(action) {
1890
+ try {
1891
+ return await action();
1892
+ } catch (err) {
1893
+ const message = err instanceof Error ? err.message : String(err);
1894
+ if (!/failed to fetch|fetch failed/i.test(message)) {
1895
+ throw err;
1896
+ }
1897
+ return action();
1898
+ }
1899
+ }
1900
+ function getRedirectTokenFromProcessResponse(json, options) {
1901
+ const directCandidates = [
1902
+ json?.threeDSecureToken,
1903
+ json?.clientSecret,
1904
+ json?.stripeClientSecret
1905
+ ];
1906
+ for (const candidate of directCandidates) {
1907
+ if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
1908
+ return candidate;
1909
+ }
1910
+ }
1911
+ const nested = json?.data;
1912
+ if (nested && typeof nested === "object") {
1913
+ const nestedRecord = nested;
1914
+ const nestedCandidates = [
1915
+ nestedRecord.threeDSecureToken,
1916
+ nestedRecord.clientSecret,
1917
+ nestedRecord.stripeClientSecret,
1918
+ nestedRecord.id
1919
+ ];
1920
+ for (const candidate of nestedCandidates) {
1921
+ if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
1922
+ return candidate;
1923
+ }
1924
+ }
1925
+ const nestedGatewayData = nestedRecord.gatewayData;
1926
+ if (nestedGatewayData && typeof nestedGatewayData === "object") {
1927
+ const nestedGatewayDataRecord = nestedGatewayData;
1928
+ const gatewayCandidates = [
1929
+ nestedGatewayDataRecord.stripeClientSecret,
1930
+ nestedGatewayDataRecord.clientSecret
1931
+ ];
1932
+ for (const candidate of gatewayCandidates) {
1933
+ if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
1934
+ return candidate;
1935
+ }
1936
+ }
1937
+ }
1938
+ }
1939
+ return void 0;
1940
+ }
1941
+ async function recover3DSRedirectResult({
1942
+ billingApiUrl,
1943
+ sessionId,
1944
+ responseJson
1945
+ }) {
1946
+ const directToken = getRedirectTokenFromProcessResponse(responseJson, {
1947
+ requirePaymentIntentClientSecret: true
1948
+ });
1949
+ if (typeof directToken === "string" && directToken.length > 0) {
1950
+ return {
1951
+ type: "3ds_required",
1952
+ threeDSecureToken: directToken
1953
+ };
1954
+ }
1955
+ if (!sessionId) {
1956
+ return null;
1957
+ }
1958
+ try {
1959
+ const api = new PaymentAPI(billingApiUrl);
1960
+ const unified = await api.getUnifiedCheckoutSession(sessionId);
1961
+ const refreshedToken = unified.provider === "stripe" ? unified.data.stripe?.clientSecret : void 0;
1962
+ if (isStripePaymentIntentClientSecret(refreshedToken)) {
1963
+ return {
1964
+ type: "3ds_required",
1965
+ threeDSecureToken: refreshedToken
1966
+ };
1967
+ }
1968
+ } catch {
1969
+ }
1970
+ return null;
1971
+ }
1972
+ async function processSavedPaymentForMode({
1973
+ billingApiUrl,
1974
+ sessionId,
1975
+ session,
1976
+ tokenizedData,
1977
+ returnUrl
1978
+ }) {
1979
+ const baseUrl = billingApiUrl.replace(/\/+$/, "");
1980
+ const resolvedSessionId = sessionId ?? session.id;
1981
+ const customerId = session.customer?.id ?? session.accountData?.userId ?? "";
1982
+ const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
1983
+ const firstName = session.customer?.firstName ?? session.accountData?.firstName ?? "";
1984
+ const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
1985
+ const country = session.customer?.country ?? session.accountData?.country ?? void 0;
1986
+ const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
1987
+ const response = await retryOnceOnFetchFailure(() => fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
1988
+ method: "POST",
1989
+ headers: {
1990
+ "Content-Type": "application/json",
1991
+ "x-user-id": customerId
1992
+ },
1993
+ body: JSON.stringify({
1994
+ sessionId: resolvedSessionId,
1995
+ tokenizedData,
1996
+ accountData: {
1997
+ userId: customerId,
1998
+ email: customerEmail,
1999
+ firstName,
2000
+ lastName,
2001
+ country,
2002
+ zip
2003
+ },
2004
+ returnUrl: returnUrl ?? resolveSavedPaymentReturnUrl(session)
2005
+ })
2006
+ }));
2007
+ if (response.ok) {
2008
+ return {
2009
+ type: "success",
2010
+ result: {
2011
+ status: "succeeded",
2012
+ paymentIntentId: tokenizedData?.threeDSecureActionResultTokenId,
2013
+ paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedData)
2014
+ }
2015
+ };
2016
+ }
2017
+ const json = await response.json().catch(() => null);
2018
+ if (json?.type === "paypal_redirect_required" || json?.type === "3ds_required") {
2019
+ const redirectToken = getRedirectTokenFromProcessResponse(json);
2020
+ if (!redirectToken) {
2021
+ throw new FloPayError3(
2022
+ "Authentication is required but no redirect token was provided.",
2023
+ "api_error",
2024
+ { code: "authentication_required" }
2025
+ );
2026
+ }
2027
+ return {
2028
+ type: json.type,
2029
+ threeDSecureToken: redirectToken,
2030
+ paymentMethodId: json.paymentMethodId
2031
+ };
2032
+ }
2033
+ if (json?.gatewayErrorCode === "authentication_required") {
2034
+ const recoveredRedirect = await recover3DSRedirectResult({
2035
+ billingApiUrl: baseUrl,
2036
+ sessionId: resolvedSessionId,
2037
+ responseJson: json
2038
+ });
2039
+ if (recoveredRedirect) {
2040
+ return recoveredRedirect;
2041
+ }
2042
+ throw Object.assign(
2043
+ new FloPayError3(
2044
+ "Your card requires authentication. Please enter your payment details below.",
2045
+ "api_error",
2046
+ { code: "authentication_required" }
2047
+ ),
2048
+ { checkoutMethod: "card" }
2049
+ );
2050
+ }
2051
+ throw new FloPayError3(
2052
+ json?.message ?? "Payment failed. Please try again.",
2053
+ "api_error",
2054
+ {
2055
+ code: json?.code ?? json?.gatewayErrorCode,
2056
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
2057
+ }
2058
+ );
2059
+ }
2060
+ async function processSavedPaymentWithIntent({
2061
+ billingApiUrl,
2062
+ sessionId,
2063
+ session,
2064
+ paymentMethodId,
2065
+ flopay,
2066
+ returnUrl
2067
+ }) {
2068
+ const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
2069
+ if (!customerEmail) {
2070
+ throw Object.assign(
2071
+ new FloPayError3("Customer email is required to create a payment intent.", "validation_error", {
2072
+ param: "email"
2073
+ }),
2074
+ { checkoutMethod: "card" }
2075
+ );
2076
+ }
2077
+ const api = new PaymentAPI(billingApiUrl);
2078
+ const intentResponse = await retryOnceOnFetchFailure(() => api.createPaymentIntent(
2079
+ sessionId,
2080
+ customerEmail,
2081
+ paymentMethodId
2082
+ ));
2083
+ const intentJson = await intentResponse.json().catch(() => null);
2084
+ if (!intentResponse.ok) {
2085
+ throw Object.assign(
2086
+ new FloPayError3(
2087
+ intentJson?.message ?? "Failed to create payment intent.",
2088
+ "api_error",
2089
+ { code: intentJson?.code ?? intentJson?.gatewayErrorCode }
2090
+ ),
2091
+ { checkoutMethod: "card" }
2092
+ );
2093
+ }
2094
+ const intentClientSecret = getRedirectTokenFromProcessResponse(intentJson, {
2095
+ requirePaymentIntentClientSecret: true
2096
+ });
2097
+ if (!intentClientSecret) {
2098
+ throw Object.assign(
2099
+ new FloPayError3("No client secret in payment intent response.", "api_error"),
2100
+ { checkoutMethod: "card" }
2101
+ );
2102
+ }
2103
+ const confirmResult = await flopay.confirmCardPayment({
2104
+ clientSecret: intentClientSecret,
2105
+ paymentMethodId
2106
+ });
2107
+ if (confirmResult.error) {
2108
+ throw Object.assign(
2109
+ confirmResult.error,
2110
+ { checkoutMethod: "card" }
2111
+ );
2112
+ }
2113
+ if (!confirmResult.paymentIntentId || confirmResult.status !== "succeeded" && confirmResult.status !== "processing" && confirmResult.status !== "requires_capture") {
2114
+ throw Object.assign(
2115
+ new FloPayError3(
2116
+ `Payment intent in unexpected status: ${confirmResult.status}`,
2117
+ "api_error",
2118
+ {
2119
+ code: confirmResult.status === "requires_action" ? "authentication_required" : void 0
2120
+ }
2121
+ ),
2122
+ { checkoutMethod: "card" }
2123
+ );
2124
+ }
2125
+ const followUp = await processSavedPaymentForMode({
2126
+ billingApiUrl,
2127
+ sessionId,
2128
+ session,
2129
+ tokenizedData: {
2130
+ id: paymentMethodId,
2131
+ type: "card",
2132
+ threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
2133
+ originalPaymentMethodId: paymentMethodId
2134
+ },
2135
+ returnUrl
2136
+ });
2137
+ const finalResult = followUp.type === "success" ? followUp.result : await handleSavedPaymentRedirectResult(followUp, {
2138
+ flopay,
2139
+ paypalFlopay: null,
2140
+ attempt3DS: true,
2141
+ billingApiUrl,
2142
+ sessionId,
2143
+ session,
2144
+ returnUrl
2145
+ });
2146
+ return {
2147
+ ...finalResult,
2148
+ paymentIntentId: finalResult.paymentIntentId ?? confirmResult.paymentIntentId,
2149
+ paymentMethodId: finalResult.paymentMethodId ?? paymentMethodId
2150
+ };
2151
+ }
2152
+ async function handleSavedPaymentRedirectResult(redirectResult, {
2153
+ flopay,
2154
+ paypalFlopay,
2155
+ attempt3DS,
2156
+ billingApiUrl,
2157
+ sessionId,
2158
+ session,
2159
+ returnUrl
2160
+ }) {
2161
+ const stripe = flopay?.getRawProvider();
2162
+ if (!stripe) {
2163
+ throw new FloPayError3("Payment provider is not available.", "api_error");
2164
+ }
2165
+ if (redirectResult.type === "3ds_required") {
2166
+ if (!attempt3DS || !redirectResult.threeDSecureToken) {
2167
+ throw Object.assign(
2168
+ new FloPayError3(
2169
+ "Your card requires authentication. Please enter your payment details below.",
2170
+ "api_error",
2171
+ { code: "authentication_required" }
2172
+ ),
2173
+ { checkoutMethod: "card" }
2174
+ );
2175
+ }
2176
+ let paymentIntent = null;
2177
+ let savedPaymentMethodId = redirectResult.paymentMethodId;
2178
+ if (typeof stripe.retrievePaymentIntent === "function") {
2179
+ const { paymentIntent: existingPaymentIntent, error: retrieveError } = await stripe.retrievePaymentIntent(
2180
+ redirectResult.threeDSecureToken
2181
+ );
2182
+ if (retrieveError) {
2183
+ throw Object.assign(
2184
+ new FloPayError3(
2185
+ retrieveError.message ?? "Failed to retrieve 3DS payment status.",
2186
+ "api_error",
2187
+ { code: retrieveError.code }
2188
+ ),
2189
+ { checkoutMethod: "card" }
2190
+ );
2191
+ }
2192
+ if (!savedPaymentMethodId && existingPaymentIntent?.payment_method) {
2193
+ if (typeof existingPaymentIntent.payment_method === "string") {
2194
+ savedPaymentMethodId = existingPaymentIntent.payment_method;
2195
+ } else if ("id" in existingPaymentIntent.payment_method) {
2196
+ savedPaymentMethodId = existingPaymentIntent.payment_method.id;
2197
+ }
2198
+ }
2199
+ }
2200
+ if (savedPaymentMethodId && typeof stripe.confirmCardPayment === "function") {
2201
+ const { error: confirmError, paymentIntent: confirmedPaymentIntent } = await stripe.confirmCardPayment(
2202
+ redirectResult.threeDSecureToken,
2203
+ {
2204
+ payment_method: savedPaymentMethodId,
2205
+ return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
2206
+ }
2207
+ );
2208
+ if (confirmError) {
2209
+ throw Object.assign(
2210
+ new FloPayError3(
2211
+ confirmError.message ?? "3DS authentication failed.",
2212
+ "api_error",
2213
+ { code: confirmError.code }
2214
+ ),
2215
+ { checkoutMethod: "card" }
2216
+ );
2217
+ }
2218
+ paymentIntent = confirmedPaymentIntent ?? null;
2219
+ } else {
2220
+ const { error: nextActionError, paymentIntent: nextActionPaymentIntent } = await stripe.handleNextAction({
2221
+ clientSecret: redirectResult.threeDSecureToken
2222
+ });
2223
+ if (nextActionError) {
2224
+ throw Object.assign(
2225
+ new FloPayError3(
2226
+ nextActionError.message ?? "3DS authentication failed.",
2227
+ "api_error",
2228
+ { code: nextActionError.code }
2229
+ ),
2230
+ { checkoutMethod: "card" }
2231
+ );
2232
+ }
2233
+ paymentIntent = nextActionPaymentIntent ?? null;
2234
+ }
2235
+ if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
2236
+ const followUp = await processSavedPaymentForMode({
2237
+ billingApiUrl,
2238
+ sessionId,
2239
+ session,
2240
+ tokenizedData: {
2241
+ id: paymentIntent.id,
2242
+ type: "card",
2243
+ threeDSecureActionResultTokenId: paymentIntent.id,
2244
+ originalPaymentMethodId: savedPaymentMethodId
2245
+ },
2246
+ returnUrl
2247
+ });
2248
+ if (followUp.type === "success") {
2249
+ return {
2250
+ ...followUp.result,
2251
+ paymentIntentId: followUp.result.paymentIntentId ?? paymentIntent.id,
2252
+ paymentMethodId: followUp.result.paymentMethodId ?? savedPaymentMethodId
2253
+ };
2254
+ }
2255
+ return handleSavedPaymentRedirectResult(followUp, {
2256
+ flopay,
2257
+ paypalFlopay,
2258
+ attempt3DS,
2259
+ billingApiUrl,
2260
+ sessionId,
2261
+ session,
2262
+ returnUrl
2263
+ });
2264
+ }
2265
+ throw Object.assign(
2266
+ new FloPayError3("3DS authentication did not complete successfully.", "api_error"),
2267
+ { checkoutMethod: "card" }
2268
+ );
2269
+ }
2270
+ if (redirectResult.type === "paypal_redirect_required") {
2271
+ const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
2272
+ if (!paypalStripe) {
2273
+ throw Object.assign(
2274
+ new FloPayError3("PayPal is not available.", "api_error"),
2275
+ { checkoutMethod: "paypal" }
2276
+ );
2277
+ }
2278
+ const confirmParams = {
2279
+ return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
2280
+ };
2281
+ if (redirectResult.paymentMethodId) {
2282
+ confirmParams["payment_method"] = redirectResult.paymentMethodId;
2283
+ }
2284
+ const { error } = await paypalStripe.confirmPayment({
2285
+ clientSecret: redirectResult.threeDSecureToken,
2286
+ confirmParams,
2287
+ redirect: "if_required"
2288
+ });
2289
+ if (error) {
2290
+ throw Object.assign(
2291
+ new FloPayError3(
2292
+ error.message ?? "PayPal authorization failed.",
2293
+ "api_error",
2294
+ { code: error.code }
2295
+ ),
2296
+ { checkoutMethod: "paypal" }
2297
+ );
2298
+ }
2299
+ return { status: "succeeded" };
2300
+ }
2301
+ throw new FloPayError3("Unsupported payment redirect state.", "api_error");
2302
+ }
2303
+ function normalizeSavedPaymentError(err) {
2304
+ if (err instanceof FloPayError3) {
2305
+ return err;
2306
+ }
2307
+ return new FloPayError3(
2308
+ err instanceof Error ? err.message : "Payment failed. Please try again.",
2309
+ "api_error"
2310
+ );
2311
+ }
2312
+ function resolveSavedPaymentPublishableKeys(unified, fallbackPublishableKey) {
2313
+ let publishableKey;
2314
+ let paypalPublishableKey;
2315
+ if (unified.provider === "stripe") {
2316
+ publishableKey = unified.data.stripe?.publishableKey;
2317
+ paypalPublishableKey = unified.data.stripe?.paypalPublishableKey ?? void 0;
2318
+ }
2319
+ if (!publishableKey) {
2320
+ publishableKey = fallbackPublishableKey;
2321
+ }
2322
+ if (!publishableKey) {
2323
+ throw new FloPayError3(
2324
+ "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
2325
+ "validation_error"
2326
+ );
2327
+ }
2328
+ return {
2329
+ publishableKey,
2330
+ paypalPublishableKey
2331
+ };
2332
+ }
2333
+ async function loadSavedPaymentProviders({
2334
+ publishableKey,
2335
+ paypalPublishableKey,
2336
+ billingApiUrl,
2337
+ locale
2338
+ }) {
2339
+ const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
2340
+ const [instance, paypalInstanceOrError] = await Promise.all([
2341
+ loadFloPay(publishableKey, {
2342
+ billingApiUrl,
2343
+ locale
2344
+ }),
2345
+ needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
2346
+ billingApiUrl,
2347
+ locale
2348
+ }).catch((err) => {
2349
+ console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
2350
+ return null;
2351
+ }) : Promise.resolve(null)
2352
+ ]);
2353
+ return {
2354
+ flopay: instance,
2355
+ paypalFlopay: paypalPublishableKey ? needsSeparatePaypal ? paypalInstanceOrError : instance : null
2356
+ };
2357
+ }
2358
+
1809
2359
  // src/flopay-checkout.tsx
1810
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
2360
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1811
2361
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
2362
+ function sleep(ms) {
2363
+ return new Promise((resolve) => setTimeout(resolve, ms));
2364
+ }
1812
2365
  function hasInlineSessionPatchData(patch) {
1813
2366
  if (!patch) return false;
1814
2367
  return Boolean(
@@ -1839,12 +2392,14 @@ function FloPayCheckout({
1839
2392
  cardButtonContent,
1840
2393
  cardBackButtonContent,
1841
2394
  cardTitleContent,
2395
+ showSecurityFooter = true,
1842
2396
  onButtonClick,
1843
2397
  onBeforeButtonClick,
1844
2398
  enableAVS,
1845
2399
  avsLayout,
1846
2400
  submitLabel,
1847
2401
  className,
2402
+ initialErrorMessage = null,
1848
2403
  children,
1849
2404
  checkoutMode: checkoutModeProp,
1850
2405
  confirmLabel,
@@ -1861,11 +2416,15 @@ function FloPayCheckout({
1861
2416
  const paypalFlopayRef = useRef3(null);
1862
2417
  const [session, setSession] = useState3(null);
1863
2418
  const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
2419
+ const activeSessionId = sessionIdProp ?? resolvedSessionId;
2420
+ const initSessionDependency = createSessionParams ? "" : activeSessionId;
1864
2421
  const [isLoading, setIsLoading] = useState3(true);
1865
2422
  const [loadError, setLoadError] = useState3(null);
1866
2423
  const [currentMode, setCurrentMode] = useState3("full");
1867
2424
  const [confirmProcessing, setConfirmProcessing] = useState3(false);
1868
- const [modeError, setModeError] = useState3(null);
2425
+ const [modeError, setModeError] = useState3(initialErrorMessage);
2426
+ const [modeOverlayStatus, setModeOverlayStatus] = useState3(null);
2427
+ const [modeOverlayError, setModeOverlayError] = useState3(null);
1869
2428
  const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
1870
2429
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState3("");
1871
2430
  const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
@@ -1894,125 +2453,84 @@ function FloPayCheckout({
1894
2453
  () => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
1895
2454
  [createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
1896
2455
  );
1897
- const effectiveCreateSession = useMemo3(
2456
+ const effectiveCreateSessionBase = useMemo3(
1898
2457
  () => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
1899
2458
  [createSessionParams, activeCreateSessionPatch]
1900
2459
  );
2460
+ const effectiveCreateSessionMode = checkoutModeProp ?? effectiveCreateSessionBase?.checkoutMode ?? "full";
2461
+ const effectiveCreateSession = useMemo3(
2462
+ () => effectiveCreateSessionBase ? {
2463
+ ...effectiveCreateSessionBase,
2464
+ checkoutMode: effectiveCreateSessionMode
2465
+ } : void 0,
2466
+ [effectiveCreateSessionBase, effectiveCreateSessionMode]
2467
+ );
1901
2468
  useEffect4(() => {
1902
2469
  setCreateSessionPatch(void 0);
1903
2470
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
1904
2471
  }, [baseCreateSessionHash]);
2472
+ useEffect4(() => {
2473
+ setModeError(initialErrorMessage);
2474
+ }, [initialErrorMessage]);
1905
2475
  const emitDecline = useCallback2(
1906
2476
  (method, input, overrides) => {
1907
2477
  onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
1908
2478
  },
1909
2479
  []
1910
2480
  );
1911
- const processPaymentForMode = useCallback2(
1912
- async (sess) => {
1913
- const baseUrl = resolvedBillingUrl.replace(/\/+$/, "");
1914
- const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
1915
- method: "POST",
1916
- headers: {
1917
- "Content-Type": "application/json",
1918
- "x-user-id": sess.customer?.id ?? ""
1919
- },
1920
- body: JSON.stringify({
1921
- sessionId: resolvedSessionId,
1922
- tokenizedData: { id: void 0 },
1923
- accountData: {
1924
- userId: sess.customer?.id ?? "",
1925
- email: sess.customer?.email ?? "",
1926
- firstName: sess.customer?.firstName ?? "",
1927
- lastName: sess.customer?.lastName ?? ""
1928
- }
1929
- })
1930
- });
1931
- if (response.ok) {
1932
- onCompleteRef.current?.({ status: "succeeded" });
1933
- return null;
1934
- }
1935
- const json = await response.json().catch(() => null);
1936
- if ((json?.type === "paypal_redirect_required" || json?.type === "3ds_required") && json?.threeDSecureToken) {
1937
- return {
1938
- type: json.type,
1939
- threeDSecureToken: json.threeDSecureToken,
1940
- paymentMethodId: json.paymentMethodId
1941
- };
1942
- }
1943
- if (json?.gatewayErrorCode === "authentication_required") {
1944
- return {
1945
- type: "3ds_required",
1946
- threeDSecureToken: ""
1947
- // No client secret available
1948
- };
1949
- }
1950
- throw new FloPayError3(
1951
- json?.message ?? "Payment failed. Please try again.",
1952
- "api_error",
1953
- {
1954
- code: json?.code ?? json?.gatewayErrorCode,
1955
- declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1956
- }
1957
- );
1958
- },
1959
- [resolvedBillingUrl, resolvedSessionId]
1960
- );
1961
- const handleRedirectResult = useCallback2(
1962
- async (redirectResult, sess, options) => {
1963
- const stripe = flopayRef.current?.getRawProvider();
1964
- if (!stripe) return false;
1965
- if (redirectResult.type === "3ds_required") {
1966
- if (!options?.attempt3DS || !redirectResult.threeDSecureToken) {
1967
- setModeError("Your card requires authentication. Please enter your payment details below.");
1968
- return false;
1969
- }
1970
- const { error: nextActionError, paymentIntent } = await stripe.handleNextAction({
1971
- clientSecret: redirectResult.threeDSecureToken
2481
+ const runSavedPaymentFlow = useCallback2(
2482
+ async (sess, options) => {
2483
+ setModeError(null);
2484
+ setModeOverlayError(null);
2485
+ setModeOverlayStatus("processing");
2486
+ const activeSessionId2 = options?.sessionId ?? sess.id;
2487
+ try {
2488
+ const result = await processSavedPaymentForMode({
2489
+ billingApiUrl: resolvedBillingUrl,
2490
+ sessionId: activeSessionId2,
2491
+ session: sess
1972
2492
  });
1973
- if (nextActionError) {
1974
- setModeError(nextActionError.message ?? "3DS authentication failed.");
1975
- emitDecline("card", nextActionError.message ?? "3DS authentication failed.", {
1976
- code: nextActionError.code
1977
- });
1978
- return false;
1979
- }
1980
- if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded")) {
1981
- onCompleteRef.current?.({ status: "succeeded", paymentIntentId: paymentIntent.id });
1982
- return true;
2493
+ const paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
2494
+ flopay: flopayRef.current,
2495
+ paypalFlopay: paypalFlopayRef.current,
2496
+ attempt3DS: options?.attempt3DS,
2497
+ billingApiUrl: resolvedBillingUrl,
2498
+ sessionId: activeSessionId2,
2499
+ session: sess
2500
+ });
2501
+ setModeOverlayStatus("success");
2502
+ await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2503
+ onCompleteRef.current?.(paymentResult);
2504
+ return true;
2505
+ } catch (err) {
2506
+ const floPayErr = normalizeSavedPaymentError(err);
2507
+ const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
2508
+ setModeError(floPayErr.message);
2509
+ setModeOverlayError(floPayErr.message);
2510
+ if (options?.fallbackToFull) {
2511
+ setCurrentMode("full");
1983
2512
  }
2513
+ onErrorRef.current?.(floPayErr);
2514
+ emitDecline(method, floPayErr, {
2515
+ code: floPayErr.code,
2516
+ declineCode: floPayErr.declineCode
2517
+ });
2518
+ setModeOverlayStatus("error");
2519
+ await Promise.allSettled([
2520
+ sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
2521
+ options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
2522
+ ]);
1984
2523
  return false;
2524
+ } finally {
2525
+ setModeOverlayStatus(null);
2526
+ setModeOverlayError(null);
1985
2527
  }
1986
- if (redirectResult.type === "paypal_redirect_required") {
1987
- const paypalStripe = paypalFlopayRef.current?.getRawProvider();
1988
- if (!paypalStripe) {
1989
- setModeError("PayPal is not available.");
1990
- return false;
1991
- }
1992
- const confirmParams = {
1993
- return_url: window.location.href
1994
- };
1995
- if (redirectResult.paymentMethodId) {
1996
- confirmParams["payment_method"] = redirectResult.paymentMethodId;
1997
- }
1998
- const { error } = await paypalStripe.confirmPayment({
1999
- clientSecret: redirectResult.threeDSecureToken,
2000
- confirmParams,
2001
- redirect: "if_required"
2002
- });
2003
- if (error) {
2004
- setModeError(error.message ?? "PayPal authorization failed.");
2005
- emitDecline("paypal", error.message ?? "PayPal authorization failed.", {
2006
- code: error.code
2007
- });
2008
- return false;
2009
- }
2010
- onCompleteRef.current?.({ status: "succeeded" });
2011
- return true;
2012
- }
2013
- return false;
2014
2528
  },
2015
- [resolvedBillingUrl, resolvedSessionId, emitDecline]
2529
+ [
2530
+ emitDecline,
2531
+ normalizeSavedPaymentError,
2532
+ resolvedBillingUrl
2533
+ ]
2016
2534
  );
2017
2535
  const inflightRef = useRef3(/* @__PURE__ */ new Map());
2018
2536
  const initializedHashRef = useRef3(null);
@@ -2044,8 +2562,14 @@ function FloPayCheckout({
2044
2562
  useEffect4(() => {
2045
2563
  setResolvedSessionId(sessionIdProp ?? "");
2046
2564
  }, [sessionIdProp]);
2565
+ useEffect4(() => {
2566
+ autoCheckoutAttempted.current = false;
2567
+ setModeError(initialErrorMessage);
2568
+ setModeOverlayError(null);
2569
+ setModeOverlayStatus(null);
2570
+ }, [createSessionHash, initialErrorMessage, sessionIdProp]);
2047
2571
  async function resolveInlineSession(params, cacheKey) {
2048
- const api = new PaymentAPI(resolvedBillingUrl);
2572
+ const api = new PaymentAPI2(resolvedBillingUrl);
2049
2573
  let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
2050
2574
  let realResult = null;
2051
2575
  if (sid) {
@@ -2074,7 +2598,7 @@ function FloPayCheckout({
2074
2598
  async (patch) => {
2075
2599
  const baseParams = createSessionParamsRef.current;
2076
2600
  if (!baseParams) {
2077
- throw new FloPayError3("createSession is required to bootstrap checkout.", "validation_error");
2601
+ throw new FloPayError4("createSession is required to bootstrap checkout.", "validation_error");
2078
2602
  }
2079
2603
  const mergedParams = mergeInlineSessionPatch(baseParams, patch);
2080
2604
  const cacheKey = hashCreateParams(mergedParams);
@@ -2103,43 +2627,23 @@ function FloPayCheckout({
2103
2627
  if (sid) {
2104
2628
  setResolvedSessionId(sid);
2105
2629
  }
2106
- let publishableKey;
2107
- let paypalPublishableKey;
2108
- if (realResult.provider === "stripe") {
2109
- publishableKey = realResult.data.stripe?.publishableKey;
2110
- paypalPublishableKey = realResult.data.stripe?.paypalPublishableKey;
2111
- }
2112
- if (!publishableKey) publishableKey = fallbackPublishableKey;
2113
- if (!publishableKey) {
2114
- throw new FloPayError3(
2115
- "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
2116
- "validation_error"
2117
- );
2118
- }
2119
- const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
2120
- const [instance, paypalInstanceOrError] = await Promise.all([
2121
- loadFloPay(publishableKey, {
2122
- billingApiUrl: resolvedBillingUrl,
2123
- locale
2124
- }),
2125
- needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
2126
- billingApiUrl: resolvedBillingUrl,
2127
- locale
2128
- }).catch((err) => {
2129
- console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
2130
- return null;
2131
- }) : Promise.resolve(null)
2132
- ]);
2630
+ const {
2631
+ publishableKey,
2632
+ paypalPublishableKey
2633
+ } = resolveSavedPaymentPublishableKeys(realResult, fallbackPublishableKey);
2634
+ const {
2635
+ flopay: instance,
2636
+ paypalFlopay: paypalInstance
2637
+ } = await loadSavedPaymentProviders({
2638
+ publishableKey,
2639
+ paypalPublishableKey,
2640
+ billingApiUrl: resolvedBillingUrl,
2641
+ locale
2642
+ });
2133
2643
  flopayRef.current = instance;
2134
2644
  setFloPay(instance);
2135
- if (paypalPublishableKey) {
2136
- const paypalInstance = needsSeparatePaypal ? paypalInstanceOrError : instance;
2137
- paypalFlopayRef.current = paypalInstance;
2138
- setPaypalFloPay(paypalInstance);
2139
- } else {
2140
- paypalFlopayRef.current = null;
2141
- setPaypalFloPay(null);
2142
- }
2645
+ paypalFlopayRef.current = paypalInstance;
2646
+ setPaypalFloPay(paypalInstance);
2143
2647
  setIsLoading(false);
2144
2648
  } catch (err) {
2145
2649
  if (initializedHashRef.current === cacheKey) {
@@ -2178,17 +2682,57 @@ function FloPayCheckout({
2178
2682
  let cancelled = false;
2179
2683
  setLoadError(null);
2180
2684
  if (createSessionHash) {
2181
- if (initializedHashRef.current === createSessionHash) return;
2182
2685
  const params = createSessionParamsRef.current;
2183
2686
  setSession(buildSyntheticSession(params, checkoutModeProp));
2184
- setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
2687
+ setCurrentMode(effectiveCreateSessionMode);
2185
2688
  setIsLoading(false);
2689
+ if (initializedHashRef.current === createSessionHash) {
2690
+ return () => {
2691
+ cancelled = true;
2692
+ };
2693
+ }
2186
2694
  (async () => {
2695
+ const shouldAutoProcessInlineSession = effectiveCreateSessionMode === "auto" && !autoCheckoutAttempted.current;
2696
+ if (shouldAutoProcessInlineSession) {
2697
+ setModeError(null);
2698
+ setModeOverlayError(null);
2699
+ setModeOverlayStatus("processing");
2700
+ }
2187
2701
  try {
2188
- await bootstrapInlineSession();
2702
+ const resolved = await bootstrapInlineSession();
2703
+ const resolvedSession = resolved.result.data.session ?? null;
2704
+ if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
2705
+ autoCheckoutAttempted.current = true;
2706
+ await runSavedPaymentFlow(resolvedSession, {
2707
+ attempt3DS: true,
2708
+ fallbackToFull: true,
2709
+ sessionId: resolved.sid || resolvedSession.id
2710
+ });
2711
+ return;
2712
+ }
2713
+ if (!cancelled && shouldAutoProcessInlineSession) {
2714
+ setModeOverlayStatus(null);
2715
+ setModeOverlayError(null);
2716
+ }
2189
2717
  } catch (err) {
2190
2718
  if (cancelled) return;
2191
- const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to create session", "api_error");
2719
+ const errorCode = err instanceof FloPayError4 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
2720
+ if (shouldAutoProcessInlineSession && errorCode === "session_auto_completed") {
2721
+ autoCheckoutAttempted.current = true;
2722
+ setModeOverlayStatus("success");
2723
+ await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2724
+ if (!cancelled) {
2725
+ onCompleteRef.current?.({ status: "succeeded" });
2726
+ setModeOverlayStatus(null);
2727
+ setModeOverlayError(null);
2728
+ }
2729
+ return;
2730
+ }
2731
+ if (shouldAutoProcessInlineSession) {
2732
+ setModeOverlayStatus(null);
2733
+ setModeOverlayError(null);
2734
+ }
2735
+ const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to create session", "api_error");
2192
2736
  setLoadError(floPayErr);
2193
2737
  }
2194
2738
  })();
@@ -2199,14 +2743,14 @@ function FloPayCheckout({
2199
2743
  setIsLoading(true);
2200
2744
  async function init() {
2201
2745
  try {
2202
- const api = new PaymentAPI(resolvedBillingUrl);
2203
- const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
2746
+ const api = new PaymentAPI2(resolvedBillingUrl);
2747
+ const result = await api.getUnifiedCheckoutSession(activeSessionId);
2204
2748
  if (cancelled) return;
2205
2749
  setUnified(result);
2206
2750
  const sess = result.data.session ?? null;
2207
2751
  setSession(sess);
2208
2752
  if (!sess) {
2209
- throw new FloPayError3("No session data returned", "api_error");
2753
+ throw new FloPayError4("No session data returned", "api_error");
2210
2754
  }
2211
2755
  if (sess.status === "complete") {
2212
2756
  setIsLoading(false);
@@ -2220,22 +2764,20 @@ function FloPayCheckout({
2220
2764
  autoCheckoutAttempted.current = true;
2221
2765
  const stripeInitPromise = initStripe(result);
2222
2766
  try {
2223
- const redirectResult = await processPaymentForMode(sess);
2224
- if (!redirectResult) {
2225
- if (!cancelled) setIsLoading(false);
2226
- return;
2227
- }
2767
+ await runSavedPaymentFlow(sess, {
2768
+ attempt3DS: true,
2769
+ fallbackToFull: true,
2770
+ ensureProvidersReady: () => stripeInitPromise,
2771
+ sessionId: activeSessionId || sess.id
2772
+ });
2228
2773
  if (cancelled) return;
2229
2774
  await stripeInitPromise;
2230
- const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
2231
2775
  if (!cancelled) {
2232
- if (!handled) setCurrentMode("full");
2233
2776
  setIsLoading(false);
2234
2777
  }
2235
2778
  return;
2236
2779
  } catch {
2237
2780
  if (cancelled) return;
2238
- setCurrentMode("full");
2239
2781
  await stripeInitPromise;
2240
2782
  if (!cancelled) setIsLoading(false);
2241
2783
  return;
@@ -2245,81 +2787,55 @@ function FloPayCheckout({
2245
2787
  if (!cancelled) setIsLoading(false);
2246
2788
  } catch (err) {
2247
2789
  if (cancelled) return;
2248
- const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
2790
+ const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
2249
2791
  setLoadError(floPayErr);
2250
2792
  setIsLoading(false);
2251
2793
  }
2252
2794
  }
2253
2795
  async function initStripe(result) {
2254
- let publishableKey;
2255
- let paypalPublishableKey;
2256
- if (result.provider === "stripe") {
2257
- publishableKey = result.data.stripe?.publishableKey;
2258
- paypalPublishableKey = result.data.stripe?.paypalPublishableKey;
2259
- }
2260
- if (!publishableKey) publishableKey = fallbackPublishableKey;
2261
- if (!publishableKey) {
2262
- throw new FloPayError3(
2263
- "No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
2264
- "validation_error"
2265
- );
2266
- }
2267
- const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
2268
- const [instance, paypalInstanceOrError] = await Promise.all([
2269
- loadFloPay(publishableKey, {
2270
- billingApiUrl: resolvedBillingUrl,
2271
- locale
2272
- }),
2273
- needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
2274
- billingApiUrl: resolvedBillingUrl,
2275
- locale
2276
- }).catch((err) => {
2277
- console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
2278
- return null;
2279
- }) : Promise.resolve(null)
2280
- ]);
2796
+ const {
2797
+ publishableKey,
2798
+ paypalPublishableKey
2799
+ } = resolveSavedPaymentPublishableKeys(result, fallbackPublishableKey);
2800
+ const {
2801
+ flopay: instance,
2802
+ paypalFlopay: paypalInstance
2803
+ } = await loadSavedPaymentProviders({
2804
+ publishableKey,
2805
+ paypalPublishableKey,
2806
+ billingApiUrl: resolvedBillingUrl,
2807
+ locale
2808
+ });
2281
2809
  flopayRef.current = instance;
2282
2810
  setFloPay(instance);
2283
- if (paypalPublishableKey) {
2284
- const paypalInstance = needsSeparatePaypal ? paypalInstanceOrError : instance;
2285
- paypalFlopayRef.current = paypalInstance;
2286
- setPaypalFloPay(paypalInstance);
2287
- } else {
2288
- paypalFlopayRef.current = null;
2289
- setPaypalFloPay(null);
2290
- }
2811
+ paypalFlopayRef.current = paypalInstance;
2812
+ setPaypalFloPay(paypalInstance);
2291
2813
  }
2292
2814
  init();
2293
2815
  return () => {
2294
2816
  cancelled = true;
2295
2817
  };
2296
- }, [resolvedSessionId, createSessionHash, checkoutModeProp, bootstrapInlineSession]);
2818
+ }, [
2819
+ bootstrapInlineSession,
2820
+ checkoutModeProp,
2821
+ createSessionHash,
2822
+ effectiveCreateSessionMode,
2823
+ initSessionDependency,
2824
+ runSavedPaymentFlow
2825
+ ]);
2297
2826
  const handleConfirmCheckout = useCallback2(async () => {
2298
2827
  if (confirmProcessing || !session) return;
2299
2828
  setConfirmProcessing(true);
2300
2829
  setModeError(null);
2301
2830
  try {
2302
- const redirectResult = await processPaymentForMode(session);
2303
- if (!redirectResult) {
2304
- return;
2305
- }
2306
- const handled = await handleRedirectResult(redirectResult, session);
2307
- if (!handled) {
2308
- setCurrentMode("full");
2309
- }
2310
- } catch (err) {
2311
- const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(
2312
- err instanceof Error ? err.message : "Payment failed",
2313
- "api_error"
2314
- );
2315
- setModeError(floPayErr.message);
2316
- onError?.(floPayErr);
2317
- emitDecline("card", floPayErr);
2318
- setCurrentMode("full");
2831
+ await runSavedPaymentFlow(session, {
2832
+ fallbackToFull: true,
2833
+ sessionId: activeSessionId || session.id
2834
+ });
2319
2835
  } finally {
2320
2836
  setConfirmProcessing(false);
2321
2837
  }
2322
- }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError, emitDecline]);
2838
+ }, [activeSessionId, confirmProcessing, runSavedPaymentFlow, session]);
2323
2839
  const providerOptions = useMemo3(() => {
2324
2840
  if (!unified || !session) return void 0;
2325
2841
  const opts = {
@@ -2337,7 +2853,7 @@ function FloPayCheckout({
2337
2853
  return opts;
2338
2854
  }, [unified, session, appearance, resolvedBillingUrl]);
2339
2855
  const shouldHandleInlineSessionPatch = Boolean(
2340
- createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession?.checkoutMode ?? checkoutModeProp ?? "full") === "full"
2856
+ createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && effectiveCreateSessionMode === "full"
2341
2857
  );
2342
2858
  const checkoutValue = useMemo3(
2343
2859
  () => ({
@@ -2361,37 +2877,55 @@ function FloPayCheckout({
2361
2877
  ]
2362
2878
  );
2363
2879
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
2880
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx6(
2881
+ ProcessingOverlay,
2882
+ {
2883
+ status: modeOverlayStatus,
2884
+ errorMessage: modeOverlayError
2885
+ }
2886
+ ) : null;
2364
2887
  if (isLoading) {
2365
- if (loadingNode) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingNode });
2888
+ if (loadingNode) {
2889
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2890
+ loadingNode,
2891
+ modeOverlay
2892
+ ] });
2893
+ }
2366
2894
  if (layout === "buttons") {
2367
- const skeletonBar = (h) => /* @__PURE__ */ jsx5("div", { style: {
2895
+ const skeletonBar = (h) => /* @__PURE__ */ jsx6("div", { style: {
2368
2896
  height: h,
2369
2897
  borderRadius: 8,
2370
2898
  background: "#e5e7eb",
2371
2899
  animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
2372
2900
  } });
2373
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2374
- showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2375
- (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2376
- skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2377
- /* @__PURE__ */ jsx5("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2901
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2902
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2903
+ showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2904
+ (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2905
+ skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2906
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2907
+ ] }),
2908
+ modeOverlay
2378
2909
  ] });
2379
2910
  }
2380
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
2381
- /* @__PURE__ */ jsx5("div", { style: {
2382
- width: 24,
2383
- height: 24,
2384
- border: "2px solid #e5e7eb",
2385
- borderTopColor: "#6b7280",
2386
- borderRadius: "50%",
2387
- animation: "spin 0.6s linear infinite"
2388
- } }),
2389
- /* @__PURE__ */ jsx5("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
2911
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2912
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
2913
+ /* @__PURE__ */ jsx6("div", { style: {
2914
+ width: 24,
2915
+ height: 24,
2916
+ border: "2px solid #e5e7eb",
2917
+ borderTopColor: "#6b7280",
2918
+ borderRadius: "50%",
2919
+ animation: "spin 0.6s linear infinite"
2920
+ } }),
2921
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
2922
+ ] }),
2923
+ modeOverlay
2390
2924
  ] });
2391
2925
  }
2392
2926
  if (loadError) {
2393
- if (errorNode) return /* @__PURE__ */ jsx5(Fragment3, { children: errorNode(loadError) });
2394
- return /* @__PURE__ */ jsx5(
2927
+ if (errorNode) return /* @__PURE__ */ jsx6(Fragment3, { children: errorNode(loadError) });
2928
+ return /* @__PURE__ */ jsx6(
2395
2929
  "div",
2396
2930
  {
2397
2931
  style: {
@@ -2405,90 +2939,100 @@ function FloPayCheckout({
2405
2939
  );
2406
2940
  }
2407
2941
  if (shouldShowInterimButtons) {
2408
- return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx5(
2409
- InterimButtonsView,
2410
- {
2411
- onButtonClick,
2412
- showPayPal,
2413
- showApplePay,
2414
- showGooglePay,
2415
- buttonsTheme,
2416
- buttonsStyles,
2417
- cardButtonContent,
2418
- cardBackButtonContent,
2419
- cardTitleContent
2420
- }
2421
- ) });
2942
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2943
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
2944
+ InterimButtonsView,
2945
+ {
2946
+ onButtonClick,
2947
+ showPayPal,
2948
+ showApplePay,
2949
+ showGooglePay,
2950
+ buttonsTheme,
2951
+ buttonsStyles,
2952
+ cardButtonContent,
2953
+ cardBackButtonContent,
2954
+ cardTitleContent
2955
+ }
2956
+ ) }),
2957
+ modeOverlay
2958
+ ] });
2959
+ }
2960
+ if (!flopay || !providerOptions) {
2961
+ return /* @__PURE__ */ jsx6(Fragment3, { children: modeOverlay });
2422
2962
  }
2423
- if (!flopay || !providerOptions) return /* @__PURE__ */ jsx5(Fragment3, {});
2424
2963
  if (currentMode === "confirm") {
2425
- return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx5(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs3("div", { className, children: [
2426
- modeError && /* @__PURE__ */ jsx5(
2964
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2965
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs4("div", { className, children: [
2966
+ modeError && /* @__PURE__ */ jsx6(
2967
+ "div",
2968
+ {
2969
+ style: {
2970
+ color: "#dc2626",
2971
+ fontSize: "0.875rem",
2972
+ marginBottom: "0.75rem",
2973
+ textAlign: "center"
2974
+ },
2975
+ children: modeError
2976
+ }
2977
+ ),
2978
+ renderConfirmButton ? renderConfirmButton({
2979
+ onConfirm: handleConfirmCheckout,
2980
+ isProcessing: confirmProcessing
2981
+ }) : /* @__PURE__ */ jsx6(
2982
+ "button",
2983
+ {
2984
+ type: "button",
2985
+ onClick: handleConfirmCheckout,
2986
+ disabled: confirmProcessing,
2987
+ style: {
2988
+ width: "100%",
2989
+ padding: "0.875rem",
2990
+ backgroundColor: "#4A49FF",
2991
+ color: "white",
2992
+ border: "none",
2993
+ borderRadius: "8px",
2994
+ fontSize: "1rem",
2995
+ fontWeight: 600,
2996
+ cursor: confirmProcessing ? "not-allowed" : "pointer",
2997
+ opacity: confirmProcessing ? 0.6 : 1
2998
+ },
2999
+ children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
3000
+ }
3001
+ )
3002
+ ] }) }) }),
3003
+ modeOverlay
3004
+ ] });
3005
+ }
3006
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3007
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
3008
+ modeError && /* @__PURE__ */ jsx6(
2427
3009
  "div",
2428
3010
  {
2429
3011
  style: {
2430
- color: "#dc2626",
2431
- fontSize: "0.875rem",
3012
+ padding: "0.75rem 1rem",
2432
3013
  marginBottom: "0.75rem",
2433
- textAlign: "center"
3014
+ backgroundColor: "#FEF3C7",
3015
+ border: "1px solid #F59E0B",
3016
+ borderRadius: "8px",
3017
+ color: "#92400E",
3018
+ fontSize: "0.875rem"
2434
3019
  },
2435
3020
  children: modeError
2436
3021
  }
2437
3022
  ),
2438
- renderConfirmButton ? renderConfirmButton({
2439
- onConfirm: handleConfirmCheckout,
2440
- isProcessing: confirmProcessing
2441
- }) : /* @__PURE__ */ jsx5(
2442
- "button",
3023
+ /* @__PURE__ */ jsx6(
3024
+ SessionInjector,
2443
3025
  {
2444
- type: "button",
2445
- onClick: handleConfirmCheckout,
2446
- disabled: confirmProcessing,
2447
- style: {
2448
- width: "100%",
2449
- padding: "0.875rem",
2450
- backgroundColor: "#4A49FF",
2451
- color: "white",
2452
- border: "none",
2453
- borderRadius: "8px",
2454
- fontSize: "1rem",
2455
- fontWeight: 600,
2456
- cursor: confirmProcessing ? "not-allowed" : "pointer",
2457
- opacity: confirmProcessing ? 0.6 : 1
2458
- },
2459
- children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
3026
+ sessionId: activeSessionId,
3027
+ billingApiUrl: resolvedBillingUrl,
3028
+ session,
3029
+ children
2460
3030
  }
2461
3031
  )
2462
- ] }) }) });
2463
- }
2464
- return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs3(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: [
2465
- modeError && /* @__PURE__ */ jsx5(
2466
- "div",
2467
- {
2468
- style: {
2469
- padding: "0.75rem 1rem",
2470
- marginBottom: "0.75rem",
2471
- backgroundColor: "#FEF3C7",
2472
- border: "1px solid #F59E0B",
2473
- borderRadius: "8px",
2474
- color: "#92400E",
2475
- fontSize: "0.875rem"
2476
- },
2477
- children: modeError
2478
- }
2479
- ),
2480
- children ? /* @__PURE__ */ jsx5(
2481
- SessionInjector,
2482
- {
2483
- sessionId: resolvedSessionId,
2484
- billingApiUrl: resolvedBillingUrl,
2485
- session,
2486
- children
2487
- }
2488
- ) : /* @__PURE__ */ jsx5(
3032
+ ] }) : /* @__PURE__ */ jsx6(
2489
3033
  SplitCardForm,
2490
3034
  {
2491
- sessionId: resolvedSessionId,
3035
+ sessionId: activeSessionId,
2492
3036
  email: session?.customer?.email,
2493
3037
  userId: session?.customer?.id,
2494
3038
  firstName: session?.customer?.firstName,
@@ -2498,6 +3042,8 @@ function FloPayCheckout({
2498
3042
  onComplete,
2499
3043
  onError,
2500
3044
  onDecline,
3045
+ error: modeError,
3046
+ onErrorChange: setModeError,
2501
3047
  onFullNameChange,
2502
3048
  showPayPal,
2503
3049
  showApplePay,
@@ -2508,6 +3054,7 @@ function FloPayCheckout({
2508
3054
  cardButtonContent,
2509
3055
  cardBackButtonContent,
2510
3056
  cardTitleContent,
3057
+ showSecurityFooter,
2511
3058
  onButtonClick,
2512
3059
  onBeforeButtonClick,
2513
3060
  enableAVS,
@@ -2518,8 +3065,9 @@ function FloPayCheckout({
2518
3065
  submitLabel,
2519
3066
  className
2520
3067
  }
2521
- )
2522
- ] }) });
3068
+ ) }) }),
3069
+ modeOverlay
3070
+ ] });
2523
3071
  }
2524
3072
  function SessionInjector({
2525
3073
  sessionId,
@@ -2527,8 +3075,8 @@ function SessionInjector({
2527
3075
  session,
2528
3076
  children
2529
3077
  }) {
2530
- return /* @__PURE__ */ jsx5(Fragment3, { children: React5.Children.map(children, (child) => {
2531
- if (!React5.isValidElement(child)) return child;
3078
+ return /* @__PURE__ */ jsx6(Fragment3, { children: React6.Children.map(children, (child) => {
3079
+ if (!React6.isValidElement(child)) return child;
2532
3080
  const existing = child.props;
2533
3081
  const injected = {};
2534
3082
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -2542,7 +3090,7 @@ function SessionInjector({
2542
3090
  injected.lastName = session.customer.lastName;
2543
3091
  }
2544
3092
  if (Object.keys(injected).length === 0) return child;
2545
- return React5.cloneElement(child, injected);
3093
+ return React6.cloneElement(child, injected);
2546
3094
  }) });
2547
3095
  }
2548
3096
  function InterimButtonsView({
@@ -2581,7 +3129,7 @@ function InterimButtonsView({
2581
3129
  title: { ...base.title, ...stylesOverride.title }
2582
3130
  };
2583
3131
  }, [buttonsTheme, stylesOverride]);
2584
- const skeleton = (h) => /* @__PURE__ */ jsx5("div", { style: {
3132
+ const skeleton = (h) => /* @__PURE__ */ jsx6("div", { style: {
2585
3133
  height: h,
2586
3134
  borderRadius: 8,
2587
3135
  background: "#e5e7eb",
@@ -2593,15 +3141,15 @@ function InterimButtonsView({
2593
3141
  const inputBg = bStyles.cardInputBackground ?? "white";
2594
3142
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
2595
3143
  const hideTitle = isEmptySlotContent(cardTitleContent);
2596
- return /* @__PURE__ */ jsxs3("div", { style: {
3144
+ return /* @__PURE__ */ jsxs4("div", { style: {
2597
3145
  backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
2598
3146
  borderRadius: "8px",
2599
3147
  animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
2600
3148
  overflow: "hidden",
2601
3149
  ...bStyles.cardFormContainer
2602
3150
  }, children: [
2603
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
2604
- /* @__PURE__ */ jsxs3(
3151
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
3152
+ /* @__PURE__ */ jsxs4(
2605
3153
  "button",
2606
3154
  {
2607
3155
  type: "button",
@@ -2628,7 +3176,7 @@ function InterimButtonsView({
2628
3176
  ...bStyles.backButton
2629
3177
  },
2630
3178
  children: [
2631
- /* @__PURE__ */ jsx5("span", { style: {
3179
+ /* @__PURE__ */ jsx6("span", { style: {
2632
3180
  display: "inline-flex",
2633
3181
  alignItems: "center",
2634
3182
  justifyContent: "center",
@@ -2637,12 +3185,12 @@ function InterimButtonsView({
2637
3185
  borderRadius: "50%",
2638
3186
  backgroundColor: "#f3f4f6",
2639
3187
  ...bStyles.backButtonIcon
2640
- }, children: /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M15 18l-6-6 6-6" }) }) }),
2641
- /* @__PURE__ */ jsx5(BackButtonContentSlot, { content: cardBackButtonContent })
3188
+ }, children: /* @__PURE__ */ jsx6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "M15 18l-6-6 6-6" }) }) }),
3189
+ /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
2642
3190
  ]
2643
3191
  }
2644
3192
  ),
2645
- hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
3193
+ hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
2646
3194
  flex: 1,
2647
3195
  textAlign: "center",
2648
3196
  fontWeight: 600,
@@ -2650,15 +3198,15 @@ function InterimButtonsView({
2650
3198
  color: "#262833",
2651
3199
  paddingRight: 80,
2652
3200
  ...bStyles.title
2653
- }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
3201
+ }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
2654
3202
  ] }),
2655
- /* @__PURE__ */ jsx5("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx5("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2656
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
2657
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx5("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2658
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx5("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
3203
+ /* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3204
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3205
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3206
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
2659
3207
  ] }),
2660
- /* @__PURE__ */ jsx5("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx5("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2661
- /* @__PURE__ */ jsx5("div", { style: {
3208
+ /* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3209
+ /* @__PURE__ */ jsx6("div", { style: {
2662
3210
  height: 50,
2663
3211
  borderRadius: 8,
2664
3212
  marginTop: 16,
@@ -2667,7 +3215,7 @@ function InterimButtonsView({
2667
3215
  ...bStyles.submitButton,
2668
3216
  opacity: 0.5
2669
3217
  } }),
2670
- /* @__PURE__ */ jsx5("style", { children: `
3218
+ /* @__PURE__ */ jsx6("style", { children: `
2671
3219
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
2672
3220
  @keyframes flopay-interim-expand {
2673
3221
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -2677,10 +3225,10 @@ function InterimButtonsView({
2677
3225
  ` })
2678
3226
  ] });
2679
3227
  }
2680
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
3228
+ return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2681
3229
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2682
3230
  (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2683
- /* @__PURE__ */ jsx5(
3231
+ /* @__PURE__ */ jsx6(
2684
3232
  "button",
2685
3233
  {
2686
3234
  type: "button",
@@ -2720,10 +3268,10 @@ function InterimButtonsView({
2720
3268
  onMouseUp: (e) => {
2721
3269
  e.currentTarget.style.transform = "scale(1)";
2722
3270
  },
2723
- children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
3271
+ children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
2724
3272
  }
2725
3273
  ),
2726
- errorMessage && /* @__PURE__ */ jsxs3("div", { style: {
3274
+ errorMessage && /* @__PURE__ */ jsxs4("div", { style: {
2727
3275
  margin: "0.25rem 0",
2728
3276
  padding: "0.625rem 0.875rem",
2729
3277
  background: "#FEF2F2",
@@ -2737,21 +3285,21 @@ function InterimButtonsView({
2737
3285
  gap: "0.5rem",
2738
3286
  ...bStyles.errorBanner
2739
3287
  }, children: [
2740
- /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
3288
+ /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2741
3289
  errorMessage
2742
3290
  ] }),
2743
- /* @__PURE__ */ jsx5("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
3291
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2744
3292
  ] });
2745
3293
  }
2746
3294
 
2747
3295
  // src/checkout-form.tsx
2748
- import { FloPayError as FloPayError4 } from "@flopay/shared";
3296
+ import { FloPayError as FloPayError5 } from "@flopay/shared";
2749
3297
  import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useState as useState4 } from "react";
2750
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
3298
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2751
3299
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
2752
3300
  var CheckoutForm = forwardRef2(
2753
3301
  function CheckoutForm2(props, ref) {
2754
- return /* @__PURE__ */ jsx6(CheckoutFormInner, { ...props, innerRef: ref });
3302
+ return /* @__PURE__ */ jsx7(CheckoutFormInner, { ...props, innerRef: ref });
2755
3303
  }
2756
3304
  );
2757
3305
  function CheckoutFormInner({
@@ -2826,7 +3374,8 @@ function CheckoutFormInner({
2826
3374
  if (response.ok) {
2827
3375
  onComplete?.({
2828
3376
  status: "succeeded",
2829
- paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
3377
+ paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
3378
+ paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
2830
3379
  });
2831
3380
  return;
2832
3381
  }
@@ -2853,7 +3402,8 @@ function CheckoutFormInner({
2853
3402
  await processPaymentInternal({
2854
3403
  id: result.paymentIntentId,
2855
3404
  type: "card",
2856
- threeDSecureActionResultTokenId: result.paymentIntentId
3405
+ threeDSecureActionResultTokenId: result.paymentIntentId,
3406
+ originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
2857
3407
  });
2858
3408
  }
2859
3409
  } finally {
@@ -2982,7 +3532,7 @@ function CheckoutFormInner({
2982
3532
  return;
2983
3533
  }
2984
3534
  if (!sessionId || !email) {
2985
- throw new FloPayError4("Missing sessionId or email", "validation_error");
3535
+ throw new FloPayError5("Missing sessionId or email", "validation_error");
2986
3536
  }
2987
3537
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2988
3538
  method: "POST",
@@ -2994,10 +3544,10 @@ function CheckoutFormInner({
2994
3544
  isPaypal: false
2995
3545
  })
2996
3546
  });
2997
- if (!intentResponse.ok) throw new FloPayError4("Failed to create payment intent", "api_error");
3547
+ if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
2998
3548
  const intentJson = await intentResponse.json();
2999
3549
  const intentClientSecret = intentJson.data?.id;
3000
- if (!intentClientSecret) throw new FloPayError4("No client_secret in payment intent response", "api_error");
3550
+ if (!intentClientSecret) throw new FloPayError5("No client_secret in payment intent response", "api_error");
3001
3551
  const confirmResult = await flopay.confirmCardPayment({
3002
3552
  clientSecret: intentClientSecret,
3003
3553
  paymentMethodId: pmResult.paymentMethodId
@@ -3011,7 +3561,8 @@ function CheckoutFormInner({
3011
3561
  dispatchTokenizedBody({
3012
3562
  id: pmResult.paymentMethodId,
3013
3563
  type: "card",
3014
- threeDSecureActionResultTokenId: confirmResult.paymentIntentId
3564
+ threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
3565
+ originalPaymentMethodId: pmResult.paymentMethodId
3015
3566
  });
3016
3567
  } catch (err) {
3017
3568
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
@@ -3025,8 +3576,8 @@ function CheckoutFormInner({
3025
3576
  [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
3026
3577
  );
3027
3578
  const isReady = flopay !== null && elements !== null;
3028
- return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3029
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-overlay", style: {
3579
+ return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3580
+ (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-overlay", style: {
3030
3581
  position: "absolute",
3031
3582
  inset: 0,
3032
3583
  background: "rgba(255,255,255,0.7)",
@@ -3035,12 +3586,12 @@ function CheckoutFormInner({
3035
3586
  justifyContent: "center",
3036
3587
  zIndex: 10
3037
3588
  }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
3038
- !isReady && /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
3039
- isReady && /* @__PURE__ */ jsxs4(Fragment4, { children: [
3040
- /* @__PURE__ */ jsx6(PaymentElement, { options: { layout } }),
3041
- showAddress && /* @__PURE__ */ jsx6(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
3042
- displayError && /* @__PURE__ */ jsx6("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
3043
- children ?? /* @__PURE__ */ jsx6(
3589
+ !isReady && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
3590
+ isReady && /* @__PURE__ */ jsxs5(Fragment4, { children: [
3591
+ /* @__PURE__ */ jsx7(PaymentElement, { options: { layout } }),
3592
+ showAddress && /* @__PURE__ */ jsx7(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
3593
+ displayError && /* @__PURE__ */ jsx7("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
3594
+ children ?? /* @__PURE__ */ jsx7(
3044
3595
  "button",
3045
3596
  {
3046
3597
  type: "submit",
@@ -3054,9 +3605,9 @@ function CheckoutFormInner({
3054
3605
  }
3055
3606
 
3056
3607
  // src/paypal-button.tsx
3057
- import { FloPayError as FloPayError5 } from "@flopay/shared";
3608
+ import { FloPayError as FloPayError6 } from "@flopay/shared";
3058
3609
  import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
3059
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3610
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3060
3611
  function PayPalButton({
3061
3612
  sessionId,
3062
3613
  billingApiUrl,
@@ -3174,7 +3725,7 @@ function PayPalButton({
3174
3725
  setSubmitting(true);
3175
3726
  onErrorChange?.(null);
3176
3727
  if (!sessionId || !email) {
3177
- throw new FloPayError5("Missing sessionId or email for PayPal payment", "validation_error");
3728
+ throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
3178
3729
  }
3179
3730
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3180
3731
  method: "POST",
@@ -3186,10 +3737,10 @@ function PayPalButton({
3186
3737
  isPaypal: "true"
3187
3738
  })
3188
3739
  });
3189
- if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
3740
+ if (!intentResponse.ok) throw new FloPayError6("Failed to create payment intent", "api_error");
3190
3741
  const intentJson = await intentResponse.json();
3191
3742
  const intentClientSecret = intentJson.data?.id;
3192
- if (!intentClientSecret) throw new FloPayError5("No client_secret in response", "api_error");
3743
+ if (!intentClientSecret) throw new FloPayError6("No client_secret in response", "api_error");
3193
3744
  const result = await flopay.confirmPayment({
3194
3745
  clientSecret: intentClientSecret,
3195
3746
  returnUrl: window.location.href
@@ -3211,11 +3762,11 @@ function PayPalButton({
3211
3762
  }
3212
3763
  }, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
3213
3764
  if (!flopay || !elements) {
3214
- return /* @__PURE__ */ jsx7("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
3765
+ return /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
3215
3766
  }
3216
- return /* @__PURE__ */ jsxs5(Fragment5, { children: [
3217
- !ready && /* @__PURE__ */ jsx7("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
3218
- /* @__PURE__ */ jsx7("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx7(
3767
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
3768
+ !ready && /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
3769
+ /* @__PURE__ */ jsx8("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx8(
3219
3770
  "button",
3220
3771
  {
3221
3772
  type: "button",
@@ -3237,7 +3788,7 @@ function PayPalButton({
3237
3788
  children: submitting ? "Processing..." : "PayPal"
3238
3789
  }
3239
3790
  ) }),
3240
- (submitting || isProcessing) && /* @__PURE__ */ jsx7("div", { style: {
3791
+ (submitting || isProcessing) && /* @__PURE__ */ jsx8("div", { style: {
3241
3792
  position: "fixed",
3242
3793
  inset: 0,
3243
3794
  background: "rgba(0,0,0,0.4)",
@@ -3245,7 +3796,7 @@ function PayPalButton({
3245
3796
  alignItems: "center",
3246
3797
  justifyContent: "center",
3247
3798
  zIndex: 1e3
3248
- }, children: /* @__PURE__ */ jsx7("div", { style: {
3799
+ }, children: /* @__PURE__ */ jsx8("div", { style: {
3249
3800
  background: "white",
3250
3801
  borderRadius: 8,
3251
3802
  padding: "1.5rem",
@@ -3255,6 +3806,649 @@ function PayPalButton({
3255
3806
  }, children: "Processing PayPal payment..." }) })
3256
3807
  ] });
3257
3808
  }
3809
+
3810
+ // src/automatic-payment-button.tsx
3811
+ import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
3812
+ import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
3813
+ import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
3814
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3815
+ var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
3816
+ var PAYPAL_RESUME_STORAGE_KEY = "flopay_automatic_payment_button_resume";
3817
+ function sleep2(ms) {
3818
+ return new Promise((resolve) => setTimeout(resolve, ms));
3819
+ }
3820
+ function coerceError(err, fallbackMessage) {
3821
+ if (err instanceof FloPayError7) {
3822
+ return err;
3823
+ }
3824
+ return new FloPayError7(
3825
+ err instanceof Error ? err.message : fallbackMessage,
3826
+ "api_error"
3827
+ );
3828
+ }
3829
+ function canUseStorage() {
3830
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
3831
+ }
3832
+ function shouldShowFallbackCheckout(error, sessionId) {
3833
+ if (!sessionId) return false;
3834
+ if (error.type === "validation_error") return false;
3835
+ if (error.checkoutMethod && error.checkoutMethod !== "card") return false;
3836
+ return true;
3837
+ }
3838
+ function readPayPalResumeState() {
3839
+ if (!canUseStorage()) return null;
3840
+ try {
3841
+ const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
3842
+ if (!raw) return null;
3843
+ return JSON.parse(raw);
3844
+ } catch {
3845
+ return null;
3846
+ }
3847
+ }
3848
+ function persistPayPalResumeState(state) {
3849
+ if (!canUseStorage()) return;
3850
+ try {
3851
+ window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
3852
+ } catch (error) {
3853
+ console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
3854
+ }
3855
+ }
3856
+ function clearPayPalResumeState() {
3857
+ if (!canUseStorage()) return;
3858
+ try {
3859
+ window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
3860
+ } catch (error) {
3861
+ console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
3862
+ }
3863
+ }
3864
+ function clearPayPalRedirectParams() {
3865
+ if (typeof window === "undefined") return;
3866
+ const url = new URL(window.location.href);
3867
+ const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
3868
+ let changed = false;
3869
+ for (const key of keys) {
3870
+ if (url.searchParams.has(key)) {
3871
+ url.searchParams.delete(key);
3872
+ changed = true;
3873
+ }
3874
+ }
3875
+ if (changed) {
3876
+ window.history.replaceState({}, "", url.toString());
3877
+ }
3878
+ }
3879
+ function resolveCreateSessionDraft(props) {
3880
+ if (props.createSession) {
3881
+ return {
3882
+ ...props.createSession,
3883
+ checkoutMode: "auto"
3884
+ };
3885
+ }
3886
+ if (!props.clientId || !props.account || !props.successUrl || !props.cancelUrl) {
3887
+ return null;
3888
+ }
3889
+ return {
3890
+ clientId: props.clientId,
3891
+ items: props.items,
3892
+ subscriptions: props.subscriptions,
3893
+ account: props.account,
3894
+ successUrl: props.successUrl,
3895
+ cancelUrl: props.cancelUrl,
3896
+ couponCodes: props.couponCodes,
3897
+ tagsData: props.tagsData,
3898
+ utmMetadata: props.utmMetadata,
3899
+ checkoutMode: "auto"
3900
+ };
3901
+ }
3902
+ function FloPayAutomaticPaymentButton({
3903
+ sessionId,
3904
+ createSession,
3905
+ paymentMethodId,
3906
+ clientId,
3907
+ items,
3908
+ subscriptions,
3909
+ account,
3910
+ successUrl,
3911
+ cancelUrl,
3912
+ couponCodes,
3913
+ tagsData,
3914
+ utmMetadata,
3915
+ billingApiUrl,
3916
+ locale,
3917
+ fallbackPublishableKey,
3918
+ buttonsTheme,
3919
+ buttonsStyles: stylesOverride,
3920
+ onSuccess,
3921
+ onError,
3922
+ onDecline,
3923
+ children,
3924
+ disabled = false,
3925
+ type = "button",
3926
+ style,
3927
+ ...buttonProps
3928
+ }) {
3929
+ const resolvedBillingUrl = useMemo4(
3930
+ () => resolveBillingApiUrl4(billingApiUrl),
3931
+ [billingApiUrl]
3932
+ );
3933
+ const createSessionDraft = useMemo4(
3934
+ () => resolveCreateSessionDraft({
3935
+ createSession,
3936
+ clientId,
3937
+ items,
3938
+ subscriptions,
3939
+ account,
3940
+ successUrl,
3941
+ cancelUrl,
3942
+ couponCodes,
3943
+ tagsData,
3944
+ utmMetadata
3945
+ }),
3946
+ [
3947
+ account,
3948
+ cancelUrl,
3949
+ clientId,
3950
+ couponCodes,
3951
+ createSession,
3952
+ items,
3953
+ subscriptions,
3954
+ successUrl,
3955
+ tagsData,
3956
+ utmMetadata
3957
+ ]
3958
+ );
3959
+ const [isProcessing, setIsProcessing] = useState6(false);
3960
+ const [overlayStatus, setOverlayStatus] = useState6(null);
3961
+ const [overlayError, setOverlayError] = useState6(null);
3962
+ const [fallbackSession, setFallbackSession] = useState6(null);
3963
+ const automaticPaymentToken = useMemo4(
3964
+ () => paymentMethodId ? {
3965
+ id: paymentMethodId,
3966
+ type: "card"
3967
+ } : void 0,
3968
+ [paymentMethodId]
3969
+ );
3970
+ const isMountedRef = useRef5(true);
3971
+ const resumeAttemptedRef = useRef5(false);
3972
+ const fallbackSessionRef = useRef5(fallbackSession);
3973
+ const onSuccessRef = useRef5(onSuccess);
3974
+ const onErrorRef = useRef5(onError);
3975
+ const onDeclineRef = useRef5(onDecline);
3976
+ useEffect7(() => {
3977
+ fallbackSessionRef.current = fallbackSession;
3978
+ }, [fallbackSession]);
3979
+ useEffect7(() => {
3980
+ onSuccessRef.current = onSuccess;
3981
+ }, [onSuccess]);
3982
+ useEffect7(() => {
3983
+ onErrorRef.current = onError;
3984
+ }, [onError]);
3985
+ useEffect7(() => {
3986
+ onDeclineRef.current = onDecline;
3987
+ }, [onDecline]);
3988
+ useEffect7(() => {
3989
+ isMountedRef.current = true;
3990
+ return () => {
3991
+ isMountedRef.current = false;
3992
+ };
3993
+ }, []);
3994
+ useEffect7(() => {
3995
+ if (!fallbackSession || typeof window === "undefined") {
3996
+ return;
3997
+ }
3998
+ const handleKeyDown = (event) => {
3999
+ if (event.key === "Escape") {
4000
+ setFallbackSession(null);
4001
+ }
4002
+ };
4003
+ window.addEventListener("keydown", handleKeyDown);
4004
+ return () => {
4005
+ window.removeEventListener("keydown", handleKeyDown);
4006
+ };
4007
+ }, [fallbackSession]);
4008
+ const emitDecline = useCallback5((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
4009
+ onDeclineRef.current?.(buildDeclineEvent(method, error, {
4010
+ code: error.code,
4011
+ declineCode: error.declineCode
4012
+ }));
4013
+ }, []);
4014
+ const showSuccess = useCallback5(async (event) => {
4015
+ if (!isMountedRef.current) return;
4016
+ setOverlayError(null);
4017
+ setOverlayStatus("success");
4018
+ await sleep2(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
4019
+ if (!isMountedRef.current) return;
4020
+ onSuccessRef.current?.(event);
4021
+ }, []);
4022
+ const showError = useCallback5(async (error, options) => {
4023
+ if (!isMountedRef.current) return;
4024
+ onErrorRef.current?.(error);
4025
+ if (options?.emitDecline) {
4026
+ emitDecline(error, options.method);
4027
+ }
4028
+ setOverlayError(error.message);
4029
+ setOverlayStatus("error");
4030
+ await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
4031
+ }, [emitDecline]);
4032
+ const processResolvedSession = useCallback5(async (apiResult, resolvedSessionId) => {
4033
+ const session = apiResult.data.session ?? null;
4034
+ if (!session) {
4035
+ throw new FloPayError7("No session data returned", "api_error");
4036
+ }
4037
+ if (session.status === "complete") {
4038
+ await showSuccess({
4039
+ result: { status: "succeeded" },
4040
+ session,
4041
+ sessionId: session.id || resolvedSessionId,
4042
+ autoCompleted: false
4043
+ });
4044
+ return;
4045
+ }
4046
+ try {
4047
+ const result = await processSavedPaymentForMode({
4048
+ billingApiUrl: resolvedBillingUrl,
4049
+ sessionId: resolvedSessionId ?? session.id,
4050
+ session,
4051
+ tokenizedData: automaticPaymentToken
4052
+ });
4053
+ let paymentResult = result.type === "success" ? result.result : null;
4054
+ if (result.type !== "success") {
4055
+ const {
4056
+ publishableKey,
4057
+ paypalPublishableKey
4058
+ } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4059
+ if (result.type === "paypal_redirect_required") {
4060
+ persistPayPalResumeState({
4061
+ sessionId: session.id || resolvedSessionId,
4062
+ publishableKey,
4063
+ paypalPublishableKey
4064
+ });
4065
+ }
4066
+ const {
4067
+ flopay,
4068
+ paypalFlopay
4069
+ } = await loadSavedPaymentProviders({
4070
+ publishableKey,
4071
+ paypalPublishableKey,
4072
+ billingApiUrl: resolvedBillingUrl,
4073
+ locale
4074
+ });
4075
+ paymentResult = await handleSavedPaymentRedirectResult(result, {
4076
+ flopay,
4077
+ paypalFlopay,
4078
+ attempt3DS: true,
4079
+ billingApiUrl: resolvedBillingUrl,
4080
+ sessionId: session.id || resolvedSessionId,
4081
+ session
4082
+ });
4083
+ if (result.type === "paypal_redirect_required") {
4084
+ clearPayPalResumeState();
4085
+ }
4086
+ }
4087
+ await showSuccess({
4088
+ result: paymentResult ? {
4089
+ ...paymentResult,
4090
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4091
+ } : {
4092
+ status: "succeeded",
4093
+ paymentMethodId: paymentMethodId ?? void 0
4094
+ },
4095
+ session,
4096
+ sessionId: session.id || resolvedSessionId,
4097
+ autoCompleted: false
4098
+ });
4099
+ } catch (err) {
4100
+ let floPayErr = normalizeSavedPaymentError(err);
4101
+ const fallbackSessionId = session.id || resolvedSessionId;
4102
+ if (floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
4103
+ try {
4104
+ const {
4105
+ publishableKey,
4106
+ paypalPublishableKey
4107
+ } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4108
+ const {
4109
+ flopay
4110
+ } = await loadSavedPaymentProviders({
4111
+ publishableKey,
4112
+ paypalPublishableKey,
4113
+ billingApiUrl: resolvedBillingUrl,
4114
+ locale
4115
+ });
4116
+ const paymentResult = await processSavedPaymentWithIntent({
4117
+ billingApiUrl: resolvedBillingUrl,
4118
+ sessionId: fallbackSessionId,
4119
+ session,
4120
+ paymentMethodId: automaticPaymentToken.id,
4121
+ flopay
4122
+ });
4123
+ await showSuccess({
4124
+ result: {
4125
+ ...paymentResult,
4126
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4127
+ },
4128
+ session,
4129
+ sessionId: fallbackSessionId,
4130
+ autoCompleted: false
4131
+ });
4132
+ return;
4133
+ } catch (intentRecoveryErr) {
4134
+ floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
4135
+ }
4136
+ }
4137
+ await showError(floPayErr, {
4138
+ emitDecline: true,
4139
+ method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
4140
+ });
4141
+ if (shouldShowFallbackCheckout(floPayErr, fallbackSessionId) && fallbackSessionId && isMountedRef.current) {
4142
+ setFallbackSession({
4143
+ sessionId: fallbackSessionId,
4144
+ errorMessage: floPayErr.message
4145
+ });
4146
+ }
4147
+ }
4148
+ }, [
4149
+ fallbackPublishableKey,
4150
+ locale,
4151
+ automaticPaymentToken,
4152
+ paymentMethodId,
4153
+ resolvedBillingUrl,
4154
+ showError,
4155
+ showSuccess
4156
+ ]);
4157
+ const handleButtonClick = useCallback5(async (event) => {
4158
+ buttonProps.onClick?.(event);
4159
+ if (event.defaultPrevented || disabled || isProcessing) {
4160
+ return;
4161
+ }
4162
+ setFallbackSession(null);
4163
+ if (sessionId && createSessionDraft) {
4164
+ const error = new FloPayError7(
4165
+ "Provide either sessionId or create-session props, not both.",
4166
+ "validation_error"
4167
+ );
4168
+ await showError(error);
4169
+ if (isMountedRef.current) {
4170
+ setOverlayStatus(null);
4171
+ setOverlayError(null);
4172
+ }
4173
+ return;
4174
+ }
4175
+ if (!sessionId && !createSessionDraft) {
4176
+ const error = new FloPayError7(
4177
+ "Provide a sessionId or the props required to create an automatic payment session.",
4178
+ "validation_error"
4179
+ );
4180
+ await showError(error);
4181
+ if (isMountedRef.current) {
4182
+ setOverlayStatus(null);
4183
+ setOverlayError(null);
4184
+ }
4185
+ return;
4186
+ }
4187
+ setIsProcessing(true);
4188
+ setOverlayError(null);
4189
+ setOverlayStatus("processing");
4190
+ try {
4191
+ const api = new PaymentAPI3(resolvedBillingUrl);
4192
+ if (sessionId) {
4193
+ const result = await api.getUnifiedCheckoutSession(sessionId);
4194
+ await processResolvedSession(result, sessionId);
4195
+ return;
4196
+ }
4197
+ try {
4198
+ const result = await api.createAndFetchSession(createSessionDraft);
4199
+ await processResolvedSession(result, result.data.session?.id ?? null);
4200
+ } catch (err) {
4201
+ if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
4202
+ await showSuccess({
4203
+ result: { status: "succeeded" },
4204
+ session: null,
4205
+ sessionId: null,
4206
+ autoCompleted: true
4207
+ });
4208
+ return;
4209
+ }
4210
+ throw err;
4211
+ }
4212
+ } catch (err) {
4213
+ await showError(
4214
+ coerceError(err, "Automatic payment failed. Please try again.")
4215
+ );
4216
+ } finally {
4217
+ if (isMountedRef.current) {
4218
+ setOverlayStatus(null);
4219
+ setOverlayError(null);
4220
+ setIsProcessing(false);
4221
+ }
4222
+ }
4223
+ }, [
4224
+ buttonProps,
4225
+ createSessionDraft,
4226
+ disabled,
4227
+ isProcessing,
4228
+ processResolvedSession,
4229
+ resolvedBillingUrl,
4230
+ sessionId,
4231
+ showError,
4232
+ showSuccess
4233
+ ]);
4234
+ const handleFallbackComplete = useCallback5((result) => {
4235
+ const activeFallback = fallbackSessionRef.current;
4236
+ setFallbackSession(null);
4237
+ onSuccessRef.current?.({
4238
+ result,
4239
+ session: null,
4240
+ sessionId: activeFallback?.sessionId ?? null,
4241
+ autoCompleted: false
4242
+ });
4243
+ }, []);
4244
+ const handleFallbackError = useCallback5((error) => {
4245
+ onErrorRef.current?.(error);
4246
+ }, []);
4247
+ const handleFallbackDecline = useCallback5((decline) => {
4248
+ onDeclineRef.current?.(decline);
4249
+ }, []);
4250
+ useEffect7(() => {
4251
+ if (typeof window === "undefined" || resumeAttemptedRef.current) {
4252
+ return;
4253
+ }
4254
+ const params = new URLSearchParams(window.location.search);
4255
+ const clientSecret = params.get("payment_intent_client_secret");
4256
+ if (!clientSecret) {
4257
+ return;
4258
+ }
4259
+ const resumeState = readPayPalResumeState();
4260
+ if (!resumeState) {
4261
+ return;
4262
+ }
4263
+ resumeAttemptedRef.current = true;
4264
+ void (async () => {
4265
+ setIsProcessing(true);
4266
+ setOverlayError(null);
4267
+ setOverlayStatus("processing");
4268
+ try {
4269
+ if (params.get("redirect_status") === "failed") {
4270
+ throw Object.assign(
4271
+ new FloPayError7("PayPal payment was declined. Please try again.", "api_error"),
4272
+ { checkoutMethod: "paypal" }
4273
+ );
4274
+ }
4275
+ const {
4276
+ flopay,
4277
+ paypalFlopay
4278
+ } = await loadSavedPaymentProviders({
4279
+ publishableKey: resumeState.publishableKey,
4280
+ paypalPublishableKey: resumeState.paypalPublishableKey,
4281
+ billingApiUrl: resolvedBillingUrl,
4282
+ locale
4283
+ });
4284
+ const paypalStripe = (paypalFlopay ?? flopay).getRawProvider();
4285
+ if (!paypalStripe) {
4286
+ throw Object.assign(
4287
+ new FloPayError7("PayPal is not available.", "api_error"),
4288
+ { checkoutMethod: "paypal" }
4289
+ );
4290
+ }
4291
+ const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
4292
+ if (error) {
4293
+ throw Object.assign(
4294
+ new FloPayError7(
4295
+ error.message ?? "Failed to retrieve PayPal payment status.",
4296
+ "api_error",
4297
+ { code: error.code }
4298
+ ),
4299
+ { checkoutMethod: "paypal" }
4300
+ );
4301
+ }
4302
+ if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
4303
+ await showSuccess({
4304
+ result: { status: "succeeded", paymentIntentId: paymentIntent.id },
4305
+ session: null,
4306
+ sessionId: resumeState.sessionId,
4307
+ autoCompleted: false
4308
+ });
4309
+ } else {
4310
+ throw Object.assign(
4311
+ new FloPayError7("PayPal payment was not completed. Please try again.", "api_error"),
4312
+ { checkoutMethod: "paypal" }
4313
+ );
4314
+ }
4315
+ } catch (err) {
4316
+ const floPayErr = normalizeSavedPaymentError(err);
4317
+ await showError(floPayErr, {
4318
+ emitDecline: true,
4319
+ method: floPayErr.checkoutMethod ?? "paypal"
4320
+ });
4321
+ } finally {
4322
+ clearPayPalResumeState();
4323
+ clearPayPalRedirectParams();
4324
+ if (isMountedRef.current) {
4325
+ setOverlayStatus(null);
4326
+ setOverlayError(null);
4327
+ setIsProcessing(false);
4328
+ }
4329
+ }
4330
+ })();
4331
+ }, [locale, resolvedBillingUrl, showError, showSuccess]);
4332
+ const bStyles = useMemo4(() => {
4333
+ const base = resolveButtonsLayoutTheme3(buttonsTheme);
4334
+ if (!stylesOverride) return base;
4335
+ return {
4336
+ ...base,
4337
+ ...stylesOverride,
4338
+ cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
4339
+ };
4340
+ }, [buttonsTheme, stylesOverride]);
4341
+ const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
4342
+ return /* @__PURE__ */ jsxs7(Fragment6, { children: [
4343
+ /* @__PURE__ */ jsx9(
4344
+ "button",
4345
+ {
4346
+ ...buttonProps,
4347
+ type,
4348
+ onClick: handleButtonClick,
4349
+ disabled: disabled || isProcessing,
4350
+ "aria-busy": isProcessing,
4351
+ style: {
4352
+ width: "100%",
4353
+ ...cardButtonSizing,
4354
+ backgroundColor: "white",
4355
+ color: "#262833",
4356
+ border: "1px solid #d1d5db",
4357
+ borderRadius: "8px",
4358
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
4359
+ fontWeight: 600,
4360
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
4361
+ display: "flex",
4362
+ alignItems: "center",
4363
+ justifyContent: "center",
4364
+ gap: "0.625rem",
4365
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
4366
+ transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
4367
+ position: "relative",
4368
+ opacity: disabled || isProcessing ? 0.6 : 1,
4369
+ ...bStyles.cardButton,
4370
+ ...style
4371
+ },
4372
+ onMouseDown: (e) => {
4373
+ buttonProps.onMouseDown?.(e);
4374
+ if (!e.defaultPrevented) {
4375
+ e.currentTarget.style.transform = "scale(0.985)";
4376
+ }
4377
+ },
4378
+ onMouseUp: (e) => {
4379
+ buttonProps.onMouseUp?.(e);
4380
+ if (!e.defaultPrevented) {
4381
+ e.currentTarget.style.transform = "scale(1)";
4382
+ }
4383
+ },
4384
+ children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: children })
4385
+ }
4386
+ ),
4387
+ overlayStatus && /* @__PURE__ */ jsx9(
4388
+ ProcessingOverlay,
4389
+ {
4390
+ status: overlayStatus,
4391
+ errorMessage: overlayError
4392
+ }
4393
+ ),
4394
+ fallbackSession && /* @__PURE__ */ jsx9(
4395
+ "div",
4396
+ {
4397
+ "data-testid": "flopay-automatic-payment-fallback",
4398
+ role: "dialog",
4399
+ "aria-modal": "true",
4400
+ onClick: (event) => {
4401
+ if (event.target === event.currentTarget) {
4402
+ setFallbackSession(null);
4403
+ }
4404
+ },
4405
+ style: {
4406
+ position: "fixed",
4407
+ inset: 0,
4408
+ background: "rgba(0, 0, 0, 0.45)",
4409
+ display: "flex",
4410
+ alignItems: "center",
4411
+ justifyContent: "center",
4412
+ padding: "1.5rem",
4413
+ zIndex: 1100
4414
+ },
4415
+ children: /* @__PURE__ */ jsx9(
4416
+ "div",
4417
+ {
4418
+ style: {
4419
+ width: "100%",
4420
+ maxWidth: 520,
4421
+ maxHeight: "90vh",
4422
+ overflowY: "auto",
4423
+ background: "white",
4424
+ borderRadius: 20,
4425
+ padding: "1.5rem",
4426
+ boxShadow: "0 24px 80px rgba(15, 23, 42, 0.28)",
4427
+ display: "flex",
4428
+ flexDirection: "column",
4429
+ gap: "1rem"
4430
+ },
4431
+ children: /* @__PURE__ */ jsx9(
4432
+ FloPayCheckout,
4433
+ {
4434
+ sessionId: fallbackSession.sessionId,
4435
+ checkoutMode: "full",
4436
+ billingApiUrl: resolvedBillingUrl,
4437
+ fallbackPublishableKey,
4438
+ initialErrorMessage: fallbackSession.errorMessage,
4439
+ cardTitleContent: null,
4440
+ showSecurityFooter: false,
4441
+ onComplete: handleFallbackComplete,
4442
+ onError: handleFallbackError,
4443
+ onDecline: handleFallbackDecline
4444
+ }
4445
+ )
4446
+ }
4447
+ )
4448
+ }
4449
+ )
4450
+ ] });
4451
+ }
3258
4452
  export {
3259
4453
  AddressElement,
3260
4454
  CardCvcElement,
@@ -3262,6 +4456,7 @@ export {
3262
4456
  CardExpiryElement,
3263
4457
  CardNumberElement,
3264
4458
  CheckoutForm,
4459
+ FloPayAutomaticPaymentButton,
3265
4460
  FloPayCheckout,
3266
4461
  FloPayProvider,
3267
4462
  PayPalButton,