@flopay/react 1.2.8 → 1.3.0

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
@@ -100,7 +100,7 @@ function FloPayProvider({
100
100
  }
101
101
 
102
102
  // src/flopay-checkout.tsx
103
- import React7, { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
103
+ import React8, { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef5, useState as useState4 } from "react";
104
104
  import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
105
105
  import { SDK_VERSION, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2, resolveTheme as resolveTheme2 } from "@flopay/shared";
106
106
 
@@ -248,9 +248,7 @@ import {
248
248
  resolveAVSConfig,
249
249
  isAVSFieldVisible,
250
250
  getStateFromPostalCode,
251
- filterStripeMethodsByCurrency,
252
251
  filterStripeMethodsByCountry,
253
- filterStripeMethodsByAmount,
254
252
  getStripeMethodDisplayName,
255
253
  hasVendoredStripeMethodLogo,
256
254
  needsStripeMethodExplicitConfirm,
@@ -259,7 +257,80 @@ import {
259
257
  stripeExpressMethodToOptionKey
260
258
  } from "@flopay/shared";
261
259
  import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
262
- import React6, { forwardRef, useCallback, useContext as useContext3, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
260
+
261
+ // src/vault-card-fields.tsx
262
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
263
+ import { jsx as jsx4 } from "react/jsx-runtime";
264
+ function VaultCardFields({
265
+ capture,
266
+ html,
267
+ messageToken,
268
+ expectedOrigin,
269
+ theme,
270
+ containerStyle,
271
+ onReady,
272
+ onError,
273
+ onValidation
274
+ }) {
275
+ const containerRef = useRef2(null);
276
+ const onReadyRef = useRef2(onReady);
277
+ const onErrorRef = useRef2(onError);
278
+ const onValidationRef = useRef2(onValidation);
279
+ onReadyRef.current = onReady;
280
+ onErrorRef.current = onError;
281
+ onValidationRef.current = onValidation;
282
+ const themeRef = useRef2(theme);
283
+ themeRef.current = theme;
284
+ useEffect3(() => {
285
+ const el = containerRef.current;
286
+ if (!el) return;
287
+ let active = true;
288
+ let readyEmitted = false;
289
+ const emitReadyOnce = () => {
290
+ if (!active || readyEmitted) return;
291
+ readyEmitted = true;
292
+ onReadyRef.current?.();
293
+ };
294
+ const offReady = capture.on("ready", () => {
295
+ emitReadyOnce();
296
+ });
297
+ const offError = capture.on("error", (event) => {
298
+ onErrorRef.current?.(event.message ?? "There was a problem loading the secure card form.");
299
+ });
300
+ const offValidation = capture.on("validation", (event) => {
301
+ onValidationRef.current?.(event.message ?? null);
302
+ });
303
+ const mountOptions = {
304
+ html,
305
+ ...messageToken ? { messageToken } : {},
306
+ ...expectedOrigin ? { expectedOrigin } : {},
307
+ ...themeRef.current ? { theme: themeRef.current } : {}
308
+ };
309
+ capture.mount(el, mountOptions).then(() => {
310
+ emitReadyOnce();
311
+ }).catch((err) => {
312
+ if (active) {
313
+ onErrorRef.current?.(
314
+ err instanceof Error ? err.message : "Failed to load the secure card form."
315
+ );
316
+ }
317
+ });
318
+ return () => {
319
+ active = false;
320
+ offReady();
321
+ offError();
322
+ offValidation();
323
+ capture.unmount();
324
+ };
325
+ }, [capture, html, messageToken, expectedOrigin]);
326
+ useEffect3(() => {
327
+ if (theme) capture.applyTheme?.(theme);
328
+ }, [capture, theme]);
329
+ return /* @__PURE__ */ jsx4("div", { ref: containerRef, "data-testid": "flopay-vault-card-fields", style: containerStyle });
330
+ }
331
+
332
+ // src/split-card-form.tsx
333
+ import React7, { forwardRef, useCallback, useContext as useContext3, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
263
334
 
264
335
  // src/hooks.ts
265
336
  import { useContext as useContext2 } from "react";
@@ -286,14 +357,14 @@ function useBillingApiUrl() {
286
357
 
287
358
  // src/processing-overlay.tsx
288
359
  import "react";
289
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
360
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
290
361
  var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
291
362
  var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
292
363
  function ProcessingOverlay({
293
364
  status,
294
365
  errorMessage
295
366
  }) {
296
- return /* @__PURE__ */ jsx4(
367
+ return /* @__PURE__ */ jsx5(
297
368
  "div",
298
369
  {
299
370
  "data-testid": "flopay-processing-overlay",
@@ -337,14 +408,14 @@ function ProcessingOverlay({
337
408
  xmlns: "http://www.w3.org/2000/svg",
338
409
  style: { animation: "flopay-spin 0.8s linear infinite" },
339
410
  children: [
340
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
341
- /* @__PURE__ */ jsx4("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
411
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
412
+ /* @__PURE__ */ jsx5("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
342
413
  ]
343
414
  }
344
415
  ),
345
- 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: [
346
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
347
- /* @__PURE__ */ jsx4(
416
+ status === "success" && /* @__PURE__ */ jsx5("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: [
417
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
418
+ /* @__PURE__ */ jsx5(
348
419
  "path",
349
420
  {
350
421
  d: "M7 12.5l3 3 7-7",
@@ -360,9 +431,9 @@ function ProcessingOverlay({
360
431
  }
361
432
  )
362
433
  ] }) }),
363
- 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: [
364
- /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
365
- /* @__PURE__ */ jsx4(
434
+ status === "error" && /* @__PURE__ */ jsx5("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: [
435
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
436
+ /* @__PURE__ */ jsx5(
366
437
  "path",
367
438
  {
368
439
  d: "M8 8l8 8M16 8l-8 8",
@@ -394,7 +465,7 @@ function ProcessingOverlay({
394
465
  ]
395
466
  }
396
467
  ),
397
- status === "success" && /* @__PURE__ */ jsx4(
468
+ status === "success" && /* @__PURE__ */ jsx5(
398
469
  "p",
399
470
  {
400
471
  style: {
@@ -408,7 +479,7 @@ function ProcessingOverlay({
408
479
  children: "You will be automatically redirected, do not close or navigate away from this window."
409
480
  }
410
481
  ),
411
- status === "error" && errorMessage && /* @__PURE__ */ jsx4(
482
+ status === "error" && errorMessage && /* @__PURE__ */ jsx5(
412
483
  "p",
413
484
  {
414
485
  style: {
@@ -422,7 +493,7 @@ function ProcessingOverlay({
422
493
  children: errorMessage
423
494
  }
424
495
  ),
425
- /* @__PURE__ */ jsx4("style", { children: `
496
+ /* @__PURE__ */ jsx5("style", { children: `
426
497
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
427
498
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
428
499
  @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
@@ -696,11 +767,11 @@ function isInAppBrowser(userAgent) {
696
767
  }
697
768
 
698
769
  // src/direct-paypal-button.tsx
699
- import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
770
+ import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState2 } from "react";
700
771
  import { loadScript } from "@paypal/paypal-js";
701
772
  import { PaymentAPI } from "@flopay/js";
702
773
  import { FloPayError as FloPayError2, normalizeGatewayEnvironment } from "@flopay/shared";
703
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
774
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
704
775
  var DEFAULT_BUTTON_HEIGHT = 45;
705
776
  function DirectPayPalButton({
706
777
  sessionId,
@@ -723,7 +794,7 @@ function DirectPayPalButton({
723
794
  existingOrderId,
724
795
  debug = false
725
796
  }) {
726
- const containerRef = useRef2(null);
797
+ const containerRef = useRef3(null);
727
798
  const [ready, setReady] = useState2(false);
728
799
  const [failed, setFailed] = useState2(false);
729
800
  const [submitting, setSubmitting] = useState2(false);
@@ -733,52 +804,52 @@ function DirectPayPalButton({
733
804
  if (!debug) return;
734
805
  setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
735
806
  };
736
- const onTokenizedBodyRef = useRef2(onTokenizedBody);
737
- const onCompleteRef = useRef2(onComplete);
738
- const onErrorChangeRef = useRef2(onErrorChange);
739
- const onDeclineRef = useRef2(onDecline);
740
- const onButtonClickRef = useRef2(onButtonClick);
741
- const onLoadStateChangeRef = useRef2(onLoadStateChange);
742
- const runBeforeButtonClickRef = useRef2(runBeforeButtonClick);
743
- const sessionRef = useRef2(session);
744
- const emailRef = useRef2(email);
745
- const nonceRef = useRef2(nonce);
746
- const beforeClickRef = useRef2(null);
747
- useEffect3(() => {
807
+ const onTokenizedBodyRef = useRef3(onTokenizedBody);
808
+ const onCompleteRef = useRef3(onComplete);
809
+ const onErrorChangeRef = useRef3(onErrorChange);
810
+ const onDeclineRef = useRef3(onDecline);
811
+ const onButtonClickRef = useRef3(onButtonClick);
812
+ const onLoadStateChangeRef = useRef3(onLoadStateChange);
813
+ const runBeforeButtonClickRef = useRef3(runBeforeButtonClick);
814
+ const sessionRef = useRef3(session);
815
+ const emailRef = useRef3(email);
816
+ const nonceRef = useRef3(nonce);
817
+ const beforeClickRef = useRef3(null);
818
+ useEffect4(() => {
748
819
  onTokenizedBodyRef.current = onTokenizedBody;
749
820
  }, [onTokenizedBody]);
750
- useEffect3(() => {
821
+ useEffect4(() => {
751
822
  onCompleteRef.current = onComplete;
752
823
  }, [onComplete]);
753
- useEffect3(() => {
824
+ useEffect4(() => {
754
825
  onErrorChangeRef.current = onErrorChange;
755
826
  }, [onErrorChange]);
756
- useEffect3(() => {
827
+ useEffect4(() => {
757
828
  onDeclineRef.current = onDecline;
758
829
  }, [onDecline]);
759
- useEffect3(() => {
830
+ useEffect4(() => {
760
831
  onButtonClickRef.current = onButtonClick;
761
832
  }, [onButtonClick]);
762
- useEffect3(() => {
833
+ useEffect4(() => {
763
834
  onLoadStateChangeRef.current = onLoadStateChange;
764
835
  }, [onLoadStateChange]);
765
- useEffect3(() => {
836
+ useEffect4(() => {
766
837
  runBeforeButtonClickRef.current = runBeforeButtonClick;
767
838
  }, [runBeforeButtonClick]);
768
- useEffect3(() => {
839
+ useEffect4(() => {
769
840
  sessionRef.current = session;
770
841
  }, [session]);
771
- useEffect3(() => {
842
+ useEffect4(() => {
772
843
  emailRef.current = email;
773
844
  }, [email]);
774
- useEffect3(() => {
845
+ useEffect4(() => {
775
846
  nonceRef.current = nonce;
776
847
  }, [nonce]);
777
- useEffect3(() => {
848
+ useEffect4(() => {
778
849
  onLoadStateChangeRef.current?.(ready && !failed);
779
850
  }, [ready, failed]);
780
851
  const normalizedEnv = normalizeGatewayEnvironment(environment);
781
- useEffect3(() => {
852
+ useEffect4(() => {
782
853
  const maskedClient = clientId ? `${clientId.slice(0, 6)}\u2026(len ${clientId.length})` : "(empty)";
783
854
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "(no navigator)";
784
855
  appendDebug(`mount clientId=${maskedClient} env=${environment ?? "(unset)"}\u2192${normalizedEnv ?? "live"} ccy=${currency} sub=${isSubscription}`);
@@ -1215,7 +1286,7 @@ ${debugLines.join("\n")}`
1215
1286
  }
1216
1287
  ) : null;
1217
1288
  if (failed) {
1218
- return debug ? /* @__PURE__ */ jsx5("div", { children: debugPanel }) : null;
1289
+ return debug ? /* @__PURE__ */ jsx6("div", { children: debugPanel }) : null;
1219
1290
  }
1220
1291
  return (
1221
1292
  // Single wrapper so the parent flex container sees exactly one flex item
@@ -1225,7 +1296,7 @@ ${debugLines.join("\n")}`
1225
1296
  /* @__PURE__ */ jsxs3("div", { children: [
1226
1297
  debugPanel,
1227
1298
  /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT }, children: [
1228
- !ready && /* @__PURE__ */ jsx5(
1299
+ !ready && /* @__PURE__ */ jsx6(
1229
1300
  "div",
1230
1301
  {
1231
1302
  "data-testid": "flopay-direct-paypal-placeholder",
@@ -1239,7 +1310,7 @@ ${debugLines.join("\n")}`
1239
1310
  }
1240
1311
  }
1241
1312
  ),
1242
- /* @__PURE__ */ jsx5(
1313
+ /* @__PURE__ */ jsx6(
1243
1314
  "div",
1244
1315
  {
1245
1316
  ref: containerRef,
@@ -1259,10 +1330,28 @@ ${debugLines.join("\n")}`
1259
1330
 
1260
1331
  // src/split-card-form.tsx
1261
1332
  import { FloPayError as FloPayError3, isSetupIntentClientSecret as isSetupIntentClientSecret2, resolveTheme } from "@flopay/shared";
1262
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1333
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1263
1334
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1264
1335
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1265
1336
  var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
1337
+ function toVaultMount(block) {
1338
+ if (!block?.html) return null;
1339
+ return {
1340
+ html: block.html,
1341
+ ...block.messageToken ? { messageToken: block.messageToken } : {},
1342
+ ...block.expectedOrigin ? { expectedOrigin: block.expectedOrigin } : {}
1343
+ };
1344
+ }
1345
+ function darkenHex(hex, amount = 0.12) {
1346
+ const match = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim());
1347
+ if (!match) return hex;
1348
+ const value = parseInt(match[1], 16);
1349
+ const scale = Math.max(0, Math.min(1, 1 - amount));
1350
+ const r = Math.round((value >> 16 & 255) * scale);
1351
+ const g = Math.round((value >> 8 & 255) * scale);
1352
+ const b = Math.round((value & 255) * scale);
1353
+ return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)}`;
1354
+ }
1266
1355
  var FLOPAY_KEYFRAMES = `
1267
1356
  .paypal-buttons { margin: 0 !important; vertical-align: top !important; }
1268
1357
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
@@ -1287,18 +1376,20 @@ var FLOPAY_KEYFRAMES = `
1287
1376
  `;
1288
1377
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT = 44;
1289
1378
  function derivePrimaryTileStyle(opts) {
1290
- if (!opts.themeBundle) {
1379
+ if (!opts.themeBundle && !opts.explicitPrimaryColor) {
1291
1380
  return {
1292
1381
  backgroundColor: "white",
1293
1382
  color: "#262833",
1294
1383
  border: "1px solid #d1d5db",
1295
- borderRadius: "8px",
1384
+ borderRadius: opts.resolvedBorderRadius,
1296
1385
  boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
1297
1386
  };
1298
1387
  }
1299
- const submit = opts.submitButtonStyle ?? {};
1388
+ const submit = opts.themeBundle ? opts.submitButtonStyle ?? {} : {};
1300
1389
  return {
1301
- backgroundColor: submit.backgroundColor ?? opts.resolvedPrimaryColor,
1390
+ // `colorPrimary` wins so a per-checkout override re-skins the card / auto-pay
1391
+ // buttons in lock-step with the submit CTA (which also keys off it).
1392
+ backgroundColor: opts.resolvedPrimaryColor,
1302
1393
  color: submit.color ?? "white",
1303
1394
  border: submit.border ?? "none",
1304
1395
  borderRadius: submit.borderRadius ?? opts.resolvedBorderRadius,
@@ -1332,7 +1423,7 @@ function normalizeBeforeButtonClickError(method, err) {
1332
1423
  );
1333
1424
  }
1334
1425
  function FloPayKeyframes() {
1335
- return /* @__PURE__ */ jsx6("style", { children: FLOPAY_KEYFRAMES });
1426
+ return /* @__PURE__ */ jsx7("style", { children: FLOPAY_KEYFRAMES });
1336
1427
  }
1337
1428
  function toCssSize(value) {
1338
1429
  if (typeof value === "number") return `${value}px`;
@@ -1355,7 +1446,7 @@ function ExpressCheckoutReadySwap({
1355
1446
  }) {
1356
1447
  if (state === "unavailable" || state === "load_error") return null;
1357
1448
  return /* @__PURE__ */ jsxs4("div", { style: { position: "relative", minHeight: 44 }, children: [
1358
- /* @__PURE__ */ jsx6(
1449
+ /* @__PURE__ */ jsx7(
1359
1450
  "div",
1360
1451
  {
1361
1452
  "data-testid": placeholderTestId,
@@ -1374,7 +1465,7 @@ function ExpressCheckoutReadySwap({
1374
1465
  }
1375
1466
  }
1376
1467
  ),
1377
- /* @__PURE__ */ jsx6(
1468
+ /* @__PURE__ */ jsx7(
1378
1469
  "div",
1379
1470
  {
1380
1471
  style: {
@@ -1394,7 +1485,7 @@ function isExpressCheckoutRowVisible(state) {
1394
1485
  }
1395
1486
  var SplitCardForm = forwardRef(
1396
1487
  function SplitCardForm2(props, ref) {
1397
- return /* @__PURE__ */ jsx6(SplitCardFormInner, { ...props, innerRef: ref });
1488
+ return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props, innerRef: ref });
1398
1489
  }
1399
1490
  );
1400
1491
  function PayPalButtonInner({
@@ -1414,14 +1505,14 @@ function PayPalButtonInner({
1414
1505
  const stripe = useStripeRaw();
1415
1506
  const elements = useStripeElements();
1416
1507
  const [loadState, setLoadState] = useState3("loading");
1417
- useEffect4(() => {
1508
+ useEffect5(() => {
1418
1509
  onLoadStateChange?.(loadState);
1419
1510
  }, [loadState, onLoadStateChange]);
1420
1511
  const [submitting, setSubmitting] = useState3(false);
1421
- const paypalResumeAttempted = useRef3(false);
1422
- const beforeClickRef = useRef3(null);
1512
+ const paypalResumeAttempted = useRef4(false);
1513
+ const beforeClickRef = useRef4(null);
1423
1514
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
1424
- useEffect4(() => {
1515
+ useEffect5(() => {
1425
1516
  if (!stripe || paypalResumeAttempted.current) return;
1426
1517
  const params = new URLSearchParams(window.location.search);
1427
1518
  const paymentIntentId = params.get("payment_intent");
@@ -1627,13 +1718,13 @@ function PayPalButtonInner({
1627
1718
  }
1628
1719
  }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
1629
1720
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1630
- /* @__PURE__ */ jsx6(
1721
+ /* @__PURE__ */ jsx7(
1631
1722
  ExpressCheckoutReadySwap,
1632
1723
  {
1633
1724
  state: loadState,
1634
1725
  placeholderTestId: "flopay-paypal-placeholder",
1635
1726
  borderRadius: placeholderBorderRadius,
1636
- children: /* @__PURE__ */ jsx6(
1727
+ children: /* @__PURE__ */ jsx7(
1637
1728
  ExpressCheckoutElement,
1638
1729
  {
1639
1730
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -1660,7 +1751,7 @@ function PayPalButtonInner({
1660
1751
  )
1661
1752
  }
1662
1753
  ),
1663
- submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
1754
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
1664
1755
  ] });
1665
1756
  }
1666
1757
  function WalletButtonInner({
@@ -1680,13 +1771,13 @@ function WalletButtonInner({
1680
1771
  const stripe = useStripeRaw();
1681
1772
  const elements = useStripeElements();
1682
1773
  const [loadState, setLoadState] = useState3("loading");
1683
- useEffect4(() => {
1774
+ useEffect5(() => {
1684
1775
  onLoadStateChange?.(loadState);
1685
1776
  }, [loadState, onLoadStateChange]);
1686
1777
  const [submitting, setSubmitting] = useState3(false);
1687
1778
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
1688
- const lastWalletMethodRef = useRef3("card");
1689
- const beforeClickRef = useRef3(null);
1779
+ const lastWalletMethodRef = useRef4("card");
1780
+ const beforeClickRef = useRef4(null);
1690
1781
  const handleWalletConfirm = useCallback(
1691
1782
  async (event) => {
1692
1783
  if (!stripe || !elements) return;
@@ -1796,13 +1887,13 @@ function WalletButtonInner({
1796
1887
  [expressMethods]
1797
1888
  );
1798
1889
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1799
- /* @__PURE__ */ jsx6(
1890
+ /* @__PURE__ */ jsx7(
1800
1891
  ExpressCheckoutReadySwap,
1801
1892
  {
1802
1893
  state: loadState,
1803
1894
  placeholderTestId: "flopay-wallet-placeholder",
1804
1895
  borderRadius: placeholderBorderRadius,
1805
- children: /* @__PURE__ */ jsx6(
1896
+ children: /* @__PURE__ */ jsx7(
1806
1897
  ExpressCheckoutElement,
1807
1898
  {
1808
1899
  onReady: (event) => {
@@ -1841,7 +1932,7 @@ function WalletButtonInner({
1841
1932
  )
1842
1933
  }
1843
1934
  ),
1844
- submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
1935
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
1845
1936
  ] });
1846
1937
  }
1847
1938
  function StripeMethodButton({
@@ -1862,7 +1953,7 @@ function StripeMethodButton({
1862
1953
  const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
1863
1954
  const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
1864
1955
  const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
1865
- return /* @__PURE__ */ jsx6(
1956
+ return /* @__PURE__ */ jsx7(
1866
1957
  "button",
1867
1958
  {
1868
1959
  type: "button",
@@ -1909,7 +2000,7 @@ function StripeMethodButton({
1909
2000
  // Pulse-skeleton parity with the wallet ECE: no spinner, just the
1910
2001
  // status text on top of the pulsing background. Keeps the loading
1911
2002
  // affordance shape-equivalent across both kinds of tile.
1912
- /* @__PURE__ */ jsx6("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
2003
+ /* @__PURE__ */ jsx7("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
1913
2004
  ) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
1914
2005
  /* @__PURE__ */ jsxs4(
1915
2006
  "span",
@@ -1924,7 +2015,7 @@ function StripeMethodButton({
1924
2015
  gap: hasVendoredStripeMethodLogo(method) ? 8 : 0
1925
2016
  },
1926
2017
  children: [
1927
- brand?.logoSvg && /* @__PURE__ */ jsx6(
2018
+ brand?.logoSvg && /* @__PURE__ */ jsx7(
1928
2019
  "span",
1929
2020
  {
1930
2021
  "aria-hidden": "true",
@@ -1943,11 +2034,11 @@ function StripeMethodButton({
1943
2034
  dangerouslySetInnerHTML: { __html: brand.logoSvg }
1944
2035
  }
1945
2036
  ),
1946
- /* @__PURE__ */ jsx6("span", { children: getStripeMethodDisplayName(method) })
2037
+ /* @__PURE__ */ jsx7("span", { children: getStripeMethodDisplayName(method) })
1947
2038
  ]
1948
2039
  }
1949
2040
  ),
1950
- hasNextStep && /* @__PURE__ */ jsx6(
2041
+ hasNextStep && /* @__PURE__ */ jsx7(
1951
2042
  "svg",
1952
2043
  {
1953
2044
  width: "14",
@@ -1967,7 +2058,7 @@ function StripeMethodButton({
1967
2058
  opacity: 0.7
1968
2059
  },
1969
2060
  "aria-hidden": "true",
1970
- children: /* @__PURE__ */ jsx6("path", { d: "M9 18l6-6-6-6" })
2061
+ children: /* @__PURE__ */ jsx7("path", { d: "M9 18l6-6-6-6" })
1971
2062
  }
1972
2063
  )
1973
2064
  ] })
@@ -1990,12 +2081,13 @@ function StripeMethodInlineForm({
1990
2081
  isProcessing,
1991
2082
  submitButtonColor,
1992
2083
  submitButtonBorderRadius,
1993
- submitButtonStyle
2084
+ submitButtonStyle,
2085
+ errorText
1994
2086
  }) {
1995
2087
  const stripe = useStripeRaw();
1996
2088
  const elements = useStripeElements();
1997
2089
  const [submitting, setSubmitting] = useState3(false);
1998
- const submittingRef = useRef3(false);
2090
+ const submittingRef = useRef4(false);
1999
2091
  const [isMethodComplete, setIsMethodComplete] = useState3(false);
2000
2092
  const [loadState, setLoadState] = useState3("loading");
2001
2093
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
@@ -2140,7 +2232,7 @@ function StripeMethodInlineForm({
2140
2232
  ]);
2141
2233
  void onCancel;
2142
2234
  return /* @__PURE__ */ jsxs4("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
2143
- /* @__PURE__ */ jsx6(
2235
+ /* @__PURE__ */ jsx7(
2144
2236
  PaymentElement2,
2145
2237
  {
2146
2238
  onReady: () => setLoadState("ready"),
@@ -2160,7 +2252,7 @@ function StripeMethodInlineForm({
2160
2252
  }
2161
2253
  }
2162
2254
  ),
2163
- /* @__PURE__ */ jsx6(
2255
+ /* @__PURE__ */ jsx7(
2164
2256
  "button",
2165
2257
  {
2166
2258
  type: "button",
@@ -2186,7 +2278,23 @@ function StripeMethodInlineForm({
2186
2278
  },
2187
2279
  children: submitting ? "Processing\u2026" : `Pay with ${getStripeMethodDisplayName(method)}`
2188
2280
  }
2189
- )
2281
+ ),
2282
+ errorText && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
2283
+ margin: "0.75rem 0 0",
2284
+ padding: "0.625rem 0.875rem",
2285
+ background: "#FEF2F2",
2286
+ border: "1px solid #FECACA",
2287
+ borderRadius: "8px",
2288
+ color: "#991B1B",
2289
+ fontSize: "0.85rem",
2290
+ fontWeight: 600,
2291
+ display: "flex",
2292
+ alignItems: "center",
2293
+ gap: "0.5rem"
2294
+ }, children: [
2295
+ /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("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" }) }),
2296
+ errorText
2297
+ ] })
2190
2298
  ] });
2191
2299
  }
2192
2300
  function StripePaymentElementInner({
@@ -2216,8 +2324,8 @@ function StripePaymentElementInner({
2216
2324
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
2217
2325
  const [expandedMethod, setExpandedMethod] = useState3(null);
2218
2326
  const [submittingMethod, setSubmittingMethod] = useState3(null);
2219
- const submittingRef = useRef3(false);
2220
- useEffect4(() => {
2327
+ const submittingRef = useRef4(false);
2328
+ useEffect5(() => {
2221
2329
  if (paymentElementMethods.length > 0 && stripeInstance) {
2222
2330
  onLoadStateChange?.("ready");
2223
2331
  } else if (!stripeInstance) {
@@ -2385,7 +2493,7 @@ function StripePaymentElementInner({
2385
2493
  paymentMethodTypes: [localExpandedMethod]
2386
2494
  };
2387
2495
  }, [localExpandedMethod, paymentElementBaseOptions]);
2388
- return /* @__PURE__ */ jsx6(
2496
+ return /* @__PURE__ */ jsx7(
2389
2497
  "div",
2390
2498
  {
2391
2499
  "data-testid": "flopay-stripe-payment-element-region",
@@ -2394,8 +2502,8 @@ function StripePaymentElementInner({
2394
2502
  const isExpanded = expandedApmMethod === method;
2395
2503
  const isSubmittingThis = submittingMethod === method;
2396
2504
  const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
2397
- return /* @__PURE__ */ jsxs4(React6.Fragment, { children: [
2398
- /* @__PURE__ */ jsx6(
2505
+ return /* @__PURE__ */ jsxs4(React7.Fragment, { children: [
2506
+ /* @__PURE__ */ jsx7(
2399
2507
  StripeMethodButton,
2400
2508
  {
2401
2509
  method,
@@ -2412,12 +2520,12 @@ function StripePaymentElementInner({
2412
2520
  fontFamily: buttonAppearance?.fontFamily
2413
2521
  }
2414
2522
  ),
2415
- !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx6(
2523
+ !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx7(
2416
2524
  StripeElements,
2417
2525
  {
2418
2526
  stripe: stripeInstance,
2419
2527
  options: localInlineElementsOptions,
2420
- children: /* @__PURE__ */ jsx6(
2528
+ children: /* @__PURE__ */ jsx7(
2421
2529
  StripeMethodInlineForm,
2422
2530
  {
2423
2531
  method,
@@ -2470,6 +2578,7 @@ function SplitCardFormInner({
2470
2578
  showPayPal = true,
2471
2579
  showStripe = true,
2472
2580
  enabledPaymentMethods,
2581
+ enabledPaymentMethodCountries,
2473
2582
  showApplePay = true,
2474
2583
  showGooglePay = true,
2475
2584
  layout = "default",
@@ -2484,6 +2593,8 @@ function SplitCardFormInner({
2484
2593
  onButtonClick,
2485
2594
  onBeforeButtonClick,
2486
2595
  enableAVS: enableAVSProp,
2596
+ cardFieldOrder,
2597
+ cardPreFormSlot,
2487
2598
  avsLayout: avsLayoutProp = "row",
2488
2599
  country: countryProp,
2489
2600
  zip: zipProp,
@@ -2520,14 +2631,41 @@ function SplitCardFormInner({
2520
2631
  const [city, setCity] = useState3(cityProp ?? "");
2521
2632
  const [stateValue, setStateValue] = useState3(stateProp ?? "");
2522
2633
  const [accountPatch, setAccountPatch] = useState3({});
2523
- const zipCodeRef = useRef3(zipProp ?? "");
2524
- const selectedCountryRef = useRef3(countryProp ?? "US");
2525
- const addressLine1Ref = useRef3(addressLine1Prop ?? "");
2526
- const addressLine2Ref = useRef3(addressLine2Prop ?? "");
2527
- const cityRef = useRef3(cityProp ?? "");
2528
- const stateRef = useRef3(stateProp ?? "");
2634
+ const zipCodeRef = useRef4(zipProp ?? "");
2635
+ const selectedCountryRef = useRef4(countryProp ?? "US");
2636
+ const addressLine1Ref = useRef4(addressLine1Prop ?? "");
2637
+ const addressLine2Ref = useRef4(addressLine2Prop ?? "");
2638
+ const cityRef = useRef4(cityProp ?? "");
2639
+ const stateRef = useRef4(stateProp ?? "");
2529
2640
  const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
2530
2641
  const enableAVS = avsConfig !== null;
2642
+ const vaultBlockReady = Boolean(session?.vault?.html);
2643
+ const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
2644
+ const vaultActive = Boolean(
2645
+ showStripe && (vaultBlockReady || vaultGatewayAdvertised) && flopay && sessionId
2646
+ );
2647
+ const cardCapture = useMemo3(() => {
2648
+ if (!flopay || !vaultActive || !sessionId) return null;
2649
+ return flopay.cardCapture({ sessionId });
2650
+ }, [flopay, vaultActive, sessionId]);
2651
+ const { avsInvalid, invalidAvsFields } = useMemo3(() => {
2652
+ const none = { line1: false, city: false, state: false, zip: false };
2653
+ if (!vaultActive || !avsConfig) return { avsInvalid: false, invalidAvsFields: none };
2654
+ const cc = selectedCountry;
2655
+ const isEmpty = (field, value) => isAVSFieldVisible(field, cc) && !value.trim();
2656
+ const invalidAvsFields2 = {
2657
+ line1: isEmpty(avsConfig.address_line_1, addressLine1),
2658
+ city: isEmpty(avsConfig.city, city),
2659
+ state: isEmpty(avsConfig.state, stateValue),
2660
+ zip: isEmpty(avsConfig.postal_code, zipCode)
2661
+ };
2662
+ const avsInvalid2 = invalidAvsFields2.line1 || invalidAvsFields2.city || invalidAvsFields2.state || invalidAvsFields2.zip;
2663
+ return { avsInvalid: avsInvalid2, invalidAvsFields: invalidAvsFields2 };
2664
+ }, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue, zipCode]);
2665
+ const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState3(false);
2666
+ const [vaultMount, setVaultMount] = useState3(
2667
+ () => toVaultMount(session?.vault)
2668
+ );
2531
2669
  const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
2532
2670
  const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
2533
2671
  const showCardForm = viewState === "expanding" || viewState === "card";
@@ -2552,7 +2690,7 @@ function SplitCardFormInner({
2552
2690
  setExpandedApmMethod(null);
2553
2691
  }, TRANSITION_MS);
2554
2692
  }, []);
2555
- useEffect4(() => {
2693
+ useEffect5(() => {
2556
2694
  if (layout === "buttons" && initialCardOpen) {
2557
2695
  setViewState("card");
2558
2696
  }
@@ -2560,10 +2698,10 @@ function SplitCardFormInner({
2560
2698
  const [fullName, setFullName] = useState3("");
2561
2699
  const [formReady, setFormReady] = useState3(false);
2562
2700
  const [overlayStatus, setOverlayStatus] = useState3(null);
2563
- const processingRef = useRef3(false);
2701
+ const processingRef = useRef4(false);
2564
2702
  const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
2565
- const paypalDirectRetryRef = useRef3(paypalDirectRetry);
2566
- useEffect4(() => {
2703
+ const paypalDirectRetryRef = useRef4(paypalDirectRetry);
2704
+ useEffect5(() => {
2567
2705
  paypalDirectRetryRef.current = paypalDirectRetry;
2568
2706
  }, [paypalDirectRetry]);
2569
2707
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
@@ -2584,6 +2722,31 @@ function SplitCardFormInner({
2584
2722
  };
2585
2723
  }, [themeBundle, buttonsTheme, buttonsStylesOverride]);
2586
2724
  const appearance = appearanceOverride ?? themeBundle?.appearance;
2725
+ const vaultThemeColors = useMemo3(() => {
2726
+ const vars = appearance?.variables;
2727
+ const asString = (value) => typeof value === "string" && value ? value : void 0;
2728
+ const submitButtonStyle = bStyles.submitButton;
2729
+ const primary = asString(vars?.colorPrimary) ?? asString(submitButtonStyle?.backgroundColor) ?? "#4A49FF";
2730
+ const isButtonsLayout = layout === "buttons";
2731
+ const nameInput = bStyles.nameInput;
2732
+ return {
2733
+ primaryColor: primary,
2734
+ primaryHoverColor: asString(vars?.colorPrimaryHover) ?? darkenHex(primary, 0.12),
2735
+ inputBackgroundColor: asString(bStyles.cardInputBackground) ?? asString(vars?.colorBackground) ?? "#ffffff",
2736
+ textColor: asString(bStyles.cardInputColor) ?? asString(nameInput?.color) ?? asString(vars?.colorText) ?? "#262833",
2737
+ borderColor: asString(bStyles.cardInputBorder) ?? (isButtonsLayout ? "#e5e7eb" : "#A4A4FF"),
2738
+ placeholderColor: asString(bStyles.cardInputPlaceholderColor) ?? "#9ca3af",
2739
+ errorColor: asString(vars?.colorDanger) ?? "#dc2626",
2740
+ successColor: "#16a34a",
2741
+ fontFamily: asString(nameInput?.fontFamily) ?? asString(vars?.fontFamily) ?? "Poppins, sans-serif",
2742
+ fontSize: asString(bStyles.cardInputFontSize) ?? asString(vars?.fontSizeBase) ?? "16px",
2743
+ // `resolvedInputFontWeight` equivalent — sent as a string (the widget's
2744
+ // theme applier only honors string values) so vault inputs/placeholders
2745
+ // match the AVS fields' weight per theme (default 400).
2746
+ fontWeight: String(toCssWeight(nameInput?.fontWeight) ?? 400),
2747
+ borderRadius: asString(vars?.borderRadius) ?? "8px"
2748
+ };
2749
+ }, [appearance, bStyles, layout]);
2587
2750
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
2588
2751
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
2589
2752
  const isSelfContained = !onTokenizedBody;
@@ -2652,12 +2815,160 @@ function SplitCardFormInner({
2652
2815
  },
2653
2816
  [onErrorChange]
2654
2817
  );
2818
+ useEffect5(() => {
2819
+ if (!vaultActive || !cardCapture) return;
2820
+ cardCapture.setSubmitGate?.(avsInvalid);
2821
+ }, [vaultActive, cardCapture, avsInvalid]);
2822
+ useEffect5(() => {
2823
+ if (!vaultActive || !cardCapture) return;
2824
+ cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
2825
+ }, [vaultActive, cardCapture, cardFieldOrder, avsConfig]);
2826
+ useEffect5(() => {
2827
+ if (viewState === "expanding" || viewState === "collapsing" || viewState === "apm-expanding" || viewState === "apm-collapsing") {
2828
+ updateError(null);
2829
+ }
2830
+ }, [viewState, updateError]);
2655
2831
  const emitDecline = useCallback(
2656
2832
  (method, input, overrides) => {
2657
2833
  onDecline?.(buildDeclineEvent(method, input, overrides));
2658
2834
  },
2659
2835
  [onDecline]
2660
2836
  );
2837
+ useEffect5(() => {
2838
+ if (!vaultActive || !sessionId) {
2839
+ setVaultMount(null);
2840
+ return;
2841
+ }
2842
+ const embedded = toVaultMount(session?.vault);
2843
+ if (embedded) {
2844
+ setVaultMount(embedded);
2845
+ return;
2846
+ }
2847
+ setVaultMount(null);
2848
+ let active = true;
2849
+ new PaymentAPI2(baseUrl).getVaultCapture(sessionId, nonce).then((block) => {
2850
+ if (!active) return;
2851
+ const mount = toVaultMount(block);
2852
+ if (mount) {
2853
+ setVaultMount(mount);
2854
+ } else {
2855
+ updateError("Failed to load the secure card form.");
2856
+ }
2857
+ }).catch((err) => {
2858
+ if (active) {
2859
+ updateError(err instanceof Error ? err.message : "Failed to load the secure card form.");
2860
+ }
2861
+ });
2862
+ return () => {
2863
+ active = false;
2864
+ };
2865
+ }, [
2866
+ vaultActive,
2867
+ sessionId,
2868
+ session?.vault?.html,
2869
+ session?.vault?.messageToken,
2870
+ session?.vault?.expectedOrigin,
2871
+ baseUrl,
2872
+ nonce,
2873
+ updateError
2874
+ ]);
2875
+ useEffect5(() => {
2876
+ if (!vaultActive || !cardCapture) return;
2877
+ let cancelled = false;
2878
+ const offSubmitting = cardCapture.on("submitting", () => {
2879
+ setHasAttemptedSubmit(true);
2880
+ setOverlayStatus("processing");
2881
+ if (!sessionId || !nonce) return;
2882
+ const cc = selectedCountryRef.current || resolvedAccount.country || "US";
2883
+ const stateVisible = avsConfig ? isAVSFieldVisible(avsConfig.state, cc) : false;
2884
+ const line1Visible = avsConfig ? isAVSFieldVisible(avsConfig.address_line_1, cc) : false;
2885
+ const zipVisible = avsConfig ? isAVSFieldVisible(avsConfig.postal_code, cc) : false;
2886
+ const cityVisible = avsConfig ? isAVSFieldVisible(avsConfig.city, cc) : false;
2887
+ const line2Visible = avsConfig ? isAVSFieldVisible(avsConfig.address_line_2, cc) : false;
2888
+ const derivedState = line1Visible && !stateVisible && zipVisible ? getStateFromPostalCode(cc, zipCodeRef.current ?? "") : null;
2889
+ const stateValue2 = stateVisible ? stateRef.current : derivedState;
2890
+ void new PaymentAPI2(baseUrl).patchAccountSnapshot(sessionId, nonce, {
2891
+ accountData: {
2892
+ userId: resolvedAccount.userId ?? "",
2893
+ email: resolvedAccount.email ?? "",
2894
+ firstName: resolvedAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
2895
+ lastName: resolvedAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
2896
+ ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
2897
+ ...cityVisible && cityRef.current ? { city: cityRef.current } : {},
2898
+ ...stateValue2 ? { state: stateValue2 } : {},
2899
+ ...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
2900
+ ...line2Visible && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {},
2901
+ country: cc
2902
+ },
2903
+ ...avsCheckProp !== void 0 ? { avsCheck: avsCheckProp } : {},
2904
+ ...avsConfig ? {
2905
+ avsConfig: {
2906
+ country: isAVSFieldVisible(avsConfig.country, cc),
2907
+ postal_code: zipVisible,
2908
+ address_line_1: line1Visible,
2909
+ address_line_2: line2Visible,
2910
+ city: cityVisible,
2911
+ state: stateVisible
2912
+ }
2913
+ } : {}
2914
+ }).catch(() => {
2915
+ });
2916
+ });
2917
+ const offComplete = cardCapture.on("complete", async (event) => {
2918
+ markSessionRecentlyCompleted(event.sessionId ?? sessionId);
2919
+ setOverlayStatus("success");
2920
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
2921
+ if (cancelled) return;
2922
+ onComplete?.({
2923
+ status: "succeeded",
2924
+ paymentIntentId: event.intentId,
2925
+ checkoutMethod: "card"
2926
+ });
2927
+ });
2928
+ const offDecline = cardCapture.on("decline", async (event) => {
2929
+ const message = event.message ?? "Your payment was declined. Please try another card or contact your bank.";
2930
+ updateError(message);
2931
+ setOverlayStatus("error");
2932
+ emitDecline("card", message, event.declineReason ? { declineCode: event.declineReason } : void 0);
2933
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
2934
+ if (cancelled) return;
2935
+ setOverlayStatus(null);
2936
+ });
2937
+ const offError = cardCapture.on("error", async (event) => {
2938
+ const message = event.message ?? "There was a problem processing your payment. Please try again.";
2939
+ updateError(message);
2940
+ setOverlayStatus("error");
2941
+ onError?.(new FloPayError3(message, "api_error"));
2942
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
2943
+ if (cancelled) return;
2944
+ setOverlayStatus(null);
2945
+ });
2946
+ return () => {
2947
+ cancelled = true;
2948
+ offSubmitting();
2949
+ offComplete();
2950
+ offDecline();
2951
+ offError();
2952
+ };
2953
+ }, [
2954
+ vaultActive,
2955
+ cardCapture,
2956
+ sessionId,
2957
+ nonce,
2958
+ baseUrl,
2959
+ avsConfig,
2960
+ avsCheckProp,
2961
+ resolvedAccount.userId,
2962
+ resolvedAccount.email,
2963
+ resolvedAccount.firstName,
2964
+ resolvedAccount.lastName,
2965
+ resolvedAccount.country,
2966
+ fullName,
2967
+ onComplete,
2968
+ onError,
2969
+ updateError,
2970
+ emitDecline
2971
+ ]);
2661
2972
  const directPaypalConfigured = !!directPaypal?.clientId;
2662
2973
  const hasEnabledMethodsPayload = Array.isArray(enabledPaymentMethods);
2663
2974
  const hasEnabledMethods = hasEnabledMethodsPayload && enabledPaymentMethods.length > 0;
@@ -2681,17 +2992,18 @@ function SplitCardFormInner({
2681
2992
  }, [showApplePay, showGooglePay]);
2682
2993
  const walletExpressMethods = hasEnabledMethodsPayload ? expressMethodsForWalletRow : legacyExpressMethods;
2683
2994
  const showWallets = showStripe && walletExpressMethods.length > 0;
2684
- const paymentElementMethodsForCurrency = useMemo3(
2685
- () => filterStripeMethodsByAmount(
2686
- filterStripeMethodsByCountry(
2687
- filterStripeMethodsByCurrency(paymentElementMethods, currency),
2688
- countryProp
2689
- ),
2690
- currency,
2691
- amountInCents
2692
- ),
2693
- [paymentElementMethods, currency, countryProp, amountInCents]
2694
- );
2995
+ const apmCountry = enableAVS ? selectedCountry : countryProp;
2996
+ const paymentElementMethodsForCurrency = useMemo3(() => {
2997
+ const target = apmCountry?.trim().toUpperCase();
2998
+ if (!target) return paymentElementMethods;
2999
+ if (!enabledPaymentMethodCountries) {
3000
+ return filterStripeMethodsByCountry(paymentElementMethods, apmCountry);
3001
+ }
3002
+ return paymentElementMethods.filter((method) => {
3003
+ const allowed = enabledPaymentMethodCountries[method];
3004
+ return !allowed || allowed.length === 0 || allowed.includes(target);
3005
+ });
3006
+ }, [paymentElementMethods, apmCountry, enabledPaymentMethodCountries]);
2695
3007
  const paymentElementBaseOptions = useMemo3(() => ({
2696
3008
  mode: "payment",
2697
3009
  amount: amountInCents,
@@ -2704,7 +3016,7 @@ function SplitCardFormInner({
2704
3016
  const [paymentElementLoadState, setPaymentElementLoadState] = useState3("loading");
2705
3017
  const [directPaypalReady, setDirectPaypalReady] = useState3(false);
2706
3018
  const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
2707
- useEffect4(() => {
3019
+ useEffect5(() => {
2708
3020
  setInAppBrowserDetected(isInAppBrowser());
2709
3021
  }, []);
2710
3022
  const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
@@ -2716,8 +3028,8 @@ function SplitCardFormInner({
2716
3028
  const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
2717
3029
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
2718
3030
  const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
2719
- const validationFiredRef = useRef3(false);
2720
- useEffect4(() => {
3031
+ const validationFiredRef = useRef4(false);
3032
+ useEffect5(() => {
2721
3033
  if (validationFiredRef.current) return;
2722
3034
  if (!showStripe && !showPayPal) {
2723
3035
  validationFiredRef.current = true;
@@ -2737,8 +3049,8 @@ function SplitCardFormInner({
2737
3049
  updateError(err.message);
2738
3050
  }
2739
3051
  }, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
2740
- const deprecationLoggedRef = useRef3(false);
2741
- useEffect4(() => {
3052
+ const deprecationLoggedRef = useRef4(false);
3053
+ useEffect5(() => {
2742
3054
  if (deprecationLoggedRef.current) return;
2743
3055
  if (!hasEnabledMethods) return;
2744
3056
  const stale = [];
@@ -3064,8 +3376,8 @@ function SplitCardFormInner({
3064
3376
  }
3065
3377
  }
3066
3378
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
3067
- const stripeResumeAttemptedRef = useRef3(false);
3068
- useEffect4(() => {
3379
+ const stripeResumeAttemptedRef = useRef4(false);
3380
+ useEffect5(() => {
3069
3381
  if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
3070
3382
  const params = new URLSearchParams(window.location.search);
3071
3383
  const paymentIntentId = params.get("payment_intent");
@@ -3147,6 +3459,7 @@ function SplitCardFormInner({
3147
3459
  async (e) => {
3148
3460
  e.preventDefault();
3149
3461
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
3462
+ if (vaultActive) return;
3150
3463
  if (layout !== "buttons") {
3151
3464
  onButtonClick?.("card");
3152
3465
  }
@@ -3174,11 +3487,13 @@ function SplitCardFormInner({
3174
3487
  return;
3175
3488
  }
3176
3489
  }
3177
- const submitResult = await flopay.submitElements();
3178
- if (submitResult.error) {
3179
- updateError(submitResult.error.message);
3180
- onError?.(submitResult.error);
3181
- return;
3490
+ if (!vaultActive) {
3491
+ const submitResult = await flopay.submitElements();
3492
+ if (submitResult.error) {
3493
+ updateError(submitResult.error.message);
3494
+ onError?.(submitResult.error);
3495
+ return;
3496
+ }
3182
3497
  }
3183
3498
  const billingAddress = {};
3184
3499
  const cc = selectedCountryRef.current;
@@ -3203,75 +3518,77 @@ function SplitCardFormInner({
3203
3518
  ...fullName.trim() ? { name: fullName.trim() } : {},
3204
3519
  ...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
3205
3520
  };
3206
- const pmResult = await flopay.createPaymentMethod(billingDetails);
3207
- if (pmResult.error || !pmResult.paymentMethodId) {
3208
- updateError(pmResult.error?.message ?? "Failed to create payment method.");
3209
- return;
3210
- }
3211
3521
  if (!sessionId || !resolvedAccount.email) {
3212
3522
  throw new FloPayError3("Missing sessionId or email", "validation_error");
3213
3523
  }
3214
- const intentHeaders = { "Content-Type": "application/json" };
3215
- if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
3216
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3217
- method: "POST",
3218
- headers: intentHeaders,
3219
- body: JSON.stringify({
3220
- sessionId,
3221
- email: resolvedAccount.email,
3222
- paymentMethodType: pmResult.paymentMethodId,
3223
- isPaypal: false
3224
- })
3225
- });
3226
- if (!intentResponse.ok) {
3227
- const intentError = await buildFloPayApiErrorFromResponse(
3228
- intentResponse,
3229
- "Failed to create payment intent"
3524
+ {
3525
+ const pmResult = await flopay.createPaymentMethod(billingDetails);
3526
+ if (pmResult.error || !pmResult.paymentMethodId) {
3527
+ updateError(pmResult.error?.message ?? "Failed to create payment method.");
3528
+ return;
3529
+ }
3530
+ const intentHeaders = { "Content-Type": "application/json" };
3531
+ if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
3532
+ const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3533
+ method: "POST",
3534
+ headers: intentHeaders,
3535
+ body: JSON.stringify({
3536
+ sessionId,
3537
+ email: resolvedAccount.email,
3538
+ paymentMethodType: pmResult.paymentMethodId,
3539
+ isPaypal: false
3540
+ })
3541
+ });
3542
+ if (!intentResponse.ok) {
3543
+ const intentError = await buildFloPayApiErrorFromResponse(
3544
+ intentResponse,
3545
+ "Failed to create payment intent"
3546
+ );
3547
+ setOverlayStatus("error");
3548
+ updateError(intentError.message);
3549
+ onError?.(intentError);
3550
+ emitDecline("card", intentError);
3551
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3552
+ return;
3553
+ }
3554
+ const intentJson = await intentResponse.json();
3555
+ const intentClientSecret = intentJson.data?.id;
3556
+ if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
3557
+ const confirmResult = await flopay.confirmCardPayment({
3558
+ clientSecret: intentClientSecret,
3559
+ paymentMethodId: pmResult.paymentMethodId
3560
+ });
3561
+ if (confirmResult.error) {
3562
+ setOverlayStatus("error");
3563
+ updateError(confirmResult.error.message);
3564
+ onError?.(confirmResult.error);
3565
+ emitDecline("card", confirmResult.error);
3566
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3567
+ return;
3568
+ }
3569
+ const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
3570
+ const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
3571
+ rawProvider,
3572
+ intentClientSecret
3230
3573
  );
3231
- setOverlayStatus("error");
3232
- updateError(intentError.message);
3233
- onError?.(intentError);
3234
- emitDecline("card", intentError);
3235
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3236
- return;
3237
- }
3238
- const intentJson = await intentResponse.json();
3239
- const intentClientSecret = intentJson.data?.id;
3240
- if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
3241
- const confirmResult = await flopay.confirmCardPayment({
3242
- clientSecret: intentClientSecret,
3243
- paymentMethodId: pmResult.paymentMethodId
3244
- });
3245
- if (confirmResult.error) {
3246
- setOverlayStatus("error");
3247
- updateError(confirmResult.error.message);
3248
- onError?.(confirmResult.error);
3249
- emitDecline("card", confirmResult.error);
3250
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3251
- return;
3252
- }
3253
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
3254
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
3255
- rawProvider,
3256
- intentClientSecret
3257
- );
3258
- const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
3259
- const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
3260
- if (!paymentIntentId) {
3261
- const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
3262
- setOverlayStatus("error");
3263
- updateError(error2.message);
3264
- onError?.(error2);
3265
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3266
- return;
3574
+ const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
3575
+ const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
3576
+ if (!paymentIntentId) {
3577
+ const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
3578
+ setOverlayStatus("error");
3579
+ updateError(error2.message);
3580
+ onError?.(error2);
3581
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3582
+ return;
3583
+ }
3584
+ handedOff = isSelfContained;
3585
+ dispatchTokenizedBody({
3586
+ id: paymentMethodId,
3587
+ type: "card",
3588
+ threeDSecureActionResultTokenId: paymentIntentId,
3589
+ originalPaymentMethodId: pmResult.paymentMethodId
3590
+ });
3267
3591
  }
3268
- handedOff = isSelfContained;
3269
- dispatchTokenizedBody({
3270
- id: paymentMethodId,
3271
- type: "card",
3272
- threeDSecureActionResultTokenId: paymentIntentId,
3273
- originalPaymentMethodId: pmResult.paymentMethodId
3274
- });
3275
3592
  } catch (err) {
3276
3593
  setOverlayStatus("error");
3277
3594
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
@@ -3283,11 +3600,11 @@ function SplitCardFormInner({
3283
3600
  }
3284
3601
  }
3285
3602
  },
3286
- [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3603
+ [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, vaultActive, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3287
3604
  );
3288
- const isReady = flopay !== null && elements !== null;
3605
+ const isReady = flopay !== null && (vaultActive || elements !== null);
3289
3606
  if (!isReady) {
3290
- return /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
3607
+ return /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
3291
3608
  }
3292
3609
  const isButtons = layout === "buttons";
3293
3610
  const appearanceVars = appearance?.variables;
@@ -3295,6 +3612,8 @@ function SplitCardFormInner({
3295
3612
  const appearanceColorBg = appearanceVars?.colorBackground;
3296
3613
  const themedWrapperBg = appearanceColorBg && !SDK_DEFAULT_WHITES.has(appearanceColorBg) ? appearanceColorBg : void 0;
3297
3614
  const resolvedBorder = bStyles.cardInputBorder ?? (isButtons ? "#e5e7eb" : "#A4A4FF");
3615
+ const resolvedDangerColor = appearanceVars?.colorDanger ?? "#dc2626";
3616
+ const avsBorderColor = (fieldInvalid) => hasAttemptedSubmit && fieldInvalid ? resolvedDangerColor : resolvedBorder;
3298
3617
  const cardBg = bStyles.cardFormContainer?.backgroundColor ?? themedWrapperBg ?? (isButtons ? "white" : "#EDEDFF");
3299
3618
  const cardInputBg = bStyles.cardInputBackground ?? appearanceVars?.colorBackground ?? "white";
3300
3619
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
@@ -3306,7 +3625,9 @@ function SplitCardFormInner({
3306
3625
  const resolvedInputColor = bStyles.cardInputColor ?? (typeof nameInputOverrides?.color === "string" ? nameInputOverrides.color : void 0) ?? appearanceVars?.colorText ?? "#262833";
3307
3626
  const resolvedPlaceholderColor = bStyles.cardInputPlaceholderColor ?? "#9ca3af";
3308
3627
  const resolvedBorderRadius = appearanceVars?.borderRadius ?? "8px";
3628
+ const buttonBorderRadius = appearanceVars?.borderRadius ?? bStyles.cardButton?.borderRadius ?? resolvedBorderRadius;
3309
3629
  const resolvedPrimaryColor = appearanceVars?.colorPrimary ?? "#4A49FF";
3630
+ const resolvedPrimaryHoverColor = appearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
3310
3631
  const resolvedTitleColor = appearanceVars?.colorText ?? "#262833";
3311
3632
  const sharedInputTypography = {
3312
3633
  fontSize: resolvedInputFontSize,
@@ -3340,14 +3661,73 @@ function SplitCardFormInner({
3340
3661
  const containerOverrides = bStyles.cardFormContainer ?? {};
3341
3662
  const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
3342
3663
  const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
3664
+ const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx7(
3665
+ VaultCardFields,
3666
+ {
3667
+ capture: cardCapture,
3668
+ html: vaultMount.html,
3669
+ messageToken: vaultMount.messageToken,
3670
+ expectedOrigin: vaultMount.expectedOrigin,
3671
+ theme: vaultThemeColors,
3672
+ onReady: () => {
3673
+ setFormReady(true);
3674
+ if (!avsConfig || typeof document === "undefined") return;
3675
+ const focusFirstAvs = () => {
3676
+ const block = document.querySelector('[data-testid="flopay-avs-fields"]');
3677
+ const first = block?.querySelector(
3678
+ "input:not([disabled]), select:not([disabled])"
3679
+ );
3680
+ first?.focus();
3681
+ };
3682
+ focusFirstAvs();
3683
+ let tries = 0;
3684
+ const timer = window.setInterval(() => {
3685
+ tries += 1;
3686
+ const active = document.activeElement;
3687
+ const onIframe = !!active && active.tagName === "IFRAME" && active.id === "flopay_vault_form_iframe";
3688
+ if (onIframe) {
3689
+ focusFirstAvs();
3690
+ window.clearInterval(timer);
3691
+ } else if (tries >= 50) {
3692
+ window.clearInterval(timer);
3693
+ }
3694
+ }, 100);
3695
+ },
3696
+ onError: (message) => updateError(message),
3697
+ onValidation: (message) => {
3698
+ updateError(message);
3699
+ if (message) setOverlayStatus(null);
3700
+ }
3701
+ }
3702
+ ) : /* @__PURE__ */ jsx7(
3703
+ "div",
3704
+ {
3705
+ "data-testid": "flopay-vault-loading",
3706
+ style: {
3707
+ minHeight: 120,
3708
+ display: "flex",
3709
+ alignItems: "center",
3710
+ justifyContent: "center",
3711
+ color: "#6b7280",
3712
+ fontSize: 14
3713
+ },
3714
+ children: "Loading secure card form\u2026"
3715
+ }
3716
+ );
3343
3717
  const cardFormBlock = /* @__PURE__ */ jsxs4("div", { style: {
3344
3718
  backgroundColor: cardBg,
3345
3719
  borderRadius: containerRadius,
3346
3720
  ...containerOverrides,
3347
3721
  padding: containerPadding,
3348
- ...sharedInputPlaceholderVars
3722
+ ...sharedInputPlaceholderVars,
3723
+ // Flex column (BOTH layouts, so default and buttons>card never diverge) so
3724
+ // AVS can be ordered above the vault widget — its submit lives inside the
3725
+ // iframe, so AVS can't sit between fields and button; it goes above the
3726
+ // card form instead of after the button.
3727
+ display: "flex",
3728
+ flexDirection: "column"
3349
3729
  }, children: [
3350
- /* @__PURE__ */ jsx6("style", { children: `
3730
+ /* @__PURE__ */ jsx7("style", { children: `
3351
3731
  .flopay-shared-input::placeholder {
3352
3732
  color: var(--flopay-input-placeholder-color);
3353
3733
  opacity: 1;
@@ -3359,7 +3739,9 @@ function SplitCardFormInner({
3359
3739
  isButtons && showCardForm && /* @__PURE__ */ jsxs4("div", { style: {
3360
3740
  display: "flex",
3361
3741
  alignItems: "center",
3362
- padding: "0.75rem 0 0.625rem"
3742
+ padding: "0.75rem 0 0.625rem",
3743
+ // Keep the header on top when AVS is ordered above the card on vault.
3744
+ order: vaultActive ? -2 : 0
3363
3745
  }, children: [
3364
3746
  /* @__PURE__ */ jsxs4(
3365
3747
  "button",
@@ -3383,7 +3765,7 @@ function SplitCardFormInner({
3383
3765
  },
3384
3766
  "aria-label": "Back to payment methods",
3385
3767
  children: [
3386
- /* @__PURE__ */ jsx6("span", { style: {
3768
+ /* @__PURE__ */ jsx7("span", { style: {
3387
3769
  display: "inline-flex",
3388
3770
  alignItems: "center",
3389
3771
  justifyContent: "center",
@@ -3393,12 +3775,12 @@ function SplitCardFormInner({
3393
3775
  backgroundColor: "#f3f4f6",
3394
3776
  transition: "background-color 0.15s",
3395
3777
  ...bStyles.backButtonIcon
3396
- }, 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" }) }) }),
3397
- /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
3778
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
3779
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
3398
3780
  ]
3399
3781
  }
3400
3782
  ),
3401
- hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
3783
+ hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
3402
3784
  flex: 1,
3403
3785
  textAlign: "center",
3404
3786
  fontWeight: 600,
@@ -3406,58 +3788,65 @@ function SplitCardFormInner({
3406
3788
  color: "#262833",
3407
3789
  paddingRight: 80,
3408
3790
  ...bStyles.title
3409
- }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
3791
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
3410
3792
  ] }),
3411
- !isButtons && !hideTitle && /* @__PURE__ */ jsx6("div", { style: {
3793
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx7("div", { style: {
3412
3794
  textAlign: "center",
3413
3795
  fontWeight: 600,
3414
3796
  fontSize: "1.1rem",
3415
3797
  padding: "0.5rem 0",
3416
3798
  color: resolvedTitleColor,
3799
+ // Keep the title at the very top on the vault path. The card-form slot
3800
+ // (-2) and AVS block (-1) are ordered below it but above the card form.
3801
+ order: vaultActive ? -3 : 0,
3417
3802
  ...bStyles.title
3418
- }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) }),
3419
- /* @__PURE__ */ jsx6("div", { style: {
3420
- backgroundColor: cardInputBg,
3421
- border: `1px solid ${resolvedBorder}`,
3422
- borderTopLeftRadius: "8px",
3423
- borderTopRightRadius: "8px",
3424
- padding: "10px"
3425
- }, children: /* @__PURE__ */ jsx6(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
3426
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3427
- /* @__PURE__ */ jsx6("div", { style: {
3428
- flex: 1,
3429
- backgroundColor: cardInputBg,
3430
- // Longhand only — mixing `border` shorthand with per-side
3431
- // overrides triggers React's "shorthand vs longhand" warning
3432
- // because render order isn't deterministic.
3433
- borderTop: "none",
3434
- borderRight: "none",
3435
- borderBottom: `1px solid ${resolvedBorder}`,
3436
- borderLeft: `1px solid ${resolvedBorder}`,
3437
- borderBottomLeftRadius: "8px",
3438
- padding: "10px"
3439
- }, children: /* @__PURE__ */ jsx6(CardExpiryElement, { options: stripeElementStyle }) }),
3440
- /* @__PURE__ */ jsx6("div", { style: {
3441
- flex: 1,
3803
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) }),
3804
+ !vaultActive && /* @__PURE__ */ jsxs4(Fragment2, { children: [
3805
+ /* @__PURE__ */ jsx7("div", { style: {
3442
3806
  backgroundColor: cardInputBg,
3443
- borderTop: "none",
3444
- borderRight: `1px solid ${resolvedBorder}`,
3445
- borderBottom: `1px solid ${resolvedBorder}`,
3446
- borderLeft: `1px solid ${resolvedBorder}`,
3447
- borderBottomRightRadius: "8px",
3807
+ border: `1px solid ${resolvedBorder}`,
3808
+ borderTopLeftRadius: resolvedBorderRadius,
3809
+ borderTopRightRadius: resolvedBorderRadius,
3448
3810
  padding: "10px"
3449
- }, children: /* @__PURE__ */ jsx6(CardCvcElement, { options: stripeElementStyle }) })
3811
+ }, children: /* @__PURE__ */ jsx7(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
3812
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3813
+ /* @__PURE__ */ jsx7("div", { style: {
3814
+ flex: 1,
3815
+ backgroundColor: cardInputBg,
3816
+ // Longhand only — mixing `border` shorthand with per-side
3817
+ // overrides triggers React's "shorthand vs longhand" warning
3818
+ // because render order isn't deterministic.
3819
+ borderTop: "none",
3820
+ borderRight: "none",
3821
+ borderBottom: `1px solid ${resolvedBorder}`,
3822
+ borderLeft: `1px solid ${resolvedBorder}`,
3823
+ borderBottomLeftRadius: resolvedBorderRadius,
3824
+ padding: "10px"
3825
+ }, children: /* @__PURE__ */ jsx7(CardExpiryElement, { options: stripeElementStyle }) }),
3826
+ /* @__PURE__ */ jsx7("div", { style: {
3827
+ flex: 1,
3828
+ backgroundColor: cardInputBg,
3829
+ borderTop: "none",
3830
+ borderRight: `1px solid ${resolvedBorder}`,
3831
+ borderBottom: `1px solid ${resolvedBorder}`,
3832
+ borderLeft: `1px solid ${resolvedBorder}`,
3833
+ borderBottomRightRadius: resolvedBorderRadius,
3834
+ padding: "10px"
3835
+ }, children: /* @__PURE__ */ jsx7(CardCvcElement, { options: stripeElementStyle }) })
3836
+ ] })
3450
3837
  ] }),
3451
- /* @__PURE__ */ jsx6("div", { style: {
3838
+ !vaultActive && /* @__PURE__ */ jsx7("div", { style: {
3452
3839
  backgroundColor: cardInputBg,
3453
3840
  border: `1px solid ${resolvedBorder}`,
3454
- borderRadius: "8px",
3841
+ borderRadius: resolvedBorderRadius,
3455
3842
  marginTop: "0.5rem",
3456
3843
  padding: "10px"
3457
- }, children: /* @__PURE__ */ jsx6(
3844
+ }, children: /* @__PURE__ */ jsx7(
3458
3845
  "input",
3459
3846
  {
3460
3847
  className: "flopay-shared-input",
3848
+ id: "flopay-cc-name",
3849
+ name: "cc-name",
3461
3850
  placeholder: "Full Name on Card",
3462
3851
  autoComplete: "cc-name",
3463
3852
  value: fullName,
@@ -3474,32 +3863,32 @@ function SplitCardFormInner({
3474
3863
  }
3475
3864
  }
3476
3865
  ) }),
3477
- avsConfig && (() => {
3866
+ avsConfig && /* @__PURE__ */ jsx7("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
3478
3867
  const cc = selectedCountry;
3479
- const inputWrapStyle = (extraStyle) => ({
3868
+ const inputWrapStyle = (invalid = false) => ({
3480
3869
  backgroundColor: cardInputBg,
3481
- border: `1px solid ${resolvedBorder}`,
3482
- borderRadius: "8px",
3870
+ border: `1px solid ${avsBorderColor(invalid)}`,
3871
+ borderRadius: resolvedBorderRadius,
3483
3872
  marginTop: "0.5rem",
3484
- padding: "10px",
3485
- ...isButtons && extraStyle ? extraStyle : {}
3873
+ padding: "10px"
3486
3874
  });
3487
3875
  const inputFieldStyle = () => ({
3488
3876
  width: "100%",
3489
3877
  border: "none",
3490
3878
  outline: "none",
3491
3879
  background: "transparent",
3492
- ...sharedInputTypography,
3493
- ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
3880
+ ...sharedInputTypography
3494
3881
  });
3495
3882
  const stateOpts = getStateOptions(cc);
3496
3883
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
3497
- isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine1Input), children: /* @__PURE__ */ jsx6(
3884
+ isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx7(
3498
3885
  "input",
3499
3886
  {
3500
3887
  className: "flopay-shared-input",
3888
+ id: "flopay-billing-address-line1",
3889
+ name: "billing-address-line1",
3501
3890
  placeholder: "Street Address (e.g. 123 Main St)",
3502
- autoComplete: "address-line1",
3891
+ autoComplete: "billing address-line1",
3503
3892
  value: addressLine1,
3504
3893
  onChange: (e) => {
3505
3894
  addressLine1Ref.current = e.target.value;
@@ -3511,12 +3900,14 @@ function SplitCardFormInner({
3511
3900
  style: inputFieldStyle()
3512
3901
  }
3513
3902
  ) }),
3514
- isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine2Input), children: /* @__PURE__ */ jsx6(
3903
+ isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx7(
3515
3904
  "input",
3516
3905
  {
3517
3906
  className: "flopay-shared-input",
3907
+ id: "flopay-billing-address-line2",
3908
+ name: "billing-address-line2",
3518
3909
  placeholder: "Apt, Suite, Unit (optional)",
3519
- autoComplete: "address-line2",
3910
+ autoComplete: "billing address-line2",
3520
3911
  value: addressLine2,
3521
3912
  onChange: (e) => {
3522
3913
  addressLine2Ref.current = e.target.value;
@@ -3532,24 +3923,25 @@ function SplitCardFormInner({
3532
3923
  gap: "0",
3533
3924
  marginTop: "0.5rem"
3534
3925
  }, children: [
3535
- isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx6("div", { style: {
3926
+ isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx7("div", { style: {
3536
3927
  flex: 1,
3537
3928
  backgroundColor: cardInputBg,
3538
- borderTop: `1px solid ${resolvedBorder}`,
3539
- borderRight: `1px solid ${resolvedBorder}`,
3540
- borderBottom: `1px solid ${resolvedBorder}`,
3541
- borderLeft: `1px solid ${resolvedBorder}`,
3929
+ borderTop: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3930
+ borderRight: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3931
+ borderBottom: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3932
+ borderLeft: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3542
3933
  padding: "10px",
3543
- borderTopLeftRadius: "8px",
3544
- borderBottomLeftRadius: "8px",
3545
- ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: "8px" },
3546
- ...isButtons && bStyles.cityInput ? bStyles.cityInput : {}
3547
- }, children: /* @__PURE__ */ jsx6(
3934
+ borderTopLeftRadius: resolvedBorderRadius,
3935
+ borderBottomLeftRadius: resolvedBorderRadius,
3936
+ ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: resolvedBorderRadius }
3937
+ }, children: /* @__PURE__ */ jsx7(
3548
3938
  "input",
3549
3939
  {
3550
3940
  className: "flopay-shared-input",
3941
+ id: "flopay-billing-city",
3942
+ name: "billing-city",
3551
3943
  placeholder: "City",
3552
- autoComplete: "address-level2",
3944
+ autoComplete: "billing address-level2",
3553
3945
  value: city,
3554
3946
  onChange: (e) => {
3555
3947
  cityRef.current = e.target.value;
@@ -3561,39 +3953,42 @@ function SplitCardFormInner({
3561
3953
  style: inputFieldStyle()
3562
3954
  }
3563
3955
  ) }),
3564
- isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx6("div", { style: {
3956
+ isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx7("div", { style: {
3565
3957
  flex: 1,
3566
3958
  backgroundColor: cardInputBg,
3567
- border: `1px solid ${resolvedBorder}`,
3959
+ border: `1px solid ${avsBorderColor(invalidAvsFields.state)}`,
3568
3960
  padding: "10px",
3569
- borderTopRightRadius: "8px",
3570
- borderBottomRightRadius: "8px",
3571
- ...isAVSFieldVisible(avsConfig.city, cc) ? { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 } : { borderRadius: "8px" },
3572
- ...isButtons && bStyles.stateInput ? bStyles.stateInput : {}
3961
+ borderTopRightRadius: resolvedBorderRadius,
3962
+ borderBottomRightRadius: resolvedBorderRadius,
3963
+ ...isAVSFieldVisible(avsConfig.city, cc) ? { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 } : { borderRadius: resolvedBorderRadius }
3573
3964
  }, children: stateOpts ? /* @__PURE__ */ jsxs4(
3574
3965
  "select",
3575
3966
  {
3967
+ id: "flopay-billing-state",
3968
+ name: "billing-state",
3576
3969
  value: stateValue,
3577
3970
  onChange: (e) => {
3578
3971
  stateRef.current = e.target.value;
3579
3972
  setStateValue(e.target.value);
3580
3973
  },
3581
3974
  disabled: isSubmitting,
3582
- autoComplete: "address-level1",
3975
+ autoComplete: "billing address-level1",
3583
3976
  required: true,
3584
3977
  "data-testid": "flopay-state",
3585
3978
  style: { ...inputFieldStyle(), cursor: "pointer" },
3586
3979
  children: [
3587
- /* @__PURE__ */ jsx6("option", { value: "", children: getStateLabel(cc) }),
3588
- stateOpts.map((s) => /* @__PURE__ */ jsx6("option", { value: s.code, children: s.name }, s.code))
3980
+ /* @__PURE__ */ jsx7("option", { value: "", children: getStateLabel(cc) }),
3981
+ stateOpts.map((s) => /* @__PURE__ */ jsx7("option", { value: s.code, children: s.name }, s.code))
3589
3982
  ]
3590
3983
  }
3591
- ) : /* @__PURE__ */ jsx6(
3984
+ ) : /* @__PURE__ */ jsx7(
3592
3985
  "input",
3593
3986
  {
3594
3987
  className: "flopay-shared-input",
3988
+ id: "flopay-billing-state",
3989
+ name: "billing-state",
3595
3990
  placeholder: getStateLabel(cc),
3596
- autoComplete: "address-level1",
3991
+ autoComplete: "billing address-level1",
3597
3992
  value: stateValue,
3598
3993
  onChange: (e) => {
3599
3994
  stateRef.current = e.target.value;
@@ -3612,7 +4007,7 @@ function SplitCardFormInner({
3612
4007
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
3613
4008
  marginTop: "0.5rem"
3614
4009
  }, children: [
3615
- isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx6("div", { style: {
4010
+ isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx7("div", { style: {
3616
4011
  flex: avsLayoutProp === "row" ? 1 : void 0,
3617
4012
  backgroundColor: cardInputBg,
3618
4013
  borderTop: `1px solid ${resolvedBorder}`,
@@ -3620,11 +4015,12 @@ function SplitCardFormInner({
3620
4015
  borderBottom: `1px solid ${resolvedBorder}`,
3621
4016
  borderLeft: `1px solid ${resolvedBorder}`,
3622
4017
  padding: "10px",
3623
- ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
3624
- ...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
3625
- }, children: /* @__PURE__ */ jsx6(
4018
+ ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: resolvedBorderRadius, borderBottomLeftRadius: resolvedBorderRadius, borderRight: "none" } : { borderRadius: resolvedBorderRadius }
4019
+ }, children: /* @__PURE__ */ jsx7(
3626
4020
  "select",
3627
4021
  {
4022
+ id: "flopay-billing-country",
4023
+ name: "billing-country",
3628
4024
  value: selectedCountry,
3629
4025
  onChange: (e) => {
3630
4026
  selectedCountryRef.current = e.target.value;
@@ -3634,7 +4030,7 @@ function SplitCardFormInner({
3634
4030
  setStateValue("");
3635
4031
  },
3636
4032
  disabled: isSubmitting,
3637
- autoComplete: "country",
4033
+ autoComplete: "billing country",
3638
4034
  "data-testid": "flopay-country",
3639
4035
  style: { ...inputFieldStyle(), cursor: "pointer" },
3640
4036
  children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs4("option", { value: c.code, children: [
@@ -3644,19 +4040,20 @@ function SplitCardFormInner({
3644
4040
  ] }, c.code))
3645
4041
  }
3646
4042
  ) }),
3647
- isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx6("div", { style: {
4043
+ isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx7("div", { style: {
3648
4044
  flex: avsLayoutProp === "row" ? 1 : void 0,
3649
4045
  backgroundColor: cardInputBg,
3650
- border: `1px solid ${resolvedBorder}`,
4046
+ border: `1px solid ${avsBorderColor(invalidAvsFields.zip)}`,
3651
4047
  padding: "10px",
3652
- ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
3653
- ...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
3654
- }, children: /* @__PURE__ */ jsx6(
4048
+ ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
4049
+ }, children: /* @__PURE__ */ jsx7(
3655
4050
  "input",
3656
4051
  {
3657
4052
  className: "flopay-shared-input",
4053
+ id: "flopay-billing-postal-code",
4054
+ name: "billing-postal-code",
3658
4055
  placeholder: getPostalCodeLabel(selectedCountry),
3659
- autoComplete: "postal-code",
4056
+ autoComplete: "billing postal-code",
3660
4057
  value: zipCode,
3661
4058
  onChange: (e) => {
3662
4059
  zipCodeRef.current = e.target.value;
@@ -3671,7 +4068,9 @@ function SplitCardFormInner({
3671
4068
  ) })
3672
4069
  ] })
3673
4070
  ] });
3674
- })(),
4071
+ })() }),
4072
+ vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
4073
+ vaultActive && vaultCardFieldsNode,
3675
4074
  displayError && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
3676
4075
  margin: "0.75rem 0",
3677
4076
  padding: "0.625rem 0.875rem",
@@ -3686,15 +4085,21 @@ function SplitCardFormInner({
3686
4085
  gap: "0.5rem",
3687
4086
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
3688
4087
  }, children: [
3689
- /* @__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" }) }),
4088
+ /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("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" }) }),
3690
4089
  displayError
3691
4090
  ] }),
3692
- children ?? /* @__PURE__ */ jsx6(
4091
+ !vaultActive && (children ?? /* @__PURE__ */ jsx7(
3693
4092
  "button",
3694
4093
  {
3695
4094
  type: "submit",
3696
4095
  disabled: !formReady || isSubmitting,
3697
4096
  "data-testid": "flopay-submit",
4097
+ onMouseEnter: (e) => {
4098
+ if (formReady && !isSubmitting) e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
4099
+ },
4100
+ onMouseLeave: (e) => {
4101
+ e.currentTarget.style.backgroundColor = bStyles.submitButton?.backgroundColor ?? resolvedPrimaryColor;
4102
+ },
3698
4103
  style: {
3699
4104
  width: "100%",
3700
4105
  padding: "0.875rem",
@@ -3707,11 +4112,12 @@ function SplitCardFormInner({
3707
4112
  fontWeight: 600,
3708
4113
  cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
3709
4114
  opacity: !formReady || isSubmitting ? 0.5 : 1,
4115
+ transition: "background-color 0.15s",
3710
4116
  ...bStyles.submitButton
3711
4117
  },
3712
4118
  children: isSubmitting ? "PROCESSING..." : submitLabel
3713
4119
  }
3714
- )
4120
+ ))
3715
4121
  ] });
3716
4122
  const gateDebugStyle = {
3717
4123
  margin: 0,
@@ -3727,9 +4133,9 @@ function SplitCardFormInner({
3727
4133
  const renderDirectPaypalGateDebug = (testId) => {
3728
4134
  if (!debug) return null;
3729
4135
  if (!showPayPal || !directPaypalConfigured) {
3730
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
4136
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
3731
4137
  }
3732
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4138
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
3733
4139
  "FloPay/DirectPayPal-debug (parent gate)",
3734
4140
  ` showPayPal=${showPayPal}`,
3735
4141
  ` directPaypalConfigured=${directPaypalConfigured}`,
@@ -3742,9 +4148,9 @@ function SplitCardFormInner({
3742
4148
  const renderStripeGateDebug = (testId) => {
3743
4149
  if (!debug) return null;
3744
4150
  if (!showStripe || !stripeInstance) {
3745
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
4151
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
3746
4152
  }
3747
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4153
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
3748
4154
  "FloPay/Stripe-debug (parent gate)",
3749
4155
  ` showStripe=${showStripe}`,
3750
4156
  ` currency=${currency}`,
@@ -3774,8 +4180,8 @@ function SplitCardFormInner({
3774
4180
  const buttonsAnim = viewState === "expanding" || viewState === "apm-expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" || viewState === "apm-collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
3775
4181
  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;
3776
4182
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3777
- /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
3778
- overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4183
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4184
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
3779
4185
  /* @__PURE__ */ jsxs4("div", { style: { display: "grid" }, children: [
3780
4186
  /* @__PURE__ */ jsxs4(
3781
4187
  "div",
@@ -3795,7 +4201,7 @@ function SplitCardFormInner({
3795
4201
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
3796
4202
  renderStripeGateDebug("flopay-stripe-gate-debug"),
3797
4203
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
3798
- paypalDirectRetry && /* @__PURE__ */ jsx6(
4204
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
3799
4205
  "div",
3800
4206
  {
3801
4207
  "data-testid": "flopay-paypal-direct-retry-notice",
@@ -3811,7 +4217,7 @@ function SplitCardFormInner({
3811
4217
  children: "Please confirm your PayPal payment to complete checkout."
3812
4218
  }
3813
4219
  ),
3814
- /* @__PURE__ */ jsx6(
4220
+ /* @__PURE__ */ jsx7(
3815
4221
  DirectPayPalButton,
3816
4222
  {
3817
4223
  sessionId,
@@ -3837,7 +4243,7 @@ function SplitCardFormInner({
3837
4243
  paypalDirectRetry?.orderId ?? "fresh"
3838
4244
  )
3839
4245
  ] }),
3840
- shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
4246
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
3841
4247
  PayPalButtonInner,
3842
4248
  {
3843
4249
  sessionId,
@@ -3851,10 +4257,10 @@ function SplitCardFormInner({
3851
4257
  onDecline,
3852
4258
  runBeforeButtonClick,
3853
4259
  onLoadStateChange: setPaypalLoadState,
3854
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4260
+ placeholderBorderRadius: buttonBorderRadius
3855
4261
  }
3856
4262
  ) }),
3857
- shouldRenderWallets ? /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
4263
+ shouldRenderWallets ? /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
3858
4264
  WalletButtonInner,
3859
4265
  {
3860
4266
  sessionId,
@@ -3868,10 +4274,10 @@ function SplitCardFormInner({
3868
4274
  onDecline,
3869
4275
  runBeforeButtonClick,
3870
4276
  onLoadStateChange: setWalletLoadState,
3871
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4277
+ placeholderBorderRadius: buttonBorderRadius
3872
4278
  }
3873
- ) }) : shouldShowWallets ? /* @__PURE__ */ jsx6("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
3874
- shouldRenderPaymentElement && /* @__PURE__ */ jsx6(
4279
+ ) }) : shouldShowWallets ? /* @__PURE__ */ jsx7("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: buttonBorderRadius, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
4280
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
3875
4281
  StripePaymentElementInner,
3876
4282
  {
3877
4283
  sessionId,
@@ -3899,11 +4305,11 @@ function SplitCardFormInner({
3899
4305
  backgroundColor: bStyles.cardButton?.backgroundColor,
3900
4306
  borderColor: bStyles.cardInputBorder,
3901
4307
  textColor: bStyles.cardButton?.color,
3902
- borderRadius: bStyles.cardButton?.borderRadius
4308
+ borderRadius: buttonBorderRadius
3903
4309
  }
3904
4310
  }
3905
4311
  ),
3906
- showStripe && /* @__PURE__ */ jsx6(
4312
+ showStripe && /* @__PURE__ */ jsx7(
3907
4313
  "button",
3908
4314
  {
3909
4315
  type: "button",
@@ -3931,7 +4337,8 @@ function SplitCardFormInner({
3931
4337
  themeBundle,
3932
4338
  resolvedPrimaryColor,
3933
4339
  resolvedBorderRadius,
3934
- submitButtonStyle: bStyles.submitButton
4340
+ submitButtonStyle: bStyles.submitButton,
4341
+ explicitPrimaryColor: appearanceVars?.colorPrimary
3935
4342
  }),
3936
4343
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
3937
4344
  fontWeight: 600,
@@ -3940,17 +4347,25 @@ function SplitCardFormInner({
3940
4347
  alignItems: "center",
3941
4348
  justifyContent: "center",
3942
4349
  gap: "0.625rem",
3943
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
4350
+ transition: "background-color 0.15s, border-color 0.2s, box-shadow 0.2s, transform 0.1s",
3944
4351
  position: "relative",
3945
4352
  opacity: isSubmitting ? 0.6 : 1
3946
4353
  },
4354
+ onMouseEnter: (e) => {
4355
+ if (!isSubmitting && (themeBundle || appearanceVars?.colorPrimary)) {
4356
+ e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
4357
+ }
4358
+ },
4359
+ onMouseLeave: (e) => {
4360
+ if (themeBundle || appearanceVars?.colorPrimary) e.currentTarget.style.backgroundColor = resolvedPrimaryColor;
4361
+ },
3947
4362
  onMouseDown: (e) => {
3948
4363
  e.currentTarget.style.transform = "scale(0.985)";
3949
4364
  },
3950
4365
  onMouseUp: (e) => {
3951
4366
  e.currentTarget.style.transform = "scale(1)";
3952
4367
  },
3953
- children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
4368
+ children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
3954
4369
  }
3955
4370
  ),
3956
4371
  displayError && viewState === "buttons" && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
@@ -3967,18 +4382,18 @@ function SplitCardFormInner({
3967
4382
  gap: "0.5rem",
3968
4383
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
3969
4384
  }, children: [
3970
- /* @__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" }) }),
4385
+ /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("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" }) }),
3971
4386
  displayError
3972
4387
  ] })
3973
4388
  ]
3974
4389
  }
3975
4390
  ),
3976
- showStripe && isCardView && /* @__PURE__ */ jsx6("div", { style: {
4391
+ showStripe && isCardView && /* @__PURE__ */ jsx7("div", { style: {
3977
4392
  gridArea: "1 / 1",
3978
4393
  ...cardAnim ? { animation: cardAnim } : {},
3979
4394
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
3980
4395
  }, children: cardFormBlock }),
3981
- showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx6("div", { style: {
4396
+ showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx7("div", { style: {
3982
4397
  gridArea: "1 / 1",
3983
4398
  ...apmAnim ? { animation: apmAnim } : {},
3984
4399
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
@@ -4016,7 +4431,7 @@ function SplitCardFormInner({
4016
4431
  },
4017
4432
  "aria-label": "Back to payment methods",
4018
4433
  children: [
4019
- /* @__PURE__ */ jsx6("span", { style: {
4434
+ /* @__PURE__ */ jsx7("span", { style: {
4020
4435
  display: "inline-flex",
4021
4436
  alignItems: "center",
4022
4437
  justifyContent: "center",
@@ -4026,12 +4441,12 @@ function SplitCardFormInner({
4026
4441
  backgroundColor: "#f3f4f6",
4027
4442
  transition: "background-color 0.15s",
4028
4443
  ...bStyles.backButtonIcon
4029
- }, 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" }) }) }),
4030
- /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
4444
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
4445
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4031
4446
  ]
4032
4447
  }
4033
4448
  ),
4034
- /* @__PURE__ */ jsx6("div", { style: {
4449
+ /* @__PURE__ */ jsx7("div", { style: {
4035
4450
  flex: 1,
4036
4451
  textAlign: "center",
4037
4452
  fontWeight: 600,
@@ -4041,12 +4456,12 @@ function SplitCardFormInner({
4041
4456
  ...bStyles.title
4042
4457
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
4043
4458
  ] }),
4044
- /* @__PURE__ */ jsx6(
4459
+ /* @__PURE__ */ jsx7(
4045
4460
  StripeElements,
4046
4461
  {
4047
4462
  stripe: stripeInstance,
4048
4463
  options: apmInlineOptions,
4049
- children: /* @__PURE__ */ jsx6(
4464
+ children: /* @__PURE__ */ jsx7(
4050
4465
  StripeMethodInlineForm,
4051
4466
  {
4052
4467
  method: expandedApmMethod,
@@ -4064,7 +4479,8 @@ function SplitCardFormInner({
4064
4479
  isProcessing: isSubmitting,
4065
4480
  submitButtonColor: resolvedPrimaryColor,
4066
4481
  submitButtonBorderRadius: resolvedBorderRadius,
4067
- submitButtonStyle: bStyles.submitButton
4482
+ submitButtonStyle: bStyles.submitButton,
4483
+ errorText: displayError
4068
4484
  }
4069
4485
  )
4070
4486
  },
@@ -4076,9 +4492,9 @@ function SplitCardFormInner({
4076
4492
  }
4077
4493
  if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
4078
4494
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
4079
- /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
4080
- overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4081
- /* @__PURE__ */ jsx6("div", { style: {
4495
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4496
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4497
+ /* @__PURE__ */ jsx7("div", { style: {
4082
4498
  ...apmAnim ? { animation: apmAnim } : {},
4083
4499
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
4084
4500
  }, children: /* @__PURE__ */ jsxs4("div", { style: {
@@ -4115,7 +4531,7 @@ function SplitCardFormInner({
4115
4531
  },
4116
4532
  "aria-label": "Back to payment methods",
4117
4533
  children: [
4118
- /* @__PURE__ */ jsx6("span", { style: {
4534
+ /* @__PURE__ */ jsx7("span", { style: {
4119
4535
  display: "inline-flex",
4120
4536
  alignItems: "center",
4121
4537
  justifyContent: "center",
@@ -4125,12 +4541,12 @@ function SplitCardFormInner({
4125
4541
  backgroundColor: "#f3f4f6",
4126
4542
  transition: "background-color 0.15s",
4127
4543
  ...bStyles.backButtonIcon
4128
- }, 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" }) }) }),
4129
- /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
4544
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
4545
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4130
4546
  ]
4131
4547
  }
4132
4548
  ),
4133
- /* @__PURE__ */ jsx6("div", { style: {
4549
+ /* @__PURE__ */ jsx7("div", { style: {
4134
4550
  flex: 1,
4135
4551
  textAlign: "center",
4136
4552
  fontWeight: 600,
@@ -4140,12 +4556,12 @@ function SplitCardFormInner({
4140
4556
  ...bStyles.title
4141
4557
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
4142
4558
  ] }),
4143
- /* @__PURE__ */ jsx6(
4559
+ /* @__PURE__ */ jsx7(
4144
4560
  StripeElements,
4145
4561
  {
4146
4562
  stripe: stripeInstance,
4147
4563
  options: apmInlineOptions,
4148
- children: /* @__PURE__ */ jsx6(
4564
+ children: /* @__PURE__ */ jsx7(
4149
4565
  StripeMethodInlineForm,
4150
4566
  {
4151
4567
  method: expandedApmMethod,
@@ -4163,7 +4579,8 @@ function SplitCardFormInner({
4163
4579
  isProcessing: isSubmitting,
4164
4580
  submitButtonColor: resolvedPrimaryColor,
4165
4581
  submitButtonBorderRadius: resolvedBorderRadius,
4166
- submitButtonStyle: bStyles.submitButton
4582
+ submitButtonStyle: bStyles.submitButton,
4583
+ errorText: displayError
4167
4584
  }
4168
4585
  )
4169
4586
  },
@@ -4173,13 +4590,13 @@ function SplitCardFormInner({
4173
4590
  ] });
4174
4591
  }
4175
4592
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
4176
- /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
4177
- overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4593
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4594
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4178
4595
  /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
4179
4596
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
4180
4597
  renderStripeGateDebug("flopay-stripe-gate-debug-default"),
4181
4598
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
4182
- paypalDirectRetry && /* @__PURE__ */ jsx6(
4599
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
4183
4600
  "div",
4184
4601
  {
4185
4602
  "data-testid": "flopay-paypal-direct-retry-notice-default",
@@ -4195,7 +4612,7 @@ function SplitCardFormInner({
4195
4612
  children: "Please confirm your PayPal payment to complete checkout."
4196
4613
  }
4197
4614
  ),
4198
- /* @__PURE__ */ jsx6(
4615
+ /* @__PURE__ */ jsx7(
4199
4616
  DirectPayPalButton,
4200
4617
  {
4201
4618
  sessionId,
@@ -4221,7 +4638,7 @@ function SplitCardFormInner({
4221
4638
  paypalDirectRetry?.orderId ?? "fresh"
4222
4639
  )
4223
4640
  ] }),
4224
- shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
4641
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
4225
4642
  PayPalButtonInner,
4226
4643
  {
4227
4644
  sessionId,
@@ -4235,10 +4652,10 @@ function SplitCardFormInner({
4235
4652
  onDecline,
4236
4653
  runBeforeButtonClick,
4237
4654
  onLoadStateChange: setPaypalLoadState,
4238
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4655
+ placeholderBorderRadius: buttonBorderRadius
4239
4656
  }
4240
4657
  ) }),
4241
- shouldRenderWallets && /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
4658
+ shouldRenderWallets && /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
4242
4659
  WalletButtonInner,
4243
4660
  {
4244
4661
  sessionId,
@@ -4252,10 +4669,10 @@ function SplitCardFormInner({
4252
4669
  onDecline,
4253
4670
  runBeforeButtonClick,
4254
4671
  onLoadStateChange: setWalletLoadState,
4255
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4672
+ placeholderBorderRadius: buttonBorderRadius
4256
4673
  }
4257
4674
  ) }),
4258
- shouldRenderPaymentElement && /* @__PURE__ */ jsx6(
4675
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
4259
4676
  StripePaymentElementInner,
4260
4677
  {
4261
4678
  sessionId,
@@ -4283,7 +4700,7 @@ function SplitCardFormInner({
4283
4700
  backgroundColor: bStyles.cardButton?.backgroundColor,
4284
4701
  borderColor: bStyles.cardInputBorder,
4285
4702
  textColor: bStyles.cardButton?.color,
4286
- borderRadius: bStyles.cardButton?.borderRadius
4703
+ borderRadius: buttonBorderRadius
4287
4704
  }
4288
4705
  }
4289
4706
  )
@@ -4296,9 +4713,9 @@ function SplitCardFormInner({
4296
4713
  color: "#999",
4297
4714
  fontSize: "0.85rem"
4298
4715
  }, children: [
4299
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
4300
- /* @__PURE__ */ jsx6("span", { children: "or pay with card" }),
4301
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
4716
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
4717
+ /* @__PURE__ */ jsx7("span", { children: "or pay with card" }),
4718
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
4302
4719
  ] }),
4303
4720
  showStripe && cardFormBlock
4304
4721
  ] });
@@ -4341,6 +4758,210 @@ function getRedirectResultFromCheckoutProcessError(error) {
4341
4758
  paymentMethodId: error.paymentMethodId
4342
4759
  };
4343
4760
  }
4761
+ function isTerminalAutoCheckoutThreeDsOutcome(outcome) {
4762
+ return outcome.status === "succeeded" || outcome.status === "declined";
4763
+ }
4764
+ async function runAutoCheckoutThreeDsChallenge(input) {
4765
+ if (typeof document === "undefined" || typeof window === "undefined") {
4766
+ throw new FloPayError4("3DS challenge requires a browser environment.", "api_error", {
4767
+ code: "three_ds_no_window"
4768
+ });
4769
+ }
4770
+ if (input.signal?.aborted) {
4771
+ throw new FloPayError4("Card authentication was cancelled.", "api_error", {
4772
+ code: "three_ds_aborted"
4773
+ });
4774
+ }
4775
+ const CHALLENGE_TIMEOUT_MS = 3e5;
4776
+ const CHALLENGE_POLL_INTERVAL_MS = 1e3;
4777
+ const COMPLETE_REQUEST_TIMEOUT_MS = 15e3;
4778
+ const completionEndpoint = `${input.billingApiUrl.replace(/\/$/, "")}/v1/checkouts/sessions/${encodeURIComponent(
4779
+ input.sessionId
4780
+ )}/3ds/complete`;
4781
+ const completeThreeDs = async () => {
4782
+ const controller = new AbortController();
4783
+ const onParentAbort = () => controller.abort();
4784
+ if (input.signal?.aborted) {
4785
+ controller.abort();
4786
+ } else {
4787
+ input.signal?.addEventListener("abort", onParentAbort);
4788
+ }
4789
+ const requestTimeout = window.setTimeout(() => controller.abort(), COMPLETE_REQUEST_TIMEOUT_MS);
4790
+ try {
4791
+ const response = await fetch(completionEndpoint, {
4792
+ method: "POST",
4793
+ headers: {
4794
+ "x-checkout-session-token": input.nonce,
4795
+ "content-type": "application/json"
4796
+ },
4797
+ body: "{}",
4798
+ signal: controller.signal
4799
+ });
4800
+ if (!response.ok) {
4801
+ return { status: "unknown" };
4802
+ }
4803
+ const json = await response.json().catch(() => null);
4804
+ return coerceThreeDsOutcome(json);
4805
+ } catch {
4806
+ return { status: "unknown" };
4807
+ } finally {
4808
+ window.clearTimeout(requestTimeout);
4809
+ input.signal?.removeEventListener("abort", onParentAbort);
4810
+ }
4811
+ };
4812
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
4813
+ const backdrop = document.createElement("div");
4814
+ backdrop.setAttribute("data-flopay-auto-3ds-overlay", "1");
4815
+ backdrop.setAttribute("role", "dialog");
4816
+ backdrop.setAttribute("aria-modal", "true");
4817
+ backdrop.setAttribute("aria-label", "Card authentication");
4818
+ backdrop.style.cssText = [
4819
+ "position:fixed",
4820
+ "inset:0",
4821
+ "z-index:2147483647",
4822
+ "background:rgba(15,23,42,0.6)",
4823
+ "display:flex",
4824
+ "align-items:center",
4825
+ "justify-content:center",
4826
+ "padding:16px"
4827
+ ].join(";");
4828
+ const frame = document.createElement("iframe");
4829
+ frame.setAttribute("title", "Card authentication");
4830
+ frame.setAttribute("allow", "payment");
4831
+ frame.tabIndex = 0;
4832
+ frame.style.cssText = [
4833
+ "width:min(100%,460px)",
4834
+ "height:min(100%,640px)",
4835
+ "border:0",
4836
+ "border-radius:12px",
4837
+ "background:#fff",
4838
+ "box-shadow:0 12px 30px rgba(0,0,0,0.35)"
4839
+ ].join(";");
4840
+ frame.src = input.nextActionRedirectUrl;
4841
+ backdrop.appendChild(frame);
4842
+ document.body.appendChild(backdrop);
4843
+ frame.focus();
4844
+ let expectedReturnOrigin = null;
4845
+ try {
4846
+ expectedReturnOrigin = new URL(input.billingApiUrl, window.location.href).origin;
4847
+ } catch {
4848
+ expectedReturnOrigin = null;
4849
+ }
4850
+ try {
4851
+ const polledOutcome = await new Promise((resolve, reject) => {
4852
+ let settled = false;
4853
+ let timer = 0;
4854
+ let pollTimer = 0;
4855
+ let loadPollTimer = 0;
4856
+ let pollInFlight = false;
4857
+ const cleanup = () => {
4858
+ window.clearTimeout(timer);
4859
+ window.clearInterval(pollTimer);
4860
+ window.clearTimeout(loadPollTimer);
4861
+ window.removeEventListener("message", listener);
4862
+ frame.removeEventListener("load", onFrameLoad);
4863
+ input.signal?.removeEventListener("abort", onAbort);
4864
+ };
4865
+ const maybeResolveFromBackend = async () => {
4866
+ if (settled || pollInFlight) return;
4867
+ pollInFlight = true;
4868
+ try {
4869
+ const outcome = await completeThreeDs();
4870
+ if (settled || !isTerminalAutoCheckoutThreeDsOutcome(outcome)) return;
4871
+ settled = true;
4872
+ cleanup();
4873
+ resolve(outcome);
4874
+ } finally {
4875
+ pollInFlight = false;
4876
+ }
4877
+ };
4878
+ const onFrameLoad = () => {
4879
+ window.clearTimeout(loadPollTimer);
4880
+ loadPollTimer = window.setTimeout(() => {
4881
+ void maybeResolveFromBackend();
4882
+ }, 250);
4883
+ };
4884
+ const onAbort = () => {
4885
+ if (settled) return;
4886
+ settled = true;
4887
+ cleanup();
4888
+ reject(
4889
+ new FloPayError4("Card authentication was cancelled.", "api_error", {
4890
+ code: "three_ds_aborted"
4891
+ })
4892
+ );
4893
+ };
4894
+ const listener = (event) => {
4895
+ if (event.source !== frame.contentWindow) return;
4896
+ if (expectedReturnOrigin && event.origin !== expectedReturnOrigin) return;
4897
+ const data = event.data;
4898
+ if (!data || typeof data !== "object") return;
4899
+ const record = data;
4900
+ if (record["source"] !== "flopay-vault-3ds-return") return;
4901
+ if (settled) return;
4902
+ settled = true;
4903
+ cleanup();
4904
+ resolve(null);
4905
+ };
4906
+ timer = window.setTimeout(() => {
4907
+ if (settled) return;
4908
+ settled = true;
4909
+ cleanup();
4910
+ reject(
4911
+ new FloPayError4("Card authentication timed out.", "api_error", {
4912
+ code: "three_ds_timeout"
4913
+ })
4914
+ );
4915
+ }, CHALLENGE_TIMEOUT_MS);
4916
+ pollTimer = window.setInterval(() => {
4917
+ void maybeResolveFromBackend();
4918
+ }, CHALLENGE_POLL_INTERVAL_MS);
4919
+ frame.addEventListener("load", onFrameLoad);
4920
+ window.addEventListener("message", listener);
4921
+ input.signal?.addEventListener("abort", onAbort);
4922
+ if (input.signal?.aborted) onAbort();
4923
+ });
4924
+ if (polledOutcome) {
4925
+ return polledOutcome;
4926
+ }
4927
+ return await completeThreeDs();
4928
+ } finally {
4929
+ backdrop.parentNode?.removeChild(backdrop);
4930
+ if (previousFocus?.isConnected) {
4931
+ previousFocus.focus();
4932
+ }
4933
+ }
4934
+ }
4935
+ function coerceThreeDsOutcome(json) {
4936
+ if (!json || typeof json !== "object") return { status: "unknown" };
4937
+ const status = typeof json["status"] === "string" ? json["status"] : "unknown";
4938
+ const providerIntentId = typeof json["providerIntentId"] === "string" ? json["providerIntentId"] : null;
4939
+ switch (status) {
4940
+ case "succeeded":
4941
+ return { status: "succeeded", providerIntentId };
4942
+ case "declined":
4943
+ return {
4944
+ status: "declined",
4945
+ providerIntentId,
4946
+ declineReason: typeof json["declineReason"] === "string" ? json["declineReason"] : null,
4947
+ gatewayDeclineReason: typeof json["gatewayDeclineReason"] === "string" ? json["gatewayDeclineReason"] : null
4948
+ };
4949
+ case "requires_action":
4950
+ return {
4951
+ status: "requires_action",
4952
+ providerIntentId,
4953
+ nextActionRedirectUrl: typeof json["nextActionRedirectUrl"] === "string" ? json["nextActionRedirectUrl"] : null
4954
+ };
4955
+ case "pending":
4956
+ return { status: "pending", providerIntentId };
4957
+ case "timeout":
4958
+ return { status: "timeout", providerIntentId };
4959
+ case "no_attempt":
4960
+ return { status: "no_attempt" };
4961
+ default:
4962
+ return { status: "unknown" };
4963
+ }
4964
+ }
4344
4965
  function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment failed. Please try again.", options) {
4345
4966
  const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
4346
4967
  const rawMessage = error?.message ?? fallbackMessage;
@@ -4782,7 +5403,7 @@ async function loadSavedPaymentProviders({
4782
5403
  }
4783
5404
 
4784
5405
  // src/flopay-checkout.tsx
4785
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
5406
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
4786
5407
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
4787
5408
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
4788
5409
  var sessionInflightMap = /* @__PURE__ */ new Map();
@@ -4831,6 +5452,36 @@ function clearPayPalResumeState() {
4831
5452
  console.warn("[FloPayCheckout] Failed to clear PayPal resume state.", error);
4832
5453
  }
4833
5454
  }
5455
+ function readCachedInlineSession(cacheKey) {
5456
+ if (!canUseStorage()) return null;
5457
+ const raw = window.sessionStorage.getItem(cacheKey);
5458
+ if (!raw) return null;
5459
+ try {
5460
+ const parsed = JSON.parse(raw);
5461
+ if (parsed && typeof parsed.sid === "string" && parsed.sid) {
5462
+ return { sid: parsed.sid, nonce: typeof parsed.nonce === "string" ? parsed.nonce : void 0 };
5463
+ }
5464
+ return null;
5465
+ } catch {
5466
+ return { sid: raw };
5467
+ }
5468
+ }
5469
+ function persistCachedInlineSession(cacheKey, sid, nonce) {
5470
+ if (!canUseStorage()) return;
5471
+ try {
5472
+ window.sessionStorage.setItem(cacheKey, JSON.stringify(nonce ? { sid, nonce } : { sid }));
5473
+ } catch (error) {
5474
+ console.warn("[FloPayCheckout] Failed to persist checkout session cache.", error);
5475
+ }
5476
+ }
5477
+ function clearCachedInlineSession(cacheKey) {
5478
+ if (!canUseStorage()) return;
5479
+ try {
5480
+ window.sessionStorage.removeItem(cacheKey);
5481
+ } catch (error) {
5482
+ console.warn("[FloPayCheckout] Failed to clear checkout session cache.", error);
5483
+ }
5484
+ }
4834
5485
  function clearPayPalRedirectParams() {
4835
5486
  if (typeof window === "undefined") return;
4836
5487
  const url = new URL(window.location.href);
@@ -4889,6 +5540,8 @@ function FloPayCheckout({
4889
5540
  onButtonClick,
4890
5541
  onBeforeButtonClick,
4891
5542
  enableAVS,
5543
+ cardFieldOrder,
5544
+ cardPreFormSlot,
4892
5545
  avsLayout,
4893
5546
  submitLabel,
4894
5547
  className,
@@ -4906,9 +5559,9 @@ function FloPayCheckout({
4906
5559
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
4907
5560
  const [unified, setUnified] = useState4(null);
4908
5561
  const [flopay, setFloPay] = useState4(null);
4909
- const flopayRef = useRef4(null);
5562
+ const flopayRef = useRef5(null);
4910
5563
  const [paypalFlopay, setPaypalFloPay] = useState4(null);
4911
- const paypalFlopayRef = useRef4(null);
5564
+ const paypalFlopayRef = useRef5(null);
4912
5565
  const [session, setSession] = useState4(null);
4913
5566
  const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
4914
5567
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
@@ -4923,18 +5576,18 @@ function FloPayCheckout({
4923
5576
  const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
4924
5577
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
4925
5578
  const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
4926
- const autoCheckoutAttempted = useRef4(false);
4927
- const paypalResumeAttempted = useRef4(false);
4928
- const savedPaymentKeysRef = useRef4(null);
4929
- const onCompleteRef = useRef4(onComplete);
5579
+ const autoCheckoutAttempted = useRef5(false);
5580
+ const paypalResumeAttempted = useRef5(false);
5581
+ const savedPaymentKeysRef = useRef5(null);
5582
+ const onCompleteRef = useRef5(onComplete);
4930
5583
  onCompleteRef.current = onComplete;
4931
- const onErrorRef = useRef4(onError);
5584
+ const onErrorRef = useRef5(onError);
4932
5585
  onErrorRef.current = onError;
4933
- const onDeclineRef = useRef4(onDecline);
5586
+ const onDeclineRef = useRef5(onDecline);
4934
5587
  onDeclineRef.current = onDecline;
4935
- const onSessionCompletedRef = useRef4(onSessionCompleted);
5588
+ const onSessionCompletedRef = useRef5(onSessionCompleted);
4936
5589
  onSessionCompletedRef.current = onSessionCompleted;
4937
- useEffect5(() => {
5590
+ useEffect6(() => {
4938
5591
  console.info("[FloPay] Checkout initialized", {
4939
5592
  sdk_version: SDK_VERSION,
4940
5593
  checkout_type: checkoutType,
@@ -4962,11 +5615,11 @@ function FloPayCheckout({
4962
5615
  } : void 0,
4963
5616
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
4964
5617
  );
4965
- useEffect5(() => {
5618
+ useEffect6(() => {
4966
5619
  setCreateSessionPatch(void 0);
4967
5620
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
4968
5621
  }, [baseCreateSessionHash]);
4969
- useEffect5(() => {
5622
+ useEffect6(() => {
4970
5623
  setModeError(initialErrorMessage);
4971
5624
  }, [initialErrorMessage]);
4972
5625
  const emitDecline = useCallback2(
@@ -5093,7 +5746,7 @@ function FloPayCheckout({
5093
5746
  resolvedBillingUrl
5094
5747
  ]
5095
5748
  );
5096
- useEffect5(() => {
5749
+ useEffect6(() => {
5097
5750
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
5098
5751
  return;
5099
5752
  }
@@ -5210,7 +5863,7 @@ function FloPayCheckout({
5210
5863
  }
5211
5864
  })();
5212
5865
  }, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
5213
- const initializedHashRef = useRef4(null);
5866
+ const initializedHashRef = useRef5(null);
5214
5867
  function hashCreateParams(params) {
5215
5868
  const key = JSON.stringify({
5216
5869
  c: params?.clientId,
@@ -5247,12 +5900,12 @@ function FloPayCheckout({
5247
5900
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
5248
5901
  [effectiveCreateSession]
5249
5902
  );
5250
- const createSessionParamsRef = useRef4(effectiveCreateSession);
5903
+ const createSessionParamsRef = useRef5(effectiveCreateSession);
5251
5904
  createSessionParamsRef.current = effectiveCreateSession;
5252
- useEffect5(() => {
5905
+ useEffect6(() => {
5253
5906
  setResolvedSessionId(sessionIdProp ?? "");
5254
5907
  }, [sessionIdProp]);
5255
- useEffect5(() => {
5908
+ useEffect6(() => {
5256
5909
  autoCheckoutAttempted.current = false;
5257
5910
  setModeError(initialErrorMessage);
5258
5911
  setModeOverlayError(null);
@@ -5260,22 +5913,23 @@ function FloPayCheckout({
5260
5913
  }, [createSessionHash, initialErrorMessage, sessionIdProp]);
5261
5914
  async function resolveInlineSession(params, cacheKey) {
5262
5915
  const api = new PaymentAPI4(resolvedBillingUrl);
5263
- let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
5916
+ const cached = readCachedInlineSession(cacheKey);
5917
+ let sid = cached?.sid ?? null;
5264
5918
  let realResult = null;
5265
5919
  if (sid) {
5266
5920
  try {
5267
- realResult = await api.getUnifiedCheckoutSession(sid);
5921
+ realResult = await api.getUnifiedCheckoutSession(sid, cached?.nonce);
5268
5922
  const status = realResult.data.session?.status;
5269
5923
  if (status === "complete") {
5270
5924
  if (wasSessionRecentlyCompleted(sid)) {
5271
5925
  return { sid, result: realResult };
5272
5926
  }
5273
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
5927
+ clearCachedInlineSession(cacheKey);
5274
5928
  sid = null;
5275
5929
  realResult = null;
5276
5930
  }
5277
5931
  } catch {
5278
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
5932
+ clearCachedInlineSession(cacheKey);
5279
5933
  sid = null;
5280
5934
  }
5281
5935
  }
@@ -5289,8 +5943,8 @@ function FloPayCheckout({
5289
5943
  };
5290
5944
  realResult = await api.createAndFetchSession(paramsWithAnalytics);
5291
5945
  sid = realResult.data.session?.id ?? "";
5292
- if (sid && typeof window !== "undefined") {
5293
- window.sessionStorage.setItem(cacheKey, sid);
5946
+ if (sid) {
5947
+ persistCachedInlineSession(cacheKey, sid, realResult.data.session?.clientSecret);
5294
5948
  }
5295
5949
  }
5296
5950
  return { sid: sid ?? "", result: realResult };
@@ -5379,7 +6033,7 @@ function FloPayCheckout({
5379
6033
  resolvedSessionId,
5380
6034
  session
5381
6035
  ]);
5382
- useEffect5(() => {
6036
+ useEffect6(() => {
5383
6037
  let cancelled = false;
5384
6038
  setLoadError(null);
5385
6039
  if (createSessionHash) {
@@ -5601,7 +6255,7 @@ function FloPayCheckout({
5601
6255
  ]
5602
6256
  );
5603
6257
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && !isPaypalOnlySession && (!flopay || !providerOptions);
5604
- const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx7(
6258
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx8(
5605
6259
  ProcessingOverlay,
5606
6260
  {
5607
6261
  status: modeOverlayStatus,
@@ -5617,7 +6271,7 @@ function FloPayCheckout({
5617
6271
  }
5618
6272
  if (layout === "buttons") {
5619
6273
  const bundleRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
5620
- const skeletonBar = (h) => /* @__PURE__ */ jsx7("div", { style: {
6274
+ const skeletonBar = (h) => /* @__PURE__ */ jsx8("div", { style: {
5621
6275
  height: h,
5622
6276
  borderRadius: bundleRadius,
5623
6277
  background: "#e5e7eb",
@@ -5628,14 +6282,14 @@ function FloPayCheckout({
5628
6282
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5629
6283
  showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5630
6284
  showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5631
- /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6285
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
5632
6286
  ] }),
5633
6287
  modeOverlay
5634
6288
  ] });
5635
6289
  }
5636
6290
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5637
6291
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
5638
- /* @__PURE__ */ jsx7("div", { style: {
6292
+ /* @__PURE__ */ jsx8("div", { style: {
5639
6293
  width: 24,
5640
6294
  height: 24,
5641
6295
  border: "2px solid #e5e7eb",
@@ -5643,16 +6297,16 @@ function FloPayCheckout({
5643
6297
  borderRadius: "50%",
5644
6298
  animation: "spin 0.6s linear infinite"
5645
6299
  } }),
5646
- /* @__PURE__ */ jsx7("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
6300
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
5647
6301
  ] }),
5648
6302
  modeOverlay
5649
6303
  ] });
5650
6304
  }
5651
6305
  if (loadError) {
5652
6306
  if (errorNode) {
5653
- return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
6307
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
5654
6308
  }
5655
- return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
6309
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
5656
6310
  "div",
5657
6311
  {
5658
6312
  role: "alert",
@@ -5669,7 +6323,7 @@ function FloPayCheckout({
5669
6323
  }
5670
6324
  if (shouldShowInterimButtons) {
5671
6325
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5672
- /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
6326
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
5673
6327
  InterimButtonsView,
5674
6328
  {
5675
6329
  onButtonClick,
@@ -5690,8 +6344,8 @@ function FloPayCheckout({
5690
6344
  }
5691
6345
  if (isPaypalOnlySession && session && directPaypalConfig) {
5692
6346
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5693
- /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs5("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
5694
- modeError && /* @__PURE__ */ jsx7(
6347
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs5("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
6348
+ modeError && /* @__PURE__ */ jsx8(
5695
6349
  "div",
5696
6350
  {
5697
6351
  role: "alert",
@@ -5708,7 +6362,7 @@ function FloPayCheckout({
5708
6362
  children: modeError
5709
6363
  }
5710
6364
  ),
5711
- /* @__PURE__ */ jsx7(
6365
+ /* @__PURE__ */ jsx8(
5712
6366
  DirectPayPalButton,
5713
6367
  {
5714
6368
  sessionId: activeSessionId,
@@ -5732,12 +6386,12 @@ function FloPayCheckout({
5732
6386
  ] });
5733
6387
  }
5734
6388
  if (!flopay || !providerOptions) {
5735
- return /* @__PURE__ */ jsx7(Fragment3, { children: modeOverlay });
6389
+ return /* @__PURE__ */ jsx8(Fragment3, { children: modeOverlay });
5736
6390
  }
5737
6391
  if (currentMode === "confirm") {
5738
6392
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5739
- /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs5("div", { className, children: [
5740
- modeError && /* @__PURE__ */ jsx7(
6393
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs5("div", { className, children: [
6394
+ modeError && /* @__PURE__ */ jsx8(
5741
6395
  "div",
5742
6396
  {
5743
6397
  style: {
@@ -5752,7 +6406,7 @@ function FloPayCheckout({
5752
6406
  renderConfirmButton ? renderConfirmButton({
5753
6407
  onConfirm: handleConfirmCheckout,
5754
6408
  isProcessing: confirmProcessing
5755
- }) : /* @__PURE__ */ jsx7(
6409
+ }) : /* @__PURE__ */ jsx8(
5756
6410
  "button",
5757
6411
  {
5758
6412
  type: "button",
@@ -5778,8 +6432,8 @@ function FloPayCheckout({
5778
6432
  ] });
5779
6433
  }
5780
6434
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5781
- /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
5782
- modeError && /* @__PURE__ */ jsx7(
6435
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
6436
+ modeError && /* @__PURE__ */ jsx8(
5783
6437
  "div",
5784
6438
  {
5785
6439
  style: {
@@ -5794,7 +6448,7 @@ function FloPayCheckout({
5794
6448
  children: modeError
5795
6449
  }
5796
6450
  ),
5797
- /* @__PURE__ */ jsx7(
6451
+ /* @__PURE__ */ jsx8(
5798
6452
  SessionInjector,
5799
6453
  {
5800
6454
  sessionId: activeSessionId,
@@ -5804,7 +6458,7 @@ function FloPayCheckout({
5804
6458
  children
5805
6459
  }
5806
6460
  )
5807
- ] }) : /* @__PURE__ */ jsx7(
6461
+ ] }) : /* @__PURE__ */ jsx8(
5808
6462
  SplitCardForm,
5809
6463
  {
5810
6464
  sessionId: activeSessionId,
@@ -5824,6 +6478,7 @@ function FloPayCheckout({
5824
6478
  showPayPal,
5825
6479
  showStripe,
5826
6480
  enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
6481
+ enabledPaymentMethodCountries: unified?.data.stripe?.enabledPaymentMethodCountries,
5827
6482
  showApplePay,
5828
6483
  showGooglePay,
5829
6484
  layout,
@@ -5837,6 +6492,8 @@ function FloPayCheckout({
5837
6492
  onButtonClick,
5838
6493
  onBeforeButtonClick,
5839
6494
  enableAVS,
6495
+ ...cardFieldOrder ? { cardFieldOrder } : {},
6496
+ ...cardPreFormSlot ? { cardPreFormSlot } : {},
5840
6497
  avsLayout,
5841
6498
  country: session?.customer?.country,
5842
6499
  city: session?.customer?.city,
@@ -5864,8 +6521,8 @@ function SessionInjector({
5864
6521
  session,
5865
6522
  children
5866
6523
  }) {
5867
- return /* @__PURE__ */ jsx7(Fragment3, { children: React7.Children.map(children, (child) => {
5868
- if (!React7.isValidElement(child)) return child;
6524
+ return /* @__PURE__ */ jsx8(Fragment3, { children: React8.Children.map(children, (child) => {
6525
+ if (!React8.isValidElement(child)) return child;
5869
6526
  const existing = child.props;
5870
6527
  const injected = {};
5871
6528
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -5880,7 +6537,7 @@ function SessionInjector({
5880
6537
  injected.lastName = session.customer.lastName;
5881
6538
  }
5882
6539
  if (Object.keys(injected).length === 0) return child;
5883
- return React7.cloneElement(child, injected);
6540
+ return React8.cloneElement(child, injected);
5884
6541
  }) });
5885
6542
  }
5886
6543
  function InterimButtonsView({
@@ -5902,7 +6559,7 @@ function InterimButtonsView({
5902
6559
  }) {
5903
6560
  const [showCardForm, setShowCardForm] = useState4(false);
5904
6561
  const isCardOpenControlled = typeof cardOpen === "boolean";
5905
- useEffect5(() => {
6562
+ useEffect6(() => {
5906
6563
  if (isCardOpenControlled) {
5907
6564
  setShowCardForm(cardOpen);
5908
6565
  }
@@ -5923,7 +6580,7 @@ function InterimButtonsView({
5923
6580
  };
5924
6581
  }, [themeBundle, buttonsTheme, stylesOverride]);
5925
6582
  const skeletonRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
5926
- const skeleton = (h) => /* @__PURE__ */ jsx7("div", { style: {
6583
+ const skeleton = (h) => /* @__PURE__ */ jsx8("div", { style: {
5927
6584
  height: h,
5928
6585
  borderRadius: skeletonRadius,
5929
6586
  background: "#e5e7eb",
@@ -5970,7 +6627,7 @@ function InterimButtonsView({
5970
6627
  ...bStyles.backButton
5971
6628
  },
5972
6629
  children: [
5973
- /* @__PURE__ */ jsx7("span", { style: {
6630
+ /* @__PURE__ */ jsx8("span", { style: {
5974
6631
  display: "inline-flex",
5975
6632
  alignItems: "center",
5976
6633
  justifyContent: "center",
@@ -5979,12 +6636,12 @@ function InterimButtonsView({
5979
6636
  borderRadius: "50%",
5980
6637
  backgroundColor: "#f3f4f6",
5981
6638
  ...bStyles.backButtonIcon
5982
- }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
5983
- /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
6639
+ }, children: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "M15 18l-6-6 6-6" }) }) }),
6640
+ /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
5984
6641
  ]
5985
6642
  }
5986
6643
  ),
5987
- hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
6644
+ hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
5988
6645
  flex: 1,
5989
6646
  textAlign: "center",
5990
6647
  fontWeight: 600,
@@ -5992,11 +6649,11 @@ function InterimButtonsView({
5992
6649
  color: "#262833",
5993
6650
  paddingRight: 80,
5994
6651
  ...bStyles.title
5995
- }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
6652
+ }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
5996
6653
  ] }),
5997
- /* @__PURE__ */ jsx7("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6654
+ /* @__PURE__ */ jsx8("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx8("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
5998
6655
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex" }, children: [
5999
- /* @__PURE__ */ jsx7("div", { style: {
6656
+ /* @__PURE__ */ jsx8("div", { style: {
6000
6657
  flex: 1,
6001
6658
  backgroundColor: inputBg,
6002
6659
  borderTop: "none",
@@ -6006,8 +6663,8 @@ function InterimButtonsView({
6006
6663
  borderBottomLeftRadius: 8,
6007
6664
  padding: 12,
6008
6665
  height: 45
6009
- }, children: /* @__PURE__ */ jsx7("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6010
- /* @__PURE__ */ jsx7("div", { style: {
6666
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6667
+ /* @__PURE__ */ jsx8("div", { style: {
6011
6668
  flex: 1,
6012
6669
  backgroundColor: inputBg,
6013
6670
  borderTop: "none",
@@ -6017,10 +6674,10 @@ function InterimButtonsView({
6017
6674
  borderBottomRightRadius: 8,
6018
6675
  padding: 12,
6019
6676
  height: 45
6020
- }, children: /* @__PURE__ */ jsx7("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
6677
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
6021
6678
  ] }),
6022
- /* @__PURE__ */ jsx7("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6023
- /* @__PURE__ */ jsx7("div", { style: {
6679
+ /* @__PURE__ */ jsx8("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx8("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6680
+ /* @__PURE__ */ jsx8("div", { style: {
6024
6681
  height: 50,
6025
6682
  borderRadius: 8,
6026
6683
  marginTop: 16,
@@ -6029,7 +6686,7 @@ function InterimButtonsView({
6029
6686
  ...bStyles.submitButton,
6030
6687
  opacity: 0.5
6031
6688
  } }),
6032
- /* @__PURE__ */ jsx7("style", { children: `
6689
+ /* @__PURE__ */ jsx8("style", { children: `
6033
6690
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
6034
6691
  @keyframes flopay-interim-expand {
6035
6692
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -6042,7 +6699,7 @@ function InterimButtonsView({
6042
6699
  return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
6043
6700
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
6044
6701
  showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
6045
- showStripe && /* @__PURE__ */ jsx7(
6702
+ showStripe && /* @__PURE__ */ jsx8(
6046
6703
  "button",
6047
6704
  {
6048
6705
  type: "button",
@@ -6082,7 +6739,7 @@ function InterimButtonsView({
6082
6739
  onMouseUp: (e) => {
6083
6740
  e.currentTarget.style.transform = "scale(1)";
6084
6741
  },
6085
- children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
6742
+ children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
6086
6743
  }
6087
6744
  ),
6088
6745
  errorMessage && /* @__PURE__ */ jsxs5("div", { style: {
@@ -6099,22 +6756,22 @@ function InterimButtonsView({
6099
6756
  gap: "0.5rem",
6100
6757
  ...bStyles.errorBanner
6101
6758
  }, children: [
6102
- /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("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" }) }),
6759
+ /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx8("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" }) }),
6103
6760
  errorMessage
6104
6761
  ] }),
6105
- /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6762
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6106
6763
  ] });
6107
6764
  }
6108
6765
 
6109
6766
  // src/checkout-form.tsx
6110
6767
  import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
6111
6768
  import { FloPayError as FloPayError6 } from "@flopay/shared";
6112
- import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect6, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
6113
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6769
+ import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
6770
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
6114
6771
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
6115
6772
  var CheckoutForm = forwardRef2(
6116
6773
  function CheckoutForm2(props, ref) {
6117
- return /* @__PURE__ */ jsx8(CheckoutFormInner, { ...props, innerRef: ref });
6774
+ return /* @__PURE__ */ jsx9(CheckoutFormInner, { ...props, innerRef: ref });
6118
6775
  }
6119
6776
  );
6120
6777
  function CheckoutFormInner({
@@ -6313,7 +6970,7 @@ function CheckoutFormInner({
6313
6970
  }
6314
6971
  }
6315
6972
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
6316
- useEffect6(() => {
6973
+ useEffect7(() => {
6317
6974
  if (typeof window === "undefined") return;
6318
6975
  const stored = localStorage.getItem(WALLET_RESUME_KEY);
6319
6976
  if (!stored) return;
@@ -6424,7 +7081,7 @@ function CheckoutFormInner({
6424
7081
  );
6425
7082
  const isReady = flopay !== null && elements !== null;
6426
7083
  return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
6427
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-overlay", style: {
7084
+ (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-overlay", style: {
6428
7085
  position: "absolute",
6429
7086
  inset: 0,
6430
7087
  background: "rgba(255,255,255,0.7)",
@@ -6433,12 +7090,12 @@ function CheckoutFormInner({
6433
7090
  justifyContent: "center",
6434
7091
  zIndex: 10
6435
7092
  }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
6436
- !isReady && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
7093
+ !isReady && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
6437
7094
  isReady && /* @__PURE__ */ jsxs6(Fragment4, { children: [
6438
- /* @__PURE__ */ jsx8(PaymentElement, { options: { layout } }),
6439
- showAddress && /* @__PURE__ */ jsx8(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
6440
- displayError && /* @__PURE__ */ jsx8("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
6441
- children ?? /* @__PURE__ */ jsx8(
7095
+ /* @__PURE__ */ jsx9(PaymentElement, { options: { layout } }),
7096
+ showAddress && /* @__PURE__ */ jsx9(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
7097
+ displayError && /* @__PURE__ */ jsx9("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
7098
+ children ?? /* @__PURE__ */ jsx9(
6442
7099
  "button",
6443
7100
  {
6444
7101
  type: "submit",
@@ -6454,8 +7111,8 @@ function CheckoutFormInner({
6454
7111
  // src/paypal-button.tsx
6455
7112
  import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
6456
7113
  import { FloPayError as FloPayError7 } from "@flopay/shared";
6457
- import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState6 } from "react";
6458
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
7114
+ import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef6, useState as useState6 } from "react";
7115
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
6459
7116
  function PayPalButton({
6460
7117
  sessionId,
6461
7118
  nonce,
@@ -6475,7 +7132,7 @@ function PayPalButton({
6475
7132
  const contextBillingUrl = useBillingApiUrl();
6476
7133
  const [ready, setReady] = useState6(false);
6477
7134
  const [submitting, setSubmitting] = useState6(false);
6478
- const paypalResumeAttempted = useRef5(false);
7135
+ const paypalResumeAttempted = useRef6(false);
6479
7136
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
6480
7137
  const processPaymentInternal = useCallback4(
6481
7138
  async (tokenizedBody) => {
@@ -6515,7 +7172,7 @@ function PayPalButton({
6515
7172
  },
6516
7173
  [onTokenizedBody, processPaymentInternal]
6517
7174
  );
6518
- useEffect7(() => {
7175
+ useEffect8(() => {
6519
7176
  if (!flopay || paypalResumeAttempted.current) return;
6520
7177
  const params = new URLSearchParams(window.location.search);
6521
7178
  const paymentIntentId = params.get("payment_intent");
@@ -6597,11 +7254,11 @@ function PayPalButton({
6597
7254
  }
6598
7255
  }, [flopay, elements, sessionId, nonce, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
6599
7256
  if (!flopay || !elements) {
6600
- return /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
7257
+ return /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
6601
7258
  }
6602
7259
  return /* @__PURE__ */ jsxs7(Fragment5, { children: [
6603
- !ready && /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
6604
- /* @__PURE__ */ jsx9("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx9(
7260
+ !ready && /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
7261
+ /* @__PURE__ */ jsx10("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx10(
6605
7262
  "button",
6606
7263
  {
6607
7264
  type: "button",
@@ -6623,7 +7280,7 @@ function PayPalButton({
6623
7280
  children: submitting ? "Processing..." : "PayPal"
6624
7281
  }
6625
7282
  ) }),
6626
- (submitting || isProcessing) && /* @__PURE__ */ jsx9("div", { style: {
7283
+ (submitting || isProcessing) && /* @__PURE__ */ jsx10("div", { style: {
6627
7284
  position: "fixed",
6628
7285
  inset: 0,
6629
7286
  background: "rgba(0,0,0,0.4)",
@@ -6631,7 +7288,7 @@ function PayPalButton({
6631
7288
  alignItems: "center",
6632
7289
  justifyContent: "center",
6633
7290
  zIndex: 1e3
6634
- }, children: /* @__PURE__ */ jsx9("div", { style: {
7291
+ }, children: /* @__PURE__ */ jsx10("div", { style: {
6635
7292
  background: "white",
6636
7293
  borderRadius: 8,
6637
7294
  padding: "1.5rem",
@@ -6643,10 +7300,10 @@ function PayPalButton({
6643
7300
  }
6644
7301
 
6645
7302
  // src/automatic-payment-button.tsx
6646
- import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
7303
+ import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef7, useState as useState7 } from "react";
6647
7304
  import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
6648
7305
  import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
6649
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
7306
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
6650
7307
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
6651
7308
  function sleep2(ms) {
6652
7309
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -6714,6 +7371,7 @@ function FloPayAutomaticPaymentButton({
6714
7371
  billingApiUrl,
6715
7372
  locale,
6716
7373
  theme,
7374
+ appearance,
6717
7375
  buttonsTheme,
6718
7376
  buttonsStyles: stylesOverride,
6719
7377
  onSuccess,
@@ -6761,30 +7419,32 @@ function FloPayAutomaticPaymentButton({
6761
7419
  const [overlayStatus, setOverlayStatus] = useState7(null);
6762
7420
  const [overlayError, setOverlayError] = useState7(null);
6763
7421
  const [fallbackSession, setFallbackSession] = useState7(null);
6764
- const isMountedRef = useRef6(true);
6765
- const fallbackSessionRef = useRef6(fallbackSession);
6766
- const onSuccessRef = useRef6(onSuccess);
6767
- const onErrorRef = useRef6(onError);
6768
- const onDeclineRef = useRef6(onDecline);
6769
- useEffect8(() => {
7422
+ const isMountedRef = useRef7(true);
7423
+ const threeDsAbortRef = useRef7(null);
7424
+ const fallbackSessionRef = useRef7(fallbackSession);
7425
+ const onSuccessRef = useRef7(onSuccess);
7426
+ const onErrorRef = useRef7(onError);
7427
+ const onDeclineRef = useRef7(onDecline);
7428
+ useEffect9(() => {
6770
7429
  fallbackSessionRef.current = fallbackSession;
6771
7430
  }, [fallbackSession]);
6772
- useEffect8(() => {
7431
+ useEffect9(() => {
6773
7432
  onSuccessRef.current = onSuccess;
6774
7433
  }, [onSuccess]);
6775
- useEffect8(() => {
7434
+ useEffect9(() => {
6776
7435
  onErrorRef.current = onError;
6777
7436
  }, [onError]);
6778
- useEffect8(() => {
7437
+ useEffect9(() => {
6779
7438
  onDeclineRef.current = onDecline;
6780
7439
  }, [onDecline]);
6781
- useEffect8(() => {
7440
+ useEffect9(() => {
6782
7441
  isMountedRef.current = true;
6783
7442
  return () => {
6784
7443
  isMountedRef.current = false;
7444
+ threeDsAbortRef.current?.abort();
6785
7445
  };
6786
7446
  }, []);
6787
- useEffect8(() => {
7447
+ useEffect9(() => {
6788
7448
  if (!fallbackSession || typeof window === "undefined") {
6789
7449
  return;
6790
7450
  }
@@ -6863,6 +7523,62 @@ function FloPayAutomaticPaymentButton({
6863
7523
  return;
6864
7524
  }
6865
7525
  if (apiResult.autoProcessingError) {
7526
+ if (apiResult.autoProcessingError.type === "3ds_required" && apiResult.autoProcessingError.nextActionRedirectUrl && resolvedSessionId && effectiveNonce) {
7527
+ const threeDsAbort = new AbortController();
7528
+ threeDsAbortRef.current = threeDsAbort;
7529
+ let outcome;
7530
+ try {
7531
+ outcome = await runAutoCheckoutThreeDsChallenge({
7532
+ billingApiUrl: resolvedBillingUrl,
7533
+ sessionId: resolvedSessionId,
7534
+ nonce: effectiveNonce,
7535
+ nextActionRedirectUrl: apiResult.autoProcessingError.nextActionRedirectUrl,
7536
+ signal: threeDsAbort.signal
7537
+ });
7538
+ } finally {
7539
+ if (threeDsAbortRef.current === threeDsAbort) {
7540
+ threeDsAbortRef.current = null;
7541
+ }
7542
+ }
7543
+ if (outcome.status === "declined") {
7544
+ throw checkoutProcessErrorToFloPayError(
7545
+ {
7546
+ type: outcome.declineReason ?? "decline",
7547
+ message: "Card was declined.",
7548
+ gatewayErrorCode: outcome.gatewayDeclineReason ?? void 0
7549
+ },
7550
+ "Card was declined.",
7551
+ {
7552
+ checkoutMethod: apiResult.autoProcessingError.checkoutMethod
7553
+ }
7554
+ );
7555
+ }
7556
+ const api = new PaymentAPI7(resolvedBillingUrl);
7557
+ const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
7558
+ initialDelayMs: 0,
7559
+ nonce: effectiveNonce
7560
+ });
7561
+ const completedSession = completed.data.session;
7562
+ if (!completedSession || completedSession.status !== "complete") {
7563
+ throw checkoutProcessErrorToFloPayError(
7564
+ {
7565
+ type: "unknown",
7566
+ message: "Card authentication was not completed."
7567
+ },
7568
+ "Card authentication was not completed.",
7569
+ {
7570
+ checkoutMethod: apiResult.autoProcessingError.checkoutMethod
7571
+ }
7572
+ );
7573
+ }
7574
+ await showSuccess({
7575
+ result: { status: "succeeded" },
7576
+ session: completedSession,
7577
+ sessionId: completedSession.id || resolvedSessionId,
7578
+ autoCompleted: false
7579
+ });
7580
+ return;
7581
+ }
6866
7582
  throw checkoutProcessErrorToFloPayError(
6867
7583
  apiResult.autoProcessingError,
6868
7584
  "Automatic payment failed. Please try again.",
@@ -7030,9 +7746,26 @@ function FloPayAutomaticPaymentButton({
7030
7746
  cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
7031
7747
  };
7032
7748
  }, [themeBundle, buttonsTheme, stylesOverride]);
7749
+ const resolvedAppearance = useMemo5(() => {
7750
+ const bundleAppearance = themeBundle?.appearance;
7751
+ if (!appearance) return bundleAppearance;
7752
+ if (!bundleAppearance) return appearance;
7753
+ return {
7754
+ ...bundleAppearance,
7755
+ ...appearance,
7756
+ variables: {
7757
+ ...bundleAppearance.variables,
7758
+ ...appearance.variables
7759
+ }
7760
+ };
7761
+ }, [appearance, themeBundle]);
7762
+ const resolvedAppearanceVars = resolvedAppearance?.variables;
7763
+ const resolvedPrimaryColor = resolvedAppearanceVars?.colorPrimary ?? "#4A49FF";
7764
+ const resolvedPrimaryHoverColor = resolvedAppearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
7765
+ const resolvedButtonBorderRadius = resolvedAppearanceVars?.borderRadius ?? "8px";
7033
7766
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
7034
7767
  return /* @__PURE__ */ jsxs8(Fragment6, { children: [
7035
- /* @__PURE__ */ jsx10(
7768
+ /* @__PURE__ */ jsx11(
7036
7769
  "button",
7037
7770
  {
7038
7771
  ...buttonProps,
@@ -7048,14 +7781,17 @@ function FloPayAutomaticPaymentButton({
7048
7781
  // on top so the auto-pay button carries the *submit* button's
7049
7782
  // background and text colours — making it visually identical to
7050
7783
  // the "Confirm Payment" / "Pay with X" actions it stands in for.
7784
+ // `resolvedPrimaryColor` is the *merged* appearance value, so a
7785
+ // per-checkout `colorPrimary` override re-skins this button too.
7051
7786
  // Inline `style` consumer override stays at the end so explicit
7052
7787
  // per-button overrides still win.
7053
7788
  ...bStyles.cardButton,
7054
7789
  ...derivePrimaryTileStyle({
7055
7790
  themeBundle,
7056
- resolvedPrimaryColor: themeBundle?.appearance.variables?.colorPrimary ?? "#4A49FF",
7057
- resolvedBorderRadius: themeBundle?.appearance.variables?.borderRadius ?? "8px",
7058
- submitButtonStyle: bStyles.submitButton
7791
+ resolvedPrimaryColor,
7792
+ resolvedBorderRadius: resolvedButtonBorderRadius,
7793
+ submitButtonStyle: bStyles.submitButton,
7794
+ explicitPrimaryColor: resolvedAppearanceVars?.colorPrimary
7059
7795
  }),
7060
7796
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
7061
7797
  fontWeight: 600,
@@ -7064,11 +7800,23 @@ function FloPayAutomaticPaymentButton({
7064
7800
  alignItems: "center",
7065
7801
  justifyContent: "center",
7066
7802
  gap: "0.625rem",
7067
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
7803
+ transition: "background-color 0.15s, border-color 0.2s, box-shadow 0.2s, transform 0.1s",
7068
7804
  position: "relative",
7069
7805
  opacity: disabled || isProcessing ? 0.6 : 1,
7070
7806
  ...style
7071
7807
  },
7808
+ onMouseEnter: (e) => {
7809
+ buttonProps.onMouseEnter?.(e);
7810
+ if (!e.defaultPrevented && !disabled && !isProcessing && (themeBundle || resolvedAppearanceVars?.colorPrimary)) {
7811
+ e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
7812
+ }
7813
+ },
7814
+ onMouseLeave: (e) => {
7815
+ buttonProps.onMouseLeave?.(e);
7816
+ if (!e.defaultPrevented && (themeBundle || resolvedAppearanceVars?.colorPrimary)) {
7817
+ e.currentTarget.style.backgroundColor = resolvedPrimaryColor;
7818
+ }
7819
+ },
7072
7820
  onMouseDown: (e) => {
7073
7821
  buttonProps.onMouseDown?.(e);
7074
7822
  if (!e.defaultPrevented) {
@@ -7081,17 +7829,17 @@ function FloPayAutomaticPaymentButton({
7081
7829
  e.currentTarget.style.transform = "scale(1)";
7082
7830
  }
7083
7831
  },
7084
- children: /* @__PURE__ */ jsx10(CardButtonContentSlot, { content: children })
7832
+ children: /* @__PURE__ */ jsx11(CardButtonContentSlot, { content: children })
7085
7833
  }
7086
7834
  ),
7087
- overlayStatus && /* @__PURE__ */ jsx10(
7835
+ overlayStatus && /* @__PURE__ */ jsx11(
7088
7836
  ProcessingOverlay,
7089
7837
  {
7090
7838
  status: overlayStatus,
7091
7839
  errorMessage: overlayError
7092
7840
  }
7093
7841
  ),
7094
- fallbackSession && /* @__PURE__ */ jsx10(
7842
+ fallbackSession && /* @__PURE__ */ jsx11(
7095
7843
  "div",
7096
7844
  {
7097
7845
  "data-testid": "flopay-automatic-payment-fallback",
@@ -7112,7 +7860,7 @@ function FloPayAutomaticPaymentButton({
7112
7860
  padding: "1.5rem",
7113
7861
  zIndex: 1100
7114
7862
  },
7115
- children: /* @__PURE__ */ jsx10(
7863
+ children: /* @__PURE__ */ jsx11(
7116
7864
  "div",
7117
7865
  {
7118
7866
  style: {
@@ -7128,7 +7876,7 @@ function FloPayAutomaticPaymentButton({
7128
7876
  flexDirection: "column",
7129
7877
  gap: "1rem"
7130
7878
  },
7131
- children: /* @__PURE__ */ jsx10(
7879
+ children: /* @__PURE__ */ jsx11(
7132
7880
  FloPayCheckout,
7133
7881
  {
7134
7882
  sessionId: fallbackSession.sessionId,
@@ -7137,6 +7885,7 @@ function FloPayAutomaticPaymentButton({
7137
7885
  billingApiUrl: resolvedBillingUrl,
7138
7886
  locale,
7139
7887
  theme,
7888
+ ...resolvedAppearance ? { appearance: resolvedAppearance } : {},
7140
7889
  buttonsTheme,
7141
7890
  buttonsStyles: stylesOverride,
7142
7891
  initialErrorMessage: fallbackSession.errorMessage,
@@ -7166,6 +7915,7 @@ export {
7166
7915
  PayPalButton,
7167
7916
  PaymentElement,
7168
7917
  SplitCardForm,
7918
+ VaultCardFields,
7169
7919
  useCheckout,
7170
7920
  useElements,
7171
7921
  useFloPay,