@flopay/react 0.1.6 → 0.2.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
@@ -69,7 +69,7 @@ function FloPayProvider({
69
69
  // src/flopay-checkout.tsx
70
70
  import React4, { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
71
71
  import { loadFloPay, PaymentAPI } from "@flopay/js";
72
- import { FloPayError as FloPayError2, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData } from "@flopay/shared";
72
+ import { FloPayError as FloPayError2, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
73
73
 
74
74
  // src/elements.tsx
75
75
  import { useEffect as useEffect2, useRef, useContext } from "react";
@@ -138,6 +138,7 @@ import {
138
138
  useElements as useStripeElements,
139
139
  useStripe as useStripeRaw
140
140
  } from "@stripe/react-stripe-js";
141
+ import { resolveButtonsLayoutTheme } from "@flopay/shared";
141
142
  import { forwardRef, useCallback, useEffect as useEffect3, useImperativeHandle, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
142
143
 
143
144
  // src/hooks.ts
@@ -163,6 +164,29 @@ function useBillingApiUrl() {
163
164
  import { FloPayError } from "@flopay/shared";
164
165
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
165
166
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
167
+ var FLOPAY_KEYFRAMES = `
168
+ @keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
169
+ @keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
170
+ @keyframes flopay-buttons-enter {
171
+ 0% { opacity: 0; transform: translateX(-16px) scale(0.97); }
172
+ 100% { opacity: 1; transform: translateX(0) scale(1); }
173
+ }
174
+ @keyframes flopay-buttons-exit {
175
+ 0% { opacity: 1; transform: translateX(0) scale(1); }
176
+ 100% { opacity: 0; transform: translateX(-16px) scale(0.97); }
177
+ }
178
+ @keyframes flopay-card-enter {
179
+ 0% { opacity: 0; transform: translateX(16px) scale(0.97); }
180
+ 100% { opacity: 1; transform: translateX(0) scale(1); }
181
+ }
182
+ @keyframes flopay-card-exit {
183
+ 0% { opacity: 1; transform: translateX(0) scale(1); }
184
+ 100% { opacity: 0; transform: translateX(16px) scale(0.97); }
185
+ }
186
+ `;
187
+ function FloPayKeyframes() {
188
+ return /* @__PURE__ */ jsx3("style", { children: FLOPAY_KEYFRAMES });
189
+ }
166
190
  function ProcessingOverlay({ status, errorMessage }) {
167
191
  return /* @__PURE__ */ jsx3("div", { style: {
168
192
  position: "fixed",
@@ -252,8 +276,6 @@ function ProcessingOverlay({ status, errorMessage }) {
252
276
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
253
277
  @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
254
278
  @keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
255
- @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); } }
256
- @keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
257
279
  ` })
258
280
  ] }) });
259
281
  }
@@ -268,7 +290,8 @@ function PayPalButtonInner({
268
290
  billingApiUrl,
269
291
  onTokenizedBody,
270
292
  onErrorChange,
271
- isProcessing = false
293
+ isProcessing = false,
294
+ onButtonClick
272
295
  }) {
273
296
  const stripe = useStripeRaw();
274
297
  const elements = useStripeElements();
@@ -321,6 +344,7 @@ function PayPalButtonInner({
321
344
  }, [stripe, onTokenizedBody, onErrorChange]);
322
345
  const handlePayPalConfirm = useCallback(async (_event) => {
323
346
  if (!stripe || !elements) return;
347
+ onButtonClick?.("paypal");
324
348
  try {
325
349
  setSubmitting(true);
326
350
  onErrorChange?.(null);
@@ -375,7 +399,7 @@ function PayPalButtonInner({
375
399
  }
376
400
  }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
377
401
  return /* @__PURE__ */ jsxs(Fragment, { children: [
378
- /* @__PURE__ */ jsx3("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ jsx3(
402
+ /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
379
403
  ExpressCheckoutElement,
380
404
  {
381
405
  onReady: () => setReady(true),
@@ -403,7 +427,8 @@ function WalletButtonInner({
403
427
  showApplePay = true,
404
428
  showGooglePay = true,
405
429
  onTokenizedBody,
406
- onErrorChange
430
+ onErrorChange,
431
+ onButtonClick
407
432
  }) {
408
433
  const stripe = useStripeRaw();
409
434
  const elements = useStripeElements();
@@ -413,6 +438,8 @@ function WalletButtonInner({
413
438
  const handleWalletConfirm = useCallback(
414
439
  async (_event) => {
415
440
  if (!stripe || !elements) return;
441
+ const walletType = _event.expressPaymentType;
442
+ onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
416
443
  try {
417
444
  setSubmitting(true);
418
445
  onErrorChange?.(null);
@@ -465,7 +492,7 @@ function WalletButtonInner({
465
492
  [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
466
493
  );
467
494
  return /* @__PURE__ */ jsxs(Fragment, { children: [
468
- /* @__PURE__ */ jsx3("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ jsx3(
495
+ /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
469
496
  ExpressCheckoutElement,
470
497
  {
471
498
  onReady: () => setReady(true),
@@ -512,6 +539,9 @@ function SplitCardFormInner({
512
539
  showApplePay = true,
513
540
  showGooglePay = true,
514
541
  layout = "default",
542
+ buttonsTheme,
543
+ buttonsStyles: buttonsStylesOverride,
544
+ onButtonClick,
515
545
  totalAmount = 0,
516
546
  currency = "usd",
517
547
  innerRef
@@ -521,14 +551,38 @@ function SplitCardFormInner({
521
551
  const contextBillingUrl = useBillingApiUrl();
522
552
  const [processing, setProcessing] = useState2(false);
523
553
  const [error, setError] = useState2(null);
524
- const [showCardForm, setShowCardForm] = useState2(false);
525
554
  const [is3DSActive, setIs3DSActive] = useState2(false);
555
+ const [viewState, setViewState] = useState2("buttons");
556
+ const showCardForm = viewState === "expanding" || viewState === "card";
557
+ const TRANSITION_MS = 280;
558
+ const expandToCard = useCallback(() => {
559
+ setViewState("expanding");
560
+ setTimeout(() => setViewState("card"), TRANSITION_MS);
561
+ }, []);
562
+ const collapseToButtons = useCallback(() => {
563
+ setViewState("collapsing");
564
+ setTimeout(() => setViewState("buttons"), TRANSITION_MS);
565
+ }, []);
526
566
  const [fullName, setFullName] = useState2("");
527
567
  const [formReady, setFormReady] = useState2(false);
528
568
  const [overlayStatus, setOverlayStatus] = useState2(null);
529
569
  const processingRef = useRef2(false);
530
570
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
531
571
  const displayError = externalError ?? error;
572
+ const bStyles = useMemo2(() => {
573
+ const base = resolveButtonsLayoutTheme(buttonsTheme);
574
+ if (!buttonsStylesOverride) return base;
575
+ return {
576
+ ...base,
577
+ ...buttonsStylesOverride,
578
+ cardButton: { ...base.cardButton, ...buttonsStylesOverride.cardButton },
579
+ cardFormContainer: { ...base.cardFormContainer, ...buttonsStylesOverride.cardFormContainer },
580
+ backButton: { ...base.backButton, ...buttonsStylesOverride.backButton },
581
+ backButtonIcon: { ...base.backButtonIcon, ...buttonsStylesOverride.backButtonIcon },
582
+ submitButton: { ...base.submitButton, ...buttonsStylesOverride.submitButton },
583
+ title: { ...base.title, ...buttonsStylesOverride.title }
584
+ };
585
+ }, [buttonsTheme, buttonsStylesOverride]);
532
586
  const isSubmitting = externalProcessing ?? processing;
533
587
  const isSelfContained = !onTokenizedBody;
534
588
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
@@ -740,6 +794,7 @@ function SplitCardFormInner({
740
794
  async (e) => {
741
795
  e.preventDefault();
742
796
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
797
+ onButtonClick?.("card");
743
798
  setProcessing(true);
744
799
  setOverlayStatus("processing");
745
800
  updateError(null);
@@ -807,15 +862,25 @@ function SplitCardFormInner({
807
862
  return /* @__PURE__ */ jsx3("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
808
863
  }
809
864
  const isButtons = layout === "buttons";
810
- const cardBorderColor = isButtons ? "#e5e7eb" : "#A4A4FF";
811
- const cardBg = isButtons ? "white" : "#EDEDFF";
865
+ const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
866
+ const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
867
+ const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
868
+ const stripeElementStyle = isButtons && (bStyles.cardInputColor || bStyles.cardInputPlaceholderColor || bStyles.cardInputFontSize) ? {
869
+ style: {
870
+ base: {
871
+ ...bStyles.cardInputColor ? { color: bStyles.cardInputColor } : {},
872
+ ...bStyles.cardInputFontSize ? { fontSize: bStyles.cardInputFontSize } : {},
873
+ ...bStyles.cardInputPlaceholderColor ? { "::placeholder": { color: bStyles.cardInputPlaceholderColor } } : {}
874
+ },
875
+ invalid: { color: "#ef4444" }
876
+ }
877
+ } : {};
812
878
  const cardFormBlock = /* @__PURE__ */ jsxs("div", { style: {
813
879
  backgroundColor: cardBg,
814
880
  borderRadius: "8px",
815
881
  padding: isButtons ? "0" : "1rem",
816
- ...isButtons && showCardForm ? {
817
- animation: "flopay-expand 0.4s cubic-bezier(0.16,1,0.3,1) both"
818
- } : {}
882
+ ...isButtons ? bStyles.cardFormContainer : {},
883
+ ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
819
884
  }, children: [
820
885
  isButtons && showCardForm && /* @__PURE__ */ jsxs("div", { style: {
821
886
  display: "flex",
@@ -826,7 +891,7 @@ function SplitCardFormInner({
826
891
  "button",
827
892
  {
828
893
  type: "button",
829
- onClick: () => setShowCardForm(false),
894
+ onClick: collapseToButtons,
830
895
  style: {
831
896
  display: "inline-flex",
832
897
  alignItems: "center",
@@ -835,17 +900,12 @@ function SplitCardFormInner({
835
900
  border: "none",
836
901
  cursor: "pointer",
837
902
  color: "#4b5563",
838
- fontSize: "0.85rem",
903
+ fontSize: bStyles.backButtonFontSize ?? "0.85rem",
839
904
  fontWeight: 500,
840
905
  padding: 0,
841
906
  transition: "color 0.15s",
842
- flexShrink: 0
843
- },
844
- onMouseOver: (e) => {
845
- e.currentTarget.style.color = "#1f2937";
846
- },
847
- onMouseOut: (e) => {
848
- e.currentTarget.style.color = "#4b5563";
907
+ flexShrink: 0,
908
+ ...bStyles.backButton
849
909
  },
850
910
  "aria-label": "Back to payment methods",
851
911
  children: [
@@ -857,44 +917,53 @@ function SplitCardFormInner({
857
917
  height: 28,
858
918
  borderRadius: "50%",
859
919
  backgroundColor: "#f3f4f6",
860
- transition: "background-color 0.15s"
920
+ transition: "background-color 0.15s",
921
+ ...bStyles.backButtonIcon
861
922
  }, children: /* @__PURE__ */ jsx3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("path", { d: "M15 18l-6-6 6-6" }) }) }),
862
923
  "Go back"
863
924
  ]
864
925
  }
865
926
  ),
866
- /* @__PURE__ */ jsx3("div", { style: { flex: 1, textAlign: "center", fontWeight: 600, fontSize: "1.05rem", color: "#262833", paddingRight: 80 }, children: "Secure card checkout" })
927
+ /* @__PURE__ */ jsx3("div", { style: {
928
+ flex: 1,
929
+ textAlign: "center",
930
+ fontWeight: 600,
931
+ fontSize: bStyles.titleFontSize ?? "1.05rem",
932
+ color: "#262833",
933
+ paddingRight: 80,
934
+ ...bStyles.title
935
+ }, children: "Secure card checkout" })
867
936
  ] }),
868
937
  !isButtons && /* @__PURE__ */ jsx3("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
869
938
  /* @__PURE__ */ jsx3("div", { style: {
870
- backgroundColor: "white",
871
- border: `1px solid ${cardBorderColor}`,
939
+ backgroundColor: cardInputBg,
940
+ border: `1px solid ${resolvedBorder}`,
872
941
  borderTopLeftRadius: "8px",
873
942
  borderTopRightRadius: "8px",
874
943
  padding: "10px"
875
- }, children: /* @__PURE__ */ jsx3(CardNumberElement, { onReady: () => setFormReady(true) }) }),
944
+ }, children: /* @__PURE__ */ jsx3(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
876
945
  /* @__PURE__ */ jsxs("div", { style: { display: "flex" }, children: [
877
946
  /* @__PURE__ */ jsx3("div", { style: {
878
947
  flex: 1,
879
- backgroundColor: "white",
880
- border: `1px solid ${cardBorderColor}`,
948
+ backgroundColor: cardInputBg,
949
+ border: `1px solid ${resolvedBorder}`,
881
950
  borderTop: "none",
882
951
  borderRight: "none",
883
952
  borderBottomLeftRadius: "8px",
884
953
  padding: "10px"
885
- }, children: /* @__PURE__ */ jsx3(CardExpiryElement, {}) }),
954
+ }, children: /* @__PURE__ */ jsx3(CardExpiryElement, { options: stripeElementStyle }) }),
886
955
  /* @__PURE__ */ jsx3("div", { style: {
887
956
  flex: 1,
888
- backgroundColor: "white",
889
- border: `1px solid ${cardBorderColor}`,
957
+ backgroundColor: cardInputBg,
958
+ border: `1px solid ${resolvedBorder}`,
890
959
  borderTop: "none",
891
960
  borderBottomRightRadius: "8px",
892
961
  padding: "10px"
893
- }, children: /* @__PURE__ */ jsx3(CardCvcElement, {}) })
962
+ }, children: /* @__PURE__ */ jsx3(CardCvcElement, { options: stripeElementStyle }) })
894
963
  ] }),
895
964
  /* @__PURE__ */ jsx3("div", { style: {
896
- backgroundColor: "white",
897
- border: `1px solid ${cardBorderColor}`,
965
+ backgroundColor: cardInputBg,
966
+ border: `1px solid ${resolvedBorder}`,
898
967
  borderRadius: "8px",
899
968
  marginTop: "0.5rem",
900
969
  padding: "10px"
@@ -911,9 +980,11 @@ function SplitCardFormInner({
911
980
  width: "100%",
912
981
  border: "none",
913
982
  outline: "none",
914
- fontSize: "16px",
983
+ background: "transparent",
984
+ fontSize: bStyles.cardInputFontSize ?? "16px",
915
985
  fontFamily: "Poppins, sans-serif",
916
- color: "#262833"
986
+ color: bStyles.cardInputColor ?? "#262833",
987
+ ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
917
988
  }
918
989
  }
919
990
  ) }),
@@ -928,7 +999,8 @@ function SplitCardFormInner({
928
999
  fontWeight: 600,
929
1000
  display: "flex",
930
1001
  alignItems: "center",
931
- gap: "0.5rem"
1002
+ gap: "0.5rem",
1003
+ ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
932
1004
  }, children: [
933
1005
  /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx3("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" }) }),
934
1006
  displayError
@@ -947,10 +1019,11 @@ function SplitCardFormInner({
947
1019
  color: "white",
948
1020
  border: "none",
949
1021
  borderRadius: "8px",
950
- fontSize: "1rem",
1022
+ fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
951
1023
  fontWeight: 600,
952
1024
  cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
953
- opacity: !formReady || isSubmitting ? 0.5 : 1
1025
+ opacity: !formReady || isSubmitting ? 0.5 : 1,
1026
+ ...isButtons ? bStyles.submitButton : {}
954
1027
  },
955
1028
  children: isSubmitting ? "PROCESSING..." : submitLabel
956
1029
  }
@@ -967,103 +1040,119 @@ function SplitCardFormInner({
967
1040
  }, children: "Secure Card Checkout" })
968
1041
  ] });
969
1042
  if (layout === "buttons") {
1043
+ const isButtonsView = viewState === "buttons" || viewState === "expanding";
1044
+ const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
1045
+ 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;
1046
+ 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;
970
1047
  return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1048
+ /* @__PURE__ */ jsx3(FloPayKeyframes, {}),
971
1049
  overlayStatus && /* @__PURE__ */ jsx3(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
972
- /* @__PURE__ */ jsxs("div", { style: {
973
- display: showCardForm ? "none" : "flex",
974
- flexDirection: "column",
975
- gap: "0.5rem"
976
- }, children: [
977
- showPayPal && stripeInstance && /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx3(
978
- PayPalButtonInner,
979
- {
980
- sessionId,
981
- email,
982
- billingApiUrl: resolvedBillingApiUrl,
983
- onTokenizedBody: dispatchTokenizedBody,
984
- onErrorChange: updateError,
985
- isProcessing: isSubmitting
986
- }
987
- ) }),
988
- showWallets && stripeInstance && /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx3(
989
- WalletButtonInner,
990
- {
991
- sessionId,
992
- email,
993
- billingApiUrl: resolvedBillingApiUrl,
994
- showApplePay,
995
- showGooglePay,
996
- onTokenizedBody: dispatchTokenizedBody,
997
- onErrorChange: updateError
998
- }
999
- ) }),
1000
- /* @__PURE__ */ jsxs(
1001
- "button",
1002
- {
1003
- type: "button",
1004
- onClick: () => setShowCardForm(true),
1005
- style: {
1006
- width: "100%",
1007
- padding: "0.9rem 1rem",
1008
- backgroundColor: "white",
1009
- color: "#262833",
1010
- border: "1px solid #d1d5db",
1011
- borderRadius: "8px",
1012
- fontSize: "0.95rem",
1013
- fontWeight: 600,
1014
- cursor: "pointer",
1015
- display: "flex",
1016
- alignItems: "center",
1017
- justifyContent: "center",
1018
- gap: "0.625rem",
1019
- transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
1020
- boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
1021
- },
1022
- onMouseOver: (e) => {
1023
- e.currentTarget.style.borderColor = "#A4A4FF";
1024
- e.currentTarget.style.boxShadow = "0 0 0 1px #A4A4FF, 0 2px 8px rgba(74,73,255,0.08)";
1025
- },
1026
- onMouseOut: (e) => {
1027
- e.currentTarget.style.borderColor = "#d1d5db";
1028
- e.currentTarget.style.boxShadow = "0 1px 2px rgba(0,0,0,0.04)";
1029
- },
1030
- onMouseDown: (e) => {
1031
- e.currentTarget.style.transform = "scale(0.985)";
1032
- },
1033
- onMouseUp: (e) => {
1034
- e.currentTarget.style.transform = "scale(1)";
1035
- },
1036
- children: [
1037
- /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1038
- /* @__PURE__ */ jsx3("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
1039
- /* @__PURE__ */ jsx3("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
1040
- ] }),
1041
- "Credit / Debit Card",
1042
- /* @__PURE__ */ jsx3("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__ */ jsx3("path", { d: "M9 18l6-6-6-6" }) })
1043
- ]
1044
- }
1045
- ),
1046
- displayError && !showCardForm && /* @__PURE__ */ jsxs("div", { role: "alert", "data-testid": "flopay-error", style: {
1047
- margin: "0.25rem 0",
1048
- padding: "0.625rem 0.875rem",
1049
- background: "#FEF2F2",
1050
- border: "1px solid #FECACA",
1051
- borderRadius: "8px",
1052
- color: "#991B1B",
1053
- fontSize: "0.85rem",
1054
- fontWeight: 600,
1050
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid" }, children: [
1051
+ /* @__PURE__ */ jsxs("div", { style: {
1052
+ gridArea: "1 / 1",
1055
1053
  display: "flex",
1056
- alignItems: "center",
1057
- gap: "0.5rem"
1054
+ flexDirection: "column",
1055
+ gap: "0.5rem",
1056
+ // When card form is showing (and not transitioning), hide but keep mounted
1057
+ ...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
1058
+ ...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
1058
1059
  }, children: [
1059
- /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx3("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" }) }),
1060
- displayError
1061
- ] })
1062
- ] }),
1063
- showCardForm && cardFormBlock
1060
+ showPayPal && stripeInstance ? /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx3(
1061
+ PayPalButtonInner,
1062
+ {
1063
+ sessionId,
1064
+ email,
1065
+ billingApiUrl: resolvedBillingApiUrl,
1066
+ onTokenizedBody: dispatchTokenizedBody,
1067
+ onErrorChange: updateError,
1068
+ isProcessing: isSubmitting,
1069
+ onButtonClick
1070
+ }
1071
+ ) }) : showPayPal ? /* @__PURE__ */ jsx3("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1072
+ showWallets && stripeInstance ? /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx3(
1073
+ WalletButtonInner,
1074
+ {
1075
+ sessionId,
1076
+ email,
1077
+ billingApiUrl: resolvedBillingApiUrl,
1078
+ showApplePay,
1079
+ showGooglePay,
1080
+ onTokenizedBody: dispatchTokenizedBody,
1081
+ onErrorChange: updateError,
1082
+ onButtonClick
1083
+ }
1084
+ ) }) : showWallets ? /* @__PURE__ */ jsx3("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1085
+ /* @__PURE__ */ jsxs(
1086
+ "button",
1087
+ {
1088
+ type: "button",
1089
+ onClick: () => {
1090
+ onButtonClick?.("card");
1091
+ expandToCard();
1092
+ },
1093
+ style: {
1094
+ width: "100%",
1095
+ padding: "0.9rem 1rem",
1096
+ backgroundColor: "white",
1097
+ color: "#262833",
1098
+ border: "1px solid #d1d5db",
1099
+ borderRadius: "8px",
1100
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
1101
+ fontWeight: 600,
1102
+ cursor: "pointer",
1103
+ display: "flex",
1104
+ alignItems: "center",
1105
+ justifyContent: "center",
1106
+ gap: "0.625rem",
1107
+ transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
1108
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
1109
+ position: "relative",
1110
+ ...bStyles.cardButton
1111
+ },
1112
+ onMouseDown: (e) => {
1113
+ e.currentTarget.style.transform = "scale(0.985)";
1114
+ },
1115
+ onMouseUp: (e) => {
1116
+ e.currentTarget.style.transform = "scale(1)";
1117
+ },
1118
+ children: [
1119
+ /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1120
+ /* @__PURE__ */ jsx3("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
1121
+ /* @__PURE__ */ jsx3("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
1122
+ ] }),
1123
+ "Credit / Debit Card",
1124
+ /* @__PURE__ */ jsx3("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__ */ jsx3("path", { d: "M9 18l6-6-6-6" }) })
1125
+ ]
1126
+ }
1127
+ ),
1128
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsxs("div", { role: "alert", "data-testid": "flopay-error", style: {
1129
+ margin: "0.25rem 0",
1130
+ padding: "0.625rem 0.875rem",
1131
+ background: "#FEF2F2",
1132
+ border: "1px solid #FECACA",
1133
+ borderRadius: "8px",
1134
+ color: "#991B1B",
1135
+ fontSize: "0.85rem",
1136
+ fontWeight: 600,
1137
+ display: "flex",
1138
+ alignItems: "center",
1139
+ gap: "0.5rem",
1140
+ ...bStyles.errorBanner ? bStyles.errorBanner : {}
1141
+ }, children: [
1142
+ /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx3("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" }) }),
1143
+ displayError
1144
+ ] })
1145
+ ] }),
1146
+ isCardView && /* @__PURE__ */ jsx3("div", { style: {
1147
+ gridArea: "1 / 1",
1148
+ ...cardAnim ? { animation: cardAnim } : {},
1149
+ ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
1150
+ }, children: cardFormBlock })
1151
+ ] })
1064
1152
  ] });
1065
1153
  }
1066
1154
  return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1155
+ /* @__PURE__ */ jsx3(FloPayKeyframes, {}),
1067
1156
  overlayStatus && /* @__PURE__ */ jsx3(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1068
1157
  showWallets && stripeInstance && /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx3(
1069
1158
  WalletButtonInner,
@@ -1107,7 +1196,8 @@ function SplitCardFormInner({
1107
1196
  // src/flopay-checkout.tsx
1108
1197
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1109
1198
  function FloPayCheckout({
1110
- sessionId,
1199
+ sessionId: sessionIdProp,
1200
+ createSession: createSessionParams,
1111
1201
  billingApiUrl,
1112
1202
  appearance,
1113
1203
  locale,
@@ -1120,6 +1210,9 @@ function FloPayCheckout({
1120
1210
  showApplePay = true,
1121
1211
  showGooglePay = true,
1122
1212
  layout,
1213
+ buttonsTheme,
1214
+ buttonsStyles,
1215
+ onButtonClick,
1123
1216
  submitLabel,
1124
1217
  className,
1125
1218
  children,
@@ -1133,6 +1226,7 @@ function FloPayCheckout({
1133
1226
  const [flopay, setFloPay] = useState3(null);
1134
1227
  const flopayRef = useRef3(null);
1135
1228
  const [session, setSession] = useState3(null);
1229
+ const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
1136
1230
  const [isLoading, setIsLoading] = useState3(true);
1137
1231
  const [loadError, setLoadError] = useState3(null);
1138
1232
  const [currentMode, setCurrentMode] = useState3("full");
@@ -1155,7 +1249,7 @@ function FloPayCheckout({
1155
1249
  "x-user-id": sess.customer?.id ?? ""
1156
1250
  },
1157
1251
  body: JSON.stringify({
1158
- sessionId,
1252
+ sessionId: resolvedSessionId,
1159
1253
  tokenizedData: { id: void 0 },
1160
1254
  accountData: {
1161
1255
  userId: sess.customer?.id ?? "",
@@ -1189,7 +1283,7 @@ function FloPayCheckout({
1189
1283
  "api_error"
1190
1284
  );
1191
1285
  },
1192
- [resolvedBillingUrl, sessionId]
1286
+ [resolvedBillingUrl, resolvedSessionId]
1193
1287
  );
1194
1288
  const handleRedirectResult = useCallback2(
1195
1289
  async (redirectResult, sess, options) => {
@@ -1234,16 +1328,169 @@ function FloPayCheckout({
1234
1328
  }
1235
1329
  return false;
1236
1330
  },
1237
- [resolvedBillingUrl, sessionId]
1331
+ [resolvedBillingUrl, resolvedSessionId]
1238
1332
  );
1333
+ const inflightRef = useRef3(/* @__PURE__ */ new Map());
1334
+ const initializedHashRef = useRef3(null);
1335
+ function hashCreateParams(params) {
1336
+ const key = JSON.stringify({
1337
+ c: params?.clientId,
1338
+ i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1339
+ s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1340
+ e: params?.account.email,
1341
+ m: params?.checkoutMode ?? "full"
1342
+ });
1343
+ let h = 0;
1344
+ for (let i = 0; i < key.length; i++) {
1345
+ h = (h << 5) - h + key.charCodeAt(i) | 0;
1346
+ }
1347
+ return `flopay_session_${Math.abs(h).toString(36)}`;
1348
+ }
1349
+ const createSessionHash = useMemo3(
1350
+ () => createSessionParams ? hashCreateParams(createSessionParams) : "",
1351
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1352
+ [
1353
+ createSessionParams?.clientId,
1354
+ createSessionParams?.account?.email,
1355
+ createSessionParams?.checkoutMode,
1356
+ JSON.stringify(createSessionParams?.items),
1357
+ JSON.stringify(createSessionParams?.subscriptions)
1358
+ ]
1359
+ );
1360
+ const createSessionParamsRef = useRef3(createSessionParams);
1361
+ createSessionParamsRef.current = createSessionParams;
1239
1362
  useEffect4(() => {
1240
1363
  let cancelled = false;
1241
- setIsLoading(true);
1242
1364
  setLoadError(null);
1365
+ if (createSessionHash) {
1366
+ if (initializedHashRef.current === createSessionHash) return;
1367
+ const params = createSessionParamsRef.current;
1368
+ const totalAmount = [
1369
+ ...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
1370
+ ...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
1371
+ ].reduce((sum, v) => sum + v, 0);
1372
+ const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
1373
+ const syntheticSession = {
1374
+ id: "",
1375
+ clientSecret: "",
1376
+ mode: "payment",
1377
+ amount: Math.round(totalAmount * 100),
1378
+ currency,
1379
+ status: "open",
1380
+ customer: {
1381
+ id: params.account.userId,
1382
+ email: params.account.email,
1383
+ firstName: params.account.firstName,
1384
+ lastName: params.account.lastName
1385
+ },
1386
+ successUrl: params.successUrl,
1387
+ cancelUrl: params.cancelUrl,
1388
+ checkoutMode: params.checkoutMode ?? "full",
1389
+ items: (params.items ?? []).map((i, idx) => ({
1390
+ uuid: `synthetic-item-${idx}`,
1391
+ checkoutSessionId: "",
1392
+ providerItemId: i.providerItemId,
1393
+ providerItemName: i.providerItemName ?? i.providerItemId,
1394
+ quantity: i.quantity ?? 1,
1395
+ totalAmount: i.totalAmount,
1396
+ overrideAmount: i.overrideAmount ?? null,
1397
+ currency: i.currency ?? currency
1398
+ })),
1399
+ subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
1400
+ uuid: `synthetic-sub-${idx}`,
1401
+ checkoutSessionId: "",
1402
+ providerPlanId: s.providerPlanId,
1403
+ providerPlanName: s.providerPlanName ?? s.providerPlanId,
1404
+ quantity: s.quantity ?? 1,
1405
+ totalAmount: s.totalAmount,
1406
+ overrideAmount: s.overrideAmount ?? null,
1407
+ currency: s.currency ?? currency
1408
+ }))
1409
+ };
1410
+ setSession(syntheticSession);
1411
+ setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
1412
+ setIsLoading(false);
1413
+ const cacheKey = createSessionHash;
1414
+ async function resolveSession() {
1415
+ const api = new PaymentAPI(resolvedBillingUrl);
1416
+ let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1417
+ let realResult = null;
1418
+ if (sid) {
1419
+ try {
1420
+ realResult = await api.getUnifiedCheckoutSession(sid);
1421
+ if (realResult.data.session?.status === "complete") {
1422
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1423
+ sid = null;
1424
+ realResult = null;
1425
+ }
1426
+ } catch {
1427
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1428
+ sid = null;
1429
+ }
1430
+ }
1431
+ if (!sid) {
1432
+ realResult = await api.createAndFetchSession(createSessionParamsRef.current);
1433
+ sid = realResult.data.session?.id ?? "";
1434
+ if (sid && typeof window !== "undefined") {
1435
+ window.sessionStorage.setItem(cacheKey, sid);
1436
+ }
1437
+ }
1438
+ return { sid: sid ?? "", result: realResult };
1439
+ }
1440
+ (async () => {
1441
+ try {
1442
+ let promise = inflightRef.current.get(cacheKey);
1443
+ if (!promise) {
1444
+ promise = resolveSession();
1445
+ inflightRef.current.set(cacheKey, promise);
1446
+ }
1447
+ let resolved;
1448
+ try {
1449
+ resolved = await promise;
1450
+ } finally {
1451
+ inflightRef.current.delete(cacheKey);
1452
+ }
1453
+ if (cancelled) return;
1454
+ const { sid, result: realResult } = resolved;
1455
+ if (realResult) {
1456
+ setUnified(realResult);
1457
+ if (realResult.data.session) setSession(realResult.data.session);
1458
+ }
1459
+ if (sid) {
1460
+ setResolvedSessionId(sid);
1461
+ }
1462
+ let pk;
1463
+ if (realResult?.provider === "stripe") {
1464
+ pk = realResult.data.stripe?.publishableKey;
1465
+ }
1466
+ if (!pk) pk = fallbackPublishableKey;
1467
+ if (!pk) {
1468
+ throw new FloPayError2(
1469
+ "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1470
+ "validation_error"
1471
+ );
1472
+ }
1473
+ const instance = await loadFloPay(pk, { billingApiUrl: resolvedBillingUrl, locale });
1474
+ if (!cancelled) {
1475
+ flopayRef.current = instance;
1476
+ setFloPay(instance);
1477
+ initializedHashRef.current = cacheKey;
1478
+ }
1479
+ } catch (err) {
1480
+ if (cancelled) return;
1481
+ const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(err instanceof Error ? err.message : "Failed to create session", "api_error");
1482
+ setLoadError(floPayErr);
1483
+ }
1484
+ })();
1485
+ return () => {
1486
+ cancelled = true;
1487
+ };
1488
+ }
1489
+ setIsLoading(true);
1243
1490
  async function init() {
1244
1491
  try {
1245
1492
  const api = new PaymentAPI(resolvedBillingUrl);
1246
- const result = await api.getUnifiedCheckoutSession(sessionId);
1493
+ const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
1247
1494
  if (cancelled) return;
1248
1495
  setUnified(result);
1249
1496
  const sess = result.data.session ?? null;
@@ -1261,7 +1508,7 @@ function FloPayCheckout({
1261
1508
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
1262
1509
  if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
1263
1510
  autoCheckoutAttempted.current = true;
1264
- const stripeInitPromise = initStripe(result, sess);
1511
+ const stripeInitPromise = initStripe(result);
1265
1512
  try {
1266
1513
  const redirectResult = await processPaymentForMode(sess);
1267
1514
  if (!redirectResult) {
@@ -1272,9 +1519,7 @@ function FloPayCheckout({
1272
1519
  await stripeInitPromise;
1273
1520
  const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
1274
1521
  if (!cancelled) {
1275
- if (!handled) {
1276
- setCurrentMode("full");
1277
- }
1522
+ if (!handled) setCurrentMode("full");
1278
1523
  setIsLoading(false);
1279
1524
  }
1280
1525
  return;
@@ -1286,19 +1531,16 @@ function FloPayCheckout({
1286
1531
  return;
1287
1532
  }
1288
1533
  }
1289
- await initStripe(result, sess);
1534
+ await initStripe(result);
1290
1535
  if (!cancelled) setIsLoading(false);
1291
1536
  } catch (err) {
1292
1537
  if (cancelled) return;
1293
- const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(
1294
- err instanceof Error ? err.message : "Failed to initialize checkout",
1295
- "api_error"
1296
- );
1538
+ const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1297
1539
  setLoadError(floPayErr);
1298
1540
  setIsLoading(false);
1299
1541
  }
1300
1542
  }
1301
- async function initStripe(result, _sess) {
1543
+ async function initStripe(result) {
1302
1544
  let publishableKey;
1303
1545
  if (result.provider === "stripe") {
1304
1546
  publishableKey = result.data.stripe?.publishableKey;
@@ -1321,7 +1563,7 @@ function FloPayCheckout({
1321
1563
  return () => {
1322
1564
  cancelled = true;
1323
1565
  };
1324
- }, [sessionId, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1566
+ }, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1325
1567
  const handleConfirmCheckout = useCallback2(async () => {
1326
1568
  if (confirmProcessing || !session) return;
1327
1569
  setConfirmProcessing(true);
@@ -1373,32 +1615,32 @@ function FloPayCheckout({
1373
1615
  [session, isLoading, loadError, currentMode]
1374
1616
  );
1375
1617
  if (isLoading) {
1376
- return /* @__PURE__ */ jsx4(Fragment2, { children: loadingNode ?? /* @__PURE__ */ jsxs2(
1377
- "div",
1378
- {
1379
- style: {
1380
- display: "flex",
1381
- justifyContent: "center",
1382
- padding: 32
1383
- },
1384
- children: [
1385
- /* @__PURE__ */ jsx4(
1386
- "div",
1387
- {
1388
- style: {
1389
- width: 24,
1390
- height: 24,
1391
- border: "2px solid #e5e7eb",
1392
- borderTopColor: "#6b7280",
1393
- borderRadius: "50%",
1394
- animation: "spin 0.6s linear infinite"
1395
- }
1396
- }
1397
- ),
1398
- /* @__PURE__ */ jsx4("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1399
- ]
1400
- }
1401
- ) });
1618
+ if (loadingNode) return /* @__PURE__ */ jsx4(Fragment2, { children: loadingNode });
1619
+ if (layout === "buttons") {
1620
+ const skeletonBar = (h) => /* @__PURE__ */ jsx4("div", { style: {
1621
+ height: h,
1622
+ borderRadius: 8,
1623
+ background: "#e5e7eb",
1624
+ animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
1625
+ } });
1626
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1627
+ showPayPal && skeletonBar(44),
1628
+ (showApplePay || showGooglePay) && skeletonBar(44),
1629
+ skeletonBar(48),
1630
+ /* @__PURE__ */ jsx4("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
1631
+ ] });
1632
+ }
1633
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
1634
+ /* @__PURE__ */ jsx4("div", { style: {
1635
+ width: 24,
1636
+ height: 24,
1637
+ border: "2px solid #e5e7eb",
1638
+ borderTopColor: "#6b7280",
1639
+ borderRadius: "50%",
1640
+ animation: "spin 0.6s linear infinite"
1641
+ } }),
1642
+ /* @__PURE__ */ jsx4("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1643
+ ] });
1402
1644
  }
1403
1645
  if (loadError) {
1404
1646
  if (errorNode) return /* @__PURE__ */ jsx4(Fragment2, { children: errorNode(loadError) });
@@ -1415,6 +1657,19 @@ function FloPayCheckout({
1415
1657
  }
1416
1658
  );
1417
1659
  }
1660
+ if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
1661
+ return /* @__PURE__ */ jsx4(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx4(
1662
+ InterimButtonsView,
1663
+ {
1664
+ onButtonClick,
1665
+ showPayPal,
1666
+ showApplePay,
1667
+ showGooglePay,
1668
+ buttonsTheme,
1669
+ buttonsStyles
1670
+ }
1671
+ ) });
1672
+ }
1418
1673
  if (!flopay || !providerOptions) return /* @__PURE__ */ jsx4(Fragment2, {});
1419
1674
  if (currentMode === "confirm") {
1420
1675
  return /* @__PURE__ */ jsx4(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx4(FloPayProvider, { flopay, options: providerOptions, children: /* @__PURE__ */ jsxs2("div", { className, children: [
@@ -1475,7 +1730,7 @@ function FloPayCheckout({
1475
1730
  children ? /* @__PURE__ */ jsx4(
1476
1731
  SessionInjector,
1477
1732
  {
1478
- sessionId,
1733
+ sessionId: resolvedSessionId,
1479
1734
  billingApiUrl: resolvedBillingUrl,
1480
1735
  session,
1481
1736
  children
@@ -1483,7 +1738,7 @@ function FloPayCheckout({
1483
1738
  ) : /* @__PURE__ */ jsx4(
1484
1739
  SplitCardForm,
1485
1740
  {
1486
- sessionId,
1741
+ sessionId: resolvedSessionId,
1487
1742
  email: session?.customer?.email,
1488
1743
  userId: session?.customer?.id,
1489
1744
  firstName: session?.customer?.firstName,
@@ -1496,6 +1751,9 @@ function FloPayCheckout({
1496
1751
  showApplePay,
1497
1752
  showGooglePay,
1498
1753
  layout,
1754
+ buttonsTheme,
1755
+ buttonsStyles,
1756
+ onButtonClick,
1499
1757
  submitLabel,
1500
1758
  className
1501
1759
  }
@@ -1526,6 +1784,162 @@ function SessionInjector({
1526
1784
  return React4.cloneElement(child, injected);
1527
1785
  }) });
1528
1786
  }
1787
+ function InterimButtonsView({
1788
+ onButtonClick,
1789
+ showPayPal,
1790
+ showApplePay,
1791
+ showGooglePay,
1792
+ buttonsTheme,
1793
+ buttonsStyles: stylesOverride
1794
+ }) {
1795
+ const [showCardForm, setShowCardForm] = useState3(false);
1796
+ const bStyles = useMemo3(() => {
1797
+ const base = resolveButtonsLayoutTheme2(buttonsTheme);
1798
+ if (!stylesOverride) return base;
1799
+ return {
1800
+ ...base,
1801
+ ...stylesOverride,
1802
+ cardButton: { ...base.cardButton, ...stylesOverride.cardButton },
1803
+ cardFormContainer: { ...base.cardFormContainer, ...stylesOverride.cardFormContainer },
1804
+ submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
1805
+ title: { ...base.title, ...stylesOverride.title }
1806
+ };
1807
+ }, [buttonsTheme, stylesOverride]);
1808
+ const skeleton = (h) => /* @__PURE__ */ jsx4("div", { style: {
1809
+ height: h,
1810
+ borderRadius: 8,
1811
+ background: "#e5e7eb",
1812
+ animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
1813
+ } });
1814
+ if (showCardForm) {
1815
+ const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
1816
+ const inputBg = bStyles.cardInputBackground ?? "white";
1817
+ return /* @__PURE__ */ jsxs2("div", { style: {
1818
+ backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
1819
+ borderRadius: "8px",
1820
+ animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
1821
+ overflow: "hidden",
1822
+ ...bStyles.cardFormContainer
1823
+ }, children: [
1824
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
1825
+ /* @__PURE__ */ jsxs2(
1826
+ "button",
1827
+ {
1828
+ type: "button",
1829
+ onClick: () => setShowCardForm(false),
1830
+ style: {
1831
+ display: "inline-flex",
1832
+ alignItems: "center",
1833
+ gap: "0.5rem",
1834
+ background: "none",
1835
+ border: "none",
1836
+ cursor: "pointer",
1837
+ color: "#4b5563",
1838
+ fontSize: "0.85rem",
1839
+ fontWeight: 500,
1840
+ padding: 0,
1841
+ flexShrink: 0,
1842
+ ...bStyles.backButton
1843
+ },
1844
+ children: [
1845
+ /* @__PURE__ */ jsx4("span", { style: {
1846
+ display: "inline-flex",
1847
+ alignItems: "center",
1848
+ justifyContent: "center",
1849
+ width: 28,
1850
+ height: 28,
1851
+ borderRadius: "50%",
1852
+ backgroundColor: "#f3f4f6",
1853
+ ...bStyles.backButtonIcon
1854
+ }, children: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("path", { d: "M15 18l-6-6 6-6" }) }) }),
1855
+ "Go back"
1856
+ ]
1857
+ }
1858
+ ),
1859
+ /* @__PURE__ */ jsx4("div", { style: {
1860
+ flex: 1,
1861
+ textAlign: "center",
1862
+ fontWeight: 600,
1863
+ fontSize: "1.05rem",
1864
+ color: "#262833",
1865
+ paddingRight: 80,
1866
+ ...bStyles.title
1867
+ }, children: "Secure card checkout" })
1868
+ ] }),
1869
+ /* @__PURE__ */ jsx4("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx4("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
1870
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex" }, children: [
1871
+ /* @__PURE__ */ jsx4("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx4("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
1872
+ /* @__PURE__ */ jsx4("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx4("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
1873
+ ] }),
1874
+ /* @__PURE__ */ jsx4("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx4("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
1875
+ /* @__PURE__ */ jsx4("div", { style: {
1876
+ height: 50,
1877
+ borderRadius: 8,
1878
+ marginTop: 16,
1879
+ background: "#c8c8ff",
1880
+ animation: "flopay-interim-pulse 1.5s ease-in-out infinite",
1881
+ ...bStyles.submitButton,
1882
+ opacity: 0.5
1883
+ } }),
1884
+ /* @__PURE__ */ jsx4("style", { children: `
1885
+ @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
1886
+ @keyframes flopay-interim-expand {
1887
+ 0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
1888
+ 40% { opacity: 1; }
1889
+ 100% { opacity: 1; max-height: 600px; transform: translateY(0); }
1890
+ }
1891
+ ` })
1892
+ ] });
1893
+ }
1894
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1895
+ showPayPal && skeleton(44),
1896
+ (showApplePay || showGooglePay) && skeleton(44),
1897
+ /* @__PURE__ */ jsxs2(
1898
+ "button",
1899
+ {
1900
+ type: "button",
1901
+ onClick: () => {
1902
+ onButtonClick?.("card");
1903
+ setShowCardForm(true);
1904
+ },
1905
+ style: {
1906
+ width: "100%",
1907
+ padding: "0.9rem 1rem",
1908
+ backgroundColor: "white",
1909
+ color: "#262833",
1910
+ border: "1px solid #d1d5db",
1911
+ borderRadius: "8px",
1912
+ fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
1913
+ fontWeight: 600,
1914
+ cursor: "pointer",
1915
+ display: "flex",
1916
+ alignItems: "center",
1917
+ justifyContent: "center",
1918
+ gap: "0.625rem",
1919
+ boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
1920
+ transition: "transform 0.1s",
1921
+ position: "relative",
1922
+ ...bStyles.cardButton
1923
+ },
1924
+ onMouseDown: (e) => {
1925
+ e.currentTarget.style.transform = "scale(0.985)";
1926
+ },
1927
+ onMouseUp: (e) => {
1928
+ e.currentTarget.style.transform = "scale(1)";
1929
+ },
1930
+ children: [
1931
+ /* @__PURE__ */ jsxs2("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1932
+ /* @__PURE__ */ jsx4("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
1933
+ /* @__PURE__ */ jsx4("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
1934
+ ] }),
1935
+ "Credit / Debit Card",
1936
+ /* @__PURE__ */ jsx4("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__ */ jsx4("path", { d: "M9 18l6-6-6-6" }) })
1937
+ ]
1938
+ }
1939
+ ),
1940
+ /* @__PURE__ */ jsx4("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
1941
+ ] });
1942
+ }
1529
1943
 
1530
1944
  // src/checkout-form.tsx
1531
1945
  import { FloPayError as FloPayError3 } from "@flopay/shared";