@flopay/react 0.1.6 → 0.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.cjs CHANGED
@@ -118,7 +118,7 @@ function FloPayProvider({
118
118
  // src/flopay-checkout.tsx
119
119
  var import_react6 = __toESM(require("react"), 1);
120
120
  var import_js = require("@flopay/js");
121
- var import_shared4 = require("@flopay/shared");
121
+ var import_shared5 = require("@flopay/shared");
122
122
 
123
123
  // src/elements.tsx
124
124
  var import_react3 = require("react");
@@ -182,6 +182,7 @@ var AddressElement = createElementComponent("address", "AddressElement");
182
182
 
183
183
  // src/split-card-form.tsx
184
184
  var import_react_stripe_js = require("@stripe/react-stripe-js");
185
+ var import_shared3 = require("@flopay/shared");
185
186
  var import_react5 = require("react");
186
187
 
187
188
  // src/hooks.ts
@@ -204,9 +205,32 @@ function useBillingApiUrl() {
204
205
  }
205
206
 
206
207
  // src/split-card-form.tsx
207
- var import_shared3 = require("@flopay/shared");
208
+ var import_shared4 = require("@flopay/shared");
208
209
  var import_jsx_runtime3 = require("react/jsx-runtime");
209
210
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
211
+ var FLOPAY_KEYFRAMES = `
212
+ @keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
213
+ @keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
214
+ @keyframes flopay-buttons-enter {
215
+ 0% { opacity: 0; transform: translateX(-16px) scale(0.97); }
216
+ 100% { opacity: 1; transform: translateX(0) scale(1); }
217
+ }
218
+ @keyframes flopay-buttons-exit {
219
+ 0% { opacity: 1; transform: translateX(0) scale(1); }
220
+ 100% { opacity: 0; transform: translateX(-16px) scale(0.97); }
221
+ }
222
+ @keyframes flopay-card-enter {
223
+ 0% { opacity: 0; transform: translateX(16px) scale(0.97); }
224
+ 100% { opacity: 1; transform: translateX(0) scale(1); }
225
+ }
226
+ @keyframes flopay-card-exit {
227
+ 0% { opacity: 1; transform: translateX(0) scale(1); }
228
+ 100% { opacity: 0; transform: translateX(16px) scale(0.97); }
229
+ }
230
+ `;
231
+ function FloPayKeyframes() {
232
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: FLOPAY_KEYFRAMES });
233
+ }
210
234
  function ProcessingOverlay({ status, errorMessage }) {
211
235
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
212
236
  position: "fixed",
@@ -296,8 +320,6 @@ function ProcessingOverlay({ status, errorMessage }) {
296
320
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
297
321
  @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
298
322
  @keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
299
- @keyframes flopay-expand { 0% { opacity: 0; clip-path: inset(0 0 100% 0); transform: translateY(-8px); } 100% { opacity: 1; clip-path: inset(0 0 0 0); transform: translateY(0); } }
300
- @keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
301
323
  ` })
302
324
  ] }) });
303
325
  }
@@ -312,7 +334,8 @@ function PayPalButtonInner({
312
334
  billingApiUrl,
313
335
  onTokenizedBody,
314
336
  onErrorChange,
315
- isProcessing = false
337
+ isProcessing = false,
338
+ onButtonClick
316
339
  }) {
317
340
  const stripe = (0, import_react_stripe_js.useStripe)();
318
341
  const elements = (0, import_react_stripe_js.useElements)();
@@ -365,6 +388,7 @@ function PayPalButtonInner({
365
388
  }, [stripe, onTokenizedBody, onErrorChange]);
366
389
  const handlePayPalConfirm = (0, import_react5.useCallback)(async (_event) => {
367
390
  if (!stripe || !elements) return;
391
+ onButtonClick?.("paypal");
368
392
  try {
369
393
  setSubmitting(true);
370
394
  onErrorChange?.(null);
@@ -419,7 +443,7 @@ function PayPalButtonInner({
419
443
  }
420
444
  }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
421
445
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
422
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
446
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
423
447
  import_react_stripe_js.ExpressCheckoutElement,
424
448
  {
425
449
  onReady: () => setReady(true),
@@ -447,7 +471,8 @@ function WalletButtonInner({
447
471
  showApplePay = true,
448
472
  showGooglePay = true,
449
473
  onTokenizedBody,
450
- onErrorChange
474
+ onErrorChange,
475
+ onButtonClick
451
476
  }) {
452
477
  const stripe = (0, import_react_stripe_js.useStripe)();
453
478
  const elements = (0, import_react_stripe_js.useElements)();
@@ -457,6 +482,8 @@ function WalletButtonInner({
457
482
  const handleWalletConfirm = (0, import_react5.useCallback)(
458
483
  async (_event) => {
459
484
  if (!stripe || !elements) return;
485
+ const walletType = _event.expressPaymentType;
486
+ onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
460
487
  try {
461
488
  setSubmitting(true);
462
489
  onErrorChange?.(null);
@@ -509,7 +536,7 @@ function WalletButtonInner({
509
536
  [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
510
537
  );
511
538
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
512
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
539
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
513
540
  import_react_stripe_js.ExpressCheckoutElement,
514
541
  {
515
542
  onReady: () => setReady(true),
@@ -556,6 +583,15 @@ function SplitCardFormInner({
556
583
  showApplePay = true,
557
584
  showGooglePay = true,
558
585
  layout = "default",
586
+ buttonsTheme,
587
+ buttonsStyles: buttonsStylesOverride,
588
+ onButtonClick,
589
+ enableAVS = false,
590
+ avsLayout: avsLayoutProp = "row",
591
+ country: countryProp,
592
+ zip: zipProp,
593
+ onCountryChange,
594
+ onZipChange,
559
595
  totalAmount = 0,
560
596
  currency = "usd",
561
597
  innerRef
@@ -565,14 +601,42 @@ function SplitCardFormInner({
565
601
  const contextBillingUrl = useBillingApiUrl();
566
602
  const [processing, setProcessing] = (0, import_react5.useState)(false);
567
603
  const [error, setError] = (0, import_react5.useState)(null);
568
- const [showCardForm, setShowCardForm] = (0, import_react5.useState)(false);
569
604
  const [is3DSActive, setIs3DSActive] = (0, import_react5.useState)(false);
605
+ const [selectedCountry, setSelectedCountry] = (0, import_react5.useState)(countryProp ?? "US");
606
+ const [zipCode, setZipCode] = (0, import_react5.useState)(zipProp ?? "");
607
+ const zipCodeRef = (0, import_react5.useRef)(zipProp ?? "");
608
+ const selectedCountryRef = (0, import_react5.useRef)(countryProp ?? "US");
609
+ const [viewState, setViewState] = (0, import_react5.useState)("buttons");
610
+ const showCardForm = viewState === "expanding" || viewState === "card";
611
+ const TRANSITION_MS = 280;
612
+ const expandToCard = (0, import_react5.useCallback)(() => {
613
+ setViewState("expanding");
614
+ setTimeout(() => setViewState("card"), TRANSITION_MS);
615
+ }, []);
616
+ const collapseToButtons = (0, import_react5.useCallback)(() => {
617
+ setViewState("collapsing");
618
+ setTimeout(() => setViewState("buttons"), TRANSITION_MS);
619
+ }, []);
570
620
  const [fullName, setFullName] = (0, import_react5.useState)("");
571
621
  const [formReady, setFormReady] = (0, import_react5.useState)(false);
572
622
  const [overlayStatus, setOverlayStatus] = (0, import_react5.useState)(null);
573
623
  const processingRef = (0, import_react5.useRef)(false);
574
624
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
575
625
  const displayError = externalError ?? error;
626
+ const bStyles = (0, import_react5.useMemo)(() => {
627
+ const base = (0, import_shared3.resolveButtonsLayoutTheme)(buttonsTheme);
628
+ if (!buttonsStylesOverride) return base;
629
+ return {
630
+ ...base,
631
+ ...buttonsStylesOverride,
632
+ cardButton: { ...base.cardButton, ...buttonsStylesOverride.cardButton },
633
+ cardFormContainer: { ...base.cardFormContainer, ...buttonsStylesOverride.cardFormContainer },
634
+ backButton: { ...base.backButton, ...buttonsStylesOverride.backButton },
635
+ backButtonIcon: { ...base.backButtonIcon, ...buttonsStylesOverride.backButtonIcon },
636
+ submitButton: { ...base.submitButton, ...buttonsStylesOverride.submitButton },
637
+ title: { ...base.title, ...buttonsStylesOverride.title }
638
+ };
639
+ }, [buttonsTheme, buttonsStylesOverride]);
576
640
  const isSubmitting = externalProcessing ?? processing;
577
641
  const isSelfContained = !onTokenizedBody;
578
642
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
@@ -629,7 +693,8 @@ function SplitCardFormInner({
629
693
  userId: userId ?? "",
630
694
  email: email ?? "",
631
695
  firstName: firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
632
- lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? ""
696
+ lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
697
+ ...enableAVS ? { zip: zipCodeRef.current, country: selectedCountryRef.current } : {}
633
698
  },
634
699
  chv
635
700
  })
@@ -784,24 +849,36 @@ function SplitCardFormInner({
784
849
  async (e) => {
785
850
  e.preventDefault();
786
851
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
852
+ onButtonClick?.("card");
787
853
  setProcessing(true);
788
854
  setOverlayStatus("processing");
789
855
  updateError(null);
790
856
  let handedOff = false;
791
857
  try {
858
+ if (enableAVS && !zipCodeRef.current.trim()) {
859
+ updateError((0, import_shared3.getPostalCodeLabel)(selectedCountryRef.current) + " is required");
860
+ return;
861
+ }
792
862
  const submitResult = await flopay.submitElements();
793
863
  if (submitResult.error) {
794
864
  updateError(submitResult.error.message);
795
865
  onError?.(submitResult.error);
796
866
  return;
797
867
  }
798
- const pmResult = await flopay.createPaymentMethod();
868
+ const billingDetails = enableAVS ? {
869
+ name: fullName,
870
+ address: {
871
+ country: selectedCountryRef.current,
872
+ postal_code: zipCodeRef.current
873
+ }
874
+ } : void 0;
875
+ const pmResult = await flopay.createPaymentMethod(billingDetails);
799
876
  if (pmResult.error || !pmResult.paymentMethodId) {
800
877
  updateError(pmResult.error?.message ?? "Failed to create payment method.");
801
878
  return;
802
879
  }
803
880
  if (!sessionId || !email) {
804
- throw new import_shared3.FloPayError("Missing sessionId or email", "validation_error");
881
+ throw new import_shared4.FloPayError("Missing sessionId or email", "validation_error");
805
882
  }
806
883
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
807
884
  method: "POST",
@@ -813,10 +890,10 @@ function SplitCardFormInner({
813
890
  isPaypal: false
814
891
  })
815
892
  });
816
- if (!intentResponse.ok) throw new import_shared3.FloPayError("Failed to create payment intent", "api_error");
893
+ if (!intentResponse.ok) throw new import_shared4.FloPayError("Failed to create payment intent", "api_error");
817
894
  const intentJson = await intentResponse.json();
818
895
  const intentClientSecret = intentJson.data?.id;
819
- if (!intentClientSecret) throw new import_shared3.FloPayError("No client_secret in payment intent response", "api_error");
896
+ if (!intentClientSecret) throw new import_shared4.FloPayError("No client_secret in payment intent response", "api_error");
820
897
  const confirmResult = await flopay.confirmCardPayment({
821
898
  clientSecret: intentClientSecret,
822
899
  paymentMethodId: pmResult.paymentMethodId
@@ -851,15 +928,25 @@ function SplitCardFormInner({
851
928
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
852
929
  }
853
930
  const isButtons = layout === "buttons";
854
- const cardBorderColor = isButtons ? "#e5e7eb" : "#A4A4FF";
855
- const cardBg = isButtons ? "white" : "#EDEDFF";
931
+ const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
932
+ const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
933
+ const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
934
+ const stripeElementStyle = isButtons && (bStyles.cardInputColor || bStyles.cardInputPlaceholderColor || bStyles.cardInputFontSize) ? {
935
+ style: {
936
+ base: {
937
+ ...bStyles.cardInputColor ? { color: bStyles.cardInputColor } : {},
938
+ ...bStyles.cardInputFontSize ? { fontSize: bStyles.cardInputFontSize } : {},
939
+ ...bStyles.cardInputPlaceholderColor ? { "::placeholder": { color: bStyles.cardInputPlaceholderColor } } : {}
940
+ },
941
+ invalid: { color: "#ef4444" }
942
+ }
943
+ } : {};
856
944
  const cardFormBlock = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
857
945
  backgroundColor: cardBg,
858
946
  borderRadius: "8px",
859
947
  padding: isButtons ? "0" : "1rem",
860
- ...isButtons && showCardForm ? {
861
- animation: "flopay-expand 0.4s cubic-bezier(0.16,1,0.3,1) both"
862
- } : {}
948
+ ...isButtons ? bStyles.cardFormContainer : {},
949
+ ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
863
950
  }, children: [
864
951
  isButtons && showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
865
952
  display: "flex",
@@ -870,7 +957,7 @@ function SplitCardFormInner({
870
957
  "button",
871
958
  {
872
959
  type: "button",
873
- onClick: () => setShowCardForm(false),
960
+ onClick: collapseToButtons,
874
961
  style: {
875
962
  display: "inline-flex",
876
963
  alignItems: "center",
@@ -879,17 +966,12 @@ function SplitCardFormInner({
879
966
  border: "none",
880
967
  cursor: "pointer",
881
968
  color: "#4b5563",
882
- fontSize: "0.85rem",
969
+ fontSize: bStyles.backButtonFontSize ?? "0.85rem",
883
970
  fontWeight: 500,
884
971
  padding: 0,
885
972
  transition: "color 0.15s",
886
- flexShrink: 0
887
- },
888
- onMouseOver: (e) => {
889
- e.currentTarget.style.color = "#1f2937";
890
- },
891
- onMouseOut: (e) => {
892
- e.currentTarget.style.color = "#4b5563";
973
+ flexShrink: 0,
974
+ ...bStyles.backButton
893
975
  },
894
976
  "aria-label": "Back to payment methods",
895
977
  children: [
@@ -901,44 +983,53 @@ function SplitCardFormInner({
901
983
  height: 28,
902
984
  borderRadius: "50%",
903
985
  backgroundColor: "#f3f4f6",
904
- transition: "background-color 0.15s"
986
+ transition: "background-color 0.15s",
987
+ ...bStyles.backButtonIcon
905
988
  }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
906
989
  "Go back"
907
990
  ]
908
991
  }
909
992
  ),
910
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1, textAlign: "center", fontWeight: 600, fontSize: "1.05rem", color: "#262833", paddingRight: 80 }, children: "Secure card checkout" })
993
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
994
+ flex: 1,
995
+ textAlign: "center",
996
+ fontWeight: 600,
997
+ fontSize: bStyles.titleFontSize ?? "1.05rem",
998
+ color: "#262833",
999
+ paddingRight: 80,
1000
+ ...bStyles.title
1001
+ }, children: "Secure card checkout" })
911
1002
  ] }),
912
1003
  !isButtons && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
913
1004
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
914
- backgroundColor: "white",
915
- border: `1px solid ${cardBorderColor}`,
1005
+ backgroundColor: cardInputBg,
1006
+ border: `1px solid ${resolvedBorder}`,
916
1007
  borderTopLeftRadius: "8px",
917
1008
  borderTopRightRadius: "8px",
918
1009
  padding: "10px"
919
- }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true) }) }),
1010
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
920
1011
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex" }, children: [
921
1012
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
922
1013
  flex: 1,
923
- backgroundColor: "white",
924
- border: `1px solid ${cardBorderColor}`,
1014
+ backgroundColor: cardInputBg,
1015
+ border: `1px solid ${resolvedBorder}`,
925
1016
  borderTop: "none",
926
1017
  borderRight: "none",
927
1018
  borderBottomLeftRadius: "8px",
928
1019
  padding: "10px"
929
- }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, {}) }),
1020
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
930
1021
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
931
1022
  flex: 1,
932
- backgroundColor: "white",
933
- border: `1px solid ${cardBorderColor}`,
1023
+ backgroundColor: cardInputBg,
1024
+ border: `1px solid ${resolvedBorder}`,
934
1025
  borderTop: "none",
935
1026
  borderBottomRightRadius: "8px",
936
1027
  padding: "10px"
937
- }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, {}) })
1028
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, { options: stripeElementStyle }) })
938
1029
  ] }),
939
1030
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
940
- backgroundColor: "white",
941
- border: `1px solid ${cardBorderColor}`,
1031
+ backgroundColor: cardInputBg,
1032
+ border: `1px solid ${resolvedBorder}`,
942
1033
  borderRadius: "8px",
943
1034
  marginTop: "0.5rem",
944
1035
  padding: "10px"
@@ -955,12 +1046,91 @@ function SplitCardFormInner({
955
1046
  width: "100%",
956
1047
  border: "none",
957
1048
  outline: "none",
958
- fontSize: "16px",
1049
+ background: "transparent",
1050
+ fontSize: bStyles.cardInputFontSize ?? "16px",
959
1051
  fontFamily: "Poppins, sans-serif",
960
- color: "#262833"
1052
+ color: bStyles.cardInputColor ?? "#262833",
1053
+ ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
961
1054
  }
962
1055
  }
963
1056
  ) }),
1057
+ enableAVS && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1058
+ display: "flex",
1059
+ flexDirection: avsLayoutProp === "column" ? "column" : "row",
1060
+ gap: avsLayoutProp === "column" ? "0.5rem" : "0",
1061
+ marginTop: "0.5rem"
1062
+ }, children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
1064
+ flex: avsLayoutProp === "row" ? 1 : void 0,
1065
+ backgroundColor: cardInputBg,
1066
+ border: `1px solid ${resolvedBorder}`,
1067
+ padding: "10px",
1068
+ ...avsLayoutProp === "row" ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
1069
+ ...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
1070
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1071
+ "select",
1072
+ {
1073
+ value: selectedCountry,
1074
+ onChange: (e) => {
1075
+ selectedCountryRef.current = e.target.value;
1076
+ setSelectedCountry(e.target.value);
1077
+ onCountryChange?.(e.target.value);
1078
+ },
1079
+ disabled: isSubmitting,
1080
+ autoComplete: "country",
1081
+ "data-testid": "flopay-country",
1082
+ style: {
1083
+ width: "100%",
1084
+ border: "none",
1085
+ outline: "none",
1086
+ background: "transparent",
1087
+ fontSize: bStyles.cardInputFontSize ?? "16px",
1088
+ fontFamily: "Poppins, sans-serif",
1089
+ color: bStyles.cardInputColor ?? "#262833",
1090
+ cursor: "pointer",
1091
+ ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
1092
+ },
1093
+ children: import_shared3.COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("option", { value: c.code, children: [
1094
+ c.flag,
1095
+ " ",
1096
+ c.name
1097
+ ] }, c.code))
1098
+ }
1099
+ ) }),
1100
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
1101
+ flex: avsLayoutProp === "row" ? 1 : void 0,
1102
+ backgroundColor: cardInputBg,
1103
+ border: `1px solid ${resolvedBorder}`,
1104
+ padding: "10px",
1105
+ ...avsLayoutProp === "row" ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
1106
+ ...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
1107
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1108
+ "input",
1109
+ {
1110
+ placeholder: (0, import_shared3.getPostalCodeLabel)(selectedCountry),
1111
+ autoComplete: "postal-code",
1112
+ value: zipCode,
1113
+ onChange: (e) => {
1114
+ zipCodeRef.current = e.target.value;
1115
+ setZipCode(e.target.value);
1116
+ onZipChange?.(e.target.value);
1117
+ },
1118
+ disabled: isSubmitting,
1119
+ required: true,
1120
+ "data-testid": "flopay-zip",
1121
+ style: {
1122
+ width: "100%",
1123
+ border: "none",
1124
+ outline: "none",
1125
+ background: "transparent",
1126
+ fontSize: bStyles.cardInputFontSize ?? "16px",
1127
+ fontFamily: "Poppins, sans-serif",
1128
+ color: bStyles.cardInputColor ?? "#262833",
1129
+ ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
1130
+ }
1131
+ }
1132
+ ) })
1133
+ ] }),
964
1134
  displayError && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
965
1135
  margin: "0.75rem 0",
966
1136
  padding: "0.625rem 0.875rem",
@@ -972,7 +1142,8 @@ function SplitCardFormInner({
972
1142
  fontWeight: 600,
973
1143
  display: "flex",
974
1144
  alignItems: "center",
975
- gap: "0.5rem"
1145
+ gap: "0.5rem",
1146
+ ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
976
1147
  }, children: [
977
1148
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" }) }),
978
1149
  displayError
@@ -991,10 +1162,11 @@ function SplitCardFormInner({
991
1162
  color: "white",
992
1163
  border: "none",
993
1164
  borderRadius: "8px",
994
- fontSize: "1rem",
1165
+ fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
995
1166
  fontWeight: 600,
996
1167
  cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
997
- opacity: !formReady || isSubmitting ? 0.5 : 1
1168
+ opacity: !formReady || isSubmitting ? 0.5 : 1,
1169
+ ...isButtons ? bStyles.submitButton : {}
998
1170
  },
999
1171
  children: isSubmitting ? "PROCESSING..." : submitLabel
1000
1172
  }
@@ -1011,103 +1183,119 @@ function SplitCardFormInner({
1011
1183
  }, children: "Secure Card Checkout" })
1012
1184
  ] });
1013
1185
  if (layout === "buttons") {
1186
+ const isButtonsView = viewState === "buttons" || viewState === "expanding";
1187
+ const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
1188
+ const buttonsAnim = viewState === "expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
1189
+ 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;
1014
1190
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1191
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
1015
1192
  overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1016
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1017
- display: showCardForm ? "none" : "flex",
1018
- flexDirection: "column",
1019
- gap: "0.5rem"
1020
- }, children: [
1021
- showPayPal && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1022
- PayPalButtonInner,
1023
- {
1024
- sessionId,
1025
- email,
1026
- billingApiUrl: resolvedBillingApiUrl,
1027
- onTokenizedBody: dispatchTokenizedBody,
1028
- onErrorChange: updateError,
1029
- isProcessing: isSubmitting
1030
- }
1031
- ) }),
1032
- showWallets && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1033
- WalletButtonInner,
1034
- {
1035
- sessionId,
1036
- email,
1037
- billingApiUrl: resolvedBillingApiUrl,
1038
- showApplePay,
1039
- showGooglePay,
1040
- onTokenizedBody: dispatchTokenizedBody,
1041
- onErrorChange: updateError
1042
- }
1043
- ) }),
1044
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1045
- "button",
1046
- {
1047
- type: "button",
1048
- onClick: () => setShowCardForm(true),
1049
- style: {
1050
- width: "100%",
1051
- padding: "0.9rem 1rem",
1052
- backgroundColor: "white",
1053
- color: "#262833",
1054
- border: "1px solid #d1d5db",
1055
- borderRadius: "8px",
1056
- fontSize: "0.95rem",
1057
- fontWeight: 600,
1058
- cursor: "pointer",
1059
- display: "flex",
1060
- alignItems: "center",
1061
- justifyContent: "center",
1062
- gap: "0.625rem",
1063
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
1064
- boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
1065
- },
1066
- onMouseOver: (e) => {
1067
- e.currentTarget.style.borderColor = "#A4A4FF";
1068
- e.currentTarget.style.boxShadow = "0 0 0 1px #A4A4FF, 0 2px 8px rgba(74,73,255,0.08)";
1069
- },
1070
- onMouseOut: (e) => {
1071
- e.currentTarget.style.borderColor = "#d1d5db";
1072
- e.currentTarget.style.boxShadow = "0 1px 2px rgba(0,0,0,0.04)";
1073
- },
1074
- onMouseDown: (e) => {
1075
- e.currentTarget.style.transform = "scale(0.985)";
1076
- },
1077
- onMouseUp: (e) => {
1078
- e.currentTarget.style.transform = "scale(1)";
1079
- },
1080
- children: [
1081
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1082
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
1083
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
1084
- ] }),
1085
- "Credit / Debit Card",
1086
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { marginLeft: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 18l6-6-6-6" }) })
1087
- ]
1088
- }
1089
- ),
1090
- displayError && !showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
1091
- margin: "0.25rem 0",
1092
- padding: "0.625rem 0.875rem",
1093
- background: "#FEF2F2",
1094
- border: "1px solid #FECACA",
1095
- borderRadius: "8px",
1096
- color: "#991B1B",
1097
- fontSize: "0.85rem",
1098
- fontWeight: 600,
1193
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid" }, children: [
1194
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1195
+ gridArea: "1 / 1",
1099
1196
  display: "flex",
1100
- alignItems: "center",
1101
- gap: "0.5rem"
1197
+ flexDirection: "column",
1198
+ gap: "0.5rem",
1199
+ // When card form is showing (and not transitioning), hide but keep mounted
1200
+ ...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
1201
+ ...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
1102
1202
  }, children: [
1103
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" }) }),
1104
- displayError
1105
- ] })
1106
- ] }),
1107
- showCardForm && cardFormBlock
1203
+ showPayPal && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1204
+ PayPalButtonInner,
1205
+ {
1206
+ sessionId,
1207
+ email,
1208
+ billingApiUrl: resolvedBillingApiUrl,
1209
+ onTokenizedBody: dispatchTokenizedBody,
1210
+ onErrorChange: updateError,
1211
+ isProcessing: isSubmitting,
1212
+ onButtonClick
1213
+ }
1214
+ ) }) : showPayPal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1215
+ showWallets && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1216
+ WalletButtonInner,
1217
+ {
1218
+ sessionId,
1219
+ email,
1220
+ billingApiUrl: resolvedBillingApiUrl,
1221
+ showApplePay,
1222
+ showGooglePay,
1223
+ onTokenizedBody: dispatchTokenizedBody,
1224
+ onErrorChange: updateError,
1225
+ onButtonClick
1226
+ }
1227
+ ) }) : showWallets ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1228
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1229
+ "button",
1230
+ {
1231
+ type: "button",
1232
+ onClick: () => {
1233
+ onButtonClick?.("card");
1234
+ expandToCard();
1235
+ },
1236
+ style: {
1237
+ width: "100%",
1238
+ padding: "0.9rem 1rem",
1239
+ backgroundColor: "white",
1240
+ color: "#262833",
1241
+ border: "1px solid #d1d5db",
1242
+ borderRadius: "8px",
1243
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
1244
+ fontWeight: 600,
1245
+ cursor: "pointer",
1246
+ display: "flex",
1247
+ alignItems: "center",
1248
+ justifyContent: "center",
1249
+ gap: "0.625rem",
1250
+ transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
1251
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
1252
+ position: "relative",
1253
+ ...bStyles.cardButton
1254
+ },
1255
+ onMouseDown: (e) => {
1256
+ e.currentTarget.style.transform = "scale(0.985)";
1257
+ },
1258
+ onMouseUp: (e) => {
1259
+ e.currentTarget.style.transform = "scale(1)";
1260
+ },
1261
+ children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
1265
+ ] }),
1266
+ "Credit / Debit Card",
1267
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { position: "absolute", right: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 18l6-6-6-6" }) })
1268
+ ]
1269
+ }
1270
+ ),
1271
+ displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
1272
+ margin: "0.25rem 0",
1273
+ padding: "0.625rem 0.875rem",
1274
+ background: "#FEF2F2",
1275
+ border: "1px solid #FECACA",
1276
+ borderRadius: "8px",
1277
+ color: "#991B1B",
1278
+ fontSize: "0.85rem",
1279
+ fontWeight: 600,
1280
+ display: "flex",
1281
+ alignItems: "center",
1282
+ gap: "0.5rem",
1283
+ ...bStyles.errorBanner ? bStyles.errorBanner : {}
1284
+ }, children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" }) }),
1286
+ displayError
1287
+ ] })
1288
+ ] }),
1289
+ isCardView && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
1290
+ gridArea: "1 / 1",
1291
+ ...cardAnim ? { animation: cardAnim } : {},
1292
+ ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
1293
+ }, children: cardFormBlock })
1294
+ ] })
1108
1295
  ] });
1109
1296
  }
1110
1297
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1298
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
1111
1299
  overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1112
1300
  showWallets && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1113
1301
  WalletButtonInner,
@@ -1151,7 +1339,8 @@ function SplitCardFormInner({
1151
1339
  // src/flopay-checkout.tsx
1152
1340
  var import_jsx_runtime4 = require("react/jsx-runtime");
1153
1341
  function FloPayCheckout({
1154
- sessionId,
1342
+ sessionId: sessionIdProp,
1343
+ createSession: createSessionParams,
1155
1344
  billingApiUrl,
1156
1345
  appearance,
1157
1346
  locale,
@@ -1164,6 +1353,11 @@ function FloPayCheckout({
1164
1353
  showApplePay = true,
1165
1354
  showGooglePay = true,
1166
1355
  layout,
1356
+ buttonsTheme,
1357
+ buttonsStyles,
1358
+ onButtonClick,
1359
+ enableAVS,
1360
+ avsLayout,
1167
1361
  submitLabel,
1168
1362
  className,
1169
1363
  children,
@@ -1172,11 +1366,12 @@ function FloPayCheckout({
1172
1366
  renderConfirmButton,
1173
1367
  onSessionCompleted
1174
1368
  }) {
1175
- const resolvedBillingUrl = (0, import_shared4.resolveBillingApiUrl)(billingApiUrl);
1369
+ const resolvedBillingUrl = (0, import_shared5.resolveBillingApiUrl)(billingApiUrl);
1176
1370
  const [unified, setUnified] = (0, import_react6.useState)(null);
1177
1371
  const [flopay, setFloPay] = (0, import_react6.useState)(null);
1178
1372
  const flopayRef = (0, import_react6.useRef)(null);
1179
1373
  const [session, setSession] = (0, import_react6.useState)(null);
1374
+ const [resolvedSessionId, setResolvedSessionId] = (0, import_react6.useState)(sessionIdProp ?? "");
1180
1375
  const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
1181
1376
  const [loadError, setLoadError] = (0, import_react6.useState)(null);
1182
1377
  const [currentMode, setCurrentMode] = (0, import_react6.useState)("full");
@@ -1199,7 +1394,7 @@ function FloPayCheckout({
1199
1394
  "x-user-id": sess.customer?.id ?? ""
1200
1395
  },
1201
1396
  body: JSON.stringify({
1202
- sessionId,
1397
+ sessionId: resolvedSessionId,
1203
1398
  tokenizedData: { id: void 0 },
1204
1399
  accountData: {
1205
1400
  userId: sess.customer?.id ?? "",
@@ -1228,12 +1423,12 @@ function FloPayCheckout({
1228
1423
  // No client secret available
1229
1424
  };
1230
1425
  }
1231
- throw new import_shared4.FloPayError(
1426
+ throw new import_shared5.FloPayError(
1232
1427
  json?.message ?? "Payment failed. Please try again.",
1233
1428
  "api_error"
1234
1429
  );
1235
1430
  },
1236
- [resolvedBillingUrl, sessionId]
1431
+ [resolvedBillingUrl, resolvedSessionId]
1237
1432
  );
1238
1433
  const handleRedirectResult = (0, import_react6.useCallback)(
1239
1434
  async (redirectResult, sess, options) => {
@@ -1278,22 +1473,175 @@ function FloPayCheckout({
1278
1473
  }
1279
1474
  return false;
1280
1475
  },
1281
- [resolvedBillingUrl, sessionId]
1476
+ [resolvedBillingUrl, resolvedSessionId]
1477
+ );
1478
+ const inflightRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1479
+ const initializedHashRef = (0, import_react6.useRef)(null);
1480
+ function hashCreateParams(params) {
1481
+ const key = JSON.stringify({
1482
+ c: params?.clientId,
1483
+ i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1484
+ s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1485
+ e: params?.account.email,
1486
+ m: params?.checkoutMode ?? "full"
1487
+ });
1488
+ let h = 0;
1489
+ for (let i = 0; i < key.length; i++) {
1490
+ h = (h << 5) - h + key.charCodeAt(i) | 0;
1491
+ }
1492
+ return `flopay_session_${Math.abs(h).toString(36)}`;
1493
+ }
1494
+ const createSessionHash = (0, import_react6.useMemo)(
1495
+ () => createSessionParams ? hashCreateParams(createSessionParams) : "",
1496
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1497
+ [
1498
+ createSessionParams?.clientId,
1499
+ createSessionParams?.account?.email,
1500
+ createSessionParams?.checkoutMode,
1501
+ JSON.stringify(createSessionParams?.items),
1502
+ JSON.stringify(createSessionParams?.subscriptions)
1503
+ ]
1282
1504
  );
1505
+ const createSessionParamsRef = (0, import_react6.useRef)(createSessionParams);
1506
+ createSessionParamsRef.current = createSessionParams;
1283
1507
  (0, import_react6.useEffect)(() => {
1284
1508
  let cancelled = false;
1285
- setIsLoading(true);
1286
1509
  setLoadError(null);
1510
+ if (createSessionHash) {
1511
+ if (initializedHashRef.current === createSessionHash) return;
1512
+ const params = createSessionParamsRef.current;
1513
+ const totalAmount = [
1514
+ ...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
1515
+ ...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
1516
+ ].reduce((sum, v) => sum + v, 0);
1517
+ const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
1518
+ const syntheticSession = {
1519
+ id: "",
1520
+ clientSecret: "",
1521
+ mode: "payment",
1522
+ amount: Math.round(totalAmount * 100),
1523
+ currency,
1524
+ status: "open",
1525
+ customer: {
1526
+ id: params.account.userId,
1527
+ email: params.account.email,
1528
+ firstName: params.account.firstName,
1529
+ lastName: params.account.lastName
1530
+ },
1531
+ successUrl: params.successUrl,
1532
+ cancelUrl: params.cancelUrl,
1533
+ checkoutMode: params.checkoutMode ?? "full",
1534
+ items: (params.items ?? []).map((i, idx) => ({
1535
+ uuid: `synthetic-item-${idx}`,
1536
+ checkoutSessionId: "",
1537
+ providerItemId: i.providerItemId,
1538
+ providerItemName: i.providerItemName ?? i.providerItemId,
1539
+ quantity: i.quantity ?? 1,
1540
+ totalAmount: i.totalAmount,
1541
+ overrideAmount: i.overrideAmount ?? null,
1542
+ currency: i.currency ?? currency
1543
+ })),
1544
+ subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
1545
+ uuid: `synthetic-sub-${idx}`,
1546
+ checkoutSessionId: "",
1547
+ providerPlanId: s.providerPlanId,
1548
+ providerPlanName: s.providerPlanName ?? s.providerPlanId,
1549
+ quantity: s.quantity ?? 1,
1550
+ totalAmount: s.totalAmount,
1551
+ overrideAmount: s.overrideAmount ?? null,
1552
+ currency: s.currency ?? currency
1553
+ }))
1554
+ };
1555
+ setSession(syntheticSession);
1556
+ setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
1557
+ setIsLoading(false);
1558
+ const cacheKey = createSessionHash;
1559
+ async function resolveSession() {
1560
+ const api = new import_js.PaymentAPI(resolvedBillingUrl);
1561
+ let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1562
+ let realResult = null;
1563
+ if (sid) {
1564
+ try {
1565
+ realResult = await api.getUnifiedCheckoutSession(sid);
1566
+ if (realResult.data.session?.status === "complete") {
1567
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1568
+ sid = null;
1569
+ realResult = null;
1570
+ }
1571
+ } catch {
1572
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1573
+ sid = null;
1574
+ }
1575
+ }
1576
+ if (!sid) {
1577
+ realResult = await api.createAndFetchSession(createSessionParamsRef.current);
1578
+ sid = realResult.data.session?.id ?? "";
1579
+ if (sid && typeof window !== "undefined") {
1580
+ window.sessionStorage.setItem(cacheKey, sid);
1581
+ }
1582
+ }
1583
+ return { sid: sid ?? "", result: realResult };
1584
+ }
1585
+ (async () => {
1586
+ try {
1587
+ let promise = inflightRef.current.get(cacheKey);
1588
+ if (!promise) {
1589
+ promise = resolveSession();
1590
+ inflightRef.current.set(cacheKey, promise);
1591
+ }
1592
+ let resolved;
1593
+ try {
1594
+ resolved = await promise;
1595
+ } finally {
1596
+ inflightRef.current.delete(cacheKey);
1597
+ }
1598
+ if (cancelled) return;
1599
+ const { sid, result: realResult } = resolved;
1600
+ if (realResult) {
1601
+ setUnified(realResult);
1602
+ if (realResult.data.session) setSession(realResult.data.session);
1603
+ }
1604
+ if (sid) {
1605
+ setResolvedSessionId(sid);
1606
+ }
1607
+ let pk;
1608
+ if (realResult?.provider === "stripe") {
1609
+ pk = realResult.data.stripe?.publishableKey;
1610
+ }
1611
+ if (!pk) pk = fallbackPublishableKey;
1612
+ if (!pk) {
1613
+ throw new import_shared5.FloPayError(
1614
+ "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1615
+ "validation_error"
1616
+ );
1617
+ }
1618
+ const instance = await (0, import_js.loadFloPay)(pk, { billingApiUrl: resolvedBillingUrl, locale });
1619
+ if (!cancelled) {
1620
+ flopayRef.current = instance;
1621
+ setFloPay(instance);
1622
+ initializedHashRef.current = cacheKey;
1623
+ }
1624
+ } catch (err) {
1625
+ if (cancelled) return;
1626
+ const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to create session", "api_error");
1627
+ setLoadError(floPayErr);
1628
+ }
1629
+ })();
1630
+ return () => {
1631
+ cancelled = true;
1632
+ };
1633
+ }
1634
+ setIsLoading(true);
1287
1635
  async function init() {
1288
1636
  try {
1289
1637
  const api = new import_js.PaymentAPI(resolvedBillingUrl);
1290
- const result = await api.getUnifiedCheckoutSession(sessionId);
1638
+ const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
1291
1639
  if (cancelled) return;
1292
1640
  setUnified(result);
1293
1641
  const sess = result.data.session ?? null;
1294
1642
  setSession(sess);
1295
1643
  if (!sess) {
1296
- throw new import_shared4.FloPayError("No session data returned", "api_error");
1644
+ throw new import_shared5.FloPayError("No session data returned", "api_error");
1297
1645
  }
1298
1646
  if (sess.status === "complete") {
1299
1647
  setIsLoading(false);
@@ -1305,7 +1653,7 @@ function FloPayCheckout({
1305
1653
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
1306
1654
  if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
1307
1655
  autoCheckoutAttempted.current = true;
1308
- const stripeInitPromise = initStripe(result, sess);
1656
+ const stripeInitPromise = initStripe(result);
1309
1657
  try {
1310
1658
  const redirectResult = await processPaymentForMode(sess);
1311
1659
  if (!redirectResult) {
@@ -1316,9 +1664,7 @@ function FloPayCheckout({
1316
1664
  await stripeInitPromise;
1317
1665
  const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
1318
1666
  if (!cancelled) {
1319
- if (!handled) {
1320
- setCurrentMode("full");
1321
- }
1667
+ if (!handled) setCurrentMode("full");
1322
1668
  setIsLoading(false);
1323
1669
  }
1324
1670
  return;
@@ -1330,26 +1676,23 @@ function FloPayCheckout({
1330
1676
  return;
1331
1677
  }
1332
1678
  }
1333
- await initStripe(result, sess);
1679
+ await initStripe(result);
1334
1680
  if (!cancelled) setIsLoading(false);
1335
1681
  } catch (err) {
1336
1682
  if (cancelled) return;
1337
- const floPayErr = err instanceof import_shared4.FloPayError ? err : new import_shared4.FloPayError(
1338
- err instanceof Error ? err.message : "Failed to initialize checkout",
1339
- "api_error"
1340
- );
1683
+ const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1341
1684
  setLoadError(floPayErr);
1342
1685
  setIsLoading(false);
1343
1686
  }
1344
1687
  }
1345
- async function initStripe(result, _sess) {
1688
+ async function initStripe(result) {
1346
1689
  let publishableKey;
1347
1690
  if (result.provider === "stripe") {
1348
1691
  publishableKey = result.data.stripe?.publishableKey;
1349
1692
  }
1350
1693
  if (!publishableKey) publishableKey = fallbackPublishableKey;
1351
1694
  if (!publishableKey) {
1352
- throw new import_shared4.FloPayError(
1695
+ throw new import_shared5.FloPayError(
1353
1696
  "No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
1354
1697
  "validation_error"
1355
1698
  );
@@ -1365,7 +1708,7 @@ function FloPayCheckout({
1365
1708
  return () => {
1366
1709
  cancelled = true;
1367
1710
  };
1368
- }, [sessionId, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1711
+ }, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1369
1712
  const handleConfirmCheckout = (0, import_react6.useCallback)(async () => {
1370
1713
  if (confirmProcessing || !session) return;
1371
1714
  setConfirmProcessing(true);
@@ -1380,7 +1723,7 @@ function FloPayCheckout({
1380
1723
  setCurrentMode("full");
1381
1724
  }
1382
1725
  } catch (err) {
1383
- const floPayErr = err instanceof import_shared4.FloPayError ? err : new import_shared4.FloPayError(
1726
+ const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
1384
1727
  err instanceof Error ? err.message : "Payment failed",
1385
1728
  "api_error"
1386
1729
  );
@@ -1401,7 +1744,7 @@ function FloPayCheckout({
1401
1744
  if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
1402
1745
  opts.clientSecret = unified.data.stripe.clientSecret;
1403
1746
  } else {
1404
- const displayTotal = (0, import_shared4.buildCheckoutDisplayData)(session).total;
1747
+ const displayTotal = (0, import_shared5.buildCheckoutDisplayData)(session).total;
1405
1748
  opts.amount = Math.round(displayTotal * 100) || session.amount;
1406
1749
  opts.currency = session.currency?.toLowerCase();
1407
1750
  }
@@ -1417,32 +1760,32 @@ function FloPayCheckout({
1417
1760
  [session, isLoading, loadError, currentMode]
1418
1761
  );
1419
1762
  if (isLoading) {
1420
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1421
- "div",
1422
- {
1423
- style: {
1424
- display: "flex",
1425
- justifyContent: "center",
1426
- padding: 32
1427
- },
1428
- children: [
1429
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1430
- "div",
1431
- {
1432
- style: {
1433
- width: 24,
1434
- height: 24,
1435
- border: "2px solid #e5e7eb",
1436
- borderTopColor: "#6b7280",
1437
- borderRadius: "50%",
1438
- animation: "spin 0.6s linear infinite"
1439
- }
1440
- }
1441
- ),
1442
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1443
- ]
1444
- }
1445
- ) });
1763
+ if (loadingNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode });
1764
+ if (layout === "buttons") {
1765
+ const skeletonBar = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1766
+ height: h,
1767
+ borderRadius: 8,
1768
+ background: "#e5e7eb",
1769
+ animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
1770
+ } });
1771
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1772
+ showPayPal && skeletonBar(44),
1773
+ (showApplePay || showGooglePay) && skeletonBar(44),
1774
+ skeletonBar(48),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
1776
+ ] });
1777
+ }
1778
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
1779
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1780
+ width: 24,
1781
+ height: 24,
1782
+ border: "2px solid #e5e7eb",
1783
+ borderTopColor: "#6b7280",
1784
+ borderRadius: "50%",
1785
+ animation: "spin 0.6s linear infinite"
1786
+ } }),
1787
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1788
+ ] });
1446
1789
  }
1447
1790
  if (loadError) {
1448
1791
  if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: errorNode(loadError) });
@@ -1459,6 +1802,19 @@ function FloPayCheckout({
1459
1802
  }
1460
1803
  );
1461
1804
  }
1805
+ if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
1806
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1807
+ InterimButtonsView,
1808
+ {
1809
+ onButtonClick,
1810
+ showPayPal,
1811
+ showApplePay,
1812
+ showGooglePay,
1813
+ buttonsTheme,
1814
+ buttonsStyles
1815
+ }
1816
+ ) });
1817
+ }
1462
1818
  if (!flopay || !providerOptions) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
1463
1819
  if (currentMode === "confirm") {
1464
1820
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FloPayProvider, { flopay, options: providerOptions, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, children: [
@@ -1519,7 +1875,7 @@ function FloPayCheckout({
1519
1875
  children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1520
1876
  SessionInjector,
1521
1877
  {
1522
- sessionId,
1878
+ sessionId: resolvedSessionId,
1523
1879
  billingApiUrl: resolvedBillingUrl,
1524
1880
  session,
1525
1881
  children
@@ -1527,12 +1883,12 @@ function FloPayCheckout({
1527
1883
  ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1528
1884
  SplitCardForm,
1529
1885
  {
1530
- sessionId,
1886
+ sessionId: resolvedSessionId,
1531
1887
  email: session?.customer?.email,
1532
1888
  userId: session?.customer?.id,
1533
1889
  firstName: session?.customer?.firstName,
1534
1890
  lastName: session?.customer?.lastName,
1535
- totalAmount: session ? Math.round((0, import_shared4.buildCheckoutDisplayData)(session).total * 100) : 0,
1891
+ totalAmount: session ? Math.round((0, import_shared5.buildCheckoutDisplayData)(session).total * 100) : 0,
1536
1892
  currency: session?.currency?.toLowerCase() ?? "usd",
1537
1893
  onComplete,
1538
1894
  onError,
@@ -1540,6 +1896,13 @@ function FloPayCheckout({
1540
1896
  showApplePay,
1541
1897
  showGooglePay,
1542
1898
  layout,
1899
+ buttonsTheme,
1900
+ buttonsStyles,
1901
+ onButtonClick,
1902
+ enableAVS,
1903
+ avsLayout,
1904
+ country: session?.customer?.country,
1905
+ zip: session?.customer?.zip,
1543
1906
  submitLabel,
1544
1907
  className
1545
1908
  }
@@ -1570,9 +1933,165 @@ function SessionInjector({
1570
1933
  return import_react6.default.cloneElement(child, injected);
1571
1934
  }) });
1572
1935
  }
1936
+ function InterimButtonsView({
1937
+ onButtonClick,
1938
+ showPayPal,
1939
+ showApplePay,
1940
+ showGooglePay,
1941
+ buttonsTheme,
1942
+ buttonsStyles: stylesOverride
1943
+ }) {
1944
+ const [showCardForm, setShowCardForm] = (0, import_react6.useState)(false);
1945
+ const bStyles = (0, import_react6.useMemo)(() => {
1946
+ const base = (0, import_shared5.resolveButtonsLayoutTheme)(buttonsTheme);
1947
+ if (!stylesOverride) return base;
1948
+ return {
1949
+ ...base,
1950
+ ...stylesOverride,
1951
+ cardButton: { ...base.cardButton, ...stylesOverride.cardButton },
1952
+ cardFormContainer: { ...base.cardFormContainer, ...stylesOverride.cardFormContainer },
1953
+ submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
1954
+ title: { ...base.title, ...stylesOverride.title }
1955
+ };
1956
+ }, [buttonsTheme, stylesOverride]);
1957
+ const skeleton = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1958
+ height: h,
1959
+ borderRadius: 8,
1960
+ background: "#e5e7eb",
1961
+ animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
1962
+ } });
1963
+ if (showCardForm) {
1964
+ const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
1965
+ const inputBg = bStyles.cardInputBackground ?? "white";
1966
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1967
+ backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
1968
+ borderRadius: "8px",
1969
+ animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
1970
+ overflow: "hidden",
1971
+ ...bStyles.cardFormContainer
1972
+ }, children: [
1973
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
1974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1975
+ "button",
1976
+ {
1977
+ type: "button",
1978
+ onClick: () => setShowCardForm(false),
1979
+ style: {
1980
+ display: "inline-flex",
1981
+ alignItems: "center",
1982
+ gap: "0.5rem",
1983
+ background: "none",
1984
+ border: "none",
1985
+ cursor: "pointer",
1986
+ color: "#4b5563",
1987
+ fontSize: "0.85rem",
1988
+ fontWeight: 500,
1989
+ padding: 0,
1990
+ flexShrink: 0,
1991
+ ...bStyles.backButton
1992
+ },
1993
+ children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
1995
+ display: "inline-flex",
1996
+ alignItems: "center",
1997
+ justifyContent: "center",
1998
+ width: 28,
1999
+ height: 28,
2000
+ borderRadius: "50%",
2001
+ backgroundColor: "#f3f4f6",
2002
+ ...bStyles.backButtonIcon
2003
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
2004
+ "Go back"
2005
+ ]
2006
+ }
2007
+ ),
2008
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
2009
+ flex: 1,
2010
+ textAlign: "center",
2011
+ fontWeight: 600,
2012
+ fontSize: "1.05rem",
2013
+ color: "#262833",
2014
+ paddingRight: 80,
2015
+ ...bStyles.title
2016
+ }, children: "Secure card checkout" })
2017
+ ] }),
2018
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2019
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex" }, children: [
2020
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
2022
+ ] }),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2024
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
2025
+ height: 50,
2026
+ borderRadius: 8,
2027
+ marginTop: 16,
2028
+ background: "#c8c8ff",
2029
+ animation: "flopay-interim-pulse 1.5s ease-in-out infinite",
2030
+ ...bStyles.submitButton,
2031
+ opacity: 0.5
2032
+ } }),
2033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
2034
+ @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
2035
+ @keyframes flopay-interim-expand {
2036
+ 0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
2037
+ 40% { opacity: 1; }
2038
+ 100% { opacity: 1; max-height: 600px; transform: translateY(0); }
2039
+ }
2040
+ ` })
2041
+ ] });
2042
+ }
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2044
+ showPayPal && skeleton(44),
2045
+ (showApplePay || showGooglePay) && skeleton(44),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2047
+ "button",
2048
+ {
2049
+ type: "button",
2050
+ onClick: () => {
2051
+ onButtonClick?.("card");
2052
+ setShowCardForm(true);
2053
+ },
2054
+ style: {
2055
+ width: "100%",
2056
+ padding: "0.9rem 1rem",
2057
+ backgroundColor: "white",
2058
+ color: "#262833",
2059
+ border: "1px solid #d1d5db",
2060
+ borderRadius: "8px",
2061
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
2062
+ fontWeight: 600,
2063
+ cursor: "pointer",
2064
+ display: "flex",
2065
+ alignItems: "center",
2066
+ justifyContent: "center",
2067
+ gap: "0.625rem",
2068
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
2069
+ transition: "transform 0.1s",
2070
+ position: "relative",
2071
+ ...bStyles.cardButton
2072
+ },
2073
+ onMouseDown: (e) => {
2074
+ e.currentTarget.style.transform = "scale(0.985)";
2075
+ },
2076
+ onMouseUp: (e) => {
2077
+ e.currentTarget.style.transform = "scale(1)";
2078
+ },
2079
+ children: [
2080
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
2083
+ ] }),
2084
+ "Credit / Debit Card",
2085
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { position: "absolute", right: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 18l6-6-6-6" }) })
2086
+ ]
2087
+ }
2088
+ ),
2089
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2090
+ ] });
2091
+ }
1573
2092
 
1574
2093
  // src/checkout-form.tsx
1575
- var import_shared5 = require("@flopay/shared");
2094
+ var import_shared6 = require("@flopay/shared");
1576
2095
  var import_react7 = require("react");
1577
2096
  var import_jsx_runtime5 = require("react/jsx-runtime");
1578
2097
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
@@ -1780,7 +2299,7 @@ function CheckoutFormInner({
1780
2299
  return;
1781
2300
  }
1782
2301
  if (!sessionId || !email) {
1783
- throw new import_shared5.FloPayError("Missing sessionId or email", "validation_error");
2302
+ throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
1784
2303
  }
1785
2304
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1786
2305
  method: "POST",
@@ -1792,10 +2311,10 @@ function CheckoutFormInner({
1792
2311
  isPaypal: false
1793
2312
  })
1794
2313
  });
1795
- if (!intentResponse.ok) throw new import_shared5.FloPayError("Failed to create payment intent", "api_error");
2314
+ if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
1796
2315
  const intentJson = await intentResponse.json();
1797
2316
  const intentClientSecret = intentJson.data?.id;
1798
- if (!intentClientSecret) throw new import_shared5.FloPayError("No client_secret in payment intent response", "api_error");
2317
+ if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
1799
2318
  const confirmResult = await flopay.confirmCardPayment({
1800
2319
  clientSecret: intentClientSecret,
1801
2320
  paymentMethodId: pmResult.paymentMethodId
@@ -1850,7 +2369,7 @@ function CheckoutFormInner({
1850
2369
  }
1851
2370
 
1852
2371
  // src/paypal-button.tsx
1853
- var import_shared6 = require("@flopay/shared");
2372
+ var import_shared7 = require("@flopay/shared");
1854
2373
  var import_react8 = require("react");
1855
2374
  var import_jsx_runtime6 = require("react/jsx-runtime");
1856
2375
  function PayPalButton({
@@ -1970,7 +2489,7 @@ function PayPalButton({
1970
2489
  setSubmitting(true);
1971
2490
  onErrorChange?.(null);
1972
2491
  if (!sessionId || !email) {
1973
- throw new import_shared6.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
2492
+ throw new import_shared7.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
1974
2493
  }
1975
2494
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1976
2495
  method: "POST",
@@ -1982,10 +2501,10 @@ function PayPalButton({
1982
2501
  isPaypal: "true"
1983
2502
  })
1984
2503
  });
1985
- if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
2504
+ if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
1986
2505
  const intentJson = await intentResponse.json();
1987
2506
  const intentClientSecret = intentJson.data?.id;
1988
- if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in response", "api_error");
2507
+ if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in response", "api_error");
1989
2508
  const result = await flopay.confirmPayment({
1990
2509
  clientSecret: intentClientSecret,
1991
2510
  returnUrl: window.location.href