@flopay/react 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -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,6 +2392,7 @@ function FloPayCheckout({
1839
2392
  cardButtonContent,
1840
2393
  cardBackButtonContent,
1841
2394
  cardTitleContent,
2395
+ showSecurityFooter = true,
1842
2396
  onButtonClick,
1843
2397
  onBeforeButtonClick,
1844
2398
  enableAVS,
@@ -1861,11 +2415,15 @@ function FloPayCheckout({
1861
2415
  const paypalFlopayRef = useRef3(null);
1862
2416
  const [session, setSession] = useState3(null);
1863
2417
  const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
2418
+ const activeSessionId = sessionIdProp ?? resolvedSessionId;
2419
+ const initSessionDependency = createSessionParams ? "" : activeSessionId;
1864
2420
  const [isLoading, setIsLoading] = useState3(true);
1865
2421
  const [loadError, setLoadError] = useState3(null);
1866
2422
  const [currentMode, setCurrentMode] = useState3("full");
1867
2423
  const [confirmProcessing, setConfirmProcessing] = useState3(false);
1868
2424
  const [modeError, setModeError] = useState3(null);
2425
+ const [modeOverlayStatus, setModeOverlayStatus] = useState3(null);
2426
+ const [modeOverlayError, setModeOverlayError] = useState3(null);
1869
2427
  const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
1870
2428
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState3("");
1871
2429
  const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
@@ -1894,10 +2452,18 @@ function FloPayCheckout({
1894
2452
  () => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
1895
2453
  [createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
1896
2454
  );
1897
- const effectiveCreateSession = useMemo3(
2455
+ const effectiveCreateSessionBase = useMemo3(
1898
2456
  () => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
1899
2457
  [createSessionParams, activeCreateSessionPatch]
1900
2458
  );
2459
+ const effectiveCreateSessionMode = checkoutModeProp ?? effectiveCreateSessionBase?.checkoutMode ?? "full";
2460
+ const effectiveCreateSession = useMemo3(
2461
+ () => effectiveCreateSessionBase ? {
2462
+ ...effectiveCreateSessionBase,
2463
+ checkoutMode: effectiveCreateSessionMode
2464
+ } : void 0,
2465
+ [effectiveCreateSessionBase, effectiveCreateSessionMode]
2466
+ );
1901
2467
  useEffect4(() => {
1902
2468
  setCreateSessionPatch(void 0);
1903
2469
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
@@ -1908,111 +2474,59 @@ function FloPayCheckout({
1908
2474
  },
1909
2475
  []
1910
2476
  );
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
2477
+ const runSavedPaymentFlow = useCallback2(
2478
+ async (sess, options) => {
2479
+ setModeError(null);
2480
+ setModeOverlayError(null);
2481
+ setModeOverlayStatus("processing");
2482
+ const activeSessionId2 = options?.sessionId ?? sess.id;
2483
+ try {
2484
+ const result = await processSavedPaymentForMode({
2485
+ billingApiUrl: resolvedBillingUrl,
2486
+ sessionId: activeSessionId2,
2487
+ session: sess
1972
2488
  });
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;
2489
+ const paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
2490
+ flopay: flopayRef.current,
2491
+ paypalFlopay: paypalFlopayRef.current,
2492
+ attempt3DS: options?.attempt3DS,
2493
+ billingApiUrl: resolvedBillingUrl,
2494
+ sessionId: activeSessionId2,
2495
+ session: sess
2496
+ });
2497
+ setModeOverlayStatus("success");
2498
+ await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2499
+ onCompleteRef.current?.(paymentResult);
2500
+ return true;
2501
+ } catch (err) {
2502
+ const floPayErr = normalizeSavedPaymentError(err);
2503
+ const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
2504
+ setModeError(floPayErr.message);
2505
+ setModeOverlayError(floPayErr.message);
2506
+ if (options?.fallbackToFull) {
2507
+ setCurrentMode("full");
1983
2508
  }
2509
+ onErrorRef.current?.(floPayErr);
2510
+ emitDecline(method, floPayErr, {
2511
+ code: floPayErr.code,
2512
+ declineCode: floPayErr.declineCode
2513
+ });
2514
+ setModeOverlayStatus("error");
2515
+ await Promise.allSettled([
2516
+ sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
2517
+ options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
2518
+ ]);
1984
2519
  return false;
2520
+ } finally {
2521
+ setModeOverlayStatus(null);
2522
+ setModeOverlayError(null);
1985
2523
  }
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
2524
  },
2015
- [resolvedBillingUrl, resolvedSessionId, emitDecline]
2525
+ [
2526
+ emitDecline,
2527
+ normalizeSavedPaymentError,
2528
+ resolvedBillingUrl
2529
+ ]
2016
2530
  );
2017
2531
  const inflightRef = useRef3(/* @__PURE__ */ new Map());
2018
2532
  const initializedHashRef = useRef3(null);
@@ -2044,8 +2558,14 @@ function FloPayCheckout({
2044
2558
  useEffect4(() => {
2045
2559
  setResolvedSessionId(sessionIdProp ?? "");
2046
2560
  }, [sessionIdProp]);
2561
+ useEffect4(() => {
2562
+ autoCheckoutAttempted.current = false;
2563
+ setModeError(null);
2564
+ setModeOverlayError(null);
2565
+ setModeOverlayStatus(null);
2566
+ }, [sessionIdProp, createSessionHash]);
2047
2567
  async function resolveInlineSession(params, cacheKey) {
2048
- const api = new PaymentAPI(resolvedBillingUrl);
2568
+ const api = new PaymentAPI2(resolvedBillingUrl);
2049
2569
  let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
2050
2570
  let realResult = null;
2051
2571
  if (sid) {
@@ -2074,7 +2594,7 @@ function FloPayCheckout({
2074
2594
  async (patch) => {
2075
2595
  const baseParams = createSessionParamsRef.current;
2076
2596
  if (!baseParams) {
2077
- throw new FloPayError3("createSession is required to bootstrap checkout.", "validation_error");
2597
+ throw new FloPayError4("createSession is required to bootstrap checkout.", "validation_error");
2078
2598
  }
2079
2599
  const mergedParams = mergeInlineSessionPatch(baseParams, patch);
2080
2600
  const cacheKey = hashCreateParams(mergedParams);
@@ -2103,43 +2623,23 @@ function FloPayCheckout({
2103
2623
  if (sid) {
2104
2624
  setResolvedSessionId(sid);
2105
2625
  }
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
- ]);
2626
+ const {
2627
+ publishableKey,
2628
+ paypalPublishableKey
2629
+ } = resolveSavedPaymentPublishableKeys(realResult, fallbackPublishableKey);
2630
+ const {
2631
+ flopay: instance,
2632
+ paypalFlopay: paypalInstance
2633
+ } = await loadSavedPaymentProviders({
2634
+ publishableKey,
2635
+ paypalPublishableKey,
2636
+ billingApiUrl: resolvedBillingUrl,
2637
+ locale
2638
+ });
2133
2639
  flopayRef.current = instance;
2134
2640
  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
- }
2641
+ paypalFlopayRef.current = paypalInstance;
2642
+ setPaypalFloPay(paypalInstance);
2143
2643
  setIsLoading(false);
2144
2644
  } catch (err) {
2145
2645
  if (initializedHashRef.current === cacheKey) {
@@ -2178,17 +2678,57 @@ function FloPayCheckout({
2178
2678
  let cancelled = false;
2179
2679
  setLoadError(null);
2180
2680
  if (createSessionHash) {
2181
- if (initializedHashRef.current === createSessionHash) return;
2182
2681
  const params = createSessionParamsRef.current;
2183
2682
  setSession(buildSyntheticSession(params, checkoutModeProp));
2184
- setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
2683
+ setCurrentMode(effectiveCreateSessionMode);
2185
2684
  setIsLoading(false);
2685
+ if (initializedHashRef.current === createSessionHash) {
2686
+ return () => {
2687
+ cancelled = true;
2688
+ };
2689
+ }
2186
2690
  (async () => {
2691
+ const shouldAutoProcessInlineSession = effectiveCreateSessionMode === "auto" && !autoCheckoutAttempted.current;
2692
+ if (shouldAutoProcessInlineSession) {
2693
+ setModeError(null);
2694
+ setModeOverlayError(null);
2695
+ setModeOverlayStatus("processing");
2696
+ }
2187
2697
  try {
2188
- await bootstrapInlineSession();
2698
+ const resolved = await bootstrapInlineSession();
2699
+ const resolvedSession = resolved.result.data.session ?? null;
2700
+ if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
2701
+ autoCheckoutAttempted.current = true;
2702
+ await runSavedPaymentFlow(resolvedSession, {
2703
+ attempt3DS: true,
2704
+ fallbackToFull: true,
2705
+ sessionId: resolved.sid || resolvedSession.id
2706
+ });
2707
+ return;
2708
+ }
2709
+ if (!cancelled && shouldAutoProcessInlineSession) {
2710
+ setModeOverlayStatus(null);
2711
+ setModeOverlayError(null);
2712
+ }
2189
2713
  } catch (err) {
2190
2714
  if (cancelled) return;
2191
- const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to create session", "api_error");
2715
+ const errorCode = err instanceof FloPayError4 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
2716
+ if (shouldAutoProcessInlineSession && errorCode === "session_auto_completed") {
2717
+ autoCheckoutAttempted.current = true;
2718
+ setModeOverlayStatus("success");
2719
+ await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
2720
+ if (!cancelled) {
2721
+ onCompleteRef.current?.({ status: "succeeded" });
2722
+ setModeOverlayStatus(null);
2723
+ setModeOverlayError(null);
2724
+ }
2725
+ return;
2726
+ }
2727
+ if (shouldAutoProcessInlineSession) {
2728
+ setModeOverlayStatus(null);
2729
+ setModeOverlayError(null);
2730
+ }
2731
+ const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to create session", "api_error");
2192
2732
  setLoadError(floPayErr);
2193
2733
  }
2194
2734
  })();
@@ -2199,14 +2739,14 @@ function FloPayCheckout({
2199
2739
  setIsLoading(true);
2200
2740
  async function init() {
2201
2741
  try {
2202
- const api = new PaymentAPI(resolvedBillingUrl);
2203
- const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
2742
+ const api = new PaymentAPI2(resolvedBillingUrl);
2743
+ const result = await api.getUnifiedCheckoutSession(activeSessionId);
2204
2744
  if (cancelled) return;
2205
2745
  setUnified(result);
2206
2746
  const sess = result.data.session ?? null;
2207
2747
  setSession(sess);
2208
2748
  if (!sess) {
2209
- throw new FloPayError3("No session data returned", "api_error");
2749
+ throw new FloPayError4("No session data returned", "api_error");
2210
2750
  }
2211
2751
  if (sess.status === "complete") {
2212
2752
  setIsLoading(false);
@@ -2220,22 +2760,20 @@ function FloPayCheckout({
2220
2760
  autoCheckoutAttempted.current = true;
2221
2761
  const stripeInitPromise = initStripe(result);
2222
2762
  try {
2223
- const redirectResult = await processPaymentForMode(sess);
2224
- if (!redirectResult) {
2225
- if (!cancelled) setIsLoading(false);
2226
- return;
2227
- }
2763
+ await runSavedPaymentFlow(sess, {
2764
+ attempt3DS: true,
2765
+ fallbackToFull: true,
2766
+ ensureProvidersReady: () => stripeInitPromise,
2767
+ sessionId: activeSessionId || sess.id
2768
+ });
2228
2769
  if (cancelled) return;
2229
2770
  await stripeInitPromise;
2230
- const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
2231
2771
  if (!cancelled) {
2232
- if (!handled) setCurrentMode("full");
2233
2772
  setIsLoading(false);
2234
2773
  }
2235
2774
  return;
2236
2775
  } catch {
2237
2776
  if (cancelled) return;
2238
- setCurrentMode("full");
2239
2777
  await stripeInitPromise;
2240
2778
  if (!cancelled) setIsLoading(false);
2241
2779
  return;
@@ -2245,81 +2783,55 @@ function FloPayCheckout({
2245
2783
  if (!cancelled) setIsLoading(false);
2246
2784
  } catch (err) {
2247
2785
  if (cancelled) return;
2248
- const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
2786
+ const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
2249
2787
  setLoadError(floPayErr);
2250
2788
  setIsLoading(false);
2251
2789
  }
2252
2790
  }
2253
2791
  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
- ]);
2792
+ const {
2793
+ publishableKey,
2794
+ paypalPublishableKey
2795
+ } = resolveSavedPaymentPublishableKeys(result, fallbackPublishableKey);
2796
+ const {
2797
+ flopay: instance,
2798
+ paypalFlopay: paypalInstance
2799
+ } = await loadSavedPaymentProviders({
2800
+ publishableKey,
2801
+ paypalPublishableKey,
2802
+ billingApiUrl: resolvedBillingUrl,
2803
+ locale
2804
+ });
2281
2805
  flopayRef.current = instance;
2282
2806
  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
- }
2807
+ paypalFlopayRef.current = paypalInstance;
2808
+ setPaypalFloPay(paypalInstance);
2291
2809
  }
2292
2810
  init();
2293
2811
  return () => {
2294
2812
  cancelled = true;
2295
2813
  };
2296
- }, [resolvedSessionId, createSessionHash, checkoutModeProp, bootstrapInlineSession]);
2814
+ }, [
2815
+ bootstrapInlineSession,
2816
+ checkoutModeProp,
2817
+ createSessionHash,
2818
+ effectiveCreateSessionMode,
2819
+ initSessionDependency,
2820
+ runSavedPaymentFlow
2821
+ ]);
2297
2822
  const handleConfirmCheckout = useCallback2(async () => {
2298
2823
  if (confirmProcessing || !session) return;
2299
2824
  setConfirmProcessing(true);
2300
2825
  setModeError(null);
2301
2826
  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");
2827
+ await runSavedPaymentFlow(session, {
2828
+ fallbackToFull: true,
2829
+ sessionId: activeSessionId || session.id
2830
+ });
2319
2831
  } finally {
2320
2832
  setConfirmProcessing(false);
2321
2833
  }
2322
- }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError, emitDecline]);
2834
+ }, [activeSessionId, confirmProcessing, runSavedPaymentFlow, session]);
2323
2835
  const providerOptions = useMemo3(() => {
2324
2836
  if (!unified || !session) return void 0;
2325
2837
  const opts = {
@@ -2337,7 +2849,7 @@ function FloPayCheckout({
2337
2849
  return opts;
2338
2850
  }, [unified, session, appearance, resolvedBillingUrl]);
2339
2851
  const shouldHandleInlineSessionPatch = Boolean(
2340
- createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession?.checkoutMode ?? checkoutModeProp ?? "full") === "full"
2852
+ createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && effectiveCreateSessionMode === "full"
2341
2853
  );
2342
2854
  const checkoutValue = useMemo3(
2343
2855
  () => ({
@@ -2361,37 +2873,55 @@ function FloPayCheckout({
2361
2873
  ]
2362
2874
  );
2363
2875
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
2876
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx6(
2877
+ ProcessingOverlay,
2878
+ {
2879
+ status: modeOverlayStatus,
2880
+ errorMessage: modeOverlayError
2881
+ }
2882
+ ) : null;
2364
2883
  if (isLoading) {
2365
- if (loadingNode) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingNode });
2884
+ if (loadingNode) {
2885
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2886
+ loadingNode,
2887
+ modeOverlay
2888
+ ] });
2889
+ }
2366
2890
  if (layout === "buttons") {
2367
- const skeletonBar = (h) => /* @__PURE__ */ jsx5("div", { style: {
2891
+ const skeletonBar = (h) => /* @__PURE__ */ jsx6("div", { style: {
2368
2892
  height: h,
2369
2893
  borderRadius: 8,
2370
2894
  background: "#e5e7eb",
2371
2895
  animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
2372
2896
  } });
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; } }` })
2897
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2898
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2899
+ showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2900
+ (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2901
+ skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2902
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2903
+ ] }),
2904
+ modeOverlay
2378
2905
  ] });
2379
2906
  }
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); } }` })
2907
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2908
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
2909
+ /* @__PURE__ */ jsx6("div", { style: {
2910
+ width: 24,
2911
+ height: 24,
2912
+ border: "2px solid #e5e7eb",
2913
+ borderTopColor: "#6b7280",
2914
+ borderRadius: "50%",
2915
+ animation: "spin 0.6s linear infinite"
2916
+ } }),
2917
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
2918
+ ] }),
2919
+ modeOverlay
2390
2920
  ] });
2391
2921
  }
2392
2922
  if (loadError) {
2393
- if (errorNode) return /* @__PURE__ */ jsx5(Fragment3, { children: errorNode(loadError) });
2394
- return /* @__PURE__ */ jsx5(
2923
+ if (errorNode) return /* @__PURE__ */ jsx6(Fragment3, { children: errorNode(loadError) });
2924
+ return /* @__PURE__ */ jsx6(
2395
2925
  "div",
2396
2926
  {
2397
2927
  style: {
@@ -2405,90 +2935,100 @@ function FloPayCheckout({
2405
2935
  );
2406
2936
  }
2407
2937
  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
- ) });
2938
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2939
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
2940
+ InterimButtonsView,
2941
+ {
2942
+ onButtonClick,
2943
+ showPayPal,
2944
+ showApplePay,
2945
+ showGooglePay,
2946
+ buttonsTheme,
2947
+ buttonsStyles,
2948
+ cardButtonContent,
2949
+ cardBackButtonContent,
2950
+ cardTitleContent
2951
+ }
2952
+ ) }),
2953
+ modeOverlay
2954
+ ] });
2955
+ }
2956
+ if (!flopay || !providerOptions) {
2957
+ return /* @__PURE__ */ jsx6(Fragment3, { children: modeOverlay });
2422
2958
  }
2423
- if (!flopay || !providerOptions) return /* @__PURE__ */ jsx5(Fragment3, {});
2424
2959
  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(
2960
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2961
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs4("div", { className, children: [
2962
+ modeError && /* @__PURE__ */ jsx6(
2963
+ "div",
2964
+ {
2965
+ style: {
2966
+ color: "#dc2626",
2967
+ fontSize: "0.875rem",
2968
+ marginBottom: "0.75rem",
2969
+ textAlign: "center"
2970
+ },
2971
+ children: modeError
2972
+ }
2973
+ ),
2974
+ renderConfirmButton ? renderConfirmButton({
2975
+ onConfirm: handleConfirmCheckout,
2976
+ isProcessing: confirmProcessing
2977
+ }) : /* @__PURE__ */ jsx6(
2978
+ "button",
2979
+ {
2980
+ type: "button",
2981
+ onClick: handleConfirmCheckout,
2982
+ disabled: confirmProcessing,
2983
+ style: {
2984
+ width: "100%",
2985
+ padding: "0.875rem",
2986
+ backgroundColor: "#4A49FF",
2987
+ color: "white",
2988
+ border: "none",
2989
+ borderRadius: "8px",
2990
+ fontSize: "1rem",
2991
+ fontWeight: 600,
2992
+ cursor: confirmProcessing ? "not-allowed" : "pointer",
2993
+ opacity: confirmProcessing ? 0.6 : 1
2994
+ },
2995
+ children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
2996
+ }
2997
+ )
2998
+ ] }) }) }),
2999
+ modeOverlay
3000
+ ] });
3001
+ }
3002
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3003
+ /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
3004
+ modeError && /* @__PURE__ */ jsx6(
2427
3005
  "div",
2428
3006
  {
2429
3007
  style: {
2430
- color: "#dc2626",
2431
- fontSize: "0.875rem",
3008
+ padding: "0.75rem 1rem",
2432
3009
  marginBottom: "0.75rem",
2433
- textAlign: "center"
3010
+ backgroundColor: "#FEF3C7",
3011
+ border: "1px solid #F59E0B",
3012
+ borderRadius: "8px",
3013
+ color: "#92400E",
3014
+ fontSize: "0.875rem"
2434
3015
  },
2435
3016
  children: modeError
2436
3017
  }
2437
3018
  ),
2438
- renderConfirmButton ? renderConfirmButton({
2439
- onConfirm: handleConfirmCheckout,
2440
- isProcessing: confirmProcessing
2441
- }) : /* @__PURE__ */ jsx5(
2442
- "button",
3019
+ /* @__PURE__ */ jsx6(
3020
+ SessionInjector,
2443
3021
  {
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"
3022
+ sessionId: activeSessionId,
3023
+ billingApiUrl: resolvedBillingUrl,
3024
+ session,
3025
+ children
2460
3026
  }
2461
3027
  )
2462
- ] }) }) });
2463
- }
2464
- return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs3(FloPayProvider, { flopay, 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(
3028
+ ] }) : /* @__PURE__ */ jsx6(
2489
3029
  SplitCardForm,
2490
3030
  {
2491
- sessionId: resolvedSessionId,
3031
+ sessionId: activeSessionId,
2492
3032
  email: session?.customer?.email,
2493
3033
  userId: session?.customer?.id,
2494
3034
  firstName: session?.customer?.firstName,
@@ -2498,6 +3038,8 @@ function FloPayCheckout({
2498
3038
  onComplete,
2499
3039
  onError,
2500
3040
  onDecline,
3041
+ error: modeError,
3042
+ onErrorChange: setModeError,
2501
3043
  onFullNameChange,
2502
3044
  showPayPal,
2503
3045
  showApplePay,
@@ -2508,6 +3050,7 @@ function FloPayCheckout({
2508
3050
  cardButtonContent,
2509
3051
  cardBackButtonContent,
2510
3052
  cardTitleContent,
3053
+ showSecurityFooter,
2511
3054
  onButtonClick,
2512
3055
  onBeforeButtonClick,
2513
3056
  enableAVS,
@@ -2518,8 +3061,9 @@ function FloPayCheckout({
2518
3061
  submitLabel,
2519
3062
  className
2520
3063
  }
2521
- )
2522
- ] }) });
3064
+ ) }) }),
3065
+ modeOverlay
3066
+ ] });
2523
3067
  }
2524
3068
  function SessionInjector({
2525
3069
  sessionId,
@@ -2527,8 +3071,8 @@ function SessionInjector({
2527
3071
  session,
2528
3072
  children
2529
3073
  }) {
2530
- return /* @__PURE__ */ jsx5(Fragment3, { children: React5.Children.map(children, (child) => {
2531
- if (!React5.isValidElement(child)) return child;
3074
+ return /* @__PURE__ */ jsx6(Fragment3, { children: React6.Children.map(children, (child) => {
3075
+ if (!React6.isValidElement(child)) return child;
2532
3076
  const existing = child.props;
2533
3077
  const injected = {};
2534
3078
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -2542,7 +3086,7 @@ function SessionInjector({
2542
3086
  injected.lastName = session.customer.lastName;
2543
3087
  }
2544
3088
  if (Object.keys(injected).length === 0) return child;
2545
- return React5.cloneElement(child, injected);
3089
+ return React6.cloneElement(child, injected);
2546
3090
  }) });
2547
3091
  }
2548
3092
  function InterimButtonsView({
@@ -2581,7 +3125,7 @@ function InterimButtonsView({
2581
3125
  title: { ...base.title, ...stylesOverride.title }
2582
3126
  };
2583
3127
  }, [buttonsTheme, stylesOverride]);
2584
- const skeleton = (h) => /* @__PURE__ */ jsx5("div", { style: {
3128
+ const skeleton = (h) => /* @__PURE__ */ jsx6("div", { style: {
2585
3129
  height: h,
2586
3130
  borderRadius: 8,
2587
3131
  background: "#e5e7eb",
@@ -2593,15 +3137,15 @@ function InterimButtonsView({
2593
3137
  const inputBg = bStyles.cardInputBackground ?? "white";
2594
3138
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
2595
3139
  const hideTitle = isEmptySlotContent(cardTitleContent);
2596
- return /* @__PURE__ */ jsxs3("div", { style: {
3140
+ return /* @__PURE__ */ jsxs4("div", { style: {
2597
3141
  backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
2598
3142
  borderRadius: "8px",
2599
3143
  animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
2600
3144
  overflow: "hidden",
2601
3145
  ...bStyles.cardFormContainer
2602
3146
  }, children: [
2603
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
2604
- /* @__PURE__ */ jsxs3(
3147
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
3148
+ /* @__PURE__ */ jsxs4(
2605
3149
  "button",
2606
3150
  {
2607
3151
  type: "button",
@@ -2628,7 +3172,7 @@ function InterimButtonsView({
2628
3172
  ...bStyles.backButton
2629
3173
  },
2630
3174
  children: [
2631
- /* @__PURE__ */ jsx5("span", { style: {
3175
+ /* @__PURE__ */ jsx6("span", { style: {
2632
3176
  display: "inline-flex",
2633
3177
  alignItems: "center",
2634
3178
  justifyContent: "center",
@@ -2637,12 +3181,12 @@ function InterimButtonsView({
2637
3181
  borderRadius: "50%",
2638
3182
  backgroundColor: "#f3f4f6",
2639
3183
  ...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 })
3184
+ }, 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" }) }) }),
3185
+ /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
2642
3186
  ]
2643
3187
  }
2644
3188
  ),
2645
- hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
3189
+ hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
2646
3190
  flex: 1,
2647
3191
  textAlign: "center",
2648
3192
  fontWeight: 600,
@@ -2650,15 +3194,15 @@ function InterimButtonsView({
2650
3194
  color: "#262833",
2651
3195
  paddingRight: 80,
2652
3196
  ...bStyles.title
2653
- }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
3197
+ }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
2654
3198
  ] }),
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" } }) })
3199
+ /* @__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" } }) }),
3200
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3201
+ /* @__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" } }) }),
3202
+ /* @__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
3203
  ] }),
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: {
3204
+ /* @__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" } }) }),
3205
+ /* @__PURE__ */ jsx6("div", { style: {
2662
3206
  height: 50,
2663
3207
  borderRadius: 8,
2664
3208
  marginTop: 16,
@@ -2667,7 +3211,7 @@ function InterimButtonsView({
2667
3211
  ...bStyles.submitButton,
2668
3212
  opacity: 0.5
2669
3213
  } }),
2670
- /* @__PURE__ */ jsx5("style", { children: `
3214
+ /* @__PURE__ */ jsx6("style", { children: `
2671
3215
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
2672
3216
  @keyframes flopay-interim-expand {
2673
3217
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -2677,10 +3221,10 @@ function InterimButtonsView({
2677
3221
  ` })
2678
3222
  ] });
2679
3223
  }
2680
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
3224
+ return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2681
3225
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2682
3226
  (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2683
- /* @__PURE__ */ jsx5(
3227
+ /* @__PURE__ */ jsx6(
2684
3228
  "button",
2685
3229
  {
2686
3230
  type: "button",
@@ -2720,10 +3264,10 @@ function InterimButtonsView({
2720
3264
  onMouseUp: (e) => {
2721
3265
  e.currentTarget.style.transform = "scale(1)";
2722
3266
  },
2723
- children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
3267
+ children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
2724
3268
  }
2725
3269
  ),
2726
- errorMessage && /* @__PURE__ */ jsxs3("div", { style: {
3270
+ errorMessage && /* @__PURE__ */ jsxs4("div", { style: {
2727
3271
  margin: "0.25rem 0",
2728
3272
  padding: "0.625rem 0.875rem",
2729
3273
  background: "#FEF2F2",
@@ -2737,21 +3281,21 @@ function InterimButtonsView({
2737
3281
  gap: "0.5rem",
2738
3282
  ...bStyles.errorBanner
2739
3283
  }, 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" }) }),
3284
+ /* @__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
3285
  errorMessage
2742
3286
  ] }),
2743
- /* @__PURE__ */ jsx5("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
3287
+ /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2744
3288
  ] });
2745
3289
  }
2746
3290
 
2747
3291
  // src/checkout-form.tsx
2748
- import { FloPayError as FloPayError4 } from "@flopay/shared";
3292
+ import { FloPayError as FloPayError5 } from "@flopay/shared";
2749
3293
  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";
3294
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2751
3295
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
2752
3296
  var CheckoutForm = forwardRef2(
2753
3297
  function CheckoutForm2(props, ref) {
2754
- return /* @__PURE__ */ jsx6(CheckoutFormInner, { ...props, innerRef: ref });
3298
+ return /* @__PURE__ */ jsx7(CheckoutFormInner, { ...props, innerRef: ref });
2755
3299
  }
2756
3300
  );
2757
3301
  function CheckoutFormInner({
@@ -2777,6 +3321,7 @@ function CheckoutFormInner({
2777
3321
  innerRef
2778
3322
  }) {
2779
3323
  const flopay = useFloPay();
3324
+ const paypalFlopay = usePayPalFloPay();
2780
3325
  const elements = useElements();
2781
3326
  const contextBillingUrl = useBillingApiUrl();
2782
3327
  const [processing, setProcessing] = useState4(false);
@@ -2825,7 +3370,8 @@ function CheckoutFormInner({
2825
3370
  if (response.ok) {
2826
3371
  onComplete?.({
2827
3372
  status: "succeeded",
2828
- paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
3373
+ paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
3374
+ paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
2829
3375
  });
2830
3376
  return;
2831
3377
  }
@@ -2852,7 +3398,8 @@ function CheckoutFormInner({
2852
3398
  await processPaymentInternal({
2853
3399
  id: result.paymentIntentId,
2854
3400
  type: "card",
2855
- threeDSecureActionResultTokenId: result.paymentIntentId
3401
+ threeDSecureActionResultTokenId: result.paymentIntentId,
3402
+ originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
2856
3403
  });
2857
3404
  }
2858
3405
  } finally {
@@ -2861,20 +3408,35 @@ function CheckoutFormInner({
2861
3408
  return;
2862
3409
  }
2863
3410
  if (json?.type === "paypal_redirect_required") {
2864
- const secret = json["clientSecret"];
3411
+ const secret = json["threeDSecureToken"] ?? json["clientSecret"];
2865
3412
  const pmId = json["paymentMethodId"];
2866
- if (flopay && secret) {
2867
- localStorage.setItem(WALLET_RESUME_KEY2, JSON.stringify({
2868
- sessionId,
2869
- paymentIntentId: "",
2870
- tokenType: "card",
2871
- tokenId: pmId,
2872
- status: "pending_redirect"
2873
- }));
2874
- await flopay.confirmPayment({
2875
- clientSecret: secret,
2876
- returnUrl: window.location.href
2877
- });
3413
+ const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
3414
+ if (!paypalStripe || !secret) {
3415
+ const message = "PayPal is not available.";
3416
+ updateError(message);
3417
+ onDecline?.(buildDeclineEvent("paypal", message));
3418
+ return;
3419
+ }
3420
+ localStorage.setItem(WALLET_RESUME_KEY2, JSON.stringify({
3421
+ sessionId,
3422
+ paymentIntentId: "",
3423
+ tokenType: "card",
3424
+ tokenId: pmId ?? "",
3425
+ status: "pending_redirect"
3426
+ }));
3427
+ const confirmParams = {
3428
+ return_url: window.location.href
3429
+ };
3430
+ if (pmId) confirmParams["payment_method"] = pmId;
3431
+ const { error: confirmError } = await paypalStripe.confirmPayment({
3432
+ clientSecret: secret,
3433
+ confirmParams,
3434
+ redirect: "if_required"
3435
+ });
3436
+ if (confirmError) {
3437
+ const message = confirmError.message ?? "PayPal payment failed.";
3438
+ updateError(message);
3439
+ onDecline?.(buildDeclineEvent("paypal", message, { code: confirmError.code }));
2878
3440
  }
2879
3441
  return;
2880
3442
  }
@@ -2890,7 +3452,7 @@ function CheckoutFormInner({
2890
3452
  setProcessing(false);
2891
3453
  }
2892
3454
  },
2893
- [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError, emitDecline]
3455
+ [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
2894
3456
  );
2895
3457
  const dispatchTokenizedBody = useCallback3(
2896
3458
  (tokenizedBody) => {
@@ -2966,7 +3528,7 @@ function CheckoutFormInner({
2966
3528
  return;
2967
3529
  }
2968
3530
  if (!sessionId || !email) {
2969
- throw new FloPayError4("Missing sessionId or email", "validation_error");
3531
+ throw new FloPayError5("Missing sessionId or email", "validation_error");
2970
3532
  }
2971
3533
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2972
3534
  method: "POST",
@@ -2978,10 +3540,10 @@ function CheckoutFormInner({
2978
3540
  isPaypal: false
2979
3541
  })
2980
3542
  });
2981
- if (!intentResponse.ok) throw new FloPayError4("Failed to create payment intent", "api_error");
3543
+ if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
2982
3544
  const intentJson = await intentResponse.json();
2983
3545
  const intentClientSecret = intentJson.data?.id;
2984
- if (!intentClientSecret) throw new FloPayError4("No client_secret in payment intent response", "api_error");
3546
+ if (!intentClientSecret) throw new FloPayError5("No client_secret in payment intent response", "api_error");
2985
3547
  const confirmResult = await flopay.confirmCardPayment({
2986
3548
  clientSecret: intentClientSecret,
2987
3549
  paymentMethodId: pmResult.paymentMethodId
@@ -2995,7 +3557,8 @@ function CheckoutFormInner({
2995
3557
  dispatchTokenizedBody({
2996
3558
  id: pmResult.paymentMethodId,
2997
3559
  type: "card",
2998
- threeDSecureActionResultTokenId: confirmResult.paymentIntentId
3560
+ threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
3561
+ originalPaymentMethodId: pmResult.paymentMethodId
2999
3562
  });
3000
3563
  } catch (err) {
3001
3564
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
@@ -3009,8 +3572,8 @@ function CheckoutFormInner({
3009
3572
  [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
3010
3573
  );
3011
3574
  const isReady = flopay !== null && elements !== null;
3012
- return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3013
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-overlay", style: {
3575
+ return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3576
+ (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-overlay", style: {
3014
3577
  position: "absolute",
3015
3578
  inset: 0,
3016
3579
  background: "rgba(255,255,255,0.7)",
@@ -3019,12 +3582,12 @@ function CheckoutFormInner({
3019
3582
  justifyContent: "center",
3020
3583
  zIndex: 10
3021
3584
  }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
3022
- !isReady && /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
3023
- isReady && /* @__PURE__ */ jsxs4(Fragment4, { children: [
3024
- /* @__PURE__ */ jsx6(PaymentElement, { options: { layout } }),
3025
- showAddress && /* @__PURE__ */ jsx6(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
3026
- displayError && /* @__PURE__ */ jsx6("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
3027
- children ?? /* @__PURE__ */ jsx6(
3585
+ !isReady && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
3586
+ isReady && /* @__PURE__ */ jsxs5(Fragment4, { children: [
3587
+ /* @__PURE__ */ jsx7(PaymentElement, { options: { layout } }),
3588
+ showAddress && /* @__PURE__ */ jsx7(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
3589
+ displayError && /* @__PURE__ */ jsx7("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
3590
+ children ?? /* @__PURE__ */ jsx7(
3028
3591
  "button",
3029
3592
  {
3030
3593
  type: "submit",
@@ -3038,9 +3601,9 @@ function CheckoutFormInner({
3038
3601
  }
3039
3602
 
3040
3603
  // src/paypal-button.tsx
3041
- import { FloPayError as FloPayError5 } from "@flopay/shared";
3604
+ import { FloPayError as FloPayError6 } from "@flopay/shared";
3042
3605
  import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
3043
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3606
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3044
3607
  function PayPalButton({
3045
3608
  sessionId,
3046
3609
  billingApiUrl,
@@ -3158,7 +3721,7 @@ function PayPalButton({
3158
3721
  setSubmitting(true);
3159
3722
  onErrorChange?.(null);
3160
3723
  if (!sessionId || !email) {
3161
- throw new FloPayError5("Missing sessionId or email for PayPal payment", "validation_error");
3724
+ throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
3162
3725
  }
3163
3726
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3164
3727
  method: "POST",
@@ -3170,10 +3733,10 @@ function PayPalButton({
3170
3733
  isPaypal: "true"
3171
3734
  })
3172
3735
  });
3173
- if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
3736
+ if (!intentResponse.ok) throw new FloPayError6("Failed to create payment intent", "api_error");
3174
3737
  const intentJson = await intentResponse.json();
3175
3738
  const intentClientSecret = intentJson.data?.id;
3176
- if (!intentClientSecret) throw new FloPayError5("No client_secret in response", "api_error");
3739
+ if (!intentClientSecret) throw new FloPayError6("No client_secret in response", "api_error");
3177
3740
  const result = await flopay.confirmPayment({
3178
3741
  clientSecret: intentClientSecret,
3179
3742
  returnUrl: window.location.href
@@ -3195,11 +3758,11 @@ function PayPalButton({
3195
3758
  }
3196
3759
  }, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
3197
3760
  if (!flopay || !elements) {
3198
- return /* @__PURE__ */ jsx7("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
3761
+ return /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
3199
3762
  }
3200
- return /* @__PURE__ */ jsxs5(Fragment5, { children: [
3201
- !ready && /* @__PURE__ */ jsx7("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
3202
- /* @__PURE__ */ jsx7("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx7(
3763
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
3764
+ !ready && /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
3765
+ /* @__PURE__ */ jsx8("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx8(
3203
3766
  "button",
3204
3767
  {
3205
3768
  type: "button",
@@ -3221,7 +3784,7 @@ function PayPalButton({
3221
3784
  children: submitting ? "Processing..." : "PayPal"
3222
3785
  }
3223
3786
  ) }),
3224
- (submitting || isProcessing) && /* @__PURE__ */ jsx7("div", { style: {
3787
+ (submitting || isProcessing) && /* @__PURE__ */ jsx8("div", { style: {
3225
3788
  position: "fixed",
3226
3789
  inset: 0,
3227
3790
  background: "rgba(0,0,0,0.4)",
@@ -3229,7 +3792,7 @@ function PayPalButton({
3229
3792
  alignItems: "center",
3230
3793
  justifyContent: "center",
3231
3794
  zIndex: 1e3
3232
- }, children: /* @__PURE__ */ jsx7("div", { style: {
3795
+ }, children: /* @__PURE__ */ jsx8("div", { style: {
3233
3796
  background: "white",
3234
3797
  borderRadius: 8,
3235
3798
  padding: "1.5rem",
@@ -3239,6 +3802,676 @@ function PayPalButton({
3239
3802
  }, children: "Processing PayPal payment..." }) })
3240
3803
  ] });
3241
3804
  }
3805
+
3806
+ // src/automatic-payment-button.tsx
3807
+ import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
3808
+ import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
3809
+ import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
3810
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3811
+ var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
3812
+ var PAYPAL_RESUME_STORAGE_KEY = "flopay_automatic_payment_button_resume";
3813
+ function sleep2(ms) {
3814
+ return new Promise((resolve) => setTimeout(resolve, ms));
3815
+ }
3816
+ function coerceError(err, fallbackMessage) {
3817
+ if (err instanceof FloPayError7) {
3818
+ return err;
3819
+ }
3820
+ return new FloPayError7(
3821
+ err instanceof Error ? err.message : fallbackMessage,
3822
+ "api_error"
3823
+ );
3824
+ }
3825
+ function canUseStorage() {
3826
+ return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
3827
+ }
3828
+ function shouldShowFallbackCheckout(error, sessionId) {
3829
+ if (!sessionId) return false;
3830
+ if (error.type === "validation_error") return false;
3831
+ if (error.checkoutMethod && error.checkoutMethod !== "card") return false;
3832
+ return true;
3833
+ }
3834
+ function readPayPalResumeState() {
3835
+ if (!canUseStorage()) return null;
3836
+ try {
3837
+ const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
3838
+ if (!raw) return null;
3839
+ return JSON.parse(raw);
3840
+ } catch {
3841
+ return null;
3842
+ }
3843
+ }
3844
+ function persistPayPalResumeState(state) {
3845
+ if (!canUseStorage()) return;
3846
+ try {
3847
+ window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
3848
+ } catch (error) {
3849
+ console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
3850
+ }
3851
+ }
3852
+ function clearPayPalResumeState() {
3853
+ if (!canUseStorage()) return;
3854
+ try {
3855
+ window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
3856
+ } catch (error) {
3857
+ console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
3858
+ }
3859
+ }
3860
+ function clearPayPalRedirectParams() {
3861
+ if (typeof window === "undefined") return;
3862
+ const url = new URL(window.location.href);
3863
+ const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
3864
+ let changed = false;
3865
+ for (const key of keys) {
3866
+ if (url.searchParams.has(key)) {
3867
+ url.searchParams.delete(key);
3868
+ changed = true;
3869
+ }
3870
+ }
3871
+ if (changed) {
3872
+ window.history.replaceState({}, "", url.toString());
3873
+ }
3874
+ }
3875
+ function resolveCreateSessionDraft(props) {
3876
+ if (props.createSession) {
3877
+ return {
3878
+ ...props.createSession,
3879
+ checkoutMode: "auto"
3880
+ };
3881
+ }
3882
+ if (!props.clientId || !props.account || !props.successUrl || !props.cancelUrl) {
3883
+ return null;
3884
+ }
3885
+ return {
3886
+ clientId: props.clientId,
3887
+ items: props.items,
3888
+ subscriptions: props.subscriptions,
3889
+ account: props.account,
3890
+ successUrl: props.successUrl,
3891
+ cancelUrl: props.cancelUrl,
3892
+ couponCodes: props.couponCodes,
3893
+ tagsData: props.tagsData,
3894
+ utmMetadata: props.utmMetadata,
3895
+ checkoutMode: "auto"
3896
+ };
3897
+ }
3898
+ function FloPayAutomaticPaymentButton({
3899
+ sessionId,
3900
+ createSession,
3901
+ paymentMethodId,
3902
+ clientId,
3903
+ items,
3904
+ subscriptions,
3905
+ account,
3906
+ successUrl,
3907
+ cancelUrl,
3908
+ couponCodes,
3909
+ tagsData,
3910
+ utmMetadata,
3911
+ billingApiUrl,
3912
+ locale,
3913
+ fallbackPublishableKey,
3914
+ buttonsTheme,
3915
+ buttonsStyles: stylesOverride,
3916
+ onSuccess,
3917
+ onError,
3918
+ onDecline,
3919
+ children,
3920
+ disabled = false,
3921
+ type = "button",
3922
+ style,
3923
+ ...buttonProps
3924
+ }) {
3925
+ const resolvedBillingUrl = useMemo4(
3926
+ () => resolveBillingApiUrl4(billingApiUrl),
3927
+ [billingApiUrl]
3928
+ );
3929
+ const createSessionDraft = useMemo4(
3930
+ () => resolveCreateSessionDraft({
3931
+ createSession,
3932
+ clientId,
3933
+ items,
3934
+ subscriptions,
3935
+ account,
3936
+ successUrl,
3937
+ cancelUrl,
3938
+ couponCodes,
3939
+ tagsData,
3940
+ utmMetadata
3941
+ }),
3942
+ [
3943
+ account,
3944
+ cancelUrl,
3945
+ clientId,
3946
+ couponCodes,
3947
+ createSession,
3948
+ items,
3949
+ subscriptions,
3950
+ successUrl,
3951
+ tagsData,
3952
+ utmMetadata
3953
+ ]
3954
+ );
3955
+ const [isProcessing, setIsProcessing] = useState6(false);
3956
+ const [overlayStatus, setOverlayStatus] = useState6(null);
3957
+ const [overlayError, setOverlayError] = useState6(null);
3958
+ const [fallbackSession, setFallbackSession] = useState6(null);
3959
+ const automaticPaymentToken = useMemo4(
3960
+ () => paymentMethodId ? {
3961
+ id: paymentMethodId,
3962
+ type: "card"
3963
+ } : void 0,
3964
+ [paymentMethodId]
3965
+ );
3966
+ const isMountedRef = useRef5(true);
3967
+ const resumeAttemptedRef = useRef5(false);
3968
+ const fallbackSessionRef = useRef5(fallbackSession);
3969
+ const onSuccessRef = useRef5(onSuccess);
3970
+ const onErrorRef = useRef5(onError);
3971
+ const onDeclineRef = useRef5(onDecline);
3972
+ useEffect7(() => {
3973
+ fallbackSessionRef.current = fallbackSession;
3974
+ }, [fallbackSession]);
3975
+ useEffect7(() => {
3976
+ onSuccessRef.current = onSuccess;
3977
+ }, [onSuccess]);
3978
+ useEffect7(() => {
3979
+ onErrorRef.current = onError;
3980
+ }, [onError]);
3981
+ useEffect7(() => {
3982
+ onDeclineRef.current = onDecline;
3983
+ }, [onDecline]);
3984
+ useEffect7(() => {
3985
+ isMountedRef.current = true;
3986
+ return () => {
3987
+ isMountedRef.current = false;
3988
+ };
3989
+ }, []);
3990
+ useEffect7(() => {
3991
+ if (!fallbackSession || typeof window === "undefined") {
3992
+ return;
3993
+ }
3994
+ const handleKeyDown = (event) => {
3995
+ if (event.key === "Escape") {
3996
+ setFallbackSession(null);
3997
+ }
3998
+ };
3999
+ window.addEventListener("keydown", handleKeyDown);
4000
+ return () => {
4001
+ window.removeEventListener("keydown", handleKeyDown);
4002
+ };
4003
+ }, [fallbackSession]);
4004
+ const emitDecline = useCallback5((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
4005
+ onDeclineRef.current?.(buildDeclineEvent(method, error, {
4006
+ code: error.code,
4007
+ declineCode: error.declineCode
4008
+ }));
4009
+ }, []);
4010
+ const showSuccess = useCallback5(async (event) => {
4011
+ if (!isMountedRef.current) return;
4012
+ setOverlayError(null);
4013
+ setOverlayStatus("success");
4014
+ await sleep2(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
4015
+ if (!isMountedRef.current) return;
4016
+ onSuccessRef.current?.(event);
4017
+ }, []);
4018
+ const showError = useCallback5(async (error, options) => {
4019
+ if (!isMountedRef.current) return;
4020
+ onErrorRef.current?.(error);
4021
+ if (options?.emitDecline) {
4022
+ emitDecline(error, options.method);
4023
+ }
4024
+ setOverlayError(error.message);
4025
+ setOverlayStatus("error");
4026
+ await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
4027
+ }, [emitDecline]);
4028
+ const processResolvedSession = useCallback5(async (apiResult, resolvedSessionId) => {
4029
+ const session = apiResult.data.session ?? null;
4030
+ if (!session) {
4031
+ throw new FloPayError7("No session data returned", "api_error");
4032
+ }
4033
+ if (session.status === "complete") {
4034
+ await showSuccess({
4035
+ result: { status: "succeeded" },
4036
+ session,
4037
+ sessionId: session.id || resolvedSessionId,
4038
+ autoCompleted: false
4039
+ });
4040
+ return;
4041
+ }
4042
+ try {
4043
+ const result = await processSavedPaymentForMode({
4044
+ billingApiUrl: resolvedBillingUrl,
4045
+ sessionId: resolvedSessionId ?? session.id,
4046
+ session,
4047
+ tokenizedData: automaticPaymentToken
4048
+ });
4049
+ let paymentResult = result.type === "success" ? result.result : null;
4050
+ if (result.type !== "success") {
4051
+ const {
4052
+ publishableKey,
4053
+ paypalPublishableKey
4054
+ } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4055
+ if (result.type === "paypal_redirect_required") {
4056
+ persistPayPalResumeState({
4057
+ sessionId: session.id || resolvedSessionId,
4058
+ publishableKey,
4059
+ paypalPublishableKey
4060
+ });
4061
+ }
4062
+ const {
4063
+ flopay,
4064
+ paypalFlopay
4065
+ } = await loadSavedPaymentProviders({
4066
+ publishableKey,
4067
+ paypalPublishableKey,
4068
+ billingApiUrl: resolvedBillingUrl,
4069
+ locale
4070
+ });
4071
+ paymentResult = await handleSavedPaymentRedirectResult(result, {
4072
+ flopay,
4073
+ paypalFlopay,
4074
+ attempt3DS: true,
4075
+ billingApiUrl: resolvedBillingUrl,
4076
+ sessionId: session.id || resolvedSessionId,
4077
+ session
4078
+ });
4079
+ if (result.type === "paypal_redirect_required") {
4080
+ clearPayPalResumeState();
4081
+ }
4082
+ }
4083
+ await showSuccess({
4084
+ result: paymentResult ? {
4085
+ ...paymentResult,
4086
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4087
+ } : {
4088
+ status: "succeeded",
4089
+ paymentMethodId: paymentMethodId ?? void 0
4090
+ },
4091
+ session,
4092
+ sessionId: session.id || resolvedSessionId,
4093
+ autoCompleted: false
4094
+ });
4095
+ } catch (err) {
4096
+ let floPayErr = normalizeSavedPaymentError(err);
4097
+ const fallbackSessionId = session.id || resolvedSessionId;
4098
+ if (floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
4099
+ try {
4100
+ const {
4101
+ publishableKey,
4102
+ paypalPublishableKey
4103
+ } = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
4104
+ const {
4105
+ flopay
4106
+ } = await loadSavedPaymentProviders({
4107
+ publishableKey,
4108
+ paypalPublishableKey,
4109
+ billingApiUrl: resolvedBillingUrl,
4110
+ locale
4111
+ });
4112
+ const paymentResult = await processSavedPaymentWithIntent({
4113
+ billingApiUrl: resolvedBillingUrl,
4114
+ sessionId: fallbackSessionId,
4115
+ session,
4116
+ paymentMethodId: automaticPaymentToken.id,
4117
+ flopay
4118
+ });
4119
+ await showSuccess({
4120
+ result: {
4121
+ ...paymentResult,
4122
+ paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
4123
+ },
4124
+ session,
4125
+ sessionId: fallbackSessionId,
4126
+ autoCompleted: false
4127
+ });
4128
+ return;
4129
+ } catch (intentRecoveryErr) {
4130
+ floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
4131
+ }
4132
+ }
4133
+ await showError(floPayErr, {
4134
+ emitDecline: true,
4135
+ method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
4136
+ });
4137
+ if (shouldShowFallbackCheckout(floPayErr, fallbackSessionId) && fallbackSessionId && isMountedRef.current) {
4138
+ setFallbackSession({
4139
+ sessionId: fallbackSessionId,
4140
+ errorMessage: floPayErr.message
4141
+ });
4142
+ }
4143
+ }
4144
+ }, [
4145
+ fallbackPublishableKey,
4146
+ locale,
4147
+ automaticPaymentToken,
4148
+ paymentMethodId,
4149
+ resolvedBillingUrl,
4150
+ showError,
4151
+ showSuccess
4152
+ ]);
4153
+ const handleButtonClick = useCallback5(async (event) => {
4154
+ buttonProps.onClick?.(event);
4155
+ if (event.defaultPrevented || disabled || isProcessing) {
4156
+ return;
4157
+ }
4158
+ setFallbackSession(null);
4159
+ if (sessionId && createSessionDraft) {
4160
+ const error = new FloPayError7(
4161
+ "Provide either sessionId or create-session props, not both.",
4162
+ "validation_error"
4163
+ );
4164
+ await showError(error);
4165
+ if (isMountedRef.current) {
4166
+ setOverlayStatus(null);
4167
+ setOverlayError(null);
4168
+ }
4169
+ return;
4170
+ }
4171
+ if (!sessionId && !createSessionDraft) {
4172
+ const error = new FloPayError7(
4173
+ "Provide a sessionId or the props required to create an automatic payment session.",
4174
+ "validation_error"
4175
+ );
4176
+ await showError(error);
4177
+ if (isMountedRef.current) {
4178
+ setOverlayStatus(null);
4179
+ setOverlayError(null);
4180
+ }
4181
+ return;
4182
+ }
4183
+ setIsProcessing(true);
4184
+ setOverlayError(null);
4185
+ setOverlayStatus("processing");
4186
+ try {
4187
+ const api = new PaymentAPI3(resolvedBillingUrl);
4188
+ if (sessionId) {
4189
+ const result = await api.getUnifiedCheckoutSession(sessionId);
4190
+ await processResolvedSession(result, sessionId);
4191
+ return;
4192
+ }
4193
+ try {
4194
+ const result = await api.createAndFetchSession(createSessionDraft);
4195
+ await processResolvedSession(result, result.data.session?.id ?? null);
4196
+ } catch (err) {
4197
+ if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
4198
+ await showSuccess({
4199
+ result: { status: "succeeded" },
4200
+ session: null,
4201
+ sessionId: null,
4202
+ autoCompleted: true
4203
+ });
4204
+ return;
4205
+ }
4206
+ throw err;
4207
+ }
4208
+ } catch (err) {
4209
+ await showError(
4210
+ coerceError(err, "Automatic payment failed. Please try again.")
4211
+ );
4212
+ } finally {
4213
+ if (isMountedRef.current) {
4214
+ setOverlayStatus(null);
4215
+ setOverlayError(null);
4216
+ setIsProcessing(false);
4217
+ }
4218
+ }
4219
+ }, [
4220
+ buttonProps,
4221
+ createSessionDraft,
4222
+ disabled,
4223
+ isProcessing,
4224
+ processResolvedSession,
4225
+ resolvedBillingUrl,
4226
+ sessionId,
4227
+ showError,
4228
+ showSuccess
4229
+ ]);
4230
+ const handleFallbackComplete = useCallback5((result) => {
4231
+ const activeFallback = fallbackSessionRef.current;
4232
+ setFallbackSession(null);
4233
+ onSuccessRef.current?.({
4234
+ result,
4235
+ session: null,
4236
+ sessionId: activeFallback?.sessionId ?? null,
4237
+ autoCompleted: false
4238
+ });
4239
+ }, []);
4240
+ const handleFallbackError = useCallback5((error) => {
4241
+ onErrorRef.current?.(error);
4242
+ }, []);
4243
+ const handleFallbackDecline = useCallback5((decline) => {
4244
+ onDeclineRef.current?.(decline);
4245
+ }, []);
4246
+ useEffect7(() => {
4247
+ if (typeof window === "undefined" || resumeAttemptedRef.current) {
4248
+ return;
4249
+ }
4250
+ const params = new URLSearchParams(window.location.search);
4251
+ const clientSecret = params.get("payment_intent_client_secret");
4252
+ if (!clientSecret) {
4253
+ return;
4254
+ }
4255
+ const resumeState = readPayPalResumeState();
4256
+ if (!resumeState) {
4257
+ return;
4258
+ }
4259
+ resumeAttemptedRef.current = true;
4260
+ void (async () => {
4261
+ setIsProcessing(true);
4262
+ setOverlayError(null);
4263
+ setOverlayStatus("processing");
4264
+ try {
4265
+ if (params.get("redirect_status") === "failed") {
4266
+ throw Object.assign(
4267
+ new FloPayError7("PayPal payment was declined. Please try again.", "api_error"),
4268
+ { checkoutMethod: "paypal" }
4269
+ );
4270
+ }
4271
+ const {
4272
+ flopay,
4273
+ paypalFlopay
4274
+ } = await loadSavedPaymentProviders({
4275
+ publishableKey: resumeState.publishableKey,
4276
+ paypalPublishableKey: resumeState.paypalPublishableKey,
4277
+ billingApiUrl: resolvedBillingUrl,
4278
+ locale
4279
+ });
4280
+ const paypalStripe = (paypalFlopay ?? flopay).getRawProvider();
4281
+ if (!paypalStripe) {
4282
+ throw Object.assign(
4283
+ new FloPayError7("PayPal is not available.", "api_error"),
4284
+ { checkoutMethod: "paypal" }
4285
+ );
4286
+ }
4287
+ const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
4288
+ if (error) {
4289
+ throw Object.assign(
4290
+ new FloPayError7(
4291
+ error.message ?? "Failed to retrieve PayPal payment status.",
4292
+ "api_error",
4293
+ { code: error.code }
4294
+ ),
4295
+ { checkoutMethod: "paypal" }
4296
+ );
4297
+ }
4298
+ if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
4299
+ await showSuccess({
4300
+ result: { status: "succeeded", paymentIntentId: paymentIntent.id },
4301
+ session: null,
4302
+ sessionId: resumeState.sessionId,
4303
+ autoCompleted: false
4304
+ });
4305
+ } else {
4306
+ throw Object.assign(
4307
+ new FloPayError7("PayPal payment was not completed. Please try again.", "api_error"),
4308
+ { checkoutMethod: "paypal" }
4309
+ );
4310
+ }
4311
+ } catch (err) {
4312
+ const floPayErr = normalizeSavedPaymentError(err);
4313
+ await showError(floPayErr, {
4314
+ emitDecline: true,
4315
+ method: floPayErr.checkoutMethod ?? "paypal"
4316
+ });
4317
+ } finally {
4318
+ clearPayPalResumeState();
4319
+ clearPayPalRedirectParams();
4320
+ if (isMountedRef.current) {
4321
+ setOverlayStatus(null);
4322
+ setOverlayError(null);
4323
+ setIsProcessing(false);
4324
+ }
4325
+ }
4326
+ })();
4327
+ }, [locale, resolvedBillingUrl, showError, showSuccess]);
4328
+ const bStyles = useMemo4(() => {
4329
+ const base = resolveButtonsLayoutTheme3(buttonsTheme);
4330
+ if (!stylesOverride) return base;
4331
+ return {
4332
+ ...base,
4333
+ ...stylesOverride,
4334
+ cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
4335
+ };
4336
+ }, [buttonsTheme, stylesOverride]);
4337
+ const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
4338
+ return /* @__PURE__ */ jsxs7(Fragment6, { children: [
4339
+ /* @__PURE__ */ jsx9(
4340
+ "button",
4341
+ {
4342
+ ...buttonProps,
4343
+ type,
4344
+ onClick: handleButtonClick,
4345
+ disabled: disabled || isProcessing,
4346
+ "aria-busy": isProcessing,
4347
+ style: {
4348
+ width: "100%",
4349
+ ...cardButtonSizing,
4350
+ backgroundColor: "white",
4351
+ color: "#262833",
4352
+ border: "1px solid #d1d5db",
4353
+ borderRadius: "8px",
4354
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
4355
+ fontWeight: 600,
4356
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
4357
+ display: "flex",
4358
+ alignItems: "center",
4359
+ justifyContent: "center",
4360
+ gap: "0.625rem",
4361
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
4362
+ transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
4363
+ position: "relative",
4364
+ opacity: disabled || isProcessing ? 0.6 : 1,
4365
+ ...bStyles.cardButton,
4366
+ ...style
4367
+ },
4368
+ onMouseDown: (e) => {
4369
+ buttonProps.onMouseDown?.(e);
4370
+ if (!e.defaultPrevented) {
4371
+ e.currentTarget.style.transform = "scale(0.985)";
4372
+ }
4373
+ },
4374
+ onMouseUp: (e) => {
4375
+ buttonProps.onMouseUp?.(e);
4376
+ if (!e.defaultPrevented) {
4377
+ e.currentTarget.style.transform = "scale(1)";
4378
+ }
4379
+ },
4380
+ children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: children })
4381
+ }
4382
+ ),
4383
+ overlayStatus && /* @__PURE__ */ jsx9(
4384
+ ProcessingOverlay,
4385
+ {
4386
+ status: overlayStatus,
4387
+ errorMessage: overlayError
4388
+ }
4389
+ ),
4390
+ fallbackSession && /* @__PURE__ */ jsx9(
4391
+ "div",
4392
+ {
4393
+ "data-testid": "flopay-automatic-payment-fallback",
4394
+ role: "dialog",
4395
+ "aria-modal": "true",
4396
+ onClick: (event) => {
4397
+ if (event.target === event.currentTarget) {
4398
+ setFallbackSession(null);
4399
+ }
4400
+ },
4401
+ style: {
4402
+ position: "fixed",
4403
+ inset: 0,
4404
+ background: "rgba(0, 0, 0, 0.45)",
4405
+ display: "flex",
4406
+ alignItems: "center",
4407
+ justifyContent: "center",
4408
+ padding: "1.5rem",
4409
+ zIndex: 1100
4410
+ },
4411
+ children: /* @__PURE__ */ jsxs7(
4412
+ "div",
4413
+ {
4414
+ style: {
4415
+ width: "100%",
4416
+ maxWidth: 520,
4417
+ maxHeight: "90vh",
4418
+ overflowY: "auto",
4419
+ background: "white",
4420
+ borderRadius: 20,
4421
+ padding: "1.5rem",
4422
+ boxShadow: "0 24px 80px rgba(15, 23, 42, 0.28)",
4423
+ display: "flex",
4424
+ flexDirection: "column",
4425
+ gap: "1rem"
4426
+ },
4427
+ children: [
4428
+ /* @__PURE__ */ jsx9(
4429
+ "div",
4430
+ {
4431
+ style: {
4432
+ display: "flex",
4433
+ gap: "1rem",
4434
+ alignItems: "flex-start"
4435
+ },
4436
+ children: /* @__PURE__ */ jsx9(
4437
+ "div",
4438
+ {
4439
+ style: {
4440
+ flex: 1,
4441
+ background: "#fef2f2",
4442
+ color: "#b91c1c",
4443
+ border: "1px solid #fecaca",
4444
+ borderRadius: "12px",
4445
+ padding: "0.75rem 1rem",
4446
+ fontSize: "0.9rem",
4447
+ lineHeight: 1.5
4448
+ },
4449
+ children: fallbackSession.errorMessage
4450
+ }
4451
+ )
4452
+ }
4453
+ ),
4454
+ /* @__PURE__ */ jsx9(
4455
+ FloPayCheckout,
4456
+ {
4457
+ sessionId: fallbackSession.sessionId,
4458
+ checkoutMode: "full",
4459
+ billingApiUrl: resolvedBillingUrl,
4460
+ fallbackPublishableKey,
4461
+ cardTitleContent: null,
4462
+ showSecurityFooter: false,
4463
+ onComplete: handleFallbackComplete,
4464
+ onError: handleFallbackError,
4465
+ onDecline: handleFallbackDecline
4466
+ }
4467
+ )
4468
+ ]
4469
+ }
4470
+ )
4471
+ }
4472
+ )
4473
+ ] });
4474
+ }
3242
4475
  export {
3243
4476
  AddressElement,
3244
4477
  CardCvcElement,
@@ -3246,6 +4479,7 @@ export {
3246
4479
  CardExpiryElement,
3247
4480
  CardNumberElement,
3248
4481
  CheckoutForm,
4482
+ FloPayAutomaticPaymentButton,
3249
4483
  FloPayCheckout,
3250
4484
  FloPayProvider,
3251
4485
  PayPalButton,