@flopay/react 1.2.8 → 1.3.1

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,10 @@ import {
248
248
  resolveAVSConfig,
249
249
  isAVSFieldVisible,
250
250
  getStateFromPostalCode,
251
- filterStripeMethodsByCurrency,
251
+ isPostalCodeSupported,
252
+ isValidPostalCode,
253
+ getPostalCodeExample,
252
254
  filterStripeMethodsByCountry,
253
- filterStripeMethodsByAmount,
254
255
  getStripeMethodDisplayName,
255
256
  hasVendoredStripeMethodLogo,
256
257
  needsStripeMethodExplicitConfirm,
@@ -259,7 +260,80 @@ import {
259
260
  stripeExpressMethodToOptionKey
260
261
  } from "@flopay/shared";
261
262
  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";
263
+
264
+ // src/vault-card-fields.tsx
265
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
266
+ import { jsx as jsx4 } from "react/jsx-runtime";
267
+ function VaultCardFields({
268
+ capture,
269
+ html,
270
+ messageToken,
271
+ expectedOrigin,
272
+ theme,
273
+ containerStyle,
274
+ onReady,
275
+ onError,
276
+ onValidation
277
+ }) {
278
+ const containerRef = useRef2(null);
279
+ const onReadyRef = useRef2(onReady);
280
+ const onErrorRef = useRef2(onError);
281
+ const onValidationRef = useRef2(onValidation);
282
+ onReadyRef.current = onReady;
283
+ onErrorRef.current = onError;
284
+ onValidationRef.current = onValidation;
285
+ const themeRef = useRef2(theme);
286
+ themeRef.current = theme;
287
+ useEffect3(() => {
288
+ const el = containerRef.current;
289
+ if (!el) return;
290
+ let active = true;
291
+ let readyEmitted = false;
292
+ const emitReadyOnce = () => {
293
+ if (!active || readyEmitted) return;
294
+ readyEmitted = true;
295
+ onReadyRef.current?.();
296
+ };
297
+ const offReady = capture.on("ready", () => {
298
+ emitReadyOnce();
299
+ });
300
+ const offError = capture.on("error", (event) => {
301
+ onErrorRef.current?.(event.message ?? "There was a problem loading the secure card form.");
302
+ });
303
+ const offValidation = capture.on("validation", (event) => {
304
+ onValidationRef.current?.(event.message ?? null);
305
+ });
306
+ const mountOptions = {
307
+ html,
308
+ ...messageToken ? { messageToken } : {},
309
+ ...expectedOrigin ? { expectedOrigin } : {},
310
+ ...themeRef.current ? { theme: themeRef.current } : {}
311
+ };
312
+ capture.mount(el, mountOptions).then(() => {
313
+ emitReadyOnce();
314
+ }).catch((err) => {
315
+ if (active) {
316
+ onErrorRef.current?.(
317
+ err instanceof Error ? err.message : "Failed to load the secure card form."
318
+ );
319
+ }
320
+ });
321
+ return () => {
322
+ active = false;
323
+ offReady();
324
+ offError();
325
+ offValidation();
326
+ capture.unmount();
327
+ };
328
+ }, [capture, html, messageToken, expectedOrigin]);
329
+ useEffect3(() => {
330
+ if (theme) capture.applyTheme?.(theme);
331
+ }, [capture, theme]);
332
+ return /* @__PURE__ */ jsx4("div", { ref: containerRef, "data-testid": "flopay-vault-card-fields", style: containerStyle });
333
+ }
334
+
335
+ // src/split-card-form.tsx
336
+ import React7, { forwardRef, useCallback, useContext as useContext3, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
263
337
 
264
338
  // src/hooks.ts
265
339
  import { useContext as useContext2 } from "react";
@@ -286,14 +360,14 @@ function useBillingApiUrl() {
286
360
 
287
361
  // src/processing-overlay.tsx
288
362
  import "react";
289
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
363
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
290
364
  var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
291
365
  var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
292
366
  function ProcessingOverlay({
293
367
  status,
294
368
  errorMessage
295
369
  }) {
296
- return /* @__PURE__ */ jsx4(
370
+ return /* @__PURE__ */ jsx5(
297
371
  "div",
298
372
  {
299
373
  "data-testid": "flopay-processing-overlay",
@@ -337,14 +411,14 @@ function ProcessingOverlay({
337
411
  xmlns: "http://www.w3.org/2000/svg",
338
412
  style: { animation: "flopay-spin 0.8s linear infinite" },
339
413
  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" })
414
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
415
+ /* @__PURE__ */ jsx5("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
342
416
  ]
343
417
  }
344
418
  ),
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(
419
+ 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: [
420
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
421
+ /* @__PURE__ */ jsx5(
348
422
  "path",
349
423
  {
350
424
  d: "M7 12.5l3 3 7-7",
@@ -360,9 +434,9 @@ function ProcessingOverlay({
360
434
  }
361
435
  )
362
436
  ] }) }),
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(
437
+ 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: [
438
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
439
+ /* @__PURE__ */ jsx5(
366
440
  "path",
367
441
  {
368
442
  d: "M8 8l8 8M16 8l-8 8",
@@ -394,7 +468,7 @@ function ProcessingOverlay({
394
468
  ]
395
469
  }
396
470
  ),
397
- status === "success" && /* @__PURE__ */ jsx4(
471
+ status === "success" && /* @__PURE__ */ jsx5(
398
472
  "p",
399
473
  {
400
474
  style: {
@@ -408,7 +482,7 @@ function ProcessingOverlay({
408
482
  children: "You will be automatically redirected, do not close or navigate away from this window."
409
483
  }
410
484
  ),
411
- status === "error" && errorMessage && /* @__PURE__ */ jsx4(
485
+ status === "error" && errorMessage && /* @__PURE__ */ jsx5(
412
486
  "p",
413
487
  {
414
488
  style: {
@@ -422,7 +496,7 @@ function ProcessingOverlay({
422
496
  children: errorMessage
423
497
  }
424
498
  ),
425
- /* @__PURE__ */ jsx4("style", { children: `
499
+ /* @__PURE__ */ jsx5("style", { children: `
426
500
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
427
501
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
428
502
  @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
@@ -696,11 +770,11 @@ function isInAppBrowser(userAgent) {
696
770
  }
697
771
 
698
772
  // src/direct-paypal-button.tsx
699
- import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
773
+ import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState2 } from "react";
700
774
  import { loadScript } from "@paypal/paypal-js";
701
775
  import { PaymentAPI } from "@flopay/js";
702
776
  import { FloPayError as FloPayError2, normalizeGatewayEnvironment } from "@flopay/shared";
703
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
777
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
704
778
  var DEFAULT_BUTTON_HEIGHT = 45;
705
779
  function DirectPayPalButton({
706
780
  sessionId,
@@ -723,7 +797,7 @@ function DirectPayPalButton({
723
797
  existingOrderId,
724
798
  debug = false
725
799
  }) {
726
- const containerRef = useRef2(null);
800
+ const containerRef = useRef3(null);
727
801
  const [ready, setReady] = useState2(false);
728
802
  const [failed, setFailed] = useState2(false);
729
803
  const [submitting, setSubmitting] = useState2(false);
@@ -733,52 +807,52 @@ function DirectPayPalButton({
733
807
  if (!debug) return;
734
808
  setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
735
809
  };
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(() => {
810
+ const onTokenizedBodyRef = useRef3(onTokenizedBody);
811
+ const onCompleteRef = useRef3(onComplete);
812
+ const onErrorChangeRef = useRef3(onErrorChange);
813
+ const onDeclineRef = useRef3(onDecline);
814
+ const onButtonClickRef = useRef3(onButtonClick);
815
+ const onLoadStateChangeRef = useRef3(onLoadStateChange);
816
+ const runBeforeButtonClickRef = useRef3(runBeforeButtonClick);
817
+ const sessionRef = useRef3(session);
818
+ const emailRef = useRef3(email);
819
+ const nonceRef = useRef3(nonce);
820
+ const beforeClickRef = useRef3(null);
821
+ useEffect4(() => {
748
822
  onTokenizedBodyRef.current = onTokenizedBody;
749
823
  }, [onTokenizedBody]);
750
- useEffect3(() => {
824
+ useEffect4(() => {
751
825
  onCompleteRef.current = onComplete;
752
826
  }, [onComplete]);
753
- useEffect3(() => {
827
+ useEffect4(() => {
754
828
  onErrorChangeRef.current = onErrorChange;
755
829
  }, [onErrorChange]);
756
- useEffect3(() => {
830
+ useEffect4(() => {
757
831
  onDeclineRef.current = onDecline;
758
832
  }, [onDecline]);
759
- useEffect3(() => {
833
+ useEffect4(() => {
760
834
  onButtonClickRef.current = onButtonClick;
761
835
  }, [onButtonClick]);
762
- useEffect3(() => {
836
+ useEffect4(() => {
763
837
  onLoadStateChangeRef.current = onLoadStateChange;
764
838
  }, [onLoadStateChange]);
765
- useEffect3(() => {
839
+ useEffect4(() => {
766
840
  runBeforeButtonClickRef.current = runBeforeButtonClick;
767
841
  }, [runBeforeButtonClick]);
768
- useEffect3(() => {
842
+ useEffect4(() => {
769
843
  sessionRef.current = session;
770
844
  }, [session]);
771
- useEffect3(() => {
845
+ useEffect4(() => {
772
846
  emailRef.current = email;
773
847
  }, [email]);
774
- useEffect3(() => {
848
+ useEffect4(() => {
775
849
  nonceRef.current = nonce;
776
850
  }, [nonce]);
777
- useEffect3(() => {
851
+ useEffect4(() => {
778
852
  onLoadStateChangeRef.current?.(ready && !failed);
779
853
  }, [ready, failed]);
780
854
  const normalizedEnv = normalizeGatewayEnvironment(environment);
781
- useEffect3(() => {
855
+ useEffect4(() => {
782
856
  const maskedClient = clientId ? `${clientId.slice(0, 6)}\u2026(len ${clientId.length})` : "(empty)";
783
857
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "(no navigator)";
784
858
  appendDebug(`mount clientId=${maskedClient} env=${environment ?? "(unset)"}\u2192${normalizedEnv ?? "live"} ccy=${currency} sub=${isSubscription}`);
@@ -1215,7 +1289,7 @@ ${debugLines.join("\n")}`
1215
1289
  }
1216
1290
  ) : null;
1217
1291
  if (failed) {
1218
- return debug ? /* @__PURE__ */ jsx5("div", { children: debugPanel }) : null;
1292
+ return debug ? /* @__PURE__ */ jsx6("div", { children: debugPanel }) : null;
1219
1293
  }
1220
1294
  return (
1221
1295
  // Single wrapper so the parent flex container sees exactly one flex item
@@ -1225,7 +1299,7 @@ ${debugLines.join("\n")}`
1225
1299
  /* @__PURE__ */ jsxs3("div", { children: [
1226
1300
  debugPanel,
1227
1301
  /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT }, children: [
1228
- !ready && /* @__PURE__ */ jsx5(
1302
+ !ready && /* @__PURE__ */ jsx6(
1229
1303
  "div",
1230
1304
  {
1231
1305
  "data-testid": "flopay-direct-paypal-placeholder",
@@ -1239,7 +1313,7 @@ ${debugLines.join("\n")}`
1239
1313
  }
1240
1314
  }
1241
1315
  ),
1242
- /* @__PURE__ */ jsx5(
1316
+ /* @__PURE__ */ jsx6(
1243
1317
  "div",
1244
1318
  {
1245
1319
  ref: containerRef,
@@ -1259,10 +1333,28 @@ ${debugLines.join("\n")}`
1259
1333
 
1260
1334
  // src/split-card-form.tsx
1261
1335
  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";
1336
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1263
1337
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1264
1338
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1265
1339
  var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
1340
+ function toVaultMount(block) {
1341
+ if (!block?.html) return null;
1342
+ return {
1343
+ html: block.html,
1344
+ ...block.messageToken ? { messageToken: block.messageToken } : {},
1345
+ ...block.expectedOrigin ? { expectedOrigin: block.expectedOrigin } : {}
1346
+ };
1347
+ }
1348
+ function darkenHex(hex, amount = 0.12) {
1349
+ const match = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim());
1350
+ if (!match) return hex;
1351
+ const value = parseInt(match[1], 16);
1352
+ const scale = Math.max(0, Math.min(1, 1 - amount));
1353
+ const r = Math.round((value >> 16 & 255) * scale);
1354
+ const g = Math.round((value >> 8 & 255) * scale);
1355
+ const b = Math.round((value & 255) * scale);
1356
+ return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)}`;
1357
+ }
1266
1358
  var FLOPAY_KEYFRAMES = `
1267
1359
  .paypal-buttons { margin: 0 !important; vertical-align: top !important; }
1268
1360
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
@@ -1287,18 +1379,20 @@ var FLOPAY_KEYFRAMES = `
1287
1379
  `;
1288
1380
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT = 44;
1289
1381
  function derivePrimaryTileStyle(opts) {
1290
- if (!opts.themeBundle) {
1382
+ if (!opts.themeBundle && !opts.explicitPrimaryColor) {
1291
1383
  return {
1292
1384
  backgroundColor: "white",
1293
1385
  color: "#262833",
1294
1386
  border: "1px solid #d1d5db",
1295
- borderRadius: "8px",
1387
+ borderRadius: opts.resolvedBorderRadius,
1296
1388
  boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
1297
1389
  };
1298
1390
  }
1299
- const submit = opts.submitButtonStyle ?? {};
1391
+ const submit = opts.themeBundle ? opts.submitButtonStyle ?? {} : {};
1300
1392
  return {
1301
- backgroundColor: submit.backgroundColor ?? opts.resolvedPrimaryColor,
1393
+ // `colorPrimary` wins so a per-checkout override re-skins the card / auto-pay
1394
+ // buttons in lock-step with the submit CTA (which also keys off it).
1395
+ backgroundColor: opts.resolvedPrimaryColor,
1302
1396
  color: submit.color ?? "white",
1303
1397
  border: submit.border ?? "none",
1304
1398
  borderRadius: submit.borderRadius ?? opts.resolvedBorderRadius,
@@ -1325,6 +1419,18 @@ function getButtonMethodLabel(method) {
1325
1419
  return "Card";
1326
1420
  }
1327
1421
  }
1422
+ function malformedPostcodeMessage(country) {
1423
+ const example = getPostalCodeExample(country);
1424
+ return `Enter a valid ${getPostalCodeLabel(country)}${example ? ` (e.g. ${example})` : ""}`;
1425
+ }
1426
+ function computePostalCodeState(country, zip, visible) {
1427
+ const supported = isPostalCodeSupported(country);
1428
+ const trimmed = zip.trim();
1429
+ const required = visible && supported;
1430
+ const empty = required && !trimmed;
1431
+ const malformed = required && !!trimmed && !isValidPostalCode(country, trimmed);
1432
+ return { visible, supported, required, empty, malformed };
1433
+ }
1328
1434
  function normalizeBeforeButtonClickError(method, err) {
1329
1435
  return err instanceof FloPayError3 ? err : new FloPayError3(
1330
1436
  err instanceof Error ? err.message : `${getButtonMethodLabel(method)} before-click hook failed.`,
@@ -1332,7 +1438,7 @@ function normalizeBeforeButtonClickError(method, err) {
1332
1438
  );
1333
1439
  }
1334
1440
  function FloPayKeyframes() {
1335
- return /* @__PURE__ */ jsx6("style", { children: FLOPAY_KEYFRAMES });
1441
+ return /* @__PURE__ */ jsx7("style", { children: FLOPAY_KEYFRAMES });
1336
1442
  }
1337
1443
  function toCssSize(value) {
1338
1444
  if (typeof value === "number") return `${value}px`;
@@ -1355,7 +1461,7 @@ function ExpressCheckoutReadySwap({
1355
1461
  }) {
1356
1462
  if (state === "unavailable" || state === "load_error") return null;
1357
1463
  return /* @__PURE__ */ jsxs4("div", { style: { position: "relative", minHeight: 44 }, children: [
1358
- /* @__PURE__ */ jsx6(
1464
+ /* @__PURE__ */ jsx7(
1359
1465
  "div",
1360
1466
  {
1361
1467
  "data-testid": placeholderTestId,
@@ -1374,7 +1480,7 @@ function ExpressCheckoutReadySwap({
1374
1480
  }
1375
1481
  }
1376
1482
  ),
1377
- /* @__PURE__ */ jsx6(
1483
+ /* @__PURE__ */ jsx7(
1378
1484
  "div",
1379
1485
  {
1380
1486
  style: {
@@ -1394,7 +1500,7 @@ function isExpressCheckoutRowVisible(state) {
1394
1500
  }
1395
1501
  var SplitCardForm = forwardRef(
1396
1502
  function SplitCardForm2(props, ref) {
1397
- return /* @__PURE__ */ jsx6(SplitCardFormInner, { ...props, innerRef: ref });
1503
+ return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props, innerRef: ref });
1398
1504
  }
1399
1505
  );
1400
1506
  function PayPalButtonInner({
@@ -1414,14 +1520,14 @@ function PayPalButtonInner({
1414
1520
  const stripe = useStripeRaw();
1415
1521
  const elements = useStripeElements();
1416
1522
  const [loadState, setLoadState] = useState3("loading");
1417
- useEffect4(() => {
1523
+ useEffect5(() => {
1418
1524
  onLoadStateChange?.(loadState);
1419
1525
  }, [loadState, onLoadStateChange]);
1420
1526
  const [submitting, setSubmitting] = useState3(false);
1421
- const paypalResumeAttempted = useRef3(false);
1422
- const beforeClickRef = useRef3(null);
1527
+ const paypalResumeAttempted = useRef4(false);
1528
+ const beforeClickRef = useRef4(null);
1423
1529
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
1424
- useEffect4(() => {
1530
+ useEffect5(() => {
1425
1531
  if (!stripe || paypalResumeAttempted.current) return;
1426
1532
  const params = new URLSearchParams(window.location.search);
1427
1533
  const paymentIntentId = params.get("payment_intent");
@@ -1627,13 +1733,13 @@ function PayPalButtonInner({
1627
1733
  }
1628
1734
  }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
1629
1735
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1630
- /* @__PURE__ */ jsx6(
1736
+ /* @__PURE__ */ jsx7(
1631
1737
  ExpressCheckoutReadySwap,
1632
1738
  {
1633
1739
  state: loadState,
1634
1740
  placeholderTestId: "flopay-paypal-placeholder",
1635
1741
  borderRadius: placeholderBorderRadius,
1636
- children: /* @__PURE__ */ jsx6(
1742
+ children: /* @__PURE__ */ jsx7(
1637
1743
  ExpressCheckoutElement,
1638
1744
  {
1639
1745
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -1660,7 +1766,7 @@ function PayPalButtonInner({
1660
1766
  )
1661
1767
  }
1662
1768
  ),
1663
- submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
1769
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
1664
1770
  ] });
1665
1771
  }
1666
1772
  function WalletButtonInner({
@@ -1680,13 +1786,13 @@ function WalletButtonInner({
1680
1786
  const stripe = useStripeRaw();
1681
1787
  const elements = useStripeElements();
1682
1788
  const [loadState, setLoadState] = useState3("loading");
1683
- useEffect4(() => {
1789
+ useEffect5(() => {
1684
1790
  onLoadStateChange?.(loadState);
1685
1791
  }, [loadState, onLoadStateChange]);
1686
1792
  const [submitting, setSubmitting] = useState3(false);
1687
1793
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
1688
- const lastWalletMethodRef = useRef3("card");
1689
- const beforeClickRef = useRef3(null);
1794
+ const lastWalletMethodRef = useRef4("card");
1795
+ const beforeClickRef = useRef4(null);
1690
1796
  const handleWalletConfirm = useCallback(
1691
1797
  async (event) => {
1692
1798
  if (!stripe || !elements) return;
@@ -1796,13 +1902,13 @@ function WalletButtonInner({
1796
1902
  [expressMethods]
1797
1903
  );
1798
1904
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1799
- /* @__PURE__ */ jsx6(
1905
+ /* @__PURE__ */ jsx7(
1800
1906
  ExpressCheckoutReadySwap,
1801
1907
  {
1802
1908
  state: loadState,
1803
1909
  placeholderTestId: "flopay-wallet-placeholder",
1804
1910
  borderRadius: placeholderBorderRadius,
1805
- children: /* @__PURE__ */ jsx6(
1911
+ children: /* @__PURE__ */ jsx7(
1806
1912
  ExpressCheckoutElement,
1807
1913
  {
1808
1914
  onReady: (event) => {
@@ -1841,7 +1947,7 @@ function WalletButtonInner({
1841
1947
  )
1842
1948
  }
1843
1949
  ),
1844
- submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
1950
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
1845
1951
  ] });
1846
1952
  }
1847
1953
  function StripeMethodButton({
@@ -1862,7 +1968,7 @@ function StripeMethodButton({
1862
1968
  const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
1863
1969
  const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
1864
1970
  const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
1865
- return /* @__PURE__ */ jsx6(
1971
+ return /* @__PURE__ */ jsx7(
1866
1972
  "button",
1867
1973
  {
1868
1974
  type: "button",
@@ -1909,7 +2015,7 @@ function StripeMethodButton({
1909
2015
  // Pulse-skeleton parity with the wallet ECE: no spinner, just the
1910
2016
  // status text on top of the pulsing background. Keeps the loading
1911
2017
  // affordance shape-equivalent across both kinds of tile.
1912
- /* @__PURE__ */ jsx6("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
2018
+ /* @__PURE__ */ jsx7("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
1913
2019
  ) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
1914
2020
  /* @__PURE__ */ jsxs4(
1915
2021
  "span",
@@ -1924,7 +2030,7 @@ function StripeMethodButton({
1924
2030
  gap: hasVendoredStripeMethodLogo(method) ? 8 : 0
1925
2031
  },
1926
2032
  children: [
1927
- brand?.logoSvg && /* @__PURE__ */ jsx6(
2033
+ brand?.logoSvg && /* @__PURE__ */ jsx7(
1928
2034
  "span",
1929
2035
  {
1930
2036
  "aria-hidden": "true",
@@ -1943,11 +2049,11 @@ function StripeMethodButton({
1943
2049
  dangerouslySetInnerHTML: { __html: brand.logoSvg }
1944
2050
  }
1945
2051
  ),
1946
- /* @__PURE__ */ jsx6("span", { children: getStripeMethodDisplayName(method) })
2052
+ /* @__PURE__ */ jsx7("span", { children: getStripeMethodDisplayName(method) })
1947
2053
  ]
1948
2054
  }
1949
2055
  ),
1950
- hasNextStep && /* @__PURE__ */ jsx6(
2056
+ hasNextStep && /* @__PURE__ */ jsx7(
1951
2057
  "svg",
1952
2058
  {
1953
2059
  width: "14",
@@ -1967,7 +2073,7 @@ function StripeMethodButton({
1967
2073
  opacity: 0.7
1968
2074
  },
1969
2075
  "aria-hidden": "true",
1970
- children: /* @__PURE__ */ jsx6("path", { d: "M9 18l6-6-6-6" })
2076
+ children: /* @__PURE__ */ jsx7("path", { d: "M9 18l6-6-6-6" })
1971
2077
  }
1972
2078
  )
1973
2079
  ] })
@@ -1990,12 +2096,13 @@ function StripeMethodInlineForm({
1990
2096
  isProcessing,
1991
2097
  submitButtonColor,
1992
2098
  submitButtonBorderRadius,
1993
- submitButtonStyle
2099
+ submitButtonStyle,
2100
+ errorText
1994
2101
  }) {
1995
2102
  const stripe = useStripeRaw();
1996
2103
  const elements = useStripeElements();
1997
2104
  const [submitting, setSubmitting] = useState3(false);
1998
- const submittingRef = useRef3(false);
2105
+ const submittingRef = useRef4(false);
1999
2106
  const [isMethodComplete, setIsMethodComplete] = useState3(false);
2000
2107
  const [loadState, setLoadState] = useState3("loading");
2001
2108
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
@@ -2140,7 +2247,7 @@ function StripeMethodInlineForm({
2140
2247
  ]);
2141
2248
  void onCancel;
2142
2249
  return /* @__PURE__ */ jsxs4("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
2143
- /* @__PURE__ */ jsx6(
2250
+ /* @__PURE__ */ jsx7(
2144
2251
  PaymentElement2,
2145
2252
  {
2146
2253
  onReady: () => setLoadState("ready"),
@@ -2160,7 +2267,7 @@ function StripeMethodInlineForm({
2160
2267
  }
2161
2268
  }
2162
2269
  ),
2163
- /* @__PURE__ */ jsx6(
2270
+ /* @__PURE__ */ jsx7(
2164
2271
  "button",
2165
2272
  {
2166
2273
  type: "button",
@@ -2186,7 +2293,23 @@ function StripeMethodInlineForm({
2186
2293
  },
2187
2294
  children: submitting ? "Processing\u2026" : `Pay with ${getStripeMethodDisplayName(method)}`
2188
2295
  }
2189
- )
2296
+ ),
2297
+ errorText && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
2298
+ margin: "0.75rem 0 0",
2299
+ padding: "0.625rem 0.875rem",
2300
+ background: "#FEF2F2",
2301
+ border: "1px solid #FECACA",
2302
+ borderRadius: "8px",
2303
+ color: "#991B1B",
2304
+ fontSize: "0.85rem",
2305
+ fontWeight: 600,
2306
+ display: "flex",
2307
+ alignItems: "center",
2308
+ gap: "0.5rem"
2309
+ }, children: [
2310
+ /* @__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" }) }),
2311
+ errorText
2312
+ ] })
2190
2313
  ] });
2191
2314
  }
2192
2315
  function StripePaymentElementInner({
@@ -2216,8 +2339,8 @@ function StripePaymentElementInner({
2216
2339
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
2217
2340
  const [expandedMethod, setExpandedMethod] = useState3(null);
2218
2341
  const [submittingMethod, setSubmittingMethod] = useState3(null);
2219
- const submittingRef = useRef3(false);
2220
- useEffect4(() => {
2342
+ const submittingRef = useRef4(false);
2343
+ useEffect5(() => {
2221
2344
  if (paymentElementMethods.length > 0 && stripeInstance) {
2222
2345
  onLoadStateChange?.("ready");
2223
2346
  } else if (!stripeInstance) {
@@ -2385,7 +2508,7 @@ function StripePaymentElementInner({
2385
2508
  paymentMethodTypes: [localExpandedMethod]
2386
2509
  };
2387
2510
  }, [localExpandedMethod, paymentElementBaseOptions]);
2388
- return /* @__PURE__ */ jsx6(
2511
+ return /* @__PURE__ */ jsx7(
2389
2512
  "div",
2390
2513
  {
2391
2514
  "data-testid": "flopay-stripe-payment-element-region",
@@ -2394,8 +2517,8 @@ function StripePaymentElementInner({
2394
2517
  const isExpanded = expandedApmMethod === method;
2395
2518
  const isSubmittingThis = submittingMethod === method;
2396
2519
  const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
2397
- return /* @__PURE__ */ jsxs4(React6.Fragment, { children: [
2398
- /* @__PURE__ */ jsx6(
2520
+ return /* @__PURE__ */ jsxs4(React7.Fragment, { children: [
2521
+ /* @__PURE__ */ jsx7(
2399
2522
  StripeMethodButton,
2400
2523
  {
2401
2524
  method,
@@ -2412,12 +2535,12 @@ function StripePaymentElementInner({
2412
2535
  fontFamily: buttonAppearance?.fontFamily
2413
2536
  }
2414
2537
  ),
2415
- !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx6(
2538
+ !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx7(
2416
2539
  StripeElements,
2417
2540
  {
2418
2541
  stripe: stripeInstance,
2419
2542
  options: localInlineElementsOptions,
2420
- children: /* @__PURE__ */ jsx6(
2543
+ children: /* @__PURE__ */ jsx7(
2421
2544
  StripeMethodInlineForm,
2422
2545
  {
2423
2546
  method,
@@ -2470,6 +2593,7 @@ function SplitCardFormInner({
2470
2593
  showPayPal = true,
2471
2594
  showStripe = true,
2472
2595
  enabledPaymentMethods,
2596
+ enabledPaymentMethodCountries,
2473
2597
  showApplePay = true,
2474
2598
  showGooglePay = true,
2475
2599
  layout = "default",
@@ -2484,6 +2608,8 @@ function SplitCardFormInner({
2484
2608
  onButtonClick,
2485
2609
  onBeforeButtonClick,
2486
2610
  enableAVS: enableAVSProp,
2611
+ cardFieldOrder,
2612
+ cardPreFormSlot,
2487
2613
  avsLayout: avsLayoutProp = "row",
2488
2614
  country: countryProp,
2489
2615
  zip: zipProp,
@@ -2520,14 +2646,49 @@ function SplitCardFormInner({
2520
2646
  const [city, setCity] = useState3(cityProp ?? "");
2521
2647
  const [stateValue, setStateValue] = useState3(stateProp ?? "");
2522
2648
  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 ?? "");
2649
+ const zipCodeRef = useRef4(zipProp ?? "");
2650
+ const selectedCountryRef = useRef4(countryProp ?? "US");
2651
+ const addressLine1Ref = useRef4(addressLine1Prop ?? "");
2652
+ const addressLine2Ref = useRef4(addressLine2Prop ?? "");
2653
+ const cityRef = useRef4(cityProp ?? "");
2654
+ const stateRef = useRef4(stateProp ?? "");
2529
2655
  const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
2530
2656
  const enableAVS = avsConfig !== null;
2657
+ const vaultBlockReady = Boolean(session?.vault?.html);
2658
+ const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
2659
+ const vaultActive = Boolean(
2660
+ showStripe && (vaultBlockReady || vaultGatewayAdvertised) && flopay && sessionId
2661
+ );
2662
+ const cardCapture = useMemo3(() => {
2663
+ if (!flopay || !vaultActive || !sessionId) return null;
2664
+ return flopay.cardCapture({ sessionId });
2665
+ }, [flopay, vaultActive, sessionId]);
2666
+ const postalCodeState = useMemo3(() => {
2667
+ const cc = selectedCountry;
2668
+ const visible = avsConfig ? isAVSFieldVisible(avsConfig.postal_code, cc) : false;
2669
+ return computePostalCodeState(cc, zipCode, visible);
2670
+ }, [avsConfig, selectedCountry, zipCode]);
2671
+ const { avsInvalid, invalidAvsFields } = useMemo3(() => {
2672
+ const none = { line1: false, city: false, state: false, zip: false };
2673
+ if (!vaultActive || !avsConfig) return { avsInvalid: false, invalidAvsFields: none };
2674
+ const cc = selectedCountry;
2675
+ const isEmpty = (field, value) => isAVSFieldVisible(field, cc) && !value.trim();
2676
+ const invalidAvsFields2 = {
2677
+ line1: isEmpty(avsConfig.address_line_1, addressLine1),
2678
+ city: isEmpty(avsConfig.city, city),
2679
+ state: isEmpty(avsConfig.state, stateValue),
2680
+ // Empty (required) or malformed both block; unsupported/no-postcode
2681
+ // locales never block (`postalCodeState` fails open above).
2682
+ zip: postalCodeState.empty || postalCodeState.malformed
2683
+ };
2684
+ const avsInvalid2 = invalidAvsFields2.line1 || invalidAvsFields2.city || invalidAvsFields2.state || invalidAvsFields2.zip;
2685
+ return { avsInvalid: avsInvalid2, invalidAvsFields: invalidAvsFields2 };
2686
+ }, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue, postalCodeState]);
2687
+ const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState3(false);
2688
+ const [zipTouched, setZipTouched] = useState3(false);
2689
+ const [vaultMount, setVaultMount] = useState3(
2690
+ () => toVaultMount(session?.vault)
2691
+ );
2531
2692
  const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
2532
2693
  const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
2533
2694
  const showCardForm = viewState === "expanding" || viewState === "card";
@@ -2552,7 +2713,7 @@ function SplitCardFormInner({
2552
2713
  setExpandedApmMethod(null);
2553
2714
  }, TRANSITION_MS);
2554
2715
  }, []);
2555
- useEffect4(() => {
2716
+ useEffect5(() => {
2556
2717
  if (layout === "buttons" && initialCardOpen) {
2557
2718
  setViewState("card");
2558
2719
  }
@@ -2560,10 +2721,10 @@ function SplitCardFormInner({
2560
2721
  const [fullName, setFullName] = useState3("");
2561
2722
  const [formReady, setFormReady] = useState3(false);
2562
2723
  const [overlayStatus, setOverlayStatus] = useState3(null);
2563
- const processingRef = useRef3(false);
2724
+ const processingRef = useRef4(false);
2564
2725
  const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
2565
- const paypalDirectRetryRef = useRef3(paypalDirectRetry);
2566
- useEffect4(() => {
2726
+ const paypalDirectRetryRef = useRef4(paypalDirectRetry);
2727
+ useEffect5(() => {
2567
2728
  paypalDirectRetryRef.current = paypalDirectRetry;
2568
2729
  }, [paypalDirectRetry]);
2569
2730
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
@@ -2584,6 +2745,31 @@ function SplitCardFormInner({
2584
2745
  };
2585
2746
  }, [themeBundle, buttonsTheme, buttonsStylesOverride]);
2586
2747
  const appearance = appearanceOverride ?? themeBundle?.appearance;
2748
+ const vaultThemeColors = useMemo3(() => {
2749
+ const vars = appearance?.variables;
2750
+ const asString = (value) => typeof value === "string" && value ? value : void 0;
2751
+ const submitButtonStyle = bStyles.submitButton;
2752
+ const primary = asString(vars?.colorPrimary) ?? asString(submitButtonStyle?.backgroundColor) ?? "#4A49FF";
2753
+ const isButtonsLayout = layout === "buttons";
2754
+ const nameInput = bStyles.nameInput;
2755
+ return {
2756
+ primaryColor: primary,
2757
+ primaryHoverColor: asString(vars?.colorPrimaryHover) ?? darkenHex(primary, 0.12),
2758
+ inputBackgroundColor: asString(bStyles.cardInputBackground) ?? asString(vars?.colorBackground) ?? "#ffffff",
2759
+ textColor: asString(bStyles.cardInputColor) ?? asString(nameInput?.color) ?? asString(vars?.colorText) ?? "#262833",
2760
+ borderColor: asString(bStyles.cardInputBorder) ?? (isButtonsLayout ? "#e5e7eb" : "#A4A4FF"),
2761
+ placeholderColor: asString(bStyles.cardInputPlaceholderColor) ?? "#9ca3af",
2762
+ errorColor: asString(vars?.colorDanger) ?? "#dc2626",
2763
+ successColor: "#16a34a",
2764
+ fontFamily: asString(nameInput?.fontFamily) ?? asString(vars?.fontFamily) ?? "Poppins, sans-serif",
2765
+ fontSize: asString(bStyles.cardInputFontSize) ?? asString(vars?.fontSizeBase) ?? "16px",
2766
+ // `resolvedInputFontWeight` equivalent — sent as a string (the widget's
2767
+ // theme applier only honors string values) so vault inputs/placeholders
2768
+ // match the AVS fields' weight per theme (default 400).
2769
+ fontWeight: String(toCssWeight(nameInput?.fontWeight) ?? 400),
2770
+ borderRadius: asString(vars?.borderRadius) ?? "8px"
2771
+ };
2772
+ }, [appearance, bStyles, layout]);
2587
2773
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
2588
2774
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
2589
2775
  const isSelfContained = !onTokenizedBody;
@@ -2652,12 +2838,174 @@ function SplitCardFormInner({
2652
2838
  },
2653
2839
  [onErrorChange]
2654
2840
  );
2841
+ useEffect5(() => {
2842
+ if (!vaultActive || !cardCapture) return;
2843
+ cardCapture.setSubmitGate?.(avsInvalid);
2844
+ }, [vaultActive, cardCapture, avsInvalid]);
2845
+ useEffect5(() => {
2846
+ if (!vaultActive || !cardCapture) return;
2847
+ cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
2848
+ }, [vaultActive, cardCapture, cardFieldOrder, avsConfig]);
2849
+ useEffect5(() => {
2850
+ if (viewState === "expanding" || viewState === "collapsing" || viewState === "apm-expanding" || viewState === "apm-collapsing") {
2851
+ updateError(null);
2852
+ }
2853
+ }, [viewState, updateError]);
2655
2854
  const emitDecline = useCallback(
2656
2855
  (method, input, overrides) => {
2657
2856
  onDecline?.(buildDeclineEvent(method, input, overrides));
2658
2857
  },
2659
2858
  [onDecline]
2660
2859
  );
2860
+ useEffect5(() => {
2861
+ if (!vaultActive || !sessionId) {
2862
+ setVaultMount(null);
2863
+ return;
2864
+ }
2865
+ const embedded = toVaultMount(session?.vault);
2866
+ if (embedded) {
2867
+ setVaultMount(embedded);
2868
+ return;
2869
+ }
2870
+ setVaultMount(null);
2871
+ let active = true;
2872
+ new PaymentAPI2(baseUrl).getVaultCapture(sessionId, nonce).then((block) => {
2873
+ if (!active) return;
2874
+ const mount = toVaultMount(block);
2875
+ if (mount) {
2876
+ setVaultMount(mount);
2877
+ } else {
2878
+ updateError("Failed to load the secure card form.");
2879
+ }
2880
+ }).catch((err) => {
2881
+ if (active) {
2882
+ updateError(err instanceof Error ? err.message : "Failed to load the secure card form.");
2883
+ }
2884
+ });
2885
+ return () => {
2886
+ active = false;
2887
+ };
2888
+ }, [
2889
+ vaultActive,
2890
+ sessionId,
2891
+ session?.vault?.html,
2892
+ session?.vault?.messageToken,
2893
+ session?.vault?.expectedOrigin,
2894
+ baseUrl,
2895
+ nonce,
2896
+ updateError
2897
+ ]);
2898
+ const vaultOutcomeRef = useRef4({
2899
+ onComplete,
2900
+ onError,
2901
+ updateError,
2902
+ emitDecline,
2903
+ resolvedAccount,
2904
+ fullName,
2905
+ avsConfig,
2906
+ avsCheckProp,
2907
+ sessionId,
2908
+ nonce,
2909
+ baseUrl
2910
+ });
2911
+ vaultOutcomeRef.current = {
2912
+ onComplete,
2913
+ onError,
2914
+ updateError,
2915
+ emitDecline,
2916
+ resolvedAccount,
2917
+ fullName,
2918
+ avsConfig,
2919
+ avsCheckProp,
2920
+ sessionId,
2921
+ nonce,
2922
+ baseUrl
2923
+ };
2924
+ const vaultCompletedRef = useRef4(false);
2925
+ useEffect5(() => {
2926
+ if (!vaultActive || !cardCapture) return;
2927
+ vaultCompletedRef.current = false;
2928
+ let cancelled = false;
2929
+ const offSubmitting = cardCapture.on("submitting", () => {
2930
+ setHasAttemptedSubmit(true);
2931
+ setOverlayStatus("processing");
2932
+ const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2, baseUrl: baseUrl2, sessionId: sessionId2, nonce: nonce2 } = vaultOutcomeRef.current;
2933
+ if (!sessionId2 || !nonce2) return;
2934
+ const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
2935
+ const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
2936
+ const line1Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_1, cc) : false;
2937
+ const zipVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.postal_code, cc) : false;
2938
+ const cityVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.city, cc) : false;
2939
+ const line2Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_2, cc) : false;
2940
+ const derivedState = line1Visible && !stateVisible && zipVisible ? getStateFromPostalCode(cc, zipCodeRef.current ?? "") : null;
2941
+ const stateValue2 = stateVisible ? stateRef.current : derivedState;
2942
+ void new PaymentAPI2(baseUrl2).patchAccountSnapshot(sessionId2, nonce2, {
2943
+ accountData: {
2944
+ userId: resolvedAccount2.userId ?? "",
2945
+ email: resolvedAccount2.email ?? "",
2946
+ firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
2947
+ lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
2948
+ ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
2949
+ ...cityVisible && cityRef.current ? { city: cityRef.current } : {},
2950
+ ...stateValue2 ? { state: stateValue2 } : {},
2951
+ ...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
2952
+ ...line2Visible && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {},
2953
+ country: cc
2954
+ },
2955
+ ...avsCheckProp2 !== void 0 ? { avsCheck: avsCheckProp2 } : {},
2956
+ ...avsConfig2 ? {
2957
+ avsConfig: {
2958
+ country: isAVSFieldVisible(avsConfig2.country, cc),
2959
+ postal_code: zipVisible,
2960
+ address_line_1: line1Visible,
2961
+ address_line_2: line2Visible,
2962
+ city: cityVisible,
2963
+ state: stateVisible
2964
+ }
2965
+ } : {}
2966
+ }).catch(() => {
2967
+ });
2968
+ });
2969
+ const offComplete = cardCapture.on("complete", async (event) => {
2970
+ markSessionRecentlyCompleted(event.sessionId ?? vaultOutcomeRef.current.sessionId);
2971
+ setOverlayStatus("success");
2972
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
2973
+ if (vaultCompletedRef.current) return;
2974
+ vaultCompletedRef.current = true;
2975
+ vaultOutcomeRef.current.onComplete?.({
2976
+ status: "succeeded",
2977
+ paymentIntentId: event.intentId,
2978
+ checkoutMethod: "card"
2979
+ });
2980
+ });
2981
+ const offDecline = cardCapture.on("decline", async (event) => {
2982
+ const { updateError: updateError2, emitDecline: emitDecline2 } = vaultOutcomeRef.current;
2983
+ const message = event.message ?? "Your payment was declined. Please try another card or contact your bank.";
2984
+ updateError2(message);
2985
+ setOverlayStatus("error");
2986
+ emitDecline2("card", message, event.declineReason ? { declineCode: event.declineReason } : void 0);
2987
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
2988
+ if (cancelled) return;
2989
+ setOverlayStatus(null);
2990
+ });
2991
+ const offError = cardCapture.on("error", async (event) => {
2992
+ const { updateError: updateError2, onError: onError2 } = vaultOutcomeRef.current;
2993
+ const message = event.message ?? "There was a problem processing your payment. Please try again.";
2994
+ updateError2(message);
2995
+ setOverlayStatus("error");
2996
+ onError2?.(new FloPayError3(message, "api_error"));
2997
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
2998
+ if (cancelled) return;
2999
+ setOverlayStatus(null);
3000
+ });
3001
+ return () => {
3002
+ cancelled = true;
3003
+ offSubmitting();
3004
+ offComplete();
3005
+ offDecline();
3006
+ offError();
3007
+ };
3008
+ }, [vaultActive, cardCapture]);
2661
3009
  const directPaypalConfigured = !!directPaypal?.clientId;
2662
3010
  const hasEnabledMethodsPayload = Array.isArray(enabledPaymentMethods);
2663
3011
  const hasEnabledMethods = hasEnabledMethodsPayload && enabledPaymentMethods.length > 0;
@@ -2681,17 +3029,18 @@ function SplitCardFormInner({
2681
3029
  }, [showApplePay, showGooglePay]);
2682
3030
  const walletExpressMethods = hasEnabledMethodsPayload ? expressMethodsForWalletRow : legacyExpressMethods;
2683
3031
  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
- );
3032
+ const apmCountry = enableAVS ? selectedCountry : countryProp;
3033
+ const paymentElementMethodsForCurrency = useMemo3(() => {
3034
+ const target = apmCountry?.trim().toUpperCase();
3035
+ if (!target) return paymentElementMethods;
3036
+ if (!enabledPaymentMethodCountries) {
3037
+ return filterStripeMethodsByCountry(paymentElementMethods, apmCountry);
3038
+ }
3039
+ return paymentElementMethods.filter((method) => {
3040
+ const allowed = enabledPaymentMethodCountries[method];
3041
+ return !allowed || allowed.length === 0 || allowed.includes(target);
3042
+ });
3043
+ }, [paymentElementMethods, apmCountry, enabledPaymentMethodCountries]);
2695
3044
  const paymentElementBaseOptions = useMemo3(() => ({
2696
3045
  mode: "payment",
2697
3046
  amount: amountInCents,
@@ -2704,7 +3053,7 @@ function SplitCardFormInner({
2704
3053
  const [paymentElementLoadState, setPaymentElementLoadState] = useState3("loading");
2705
3054
  const [directPaypalReady, setDirectPaypalReady] = useState3(false);
2706
3055
  const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
2707
- useEffect4(() => {
3056
+ useEffect5(() => {
2708
3057
  setInAppBrowserDetected(isInAppBrowser());
2709
3058
  }, []);
2710
3059
  const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
@@ -2716,8 +3065,8 @@ function SplitCardFormInner({
2716
3065
  const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
2717
3066
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
2718
3067
  const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
2719
- const validationFiredRef = useRef3(false);
2720
- useEffect4(() => {
3068
+ const validationFiredRef = useRef4(false);
3069
+ useEffect5(() => {
2721
3070
  if (validationFiredRef.current) return;
2722
3071
  if (!showStripe && !showPayPal) {
2723
3072
  validationFiredRef.current = true;
@@ -2737,8 +3086,8 @@ function SplitCardFormInner({
2737
3086
  updateError(err.message);
2738
3087
  }
2739
3088
  }, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
2740
- const deprecationLoggedRef = useRef3(false);
2741
- useEffect4(() => {
3089
+ const deprecationLoggedRef = useRef4(false);
3090
+ useEffect5(() => {
2742
3091
  if (deprecationLoggedRef.current) return;
2743
3092
  if (!hasEnabledMethods) return;
2744
3093
  const stale = [];
@@ -3064,8 +3413,8 @@ function SplitCardFormInner({
3064
3413
  }
3065
3414
  }
3066
3415
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
3067
- const stripeResumeAttemptedRef = useRef3(false);
3068
- useEffect4(() => {
3416
+ const stripeResumeAttemptedRef = useRef4(false);
3417
+ useEffect5(() => {
3069
3418
  if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
3070
3419
  const params = new URLSearchParams(window.location.search);
3071
3420
  const paymentIntentId = params.get("payment_intent");
@@ -3147,6 +3496,7 @@ function SplitCardFormInner({
3147
3496
  async (e) => {
3148
3497
  e.preventDefault();
3149
3498
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
3499
+ if (vaultActive) return;
3150
3500
  if (layout !== "buttons") {
3151
3501
  onButtonClick?.("card");
3152
3502
  }
@@ -3157,10 +3507,21 @@ function SplitCardFormInner({
3157
3507
  try {
3158
3508
  if (avsConfig) {
3159
3509
  const country = selectedCountryRef.current;
3160
- if (isAVSFieldVisible(avsConfig.postal_code, country) && !zipCodeRef.current.trim()) {
3510
+ const postal = computePostalCodeState(
3511
+ country,
3512
+ zipCodeRef.current,
3513
+ isAVSFieldVisible(avsConfig.postal_code, country)
3514
+ );
3515
+ if (postal.empty) {
3516
+ setZipTouched(true);
3161
3517
  updateError(getPostalCodeLabel(country) + " is required");
3162
3518
  return;
3163
3519
  }
3520
+ if (postal.malformed) {
3521
+ setZipTouched(true);
3522
+ updateError(malformedPostcodeMessage(country));
3523
+ return;
3524
+ }
3164
3525
  if (isAVSFieldVisible(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
3165
3526
  updateError("Street address is required");
3166
3527
  return;
@@ -3174,11 +3535,13 @@ function SplitCardFormInner({
3174
3535
  return;
3175
3536
  }
3176
3537
  }
3177
- const submitResult = await flopay.submitElements();
3178
- if (submitResult.error) {
3179
- updateError(submitResult.error.message);
3180
- onError?.(submitResult.error);
3181
- return;
3538
+ if (!vaultActive) {
3539
+ const submitResult = await flopay.submitElements();
3540
+ if (submitResult.error) {
3541
+ updateError(submitResult.error.message);
3542
+ onError?.(submitResult.error);
3543
+ return;
3544
+ }
3182
3545
  }
3183
3546
  const billingAddress = {};
3184
3547
  const cc = selectedCountryRef.current;
@@ -3203,75 +3566,77 @@ function SplitCardFormInner({
3203
3566
  ...fullName.trim() ? { name: fullName.trim() } : {},
3204
3567
  ...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
3205
3568
  };
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
3569
  if (!sessionId || !resolvedAccount.email) {
3212
3570
  throw new FloPayError3("Missing sessionId or email", "validation_error");
3213
3571
  }
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"
3572
+ {
3573
+ const pmResult = await flopay.createPaymentMethod(billingDetails);
3574
+ if (pmResult.error || !pmResult.paymentMethodId) {
3575
+ updateError(pmResult.error?.message ?? "Failed to create payment method.");
3576
+ return;
3577
+ }
3578
+ const intentHeaders = { "Content-Type": "application/json" };
3579
+ if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
3580
+ const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3581
+ method: "POST",
3582
+ headers: intentHeaders,
3583
+ body: JSON.stringify({
3584
+ sessionId,
3585
+ email: resolvedAccount.email,
3586
+ paymentMethodType: pmResult.paymentMethodId,
3587
+ isPaypal: false
3588
+ })
3589
+ });
3590
+ if (!intentResponse.ok) {
3591
+ const intentError = await buildFloPayApiErrorFromResponse(
3592
+ intentResponse,
3593
+ "Failed to create payment intent"
3594
+ );
3595
+ setOverlayStatus("error");
3596
+ updateError(intentError.message);
3597
+ onError?.(intentError);
3598
+ emitDecline("card", intentError);
3599
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3600
+ return;
3601
+ }
3602
+ const intentJson = await intentResponse.json();
3603
+ const intentClientSecret = intentJson.data?.id;
3604
+ if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
3605
+ const confirmResult = await flopay.confirmCardPayment({
3606
+ clientSecret: intentClientSecret,
3607
+ paymentMethodId: pmResult.paymentMethodId
3608
+ });
3609
+ if (confirmResult.error) {
3610
+ setOverlayStatus("error");
3611
+ updateError(confirmResult.error.message);
3612
+ onError?.(confirmResult.error);
3613
+ emitDecline("card", confirmResult.error);
3614
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3615
+ return;
3616
+ }
3617
+ const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
3618
+ const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
3619
+ rawProvider,
3620
+ intentClientSecret
3230
3621
  );
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;
3622
+ const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
3623
+ const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
3624
+ if (!paymentIntentId) {
3625
+ const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
3626
+ setOverlayStatus("error");
3627
+ updateError(error2.message);
3628
+ onError?.(error2);
3629
+ await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
3630
+ return;
3631
+ }
3632
+ handedOff = isSelfContained;
3633
+ dispatchTokenizedBody({
3634
+ id: paymentMethodId,
3635
+ type: "card",
3636
+ threeDSecureActionResultTokenId: paymentIntentId,
3637
+ originalPaymentMethodId: pmResult.paymentMethodId
3638
+ });
3267
3639
  }
3268
- handedOff = isSelfContained;
3269
- dispatchTokenizedBody({
3270
- id: paymentMethodId,
3271
- type: "card",
3272
- threeDSecureActionResultTokenId: paymentIntentId,
3273
- originalPaymentMethodId: pmResult.paymentMethodId
3274
- });
3275
3640
  } catch (err) {
3276
3641
  setOverlayStatus("error");
3277
3642
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
@@ -3283,11 +3648,11 @@ function SplitCardFormInner({
3283
3648
  }
3284
3649
  }
3285
3650
  },
3286
- [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3651
+ [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, vaultActive, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
3287
3652
  );
3288
- const isReady = flopay !== null && elements !== null;
3653
+ const isReady = flopay !== null && (vaultActive || elements !== null);
3289
3654
  if (!isReady) {
3290
- return /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
3655
+ return /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
3291
3656
  }
3292
3657
  const isButtons = layout === "buttons";
3293
3658
  const appearanceVars = appearance?.variables;
@@ -3295,6 +3660,10 @@ function SplitCardFormInner({
3295
3660
  const appearanceColorBg = appearanceVars?.colorBackground;
3296
3661
  const themedWrapperBg = appearanceColorBg && !SDK_DEFAULT_WHITES.has(appearanceColorBg) ? appearanceColorBg : void 0;
3297
3662
  const resolvedBorder = bStyles.cardInputBorder ?? (isButtons ? "#e5e7eb" : "#A4A4FF");
3663
+ const resolvedDangerColor = appearanceVars?.colorDanger ?? "#dc2626";
3664
+ const avsBorderColor = (fieldInvalid) => hasAttemptedSubmit && fieldInvalid ? resolvedDangerColor : resolvedBorder;
3665
+ const showPostcodeError = (postalCodeState.malformed || postalCodeState.empty) && (zipTouched || hasAttemptedSubmit);
3666
+ const zipBorderColor = showPostcodeError ? resolvedDangerColor : avsBorderColor(invalidAvsFields.zip);
3298
3667
  const cardBg = bStyles.cardFormContainer?.backgroundColor ?? themedWrapperBg ?? (isButtons ? "white" : "#EDEDFF");
3299
3668
  const cardInputBg = bStyles.cardInputBackground ?? appearanceVars?.colorBackground ?? "white";
3300
3669
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
@@ -3306,7 +3675,9 @@ function SplitCardFormInner({
3306
3675
  const resolvedInputColor = bStyles.cardInputColor ?? (typeof nameInputOverrides?.color === "string" ? nameInputOverrides.color : void 0) ?? appearanceVars?.colorText ?? "#262833";
3307
3676
  const resolvedPlaceholderColor = bStyles.cardInputPlaceholderColor ?? "#9ca3af";
3308
3677
  const resolvedBorderRadius = appearanceVars?.borderRadius ?? "8px";
3678
+ const buttonBorderRadius = appearanceVars?.borderRadius ?? bStyles.cardButton?.borderRadius ?? resolvedBorderRadius;
3309
3679
  const resolvedPrimaryColor = appearanceVars?.colorPrimary ?? "#4A49FF";
3680
+ const resolvedPrimaryHoverColor = appearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
3310
3681
  const resolvedTitleColor = appearanceVars?.colorText ?? "#262833";
3311
3682
  const sharedInputTypography = {
3312
3683
  fontSize: resolvedInputFontSize,
@@ -3340,14 +3711,73 @@ function SplitCardFormInner({
3340
3711
  const containerOverrides = bStyles.cardFormContainer ?? {};
3341
3712
  const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
3342
3713
  const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
3714
+ const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx7(
3715
+ VaultCardFields,
3716
+ {
3717
+ capture: cardCapture,
3718
+ html: vaultMount.html,
3719
+ messageToken: vaultMount.messageToken,
3720
+ expectedOrigin: vaultMount.expectedOrigin,
3721
+ theme: vaultThemeColors,
3722
+ onReady: () => {
3723
+ setFormReady(true);
3724
+ if (!avsConfig || typeof document === "undefined") return;
3725
+ const focusFirstAvs = () => {
3726
+ const block = document.querySelector('[data-testid="flopay-avs-fields"]');
3727
+ const first = block?.querySelector(
3728
+ "input:not([disabled]), select:not([disabled])"
3729
+ );
3730
+ first?.focus();
3731
+ };
3732
+ focusFirstAvs();
3733
+ let tries = 0;
3734
+ const timer = window.setInterval(() => {
3735
+ tries += 1;
3736
+ const active = document.activeElement;
3737
+ const onIframe = !!active && active.tagName === "IFRAME" && active.id === "flopay_vault_form_iframe";
3738
+ if (onIframe) {
3739
+ focusFirstAvs();
3740
+ window.clearInterval(timer);
3741
+ } else if (tries >= 50) {
3742
+ window.clearInterval(timer);
3743
+ }
3744
+ }, 100);
3745
+ },
3746
+ onError: (message) => updateError(message),
3747
+ onValidation: (message) => {
3748
+ updateError(message);
3749
+ if (message) setOverlayStatus(null);
3750
+ }
3751
+ }
3752
+ ) : /* @__PURE__ */ jsx7(
3753
+ "div",
3754
+ {
3755
+ "data-testid": "flopay-vault-loading",
3756
+ style: {
3757
+ minHeight: 120,
3758
+ display: "flex",
3759
+ alignItems: "center",
3760
+ justifyContent: "center",
3761
+ color: "#6b7280",
3762
+ fontSize: 14
3763
+ },
3764
+ children: "Loading secure card form\u2026"
3765
+ }
3766
+ );
3343
3767
  const cardFormBlock = /* @__PURE__ */ jsxs4("div", { style: {
3344
3768
  backgroundColor: cardBg,
3345
3769
  borderRadius: containerRadius,
3346
3770
  ...containerOverrides,
3347
3771
  padding: containerPadding,
3348
- ...sharedInputPlaceholderVars
3772
+ ...sharedInputPlaceholderVars,
3773
+ // Flex column (BOTH layouts, so default and buttons>card never diverge) so
3774
+ // AVS can be ordered above the vault widget — its submit lives inside the
3775
+ // iframe, so AVS can't sit between fields and button; it goes above the
3776
+ // card form instead of after the button.
3777
+ display: "flex",
3778
+ flexDirection: "column"
3349
3779
  }, children: [
3350
- /* @__PURE__ */ jsx6("style", { children: `
3780
+ /* @__PURE__ */ jsx7("style", { children: `
3351
3781
  .flopay-shared-input::placeholder {
3352
3782
  color: var(--flopay-input-placeholder-color);
3353
3783
  opacity: 1;
@@ -3359,7 +3789,9 @@ function SplitCardFormInner({
3359
3789
  isButtons && showCardForm && /* @__PURE__ */ jsxs4("div", { style: {
3360
3790
  display: "flex",
3361
3791
  alignItems: "center",
3362
- padding: "0.75rem 0 0.625rem"
3792
+ padding: "0.75rem 0 0.625rem",
3793
+ // Keep the header on top when AVS is ordered above the card on vault.
3794
+ order: vaultActive ? -2 : 0
3363
3795
  }, children: [
3364
3796
  /* @__PURE__ */ jsxs4(
3365
3797
  "button",
@@ -3383,7 +3815,7 @@ function SplitCardFormInner({
3383
3815
  },
3384
3816
  "aria-label": "Back to payment methods",
3385
3817
  children: [
3386
- /* @__PURE__ */ jsx6("span", { style: {
3818
+ /* @__PURE__ */ jsx7("span", { style: {
3387
3819
  display: "inline-flex",
3388
3820
  alignItems: "center",
3389
3821
  justifyContent: "center",
@@ -3393,12 +3825,12 @@ function SplitCardFormInner({
3393
3825
  backgroundColor: "#f3f4f6",
3394
3826
  transition: "background-color 0.15s",
3395
3827
  ...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 })
3828
+ }, 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" }) }) }),
3829
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
3398
3830
  ]
3399
3831
  }
3400
3832
  ),
3401
- hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
3833
+ hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
3402
3834
  flex: 1,
3403
3835
  textAlign: "center",
3404
3836
  fontWeight: 600,
@@ -3406,58 +3838,65 @@ function SplitCardFormInner({
3406
3838
  color: "#262833",
3407
3839
  paddingRight: 80,
3408
3840
  ...bStyles.title
3409
- }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
3841
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
3410
3842
  ] }),
3411
- !isButtons && !hideTitle && /* @__PURE__ */ jsx6("div", { style: {
3843
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx7("div", { style: {
3412
3844
  textAlign: "center",
3413
3845
  fontWeight: 600,
3414
3846
  fontSize: "1.1rem",
3415
3847
  padding: "0.5rem 0",
3416
3848
  color: resolvedTitleColor,
3849
+ // Keep the title at the very top on the vault path. The card-form slot
3850
+ // (-2) and AVS block (-1) are ordered below it but above the card form.
3851
+ order: vaultActive ? -3 : 0,
3417
3852
  ...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,
3853
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) }),
3854
+ !vaultActive && /* @__PURE__ */ jsxs4(Fragment2, { children: [
3855
+ /* @__PURE__ */ jsx7("div", { style: {
3442
3856
  backgroundColor: cardInputBg,
3443
- borderTop: "none",
3444
- borderRight: `1px solid ${resolvedBorder}`,
3445
- borderBottom: `1px solid ${resolvedBorder}`,
3446
- borderLeft: `1px solid ${resolvedBorder}`,
3447
- borderBottomRightRadius: "8px",
3857
+ border: `1px solid ${resolvedBorder}`,
3858
+ borderTopLeftRadius: resolvedBorderRadius,
3859
+ borderTopRightRadius: resolvedBorderRadius,
3448
3860
  padding: "10px"
3449
- }, children: /* @__PURE__ */ jsx6(CardCvcElement, { options: stripeElementStyle }) })
3861
+ }, children: /* @__PURE__ */ jsx7(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
3862
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3863
+ /* @__PURE__ */ jsx7("div", { style: {
3864
+ flex: 1,
3865
+ backgroundColor: cardInputBg,
3866
+ // Longhand only — mixing `border` shorthand with per-side
3867
+ // overrides triggers React's "shorthand vs longhand" warning
3868
+ // because render order isn't deterministic.
3869
+ borderTop: "none",
3870
+ borderRight: "none",
3871
+ borderBottom: `1px solid ${resolvedBorder}`,
3872
+ borderLeft: `1px solid ${resolvedBorder}`,
3873
+ borderBottomLeftRadius: resolvedBorderRadius,
3874
+ padding: "10px"
3875
+ }, children: /* @__PURE__ */ jsx7(CardExpiryElement, { options: stripeElementStyle }) }),
3876
+ /* @__PURE__ */ jsx7("div", { style: {
3877
+ flex: 1,
3878
+ backgroundColor: cardInputBg,
3879
+ borderTop: "none",
3880
+ borderRight: `1px solid ${resolvedBorder}`,
3881
+ borderBottom: `1px solid ${resolvedBorder}`,
3882
+ borderLeft: `1px solid ${resolvedBorder}`,
3883
+ borderBottomRightRadius: resolvedBorderRadius,
3884
+ padding: "10px"
3885
+ }, children: /* @__PURE__ */ jsx7(CardCvcElement, { options: stripeElementStyle }) })
3886
+ ] })
3450
3887
  ] }),
3451
- /* @__PURE__ */ jsx6("div", { style: {
3888
+ !vaultActive && /* @__PURE__ */ jsx7("div", { style: {
3452
3889
  backgroundColor: cardInputBg,
3453
3890
  border: `1px solid ${resolvedBorder}`,
3454
- borderRadius: "8px",
3891
+ borderRadius: resolvedBorderRadius,
3455
3892
  marginTop: "0.5rem",
3456
3893
  padding: "10px"
3457
- }, children: /* @__PURE__ */ jsx6(
3894
+ }, children: /* @__PURE__ */ jsx7(
3458
3895
  "input",
3459
3896
  {
3460
3897
  className: "flopay-shared-input",
3898
+ id: "flopay-cc-name",
3899
+ name: "cc-name",
3461
3900
  placeholder: "Full Name on Card",
3462
3901
  autoComplete: "cc-name",
3463
3902
  value: fullName,
@@ -3474,32 +3913,32 @@ function SplitCardFormInner({
3474
3913
  }
3475
3914
  }
3476
3915
  ) }),
3477
- avsConfig && (() => {
3916
+ avsConfig && /* @__PURE__ */ jsx7("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
3478
3917
  const cc = selectedCountry;
3479
- const inputWrapStyle = (extraStyle) => ({
3918
+ const inputWrapStyle = (invalid = false) => ({
3480
3919
  backgroundColor: cardInputBg,
3481
- border: `1px solid ${resolvedBorder}`,
3482
- borderRadius: "8px",
3920
+ border: `1px solid ${avsBorderColor(invalid)}`,
3921
+ borderRadius: resolvedBorderRadius,
3483
3922
  marginTop: "0.5rem",
3484
- padding: "10px",
3485
- ...isButtons && extraStyle ? extraStyle : {}
3923
+ padding: "10px"
3486
3924
  });
3487
3925
  const inputFieldStyle = () => ({
3488
3926
  width: "100%",
3489
3927
  border: "none",
3490
3928
  outline: "none",
3491
3929
  background: "transparent",
3492
- ...sharedInputTypography,
3493
- ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
3930
+ ...sharedInputTypography
3494
3931
  });
3495
3932
  const stateOpts = getStateOptions(cc);
3496
3933
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
3497
- isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine1Input), children: /* @__PURE__ */ jsx6(
3934
+ isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx7(
3498
3935
  "input",
3499
3936
  {
3500
3937
  className: "flopay-shared-input",
3938
+ id: "flopay-billing-address-line1",
3939
+ name: "billing-address-line1",
3501
3940
  placeholder: "Street Address (e.g. 123 Main St)",
3502
- autoComplete: "address-line1",
3941
+ autoComplete: "billing address-line1",
3503
3942
  value: addressLine1,
3504
3943
  onChange: (e) => {
3505
3944
  addressLine1Ref.current = e.target.value;
@@ -3511,12 +3950,14 @@ function SplitCardFormInner({
3511
3950
  style: inputFieldStyle()
3512
3951
  }
3513
3952
  ) }),
3514
- isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine2Input), children: /* @__PURE__ */ jsx6(
3953
+ isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx7(
3515
3954
  "input",
3516
3955
  {
3517
3956
  className: "flopay-shared-input",
3957
+ id: "flopay-billing-address-line2",
3958
+ name: "billing-address-line2",
3518
3959
  placeholder: "Apt, Suite, Unit (optional)",
3519
- autoComplete: "address-line2",
3960
+ autoComplete: "billing address-line2",
3520
3961
  value: addressLine2,
3521
3962
  onChange: (e) => {
3522
3963
  addressLine2Ref.current = e.target.value;
@@ -3532,24 +3973,25 @@ function SplitCardFormInner({
3532
3973
  gap: "0",
3533
3974
  marginTop: "0.5rem"
3534
3975
  }, children: [
3535
- isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx6("div", { style: {
3976
+ isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx7("div", { style: {
3536
3977
  flex: 1,
3537
3978
  backgroundColor: cardInputBg,
3538
- borderTop: `1px solid ${resolvedBorder}`,
3539
- borderRight: `1px solid ${resolvedBorder}`,
3540
- borderBottom: `1px solid ${resolvedBorder}`,
3541
- borderLeft: `1px solid ${resolvedBorder}`,
3979
+ borderTop: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3980
+ borderRight: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3981
+ borderBottom: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3982
+ borderLeft: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
3542
3983
  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(
3984
+ borderTopLeftRadius: resolvedBorderRadius,
3985
+ borderBottomLeftRadius: resolvedBorderRadius,
3986
+ ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: resolvedBorderRadius }
3987
+ }, children: /* @__PURE__ */ jsx7(
3548
3988
  "input",
3549
3989
  {
3550
3990
  className: "flopay-shared-input",
3991
+ id: "flopay-billing-city",
3992
+ name: "billing-city",
3551
3993
  placeholder: "City",
3552
- autoComplete: "address-level2",
3994
+ autoComplete: "billing address-level2",
3553
3995
  value: city,
3554
3996
  onChange: (e) => {
3555
3997
  cityRef.current = e.target.value;
@@ -3561,39 +4003,42 @@ function SplitCardFormInner({
3561
4003
  style: inputFieldStyle()
3562
4004
  }
3563
4005
  ) }),
3564
- isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx6("div", { style: {
4006
+ isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx7("div", { style: {
3565
4007
  flex: 1,
3566
4008
  backgroundColor: cardInputBg,
3567
- border: `1px solid ${resolvedBorder}`,
4009
+ border: `1px solid ${avsBorderColor(invalidAvsFields.state)}`,
3568
4010
  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 : {}
4011
+ borderTopRightRadius: resolvedBorderRadius,
4012
+ borderBottomRightRadius: resolvedBorderRadius,
4013
+ ...isAVSFieldVisible(avsConfig.city, cc) ? { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 } : { borderRadius: resolvedBorderRadius }
3573
4014
  }, children: stateOpts ? /* @__PURE__ */ jsxs4(
3574
4015
  "select",
3575
4016
  {
4017
+ id: "flopay-billing-state",
4018
+ name: "billing-state",
3576
4019
  value: stateValue,
3577
4020
  onChange: (e) => {
3578
4021
  stateRef.current = e.target.value;
3579
4022
  setStateValue(e.target.value);
3580
4023
  },
3581
4024
  disabled: isSubmitting,
3582
- autoComplete: "address-level1",
4025
+ autoComplete: "billing address-level1",
3583
4026
  required: true,
3584
4027
  "data-testid": "flopay-state",
3585
4028
  style: { ...inputFieldStyle(), cursor: "pointer" },
3586
4029
  children: [
3587
- /* @__PURE__ */ jsx6("option", { value: "", children: getStateLabel(cc) }),
3588
- stateOpts.map((s) => /* @__PURE__ */ jsx6("option", { value: s.code, children: s.name }, s.code))
4030
+ /* @__PURE__ */ jsx7("option", { value: "", children: getStateLabel(cc) }),
4031
+ stateOpts.map((s) => /* @__PURE__ */ jsx7("option", { value: s.code, children: s.name }, s.code))
3589
4032
  ]
3590
4033
  }
3591
- ) : /* @__PURE__ */ jsx6(
4034
+ ) : /* @__PURE__ */ jsx7(
3592
4035
  "input",
3593
4036
  {
3594
4037
  className: "flopay-shared-input",
4038
+ id: "flopay-billing-state",
4039
+ name: "billing-state",
3595
4040
  placeholder: getStateLabel(cc),
3596
- autoComplete: "address-level1",
4041
+ autoComplete: "billing address-level1",
3597
4042
  value: stateValue,
3598
4043
  onChange: (e) => {
3599
4044
  stateRef.current = e.target.value;
@@ -3612,7 +4057,7 @@ function SplitCardFormInner({
3612
4057
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
3613
4058
  marginTop: "0.5rem"
3614
4059
  }, children: [
3615
- isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx6("div", { style: {
4060
+ isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx7("div", { style: {
3616
4061
  flex: avsLayoutProp === "row" ? 1 : void 0,
3617
4062
  backgroundColor: cardInputBg,
3618
4063
  borderTop: `1px solid ${resolvedBorder}`,
@@ -3620,11 +4065,12 @@ function SplitCardFormInner({
3620
4065
  borderBottom: `1px solid ${resolvedBorder}`,
3621
4066
  borderLeft: `1px solid ${resolvedBorder}`,
3622
4067
  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(
4068
+ ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: resolvedBorderRadius, borderBottomLeftRadius: resolvedBorderRadius, borderRight: "none" } : { borderRadius: resolvedBorderRadius }
4069
+ }, children: /* @__PURE__ */ jsx7(
3626
4070
  "select",
3627
4071
  {
4072
+ id: "flopay-billing-country",
4073
+ name: "billing-country",
3628
4074
  value: selectedCountry,
3629
4075
  onChange: (e) => {
3630
4076
  selectedCountryRef.current = e.target.value;
@@ -3634,7 +4080,7 @@ function SplitCardFormInner({
3634
4080
  setStateValue("");
3635
4081
  },
3636
4082
  disabled: isSubmitting,
3637
- autoComplete: "country",
4083
+ autoComplete: "billing country",
3638
4084
  "data-testid": "flopay-country",
3639
4085
  style: { ...inputFieldStyle(), cursor: "pointer" },
3640
4086
  children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs4("option", { value: c.code, children: [
@@ -3644,34 +4090,55 @@ function SplitCardFormInner({
3644
4090
  ] }, c.code))
3645
4091
  }
3646
4092
  ) }),
3647
- isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx6("div", { style: {
4093
+ isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx7("div", { style: {
3648
4094
  flex: avsLayoutProp === "row" ? 1 : void 0,
3649
4095
  backgroundColor: cardInputBg,
3650
- border: `1px solid ${resolvedBorder}`,
4096
+ border: `1px solid ${zipBorderColor}`,
3651
4097
  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(
4098
+ ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
4099
+ }, children: /* @__PURE__ */ jsx7(
3655
4100
  "input",
3656
4101
  {
3657
4102
  className: "flopay-shared-input",
4103
+ id: "flopay-billing-postal-code",
4104
+ name: "billing-postal-code",
3658
4105
  placeholder: getPostalCodeLabel(selectedCountry),
3659
- autoComplete: "postal-code",
4106
+ autoComplete: "billing postal-code",
3660
4107
  value: zipCode,
3661
4108
  onChange: (e) => {
3662
4109
  zipCodeRef.current = e.target.value;
3663
4110
  setZipCode(e.target.value);
3664
4111
  onZipChange?.(e.target.value);
3665
4112
  },
4113
+ onBlur: () => setZipTouched(true),
3666
4114
  disabled: isSubmitting,
3667
- required: true,
4115
+ required: postalCodeState.required,
4116
+ "aria-invalid": showPostcodeError || void 0,
4117
+ "aria-describedby": showPostcodeError ? "flopay-billing-postal-code-error" : void 0,
3668
4118
  "data-testid": "flopay-zip",
3669
4119
  style: inputFieldStyle()
3670
4120
  }
3671
4121
  ) })
3672
- ] })
4122
+ ] }),
4123
+ showPostcodeError && /* @__PURE__ */ jsx7(
4124
+ "div",
4125
+ {
4126
+ id: "flopay-billing-postal-code-error",
4127
+ role: "alert",
4128
+ "data-testid": "flopay-zip-error",
4129
+ style: {
4130
+ marginTop: "0.375rem",
4131
+ color: resolvedDangerColor,
4132
+ ...sharedInputTypography,
4133
+ fontSize: "0.75rem"
4134
+ },
4135
+ children: postalCodeState.empty ? `${getPostalCodeLabel(cc)} is required` : malformedPostcodeMessage(cc)
4136
+ }
4137
+ )
3673
4138
  ] });
3674
- })(),
4139
+ })() }),
4140
+ vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
4141
+ vaultActive && vaultCardFieldsNode,
3675
4142
  displayError && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
3676
4143
  margin: "0.75rem 0",
3677
4144
  padding: "0.625rem 0.875rem",
@@ -3686,15 +4153,21 @@ function SplitCardFormInner({
3686
4153
  gap: "0.5rem",
3687
4154
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
3688
4155
  }, 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" }) }),
4156
+ /* @__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
4157
  displayError
3691
4158
  ] }),
3692
- children ?? /* @__PURE__ */ jsx6(
4159
+ !vaultActive && (children ?? /* @__PURE__ */ jsx7(
3693
4160
  "button",
3694
4161
  {
3695
4162
  type: "submit",
3696
4163
  disabled: !formReady || isSubmitting,
3697
4164
  "data-testid": "flopay-submit",
4165
+ onMouseEnter: (e) => {
4166
+ if (formReady && !isSubmitting) e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
4167
+ },
4168
+ onMouseLeave: (e) => {
4169
+ e.currentTarget.style.backgroundColor = bStyles.submitButton?.backgroundColor ?? resolvedPrimaryColor;
4170
+ },
3698
4171
  style: {
3699
4172
  width: "100%",
3700
4173
  padding: "0.875rem",
@@ -3707,11 +4180,12 @@ function SplitCardFormInner({
3707
4180
  fontWeight: 600,
3708
4181
  cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
3709
4182
  opacity: !formReady || isSubmitting ? 0.5 : 1,
4183
+ transition: "background-color 0.15s",
3710
4184
  ...bStyles.submitButton
3711
4185
  },
3712
4186
  children: isSubmitting ? "PROCESSING..." : submitLabel
3713
4187
  }
3714
- )
4188
+ ))
3715
4189
  ] });
3716
4190
  const gateDebugStyle = {
3717
4191
  margin: 0,
@@ -3727,9 +4201,9 @@ function SplitCardFormInner({
3727
4201
  const renderDirectPaypalGateDebug = (testId) => {
3728
4202
  if (!debug) return null;
3729
4203
  if (!showPayPal || !directPaypalConfigured) {
3730
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
4204
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
3731
4205
  }
3732
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4206
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
3733
4207
  "FloPay/DirectPayPal-debug (parent gate)",
3734
4208
  ` showPayPal=${showPayPal}`,
3735
4209
  ` directPaypalConfigured=${directPaypalConfigured}`,
@@ -3742,9 +4216,9 @@ function SplitCardFormInner({
3742
4216
  const renderStripeGateDebug = (testId) => {
3743
4217
  if (!debug) return null;
3744
4218
  if (!showStripe || !stripeInstance) {
3745
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
4219
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
3746
4220
  }
3747
- return /* @__PURE__ */ jsx6("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4221
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
3748
4222
  "FloPay/Stripe-debug (parent gate)",
3749
4223
  ` showStripe=${showStripe}`,
3750
4224
  ` currency=${currency}`,
@@ -3774,8 +4248,8 @@ function SplitCardFormInner({
3774
4248
  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
4249
  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
4250
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
3777
- /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
3778
- overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4251
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4252
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
3779
4253
  /* @__PURE__ */ jsxs4("div", { style: { display: "grid" }, children: [
3780
4254
  /* @__PURE__ */ jsxs4(
3781
4255
  "div",
@@ -3795,7 +4269,7 @@ function SplitCardFormInner({
3795
4269
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
3796
4270
  renderStripeGateDebug("flopay-stripe-gate-debug"),
3797
4271
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
3798
- paypalDirectRetry && /* @__PURE__ */ jsx6(
4272
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
3799
4273
  "div",
3800
4274
  {
3801
4275
  "data-testid": "flopay-paypal-direct-retry-notice",
@@ -3811,7 +4285,7 @@ function SplitCardFormInner({
3811
4285
  children: "Please confirm your PayPal payment to complete checkout."
3812
4286
  }
3813
4287
  ),
3814
- /* @__PURE__ */ jsx6(
4288
+ /* @__PURE__ */ jsx7(
3815
4289
  DirectPayPalButton,
3816
4290
  {
3817
4291
  sessionId,
@@ -3837,7 +4311,7 @@ function SplitCardFormInner({
3837
4311
  paypalDirectRetry?.orderId ?? "fresh"
3838
4312
  )
3839
4313
  ] }),
3840
- shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
4314
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
3841
4315
  PayPalButtonInner,
3842
4316
  {
3843
4317
  sessionId,
@@ -3851,10 +4325,10 @@ function SplitCardFormInner({
3851
4325
  onDecline,
3852
4326
  runBeforeButtonClick,
3853
4327
  onLoadStateChange: setPaypalLoadState,
3854
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4328
+ placeholderBorderRadius: buttonBorderRadius
3855
4329
  }
3856
4330
  ) }),
3857
- shouldRenderWallets ? /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
4331
+ shouldRenderWallets ? /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
3858
4332
  WalletButtonInner,
3859
4333
  {
3860
4334
  sessionId,
@@ -3868,10 +4342,10 @@ function SplitCardFormInner({
3868
4342
  onDecline,
3869
4343
  runBeforeButtonClick,
3870
4344
  onLoadStateChange: setWalletLoadState,
3871
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4345
+ placeholderBorderRadius: buttonBorderRadius
3872
4346
  }
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(
4347
+ ) }) : 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,
4348
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
3875
4349
  StripePaymentElementInner,
3876
4350
  {
3877
4351
  sessionId,
@@ -3899,11 +4373,11 @@ function SplitCardFormInner({
3899
4373
  backgroundColor: bStyles.cardButton?.backgroundColor,
3900
4374
  borderColor: bStyles.cardInputBorder,
3901
4375
  textColor: bStyles.cardButton?.color,
3902
- borderRadius: bStyles.cardButton?.borderRadius
4376
+ borderRadius: buttonBorderRadius
3903
4377
  }
3904
4378
  }
3905
4379
  ),
3906
- showStripe && /* @__PURE__ */ jsx6(
4380
+ showStripe && /* @__PURE__ */ jsx7(
3907
4381
  "button",
3908
4382
  {
3909
4383
  type: "button",
@@ -3931,7 +4405,8 @@ function SplitCardFormInner({
3931
4405
  themeBundle,
3932
4406
  resolvedPrimaryColor,
3933
4407
  resolvedBorderRadius,
3934
- submitButtonStyle: bStyles.submitButton
4408
+ submitButtonStyle: bStyles.submitButton,
4409
+ explicitPrimaryColor: appearanceVars?.colorPrimary
3935
4410
  }),
3936
4411
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
3937
4412
  fontWeight: 600,
@@ -3940,17 +4415,25 @@ function SplitCardFormInner({
3940
4415
  alignItems: "center",
3941
4416
  justifyContent: "center",
3942
4417
  gap: "0.625rem",
3943
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
4418
+ transition: "background-color 0.15s, border-color 0.2s, box-shadow 0.2s, transform 0.1s",
3944
4419
  position: "relative",
3945
4420
  opacity: isSubmitting ? 0.6 : 1
3946
4421
  },
4422
+ onMouseEnter: (e) => {
4423
+ if (!isSubmitting && (themeBundle || appearanceVars?.colorPrimary)) {
4424
+ e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
4425
+ }
4426
+ },
4427
+ onMouseLeave: (e) => {
4428
+ if (themeBundle || appearanceVars?.colorPrimary) e.currentTarget.style.backgroundColor = resolvedPrimaryColor;
4429
+ },
3947
4430
  onMouseDown: (e) => {
3948
4431
  e.currentTarget.style.transform = "scale(0.985)";
3949
4432
  },
3950
4433
  onMouseUp: (e) => {
3951
4434
  e.currentTarget.style.transform = "scale(1)";
3952
4435
  },
3953
- children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
4436
+ children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
3954
4437
  }
3955
4438
  ),
3956
4439
  displayError && viewState === "buttons" && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
@@ -3967,18 +4450,18 @@ function SplitCardFormInner({
3967
4450
  gap: "0.5rem",
3968
4451
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
3969
4452
  }, 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" }) }),
4453
+ /* @__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
4454
  displayError
3972
4455
  ] })
3973
4456
  ]
3974
4457
  }
3975
4458
  ),
3976
- showStripe && isCardView && /* @__PURE__ */ jsx6("div", { style: {
4459
+ showStripe && isCardView && /* @__PURE__ */ jsx7("div", { style: {
3977
4460
  gridArea: "1 / 1",
3978
4461
  ...cardAnim ? { animation: cardAnim } : {},
3979
4462
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
3980
4463
  }, children: cardFormBlock }),
3981
- showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx6("div", { style: {
4464
+ showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx7("div", { style: {
3982
4465
  gridArea: "1 / 1",
3983
4466
  ...apmAnim ? { animation: apmAnim } : {},
3984
4467
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
@@ -4016,7 +4499,7 @@ function SplitCardFormInner({
4016
4499
  },
4017
4500
  "aria-label": "Back to payment methods",
4018
4501
  children: [
4019
- /* @__PURE__ */ jsx6("span", { style: {
4502
+ /* @__PURE__ */ jsx7("span", { style: {
4020
4503
  display: "inline-flex",
4021
4504
  alignItems: "center",
4022
4505
  justifyContent: "center",
@@ -4026,12 +4509,12 @@ function SplitCardFormInner({
4026
4509
  backgroundColor: "#f3f4f6",
4027
4510
  transition: "background-color 0.15s",
4028
4511
  ...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 })
4512
+ }, 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" }) }) }),
4513
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4031
4514
  ]
4032
4515
  }
4033
4516
  ),
4034
- /* @__PURE__ */ jsx6("div", { style: {
4517
+ /* @__PURE__ */ jsx7("div", { style: {
4035
4518
  flex: 1,
4036
4519
  textAlign: "center",
4037
4520
  fontWeight: 600,
@@ -4041,12 +4524,12 @@ function SplitCardFormInner({
4041
4524
  ...bStyles.title
4042
4525
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
4043
4526
  ] }),
4044
- /* @__PURE__ */ jsx6(
4527
+ /* @__PURE__ */ jsx7(
4045
4528
  StripeElements,
4046
4529
  {
4047
4530
  stripe: stripeInstance,
4048
4531
  options: apmInlineOptions,
4049
- children: /* @__PURE__ */ jsx6(
4532
+ children: /* @__PURE__ */ jsx7(
4050
4533
  StripeMethodInlineForm,
4051
4534
  {
4052
4535
  method: expandedApmMethod,
@@ -4064,7 +4547,8 @@ function SplitCardFormInner({
4064
4547
  isProcessing: isSubmitting,
4065
4548
  submitButtonColor: resolvedPrimaryColor,
4066
4549
  submitButtonBorderRadius: resolvedBorderRadius,
4067
- submitButtonStyle: bStyles.submitButton
4550
+ submitButtonStyle: bStyles.submitButton,
4551
+ errorText: displayError
4068
4552
  }
4069
4553
  )
4070
4554
  },
@@ -4076,9 +4560,9 @@ function SplitCardFormInner({
4076
4560
  }
4077
4561
  if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
4078
4562
  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: {
4563
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4564
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4565
+ /* @__PURE__ */ jsx7("div", { style: {
4082
4566
  ...apmAnim ? { animation: apmAnim } : {},
4083
4567
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
4084
4568
  }, children: /* @__PURE__ */ jsxs4("div", { style: {
@@ -4115,7 +4599,7 @@ function SplitCardFormInner({
4115
4599
  },
4116
4600
  "aria-label": "Back to payment methods",
4117
4601
  children: [
4118
- /* @__PURE__ */ jsx6("span", { style: {
4602
+ /* @__PURE__ */ jsx7("span", { style: {
4119
4603
  display: "inline-flex",
4120
4604
  alignItems: "center",
4121
4605
  justifyContent: "center",
@@ -4125,12 +4609,12 @@ function SplitCardFormInner({
4125
4609
  backgroundColor: "#f3f4f6",
4126
4610
  transition: "background-color 0.15s",
4127
4611
  ...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 })
4612
+ }, 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" }) }) }),
4613
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4130
4614
  ]
4131
4615
  }
4132
4616
  ),
4133
- /* @__PURE__ */ jsx6("div", { style: {
4617
+ /* @__PURE__ */ jsx7("div", { style: {
4134
4618
  flex: 1,
4135
4619
  textAlign: "center",
4136
4620
  fontWeight: 600,
@@ -4140,12 +4624,12 @@ function SplitCardFormInner({
4140
4624
  ...bStyles.title
4141
4625
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
4142
4626
  ] }),
4143
- /* @__PURE__ */ jsx6(
4627
+ /* @__PURE__ */ jsx7(
4144
4628
  StripeElements,
4145
4629
  {
4146
4630
  stripe: stripeInstance,
4147
4631
  options: apmInlineOptions,
4148
- children: /* @__PURE__ */ jsx6(
4632
+ children: /* @__PURE__ */ jsx7(
4149
4633
  StripeMethodInlineForm,
4150
4634
  {
4151
4635
  method: expandedApmMethod,
@@ -4163,7 +4647,8 @@ function SplitCardFormInner({
4163
4647
  isProcessing: isSubmitting,
4164
4648
  submitButtonColor: resolvedPrimaryColor,
4165
4649
  submitButtonBorderRadius: resolvedBorderRadius,
4166
- submitButtonStyle: bStyles.submitButton
4650
+ submitButtonStyle: bStyles.submitButton,
4651
+ errorText: displayError
4167
4652
  }
4168
4653
  )
4169
4654
  },
@@ -4173,13 +4658,13 @@ function SplitCardFormInner({
4173
4658
  ] });
4174
4659
  }
4175
4660
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
4176
- /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
4177
- overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4661
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4662
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4178
4663
  /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
4179
4664
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
4180
4665
  renderStripeGateDebug("flopay-stripe-gate-debug-default"),
4181
4666
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
4182
- paypalDirectRetry && /* @__PURE__ */ jsx6(
4667
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
4183
4668
  "div",
4184
4669
  {
4185
4670
  "data-testid": "flopay-paypal-direct-retry-notice-default",
@@ -4195,7 +4680,7 @@ function SplitCardFormInner({
4195
4680
  children: "Please confirm your PayPal payment to complete checkout."
4196
4681
  }
4197
4682
  ),
4198
- /* @__PURE__ */ jsx6(
4683
+ /* @__PURE__ */ jsx7(
4199
4684
  DirectPayPalButton,
4200
4685
  {
4201
4686
  sessionId,
@@ -4221,7 +4706,7 @@ function SplitCardFormInner({
4221
4706
  paypalDirectRetry?.orderId ?? "fresh"
4222
4707
  )
4223
4708
  ] }),
4224
- shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
4709
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
4225
4710
  PayPalButtonInner,
4226
4711
  {
4227
4712
  sessionId,
@@ -4235,10 +4720,10 @@ function SplitCardFormInner({
4235
4720
  onDecline,
4236
4721
  runBeforeButtonClick,
4237
4722
  onLoadStateChange: setPaypalLoadState,
4238
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4723
+ placeholderBorderRadius: buttonBorderRadius
4239
4724
  }
4240
4725
  ) }),
4241
- shouldRenderWallets && /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
4726
+ shouldRenderWallets && /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
4242
4727
  WalletButtonInner,
4243
4728
  {
4244
4729
  sessionId,
@@ -4252,10 +4737,10 @@ function SplitCardFormInner({
4252
4737
  onDecline,
4253
4738
  runBeforeButtonClick,
4254
4739
  onLoadStateChange: setWalletLoadState,
4255
- placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
4740
+ placeholderBorderRadius: buttonBorderRadius
4256
4741
  }
4257
4742
  ) }),
4258
- shouldRenderPaymentElement && /* @__PURE__ */ jsx6(
4743
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
4259
4744
  StripePaymentElementInner,
4260
4745
  {
4261
4746
  sessionId,
@@ -4283,7 +4768,7 @@ function SplitCardFormInner({
4283
4768
  backgroundColor: bStyles.cardButton?.backgroundColor,
4284
4769
  borderColor: bStyles.cardInputBorder,
4285
4770
  textColor: bStyles.cardButton?.color,
4286
- borderRadius: bStyles.cardButton?.borderRadius
4771
+ borderRadius: buttonBorderRadius
4287
4772
  }
4288
4773
  }
4289
4774
  )
@@ -4296,9 +4781,9 @@ function SplitCardFormInner({
4296
4781
  color: "#999",
4297
4782
  fontSize: "0.85rem"
4298
4783
  }, 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" } })
4784
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
4785
+ /* @__PURE__ */ jsx7("span", { children: "or pay with card" }),
4786
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
4302
4787
  ] }),
4303
4788
  showStripe && cardFormBlock
4304
4789
  ] });
@@ -4341,6 +4826,210 @@ function getRedirectResultFromCheckoutProcessError(error) {
4341
4826
  paymentMethodId: error.paymentMethodId
4342
4827
  };
4343
4828
  }
4829
+ function isTerminalAutoCheckoutThreeDsOutcome(outcome) {
4830
+ return outcome.status === "succeeded" || outcome.status === "declined";
4831
+ }
4832
+ async function runAutoCheckoutThreeDsChallenge(input) {
4833
+ if (typeof document === "undefined" || typeof window === "undefined") {
4834
+ throw new FloPayError4("3DS challenge requires a browser environment.", "api_error", {
4835
+ code: "three_ds_no_window"
4836
+ });
4837
+ }
4838
+ if (input.signal?.aborted) {
4839
+ throw new FloPayError4("Card authentication was cancelled.", "api_error", {
4840
+ code: "three_ds_aborted"
4841
+ });
4842
+ }
4843
+ const CHALLENGE_TIMEOUT_MS = 3e5;
4844
+ const CHALLENGE_POLL_INTERVAL_MS = 1e3;
4845
+ const COMPLETE_REQUEST_TIMEOUT_MS = 15e3;
4846
+ const completionEndpoint = `${input.billingApiUrl.replace(/\/$/, "")}/v1/checkouts/sessions/${encodeURIComponent(
4847
+ input.sessionId
4848
+ )}/3ds/complete`;
4849
+ const completeThreeDs = async () => {
4850
+ const controller = new AbortController();
4851
+ const onParentAbort = () => controller.abort();
4852
+ if (input.signal?.aborted) {
4853
+ controller.abort();
4854
+ } else {
4855
+ input.signal?.addEventListener("abort", onParentAbort);
4856
+ }
4857
+ const requestTimeout = window.setTimeout(() => controller.abort(), COMPLETE_REQUEST_TIMEOUT_MS);
4858
+ try {
4859
+ const response = await fetch(completionEndpoint, {
4860
+ method: "POST",
4861
+ headers: {
4862
+ "x-checkout-session-token": input.nonce,
4863
+ "content-type": "application/json"
4864
+ },
4865
+ body: "{}",
4866
+ signal: controller.signal
4867
+ });
4868
+ if (!response.ok) {
4869
+ return { status: "unknown" };
4870
+ }
4871
+ const json = await response.json().catch(() => null);
4872
+ return coerceThreeDsOutcome(json);
4873
+ } catch {
4874
+ return { status: "unknown" };
4875
+ } finally {
4876
+ window.clearTimeout(requestTimeout);
4877
+ input.signal?.removeEventListener("abort", onParentAbort);
4878
+ }
4879
+ };
4880
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
4881
+ const backdrop = document.createElement("div");
4882
+ backdrop.setAttribute("data-flopay-auto-3ds-overlay", "1");
4883
+ backdrop.setAttribute("role", "dialog");
4884
+ backdrop.setAttribute("aria-modal", "true");
4885
+ backdrop.setAttribute("aria-label", "Card authentication");
4886
+ backdrop.style.cssText = [
4887
+ "position:fixed",
4888
+ "inset:0",
4889
+ "z-index:2147483647",
4890
+ "background:rgba(15,23,42,0.6)",
4891
+ "display:flex",
4892
+ "align-items:center",
4893
+ "justify-content:center",
4894
+ "padding:16px"
4895
+ ].join(";");
4896
+ const frame = document.createElement("iframe");
4897
+ frame.setAttribute("title", "Card authentication");
4898
+ frame.setAttribute("allow", "payment");
4899
+ frame.tabIndex = 0;
4900
+ frame.style.cssText = [
4901
+ "width:min(100%,460px)",
4902
+ "height:min(100%,640px)",
4903
+ "border:0",
4904
+ "border-radius:12px",
4905
+ "background:#fff",
4906
+ "box-shadow:0 12px 30px rgba(0,0,0,0.35)"
4907
+ ].join(";");
4908
+ frame.src = input.nextActionRedirectUrl;
4909
+ backdrop.appendChild(frame);
4910
+ document.body.appendChild(backdrop);
4911
+ frame.focus();
4912
+ let expectedReturnOrigin = null;
4913
+ try {
4914
+ expectedReturnOrigin = new URL(input.billingApiUrl, window.location.href).origin;
4915
+ } catch {
4916
+ expectedReturnOrigin = null;
4917
+ }
4918
+ try {
4919
+ const polledOutcome = await new Promise((resolve, reject) => {
4920
+ let settled = false;
4921
+ let timer = 0;
4922
+ let pollTimer = 0;
4923
+ let loadPollTimer = 0;
4924
+ let pollInFlight = false;
4925
+ const cleanup = () => {
4926
+ window.clearTimeout(timer);
4927
+ window.clearInterval(pollTimer);
4928
+ window.clearTimeout(loadPollTimer);
4929
+ window.removeEventListener("message", listener);
4930
+ frame.removeEventListener("load", onFrameLoad);
4931
+ input.signal?.removeEventListener("abort", onAbort);
4932
+ };
4933
+ const maybeResolveFromBackend = async () => {
4934
+ if (settled || pollInFlight) return;
4935
+ pollInFlight = true;
4936
+ try {
4937
+ const outcome = await completeThreeDs();
4938
+ if (settled || !isTerminalAutoCheckoutThreeDsOutcome(outcome)) return;
4939
+ settled = true;
4940
+ cleanup();
4941
+ resolve(outcome);
4942
+ } finally {
4943
+ pollInFlight = false;
4944
+ }
4945
+ };
4946
+ const onFrameLoad = () => {
4947
+ window.clearTimeout(loadPollTimer);
4948
+ loadPollTimer = window.setTimeout(() => {
4949
+ void maybeResolveFromBackend();
4950
+ }, 250);
4951
+ };
4952
+ const onAbort = () => {
4953
+ if (settled) return;
4954
+ settled = true;
4955
+ cleanup();
4956
+ reject(
4957
+ new FloPayError4("Card authentication was cancelled.", "api_error", {
4958
+ code: "three_ds_aborted"
4959
+ })
4960
+ );
4961
+ };
4962
+ const listener = (event) => {
4963
+ if (event.source !== frame.contentWindow) return;
4964
+ if (expectedReturnOrigin && event.origin !== expectedReturnOrigin) return;
4965
+ const data = event.data;
4966
+ if (!data || typeof data !== "object") return;
4967
+ const record = data;
4968
+ if (record["source"] !== "flopay-vault-3ds-return") return;
4969
+ if (settled) return;
4970
+ settled = true;
4971
+ cleanup();
4972
+ resolve(null);
4973
+ };
4974
+ timer = window.setTimeout(() => {
4975
+ if (settled) return;
4976
+ settled = true;
4977
+ cleanup();
4978
+ reject(
4979
+ new FloPayError4("Card authentication timed out.", "api_error", {
4980
+ code: "three_ds_timeout"
4981
+ })
4982
+ );
4983
+ }, CHALLENGE_TIMEOUT_MS);
4984
+ pollTimer = window.setInterval(() => {
4985
+ void maybeResolveFromBackend();
4986
+ }, CHALLENGE_POLL_INTERVAL_MS);
4987
+ frame.addEventListener("load", onFrameLoad);
4988
+ window.addEventListener("message", listener);
4989
+ input.signal?.addEventListener("abort", onAbort);
4990
+ if (input.signal?.aborted) onAbort();
4991
+ });
4992
+ if (polledOutcome) {
4993
+ return polledOutcome;
4994
+ }
4995
+ return await completeThreeDs();
4996
+ } finally {
4997
+ backdrop.parentNode?.removeChild(backdrop);
4998
+ if (previousFocus?.isConnected) {
4999
+ previousFocus.focus();
5000
+ }
5001
+ }
5002
+ }
5003
+ function coerceThreeDsOutcome(json) {
5004
+ if (!json || typeof json !== "object") return { status: "unknown" };
5005
+ const status = typeof json["status"] === "string" ? json["status"] : "unknown";
5006
+ const providerIntentId = typeof json["providerIntentId"] === "string" ? json["providerIntentId"] : null;
5007
+ switch (status) {
5008
+ case "succeeded":
5009
+ return { status: "succeeded", providerIntentId };
5010
+ case "declined":
5011
+ return {
5012
+ status: "declined",
5013
+ providerIntentId,
5014
+ declineReason: typeof json["declineReason"] === "string" ? json["declineReason"] : null,
5015
+ gatewayDeclineReason: typeof json["gatewayDeclineReason"] === "string" ? json["gatewayDeclineReason"] : null
5016
+ };
5017
+ case "requires_action":
5018
+ return {
5019
+ status: "requires_action",
5020
+ providerIntentId,
5021
+ nextActionRedirectUrl: typeof json["nextActionRedirectUrl"] === "string" ? json["nextActionRedirectUrl"] : null
5022
+ };
5023
+ case "pending":
5024
+ return { status: "pending", providerIntentId };
5025
+ case "timeout":
5026
+ return { status: "timeout", providerIntentId };
5027
+ case "no_attempt":
5028
+ return { status: "no_attempt" };
5029
+ default:
5030
+ return { status: "unknown" };
5031
+ }
5032
+ }
4344
5033
  function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment failed. Please try again.", options) {
4345
5034
  const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
4346
5035
  const rawMessage = error?.message ?? fallbackMessage;
@@ -4782,7 +5471,7 @@ async function loadSavedPaymentProviders({
4782
5471
  }
4783
5472
 
4784
5473
  // src/flopay-checkout.tsx
4785
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
5474
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
4786
5475
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
4787
5476
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
4788
5477
  var sessionInflightMap = /* @__PURE__ */ new Map();
@@ -4831,6 +5520,36 @@ function clearPayPalResumeState() {
4831
5520
  console.warn("[FloPayCheckout] Failed to clear PayPal resume state.", error);
4832
5521
  }
4833
5522
  }
5523
+ function readCachedInlineSession(cacheKey) {
5524
+ if (!canUseStorage()) return null;
5525
+ const raw = window.sessionStorage.getItem(cacheKey);
5526
+ if (!raw) return null;
5527
+ try {
5528
+ const parsed = JSON.parse(raw);
5529
+ if (parsed && typeof parsed.sid === "string" && parsed.sid) {
5530
+ return { sid: parsed.sid, nonce: typeof parsed.nonce === "string" ? parsed.nonce : void 0 };
5531
+ }
5532
+ return null;
5533
+ } catch {
5534
+ return { sid: raw };
5535
+ }
5536
+ }
5537
+ function persistCachedInlineSession(cacheKey, sid, nonce) {
5538
+ if (!canUseStorage()) return;
5539
+ try {
5540
+ window.sessionStorage.setItem(cacheKey, JSON.stringify(nonce ? { sid, nonce } : { sid }));
5541
+ } catch (error) {
5542
+ console.warn("[FloPayCheckout] Failed to persist checkout session cache.", error);
5543
+ }
5544
+ }
5545
+ function clearCachedInlineSession(cacheKey) {
5546
+ if (!canUseStorage()) return;
5547
+ try {
5548
+ window.sessionStorage.removeItem(cacheKey);
5549
+ } catch (error) {
5550
+ console.warn("[FloPayCheckout] Failed to clear checkout session cache.", error);
5551
+ }
5552
+ }
4834
5553
  function clearPayPalRedirectParams() {
4835
5554
  if (typeof window === "undefined") return;
4836
5555
  const url = new URL(window.location.href);
@@ -4889,6 +5608,8 @@ function FloPayCheckout({
4889
5608
  onButtonClick,
4890
5609
  onBeforeButtonClick,
4891
5610
  enableAVS,
5611
+ cardFieldOrder,
5612
+ cardPreFormSlot,
4892
5613
  avsLayout,
4893
5614
  submitLabel,
4894
5615
  className,
@@ -4906,9 +5627,9 @@ function FloPayCheckout({
4906
5627
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
4907
5628
  const [unified, setUnified] = useState4(null);
4908
5629
  const [flopay, setFloPay] = useState4(null);
4909
- const flopayRef = useRef4(null);
5630
+ const flopayRef = useRef5(null);
4910
5631
  const [paypalFlopay, setPaypalFloPay] = useState4(null);
4911
- const paypalFlopayRef = useRef4(null);
5632
+ const paypalFlopayRef = useRef5(null);
4912
5633
  const [session, setSession] = useState4(null);
4913
5634
  const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
4914
5635
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
@@ -4923,18 +5644,18 @@ function FloPayCheckout({
4923
5644
  const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
4924
5645
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
4925
5646
  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);
5647
+ const autoCheckoutAttempted = useRef5(false);
5648
+ const paypalResumeAttempted = useRef5(false);
5649
+ const savedPaymentKeysRef = useRef5(null);
5650
+ const onCompleteRef = useRef5(onComplete);
4930
5651
  onCompleteRef.current = onComplete;
4931
- const onErrorRef = useRef4(onError);
5652
+ const onErrorRef = useRef5(onError);
4932
5653
  onErrorRef.current = onError;
4933
- const onDeclineRef = useRef4(onDecline);
5654
+ const onDeclineRef = useRef5(onDecline);
4934
5655
  onDeclineRef.current = onDecline;
4935
- const onSessionCompletedRef = useRef4(onSessionCompleted);
5656
+ const onSessionCompletedRef = useRef5(onSessionCompleted);
4936
5657
  onSessionCompletedRef.current = onSessionCompleted;
4937
- useEffect5(() => {
5658
+ useEffect6(() => {
4938
5659
  console.info("[FloPay] Checkout initialized", {
4939
5660
  sdk_version: SDK_VERSION,
4940
5661
  checkout_type: checkoutType,
@@ -4962,11 +5683,11 @@ function FloPayCheckout({
4962
5683
  } : void 0,
4963
5684
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
4964
5685
  );
4965
- useEffect5(() => {
5686
+ useEffect6(() => {
4966
5687
  setCreateSessionPatch(void 0);
4967
5688
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
4968
5689
  }, [baseCreateSessionHash]);
4969
- useEffect5(() => {
5690
+ useEffect6(() => {
4970
5691
  setModeError(initialErrorMessage);
4971
5692
  }, [initialErrorMessage]);
4972
5693
  const emitDecline = useCallback2(
@@ -5093,7 +5814,7 @@ function FloPayCheckout({
5093
5814
  resolvedBillingUrl
5094
5815
  ]
5095
5816
  );
5096
- useEffect5(() => {
5817
+ useEffect6(() => {
5097
5818
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
5098
5819
  return;
5099
5820
  }
@@ -5210,7 +5931,7 @@ function FloPayCheckout({
5210
5931
  }
5211
5932
  })();
5212
5933
  }, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
5213
- const initializedHashRef = useRef4(null);
5934
+ const initializedHashRef = useRef5(null);
5214
5935
  function hashCreateParams(params) {
5215
5936
  const key = JSON.stringify({
5216
5937
  c: params?.clientId,
@@ -5247,12 +5968,12 @@ function FloPayCheckout({
5247
5968
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
5248
5969
  [effectiveCreateSession]
5249
5970
  );
5250
- const createSessionParamsRef = useRef4(effectiveCreateSession);
5971
+ const createSessionParamsRef = useRef5(effectiveCreateSession);
5251
5972
  createSessionParamsRef.current = effectiveCreateSession;
5252
- useEffect5(() => {
5973
+ useEffect6(() => {
5253
5974
  setResolvedSessionId(sessionIdProp ?? "");
5254
5975
  }, [sessionIdProp]);
5255
- useEffect5(() => {
5976
+ useEffect6(() => {
5256
5977
  autoCheckoutAttempted.current = false;
5257
5978
  setModeError(initialErrorMessage);
5258
5979
  setModeOverlayError(null);
@@ -5260,22 +5981,23 @@ function FloPayCheckout({
5260
5981
  }, [createSessionHash, initialErrorMessage, sessionIdProp]);
5261
5982
  async function resolveInlineSession(params, cacheKey) {
5262
5983
  const api = new PaymentAPI4(resolvedBillingUrl);
5263
- let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
5984
+ const cached = readCachedInlineSession(cacheKey);
5985
+ let sid = cached?.sid ?? null;
5264
5986
  let realResult = null;
5265
5987
  if (sid) {
5266
5988
  try {
5267
- realResult = await api.getUnifiedCheckoutSession(sid);
5989
+ realResult = await api.getUnifiedCheckoutSession(sid, cached?.nonce);
5268
5990
  const status = realResult.data.session?.status;
5269
5991
  if (status === "complete") {
5270
5992
  if (wasSessionRecentlyCompleted(sid)) {
5271
5993
  return { sid, result: realResult };
5272
5994
  }
5273
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
5995
+ clearCachedInlineSession(cacheKey);
5274
5996
  sid = null;
5275
5997
  realResult = null;
5276
5998
  }
5277
5999
  } catch {
5278
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
6000
+ clearCachedInlineSession(cacheKey);
5279
6001
  sid = null;
5280
6002
  }
5281
6003
  }
@@ -5289,8 +6011,8 @@ function FloPayCheckout({
5289
6011
  };
5290
6012
  realResult = await api.createAndFetchSession(paramsWithAnalytics);
5291
6013
  sid = realResult.data.session?.id ?? "";
5292
- if (sid && typeof window !== "undefined") {
5293
- window.sessionStorage.setItem(cacheKey, sid);
6014
+ if (sid) {
6015
+ persistCachedInlineSession(cacheKey, sid, realResult.data.session?.clientSecret);
5294
6016
  }
5295
6017
  }
5296
6018
  return { sid: sid ?? "", result: realResult };
@@ -5379,7 +6101,7 @@ function FloPayCheckout({
5379
6101
  resolvedSessionId,
5380
6102
  session
5381
6103
  ]);
5382
- useEffect5(() => {
6104
+ useEffect6(() => {
5383
6105
  let cancelled = false;
5384
6106
  setLoadError(null);
5385
6107
  if (createSessionHash) {
@@ -5601,7 +6323,7 @@ function FloPayCheckout({
5601
6323
  ]
5602
6324
  );
5603
6325
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && !isPaypalOnlySession && (!flopay || !providerOptions);
5604
- const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx7(
6326
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx8(
5605
6327
  ProcessingOverlay,
5606
6328
  {
5607
6329
  status: modeOverlayStatus,
@@ -5617,7 +6339,7 @@ function FloPayCheckout({
5617
6339
  }
5618
6340
  if (layout === "buttons") {
5619
6341
  const bundleRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
5620
- const skeletonBar = (h) => /* @__PURE__ */ jsx7("div", { style: {
6342
+ const skeletonBar = (h) => /* @__PURE__ */ jsx8("div", { style: {
5621
6343
  height: h,
5622
6344
  borderRadius: bundleRadius,
5623
6345
  background: "#e5e7eb",
@@ -5628,14 +6350,14 @@ function FloPayCheckout({
5628
6350
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5629
6351
  showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5630
6352
  showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
5631
- /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6353
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
5632
6354
  ] }),
5633
6355
  modeOverlay
5634
6356
  ] });
5635
6357
  }
5636
6358
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5637
6359
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
5638
- /* @__PURE__ */ jsx7("div", { style: {
6360
+ /* @__PURE__ */ jsx8("div", { style: {
5639
6361
  width: 24,
5640
6362
  height: 24,
5641
6363
  border: "2px solid #e5e7eb",
@@ -5643,16 +6365,16 @@ function FloPayCheckout({
5643
6365
  borderRadius: "50%",
5644
6366
  animation: "spin 0.6s linear infinite"
5645
6367
  } }),
5646
- /* @__PURE__ */ jsx7("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
6368
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
5647
6369
  ] }),
5648
6370
  modeOverlay
5649
6371
  ] });
5650
6372
  }
5651
6373
  if (loadError) {
5652
6374
  if (errorNode) {
5653
- return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
6375
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
5654
6376
  }
5655
- return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
6377
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
5656
6378
  "div",
5657
6379
  {
5658
6380
  role: "alert",
@@ -5669,7 +6391,7 @@ function FloPayCheckout({
5669
6391
  }
5670
6392
  if (shouldShowInterimButtons) {
5671
6393
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
5672
- /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
6394
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
5673
6395
  InterimButtonsView,
5674
6396
  {
5675
6397
  onButtonClick,
@@ -5690,8 +6412,8 @@ function FloPayCheckout({
5690
6412
  }
5691
6413
  if (isPaypalOnlySession && session && directPaypalConfig) {
5692
6414
  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(
6415
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs5("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
6416
+ modeError && /* @__PURE__ */ jsx8(
5695
6417
  "div",
5696
6418
  {
5697
6419
  role: "alert",
@@ -5708,7 +6430,7 @@ function FloPayCheckout({
5708
6430
  children: modeError
5709
6431
  }
5710
6432
  ),
5711
- /* @__PURE__ */ jsx7(
6433
+ /* @__PURE__ */ jsx8(
5712
6434
  DirectPayPalButton,
5713
6435
  {
5714
6436
  sessionId: activeSessionId,
@@ -5732,12 +6454,12 @@ function FloPayCheckout({
5732
6454
  ] });
5733
6455
  }
5734
6456
  if (!flopay || !providerOptions) {
5735
- return /* @__PURE__ */ jsx7(Fragment3, { children: modeOverlay });
6457
+ return /* @__PURE__ */ jsx8(Fragment3, { children: modeOverlay });
5736
6458
  }
5737
6459
  if (currentMode === "confirm") {
5738
6460
  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(
6461
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs5("div", { className, children: [
6462
+ modeError && /* @__PURE__ */ jsx8(
5741
6463
  "div",
5742
6464
  {
5743
6465
  style: {
@@ -5752,7 +6474,7 @@ function FloPayCheckout({
5752
6474
  renderConfirmButton ? renderConfirmButton({
5753
6475
  onConfirm: handleConfirmCheckout,
5754
6476
  isProcessing: confirmProcessing
5755
- }) : /* @__PURE__ */ jsx7(
6477
+ }) : /* @__PURE__ */ jsx8(
5756
6478
  "button",
5757
6479
  {
5758
6480
  type: "button",
@@ -5778,8 +6500,8 @@ function FloPayCheckout({
5778
6500
  ] });
5779
6501
  }
5780
6502
  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(
6503
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
6504
+ modeError && /* @__PURE__ */ jsx8(
5783
6505
  "div",
5784
6506
  {
5785
6507
  style: {
@@ -5794,7 +6516,7 @@ function FloPayCheckout({
5794
6516
  children: modeError
5795
6517
  }
5796
6518
  ),
5797
- /* @__PURE__ */ jsx7(
6519
+ /* @__PURE__ */ jsx8(
5798
6520
  SessionInjector,
5799
6521
  {
5800
6522
  sessionId: activeSessionId,
@@ -5804,7 +6526,7 @@ function FloPayCheckout({
5804
6526
  children
5805
6527
  }
5806
6528
  )
5807
- ] }) : /* @__PURE__ */ jsx7(
6529
+ ] }) : /* @__PURE__ */ jsx8(
5808
6530
  SplitCardForm,
5809
6531
  {
5810
6532
  sessionId: activeSessionId,
@@ -5824,6 +6546,7 @@ function FloPayCheckout({
5824
6546
  showPayPal,
5825
6547
  showStripe,
5826
6548
  enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
6549
+ enabledPaymentMethodCountries: unified?.data.stripe?.enabledPaymentMethodCountries,
5827
6550
  showApplePay,
5828
6551
  showGooglePay,
5829
6552
  layout,
@@ -5837,6 +6560,8 @@ function FloPayCheckout({
5837
6560
  onButtonClick,
5838
6561
  onBeforeButtonClick,
5839
6562
  enableAVS,
6563
+ ...cardFieldOrder ? { cardFieldOrder } : {},
6564
+ ...cardPreFormSlot ? { cardPreFormSlot } : {},
5840
6565
  avsLayout,
5841
6566
  country: session?.customer?.country,
5842
6567
  city: session?.customer?.city,
@@ -5864,8 +6589,8 @@ function SessionInjector({
5864
6589
  session,
5865
6590
  children
5866
6591
  }) {
5867
- return /* @__PURE__ */ jsx7(Fragment3, { children: React7.Children.map(children, (child) => {
5868
- if (!React7.isValidElement(child)) return child;
6592
+ return /* @__PURE__ */ jsx8(Fragment3, { children: React8.Children.map(children, (child) => {
6593
+ if (!React8.isValidElement(child)) return child;
5869
6594
  const existing = child.props;
5870
6595
  const injected = {};
5871
6596
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -5880,7 +6605,7 @@ function SessionInjector({
5880
6605
  injected.lastName = session.customer.lastName;
5881
6606
  }
5882
6607
  if (Object.keys(injected).length === 0) return child;
5883
- return React7.cloneElement(child, injected);
6608
+ return React8.cloneElement(child, injected);
5884
6609
  }) });
5885
6610
  }
5886
6611
  function InterimButtonsView({
@@ -5902,7 +6627,7 @@ function InterimButtonsView({
5902
6627
  }) {
5903
6628
  const [showCardForm, setShowCardForm] = useState4(false);
5904
6629
  const isCardOpenControlled = typeof cardOpen === "boolean";
5905
- useEffect5(() => {
6630
+ useEffect6(() => {
5906
6631
  if (isCardOpenControlled) {
5907
6632
  setShowCardForm(cardOpen);
5908
6633
  }
@@ -5923,7 +6648,7 @@ function InterimButtonsView({
5923
6648
  };
5924
6649
  }, [themeBundle, buttonsTheme, stylesOverride]);
5925
6650
  const skeletonRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
5926
- const skeleton = (h) => /* @__PURE__ */ jsx7("div", { style: {
6651
+ const skeleton = (h) => /* @__PURE__ */ jsx8("div", { style: {
5927
6652
  height: h,
5928
6653
  borderRadius: skeletonRadius,
5929
6654
  background: "#e5e7eb",
@@ -5970,7 +6695,7 @@ function InterimButtonsView({
5970
6695
  ...bStyles.backButton
5971
6696
  },
5972
6697
  children: [
5973
- /* @__PURE__ */ jsx7("span", { style: {
6698
+ /* @__PURE__ */ jsx8("span", { style: {
5974
6699
  display: "inline-flex",
5975
6700
  alignItems: "center",
5976
6701
  justifyContent: "center",
@@ -5979,12 +6704,12 @@ function InterimButtonsView({
5979
6704
  borderRadius: "50%",
5980
6705
  backgroundColor: "#f3f4f6",
5981
6706
  ...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 })
6707
+ }, 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" }) }) }),
6708
+ /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
5984
6709
  ]
5985
6710
  }
5986
6711
  ),
5987
- hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
6712
+ hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
5988
6713
  flex: 1,
5989
6714
  textAlign: "center",
5990
6715
  fontWeight: 600,
@@ -5992,11 +6717,11 @@ function InterimButtonsView({
5992
6717
  color: "#262833",
5993
6718
  paddingRight: 80,
5994
6719
  ...bStyles.title
5995
- }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
6720
+ }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
5996
6721
  ] }),
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" } }) }),
6722
+ /* @__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
6723
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex" }, children: [
5999
- /* @__PURE__ */ jsx7("div", { style: {
6724
+ /* @__PURE__ */ jsx8("div", { style: {
6000
6725
  flex: 1,
6001
6726
  backgroundColor: inputBg,
6002
6727
  borderTop: "none",
@@ -6006,8 +6731,8 @@ function InterimButtonsView({
6006
6731
  borderBottomLeftRadius: 8,
6007
6732
  padding: 12,
6008
6733
  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: {
6734
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
6735
+ /* @__PURE__ */ jsx8("div", { style: {
6011
6736
  flex: 1,
6012
6737
  backgroundColor: inputBg,
6013
6738
  borderTop: "none",
@@ -6017,10 +6742,10 @@ function InterimButtonsView({
6017
6742
  borderBottomRightRadius: 8,
6018
6743
  padding: 12,
6019
6744
  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" } }) })
6745
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
6021
6746
  ] }),
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: {
6747
+ /* @__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" } }) }),
6748
+ /* @__PURE__ */ jsx8("div", { style: {
6024
6749
  height: 50,
6025
6750
  borderRadius: 8,
6026
6751
  marginTop: 16,
@@ -6029,7 +6754,7 @@ function InterimButtonsView({
6029
6754
  ...bStyles.submitButton,
6030
6755
  opacity: 0.5
6031
6756
  } }),
6032
- /* @__PURE__ */ jsx7("style", { children: `
6757
+ /* @__PURE__ */ jsx8("style", { children: `
6033
6758
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
6034
6759
  @keyframes flopay-interim-expand {
6035
6760
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -6042,7 +6767,7 @@ function InterimButtonsView({
6042
6767
  return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
6043
6768
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
6044
6769
  showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
6045
- showStripe && /* @__PURE__ */ jsx7(
6770
+ showStripe && /* @__PURE__ */ jsx8(
6046
6771
  "button",
6047
6772
  {
6048
6773
  type: "button",
@@ -6082,7 +6807,7 @@ function InterimButtonsView({
6082
6807
  onMouseUp: (e) => {
6083
6808
  e.currentTarget.style.transform = "scale(1)";
6084
6809
  },
6085
- children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
6810
+ children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
6086
6811
  }
6087
6812
  ),
6088
6813
  errorMessage && /* @__PURE__ */ jsxs5("div", { style: {
@@ -6099,22 +6824,22 @@ function InterimButtonsView({
6099
6824
  gap: "0.5rem",
6100
6825
  ...bStyles.errorBanner
6101
6826
  }, 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" }) }),
6827
+ /* @__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
6828
  errorMessage
6104
6829
  ] }),
6105
- /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6830
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
6106
6831
  ] });
6107
6832
  }
6108
6833
 
6109
6834
  // src/checkout-form.tsx
6110
6835
  import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
6111
6836
  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";
6837
+ import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
6838
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
6114
6839
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
6115
6840
  var CheckoutForm = forwardRef2(
6116
6841
  function CheckoutForm2(props, ref) {
6117
- return /* @__PURE__ */ jsx8(CheckoutFormInner, { ...props, innerRef: ref });
6842
+ return /* @__PURE__ */ jsx9(CheckoutFormInner, { ...props, innerRef: ref });
6118
6843
  }
6119
6844
  );
6120
6845
  function CheckoutFormInner({
@@ -6313,7 +7038,7 @@ function CheckoutFormInner({
6313
7038
  }
6314
7039
  }
6315
7040
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
6316
- useEffect6(() => {
7041
+ useEffect7(() => {
6317
7042
  if (typeof window === "undefined") return;
6318
7043
  const stored = localStorage.getItem(WALLET_RESUME_KEY);
6319
7044
  if (!stored) return;
@@ -6424,7 +7149,7 @@ function CheckoutFormInner({
6424
7149
  );
6425
7150
  const isReady = flopay !== null && elements !== null;
6426
7151
  return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
6427
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-overlay", style: {
7152
+ (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-overlay", style: {
6428
7153
  position: "absolute",
6429
7154
  inset: 0,
6430
7155
  background: "rgba(255,255,255,0.7)",
@@ -6433,12 +7158,12 @@ function CheckoutFormInner({
6433
7158
  justifyContent: "center",
6434
7159
  zIndex: 10
6435
7160
  }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
6436
- !isReady && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
7161
+ !isReady && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
6437
7162
  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(
7163
+ /* @__PURE__ */ jsx9(PaymentElement, { options: { layout } }),
7164
+ showAddress && /* @__PURE__ */ jsx9(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
7165
+ displayError && /* @__PURE__ */ jsx9("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
7166
+ children ?? /* @__PURE__ */ jsx9(
6442
7167
  "button",
6443
7168
  {
6444
7169
  type: "submit",
@@ -6454,8 +7179,8 @@ function CheckoutFormInner({
6454
7179
  // src/paypal-button.tsx
6455
7180
  import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
6456
7181
  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";
7182
+ import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef6, useState as useState6 } from "react";
7183
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
6459
7184
  function PayPalButton({
6460
7185
  sessionId,
6461
7186
  nonce,
@@ -6475,7 +7200,7 @@ function PayPalButton({
6475
7200
  const contextBillingUrl = useBillingApiUrl();
6476
7201
  const [ready, setReady] = useState6(false);
6477
7202
  const [submitting, setSubmitting] = useState6(false);
6478
- const paypalResumeAttempted = useRef5(false);
7203
+ const paypalResumeAttempted = useRef6(false);
6479
7204
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
6480
7205
  const processPaymentInternal = useCallback4(
6481
7206
  async (tokenizedBody) => {
@@ -6515,7 +7240,7 @@ function PayPalButton({
6515
7240
  },
6516
7241
  [onTokenizedBody, processPaymentInternal]
6517
7242
  );
6518
- useEffect7(() => {
7243
+ useEffect8(() => {
6519
7244
  if (!flopay || paypalResumeAttempted.current) return;
6520
7245
  const params = new URLSearchParams(window.location.search);
6521
7246
  const paymentIntentId = params.get("payment_intent");
@@ -6597,11 +7322,11 @@ function PayPalButton({
6597
7322
  }
6598
7323
  }, [flopay, elements, sessionId, nonce, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
6599
7324
  if (!flopay || !elements) {
6600
- return /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
7325
+ return /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
6601
7326
  }
6602
7327
  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(
7328
+ !ready && /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
7329
+ /* @__PURE__ */ jsx10("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx10(
6605
7330
  "button",
6606
7331
  {
6607
7332
  type: "button",
@@ -6623,7 +7348,7 @@ function PayPalButton({
6623
7348
  children: submitting ? "Processing..." : "PayPal"
6624
7349
  }
6625
7350
  ) }),
6626
- (submitting || isProcessing) && /* @__PURE__ */ jsx9("div", { style: {
7351
+ (submitting || isProcessing) && /* @__PURE__ */ jsx10("div", { style: {
6627
7352
  position: "fixed",
6628
7353
  inset: 0,
6629
7354
  background: "rgba(0,0,0,0.4)",
@@ -6631,7 +7356,7 @@ function PayPalButton({
6631
7356
  alignItems: "center",
6632
7357
  justifyContent: "center",
6633
7358
  zIndex: 1e3
6634
- }, children: /* @__PURE__ */ jsx9("div", { style: {
7359
+ }, children: /* @__PURE__ */ jsx10("div", { style: {
6635
7360
  background: "white",
6636
7361
  borderRadius: 8,
6637
7362
  padding: "1.5rem",
@@ -6643,10 +7368,10 @@ function PayPalButton({
6643
7368
  }
6644
7369
 
6645
7370
  // src/automatic-payment-button.tsx
6646
- import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
7371
+ import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef7, useState as useState7 } from "react";
6647
7372
  import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
6648
7373
  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";
7374
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
6650
7375
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
6651
7376
  function sleep2(ms) {
6652
7377
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -6714,6 +7439,7 @@ function FloPayAutomaticPaymentButton({
6714
7439
  billingApiUrl,
6715
7440
  locale,
6716
7441
  theme,
7442
+ appearance,
6717
7443
  buttonsTheme,
6718
7444
  buttonsStyles: stylesOverride,
6719
7445
  onSuccess,
@@ -6761,30 +7487,32 @@ function FloPayAutomaticPaymentButton({
6761
7487
  const [overlayStatus, setOverlayStatus] = useState7(null);
6762
7488
  const [overlayError, setOverlayError] = useState7(null);
6763
7489
  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(() => {
7490
+ const isMountedRef = useRef7(true);
7491
+ const threeDsAbortRef = useRef7(null);
7492
+ const fallbackSessionRef = useRef7(fallbackSession);
7493
+ const onSuccessRef = useRef7(onSuccess);
7494
+ const onErrorRef = useRef7(onError);
7495
+ const onDeclineRef = useRef7(onDecline);
7496
+ useEffect9(() => {
6770
7497
  fallbackSessionRef.current = fallbackSession;
6771
7498
  }, [fallbackSession]);
6772
- useEffect8(() => {
7499
+ useEffect9(() => {
6773
7500
  onSuccessRef.current = onSuccess;
6774
7501
  }, [onSuccess]);
6775
- useEffect8(() => {
7502
+ useEffect9(() => {
6776
7503
  onErrorRef.current = onError;
6777
7504
  }, [onError]);
6778
- useEffect8(() => {
7505
+ useEffect9(() => {
6779
7506
  onDeclineRef.current = onDecline;
6780
7507
  }, [onDecline]);
6781
- useEffect8(() => {
7508
+ useEffect9(() => {
6782
7509
  isMountedRef.current = true;
6783
7510
  return () => {
6784
7511
  isMountedRef.current = false;
7512
+ threeDsAbortRef.current?.abort();
6785
7513
  };
6786
7514
  }, []);
6787
- useEffect8(() => {
7515
+ useEffect9(() => {
6788
7516
  if (!fallbackSession || typeof window === "undefined") {
6789
7517
  return;
6790
7518
  }
@@ -6863,6 +7591,62 @@ function FloPayAutomaticPaymentButton({
6863
7591
  return;
6864
7592
  }
6865
7593
  if (apiResult.autoProcessingError) {
7594
+ if (apiResult.autoProcessingError.type === "3ds_required" && apiResult.autoProcessingError.nextActionRedirectUrl && resolvedSessionId && effectiveNonce) {
7595
+ const threeDsAbort = new AbortController();
7596
+ threeDsAbortRef.current = threeDsAbort;
7597
+ let outcome;
7598
+ try {
7599
+ outcome = await runAutoCheckoutThreeDsChallenge({
7600
+ billingApiUrl: resolvedBillingUrl,
7601
+ sessionId: resolvedSessionId,
7602
+ nonce: effectiveNonce,
7603
+ nextActionRedirectUrl: apiResult.autoProcessingError.nextActionRedirectUrl,
7604
+ signal: threeDsAbort.signal
7605
+ });
7606
+ } finally {
7607
+ if (threeDsAbortRef.current === threeDsAbort) {
7608
+ threeDsAbortRef.current = null;
7609
+ }
7610
+ }
7611
+ if (outcome.status === "declined") {
7612
+ throw checkoutProcessErrorToFloPayError(
7613
+ {
7614
+ type: outcome.declineReason ?? "decline",
7615
+ message: "Card was declined.",
7616
+ gatewayErrorCode: outcome.gatewayDeclineReason ?? void 0
7617
+ },
7618
+ "Card was declined.",
7619
+ {
7620
+ checkoutMethod: apiResult.autoProcessingError.checkoutMethod
7621
+ }
7622
+ );
7623
+ }
7624
+ const api = new PaymentAPI7(resolvedBillingUrl);
7625
+ const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
7626
+ initialDelayMs: 0,
7627
+ nonce: effectiveNonce
7628
+ });
7629
+ const completedSession = completed.data.session;
7630
+ if (!completedSession || completedSession.status !== "complete") {
7631
+ throw checkoutProcessErrorToFloPayError(
7632
+ {
7633
+ type: "unknown",
7634
+ message: "Card authentication was not completed."
7635
+ },
7636
+ "Card authentication was not completed.",
7637
+ {
7638
+ checkoutMethod: apiResult.autoProcessingError.checkoutMethod
7639
+ }
7640
+ );
7641
+ }
7642
+ await showSuccess({
7643
+ result: { status: "succeeded" },
7644
+ session: completedSession,
7645
+ sessionId: completedSession.id || resolvedSessionId,
7646
+ autoCompleted: false
7647
+ });
7648
+ return;
7649
+ }
6866
7650
  throw checkoutProcessErrorToFloPayError(
6867
7651
  apiResult.autoProcessingError,
6868
7652
  "Automatic payment failed. Please try again.",
@@ -7030,9 +7814,26 @@ function FloPayAutomaticPaymentButton({
7030
7814
  cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
7031
7815
  };
7032
7816
  }, [themeBundle, buttonsTheme, stylesOverride]);
7817
+ const resolvedAppearance = useMemo5(() => {
7818
+ const bundleAppearance = themeBundle?.appearance;
7819
+ if (!appearance) return bundleAppearance;
7820
+ if (!bundleAppearance) return appearance;
7821
+ return {
7822
+ ...bundleAppearance,
7823
+ ...appearance,
7824
+ variables: {
7825
+ ...bundleAppearance.variables,
7826
+ ...appearance.variables
7827
+ }
7828
+ };
7829
+ }, [appearance, themeBundle]);
7830
+ const resolvedAppearanceVars = resolvedAppearance?.variables;
7831
+ const resolvedPrimaryColor = resolvedAppearanceVars?.colorPrimary ?? "#4A49FF";
7832
+ const resolvedPrimaryHoverColor = resolvedAppearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
7833
+ const resolvedButtonBorderRadius = resolvedAppearanceVars?.borderRadius ?? "8px";
7033
7834
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
7034
7835
  return /* @__PURE__ */ jsxs8(Fragment6, { children: [
7035
- /* @__PURE__ */ jsx10(
7836
+ /* @__PURE__ */ jsx11(
7036
7837
  "button",
7037
7838
  {
7038
7839
  ...buttonProps,
@@ -7048,14 +7849,17 @@ function FloPayAutomaticPaymentButton({
7048
7849
  // on top so the auto-pay button carries the *submit* button's
7049
7850
  // background and text colours — making it visually identical to
7050
7851
  // the "Confirm Payment" / "Pay with X" actions it stands in for.
7852
+ // `resolvedPrimaryColor` is the *merged* appearance value, so a
7853
+ // per-checkout `colorPrimary` override re-skins this button too.
7051
7854
  // Inline `style` consumer override stays at the end so explicit
7052
7855
  // per-button overrides still win.
7053
7856
  ...bStyles.cardButton,
7054
7857
  ...derivePrimaryTileStyle({
7055
7858
  themeBundle,
7056
- resolvedPrimaryColor: themeBundle?.appearance.variables?.colorPrimary ?? "#4A49FF",
7057
- resolvedBorderRadius: themeBundle?.appearance.variables?.borderRadius ?? "8px",
7058
- submitButtonStyle: bStyles.submitButton
7859
+ resolvedPrimaryColor,
7860
+ resolvedBorderRadius: resolvedButtonBorderRadius,
7861
+ submitButtonStyle: bStyles.submitButton,
7862
+ explicitPrimaryColor: resolvedAppearanceVars?.colorPrimary
7059
7863
  }),
7060
7864
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
7061
7865
  fontWeight: 600,
@@ -7064,11 +7868,23 @@ function FloPayAutomaticPaymentButton({
7064
7868
  alignItems: "center",
7065
7869
  justifyContent: "center",
7066
7870
  gap: "0.625rem",
7067
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
7871
+ transition: "background-color 0.15s, border-color 0.2s, box-shadow 0.2s, transform 0.1s",
7068
7872
  position: "relative",
7069
7873
  opacity: disabled || isProcessing ? 0.6 : 1,
7070
7874
  ...style
7071
7875
  },
7876
+ onMouseEnter: (e) => {
7877
+ buttonProps.onMouseEnter?.(e);
7878
+ if (!e.defaultPrevented && !disabled && !isProcessing && (themeBundle || resolvedAppearanceVars?.colorPrimary)) {
7879
+ e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
7880
+ }
7881
+ },
7882
+ onMouseLeave: (e) => {
7883
+ buttonProps.onMouseLeave?.(e);
7884
+ if (!e.defaultPrevented && (themeBundle || resolvedAppearanceVars?.colorPrimary)) {
7885
+ e.currentTarget.style.backgroundColor = resolvedPrimaryColor;
7886
+ }
7887
+ },
7072
7888
  onMouseDown: (e) => {
7073
7889
  buttonProps.onMouseDown?.(e);
7074
7890
  if (!e.defaultPrevented) {
@@ -7081,17 +7897,17 @@ function FloPayAutomaticPaymentButton({
7081
7897
  e.currentTarget.style.transform = "scale(1)";
7082
7898
  }
7083
7899
  },
7084
- children: /* @__PURE__ */ jsx10(CardButtonContentSlot, { content: children })
7900
+ children: /* @__PURE__ */ jsx11(CardButtonContentSlot, { content: children })
7085
7901
  }
7086
7902
  ),
7087
- overlayStatus && /* @__PURE__ */ jsx10(
7903
+ overlayStatus && /* @__PURE__ */ jsx11(
7088
7904
  ProcessingOverlay,
7089
7905
  {
7090
7906
  status: overlayStatus,
7091
7907
  errorMessage: overlayError
7092
7908
  }
7093
7909
  ),
7094
- fallbackSession && /* @__PURE__ */ jsx10(
7910
+ fallbackSession && /* @__PURE__ */ jsx11(
7095
7911
  "div",
7096
7912
  {
7097
7913
  "data-testid": "flopay-automatic-payment-fallback",
@@ -7112,7 +7928,7 @@ function FloPayAutomaticPaymentButton({
7112
7928
  padding: "1.5rem",
7113
7929
  zIndex: 1100
7114
7930
  },
7115
- children: /* @__PURE__ */ jsx10(
7931
+ children: /* @__PURE__ */ jsx11(
7116
7932
  "div",
7117
7933
  {
7118
7934
  style: {
@@ -7128,7 +7944,7 @@ function FloPayAutomaticPaymentButton({
7128
7944
  flexDirection: "column",
7129
7945
  gap: "1rem"
7130
7946
  },
7131
- children: /* @__PURE__ */ jsx10(
7947
+ children: /* @__PURE__ */ jsx11(
7132
7948
  FloPayCheckout,
7133
7949
  {
7134
7950
  sessionId: fallbackSession.sessionId,
@@ -7137,6 +7953,7 @@ function FloPayAutomaticPaymentButton({
7137
7953
  billingApiUrl: resolvedBillingUrl,
7138
7954
  locale,
7139
7955
  theme,
7956
+ ...resolvedAppearance ? { appearance: resolvedAppearance } : {},
7140
7957
  buttonsTheme,
7141
7958
  buttonsStyles: stylesOverride,
7142
7959
  initialErrorMessage: fallbackSession.errorMessage,
@@ -7166,6 +7983,7 @@ export {
7166
7983
  PayPalButton,
7167
7984
  PaymentElement,
7168
7985
  SplitCardForm,
7986
+ VaultCardFields,
7169
7987
  useCheckout,
7170
7988
  useElements,
7171
7989
  useFloPay,