@flopay/react 0.1.5 → 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,7 +164,30 @@ 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";
166
- function ProcessingOverlay({ status }) {
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
+ }
190
+ function ProcessingOverlay({ status, errorMessage }) {
167
191
  return /* @__PURE__ */ jsx3("div", { style: {
168
192
  position: "fixed",
169
193
  inset: 0,
@@ -239,6 +263,14 @@ function ProcessingOverlay({ status }) {
239
263
  status === "success" && "PAYMENT SUCCESSFUL",
240
264
  status === "error" && "PAYMENT FAILED"
241
265
  ] }),
266
+ status === "error" && errorMessage && /* @__PURE__ */ jsx3("p", { style: {
267
+ fontSize: 13,
268
+ color: "#6b7280",
269
+ fontWeight: 400,
270
+ maxWidth: 260,
271
+ lineHeight: 1.4,
272
+ margin: 0
273
+ }, children: errorMessage }),
242
274
  /* @__PURE__ */ jsx3("style", { children: `
243
275
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
244
276
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
@@ -258,7 +290,8 @@ function PayPalButtonInner({
258
290
  billingApiUrl,
259
291
  onTokenizedBody,
260
292
  onErrorChange,
261
- isProcessing = false
293
+ isProcessing = false,
294
+ onButtonClick
262
295
  }) {
263
296
  const stripe = useStripeRaw();
264
297
  const elements = useStripeElements();
@@ -311,6 +344,7 @@ function PayPalButtonInner({
311
344
  }, [stripe, onTokenizedBody, onErrorChange]);
312
345
  const handlePayPalConfirm = useCallback(async (_event) => {
313
346
  if (!stripe || !elements) return;
347
+ onButtonClick?.("paypal");
314
348
  try {
315
349
  setSubmitting(true);
316
350
  onErrorChange?.(null);
@@ -365,7 +399,7 @@ function PayPalButtonInner({
365
399
  }
366
400
  }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
367
401
  return /* @__PURE__ */ jsxs(Fragment, { children: [
368
- /* @__PURE__ */ jsx3("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ jsx3(
402
+ /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
369
403
  ExpressCheckoutElement,
370
404
  {
371
405
  onReady: () => setReady(true),
@@ -393,7 +427,8 @@ function WalletButtonInner({
393
427
  showApplePay = true,
394
428
  showGooglePay = true,
395
429
  onTokenizedBody,
396
- onErrorChange
430
+ onErrorChange,
431
+ onButtonClick
397
432
  }) {
398
433
  const stripe = useStripeRaw();
399
434
  const elements = useStripeElements();
@@ -403,6 +438,8 @@ function WalletButtonInner({
403
438
  const handleWalletConfirm = useCallback(
404
439
  async (_event) => {
405
440
  if (!stripe || !elements) return;
441
+ const walletType = _event.expressPaymentType;
442
+ onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
406
443
  try {
407
444
  setSubmitting(true);
408
445
  onErrorChange?.(null);
@@ -455,7 +492,7 @@ function WalletButtonInner({
455
492
  [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
456
493
  );
457
494
  return /* @__PURE__ */ jsxs(Fragment, { children: [
458
- /* @__PURE__ */ jsx3("div", { style: { marginBottom: ready ? "0.5rem" : 0 }, children: /* @__PURE__ */ jsx3(
495
+ /* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(
459
496
  ExpressCheckoutElement,
460
497
  {
461
498
  onReady: () => setReady(true),
@@ -465,10 +502,12 @@ function WalletButtonInner({
465
502
  options: {
466
503
  buttonType: { applePay: "plain", googlePay: "plain" },
467
504
  paymentMethods: {
468
- applePay: showApplePay ? "auto" : "never",
469
- googlePay: showGooglePay ? "auto" : "never",
505
+ applePay: showApplePay ? "always" : "never",
506
+ googlePay: showGooglePay ? "always" : "never",
470
507
  paypal: "never",
471
- link: "never"
508
+ link: "never",
509
+ amazonPay: "never",
510
+ klarna: "never"
472
511
  },
473
512
  layout: { overflow: "never" }
474
513
  }
@@ -499,6 +538,10 @@ function SplitCardFormInner({
499
538
  showPayPal = true,
500
539
  showApplePay = true,
501
540
  showGooglePay = true,
541
+ layout = "default",
542
+ buttonsTheme,
543
+ buttonsStyles: buttonsStylesOverride,
544
+ onButtonClick,
502
545
  totalAmount = 0,
503
546
  currency = "usd",
504
547
  innerRef
@@ -509,12 +552,37 @@ function SplitCardFormInner({
509
552
  const [processing, setProcessing] = useState2(false);
510
553
  const [error, setError] = useState2(null);
511
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
+ }, []);
512
566
  const [fullName, setFullName] = useState2("");
513
567
  const [formReady, setFormReady] = useState2(false);
514
568
  const [overlayStatus, setOverlayStatus] = useState2(null);
515
569
  const processingRef = useRef2(false);
516
570
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
517
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]);
518
586
  const isSubmitting = externalProcessing ?? processing;
519
587
  const isSelfContained = !onTokenizedBody;
520
588
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
@@ -726,6 +794,7 @@ function SplitCardFormInner({
726
794
  async (e) => {
727
795
  e.preventDefault();
728
796
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
797
+ onButtonClick?.("card");
729
798
  setProcessing(true);
730
799
  setOverlayStatus("processing");
731
800
  updateError(null);
@@ -792,8 +861,299 @@ function SplitCardFormInner({
792
861
  if (!isReady) {
793
862
  return /* @__PURE__ */ jsx3("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
794
863
  }
864
+ const isButtons = layout === "buttons";
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
+ } : {};
878
+ const cardFormBlock = /* @__PURE__ */ jsxs("div", { style: {
879
+ backgroundColor: cardBg,
880
+ borderRadius: "8px",
881
+ padding: isButtons ? "0" : "1rem",
882
+ ...isButtons ? bStyles.cardFormContainer : {},
883
+ ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
884
+ }, children: [
885
+ isButtons && showCardForm && /* @__PURE__ */ jsxs("div", { style: {
886
+ display: "flex",
887
+ alignItems: "center",
888
+ padding: "0.75rem 0 0.625rem"
889
+ }, children: [
890
+ /* @__PURE__ */ jsxs(
891
+ "button",
892
+ {
893
+ type: "button",
894
+ onClick: collapseToButtons,
895
+ style: {
896
+ display: "inline-flex",
897
+ alignItems: "center",
898
+ gap: "0.5rem",
899
+ background: "none",
900
+ border: "none",
901
+ cursor: "pointer",
902
+ color: "#4b5563",
903
+ fontSize: bStyles.backButtonFontSize ?? "0.85rem",
904
+ fontWeight: 500,
905
+ padding: 0,
906
+ transition: "color 0.15s",
907
+ flexShrink: 0,
908
+ ...bStyles.backButton
909
+ },
910
+ "aria-label": "Back to payment methods",
911
+ children: [
912
+ /* @__PURE__ */ jsx3("span", { style: {
913
+ display: "inline-flex",
914
+ alignItems: "center",
915
+ justifyContent: "center",
916
+ width: 28,
917
+ height: 28,
918
+ borderRadius: "50%",
919
+ backgroundColor: "#f3f4f6",
920
+ transition: "background-color 0.15s",
921
+ ...bStyles.backButtonIcon
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" }) }) }),
923
+ "Go back"
924
+ ]
925
+ }
926
+ ),
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" })
936
+ ] }),
937
+ !isButtons && /* @__PURE__ */ jsx3("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
938
+ /* @__PURE__ */ jsx3("div", { style: {
939
+ backgroundColor: cardInputBg,
940
+ border: `1px solid ${resolvedBorder}`,
941
+ borderTopLeftRadius: "8px",
942
+ borderTopRightRadius: "8px",
943
+ padding: "10px"
944
+ }, children: /* @__PURE__ */ jsx3(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
945
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex" }, children: [
946
+ /* @__PURE__ */ jsx3("div", { style: {
947
+ flex: 1,
948
+ backgroundColor: cardInputBg,
949
+ border: `1px solid ${resolvedBorder}`,
950
+ borderTop: "none",
951
+ borderRight: "none",
952
+ borderBottomLeftRadius: "8px",
953
+ padding: "10px"
954
+ }, children: /* @__PURE__ */ jsx3(CardExpiryElement, { options: stripeElementStyle }) }),
955
+ /* @__PURE__ */ jsx3("div", { style: {
956
+ flex: 1,
957
+ backgroundColor: cardInputBg,
958
+ border: `1px solid ${resolvedBorder}`,
959
+ borderTop: "none",
960
+ borderBottomRightRadius: "8px",
961
+ padding: "10px"
962
+ }, children: /* @__PURE__ */ jsx3(CardCvcElement, { options: stripeElementStyle }) })
963
+ ] }),
964
+ /* @__PURE__ */ jsx3("div", { style: {
965
+ backgroundColor: cardInputBg,
966
+ border: `1px solid ${resolvedBorder}`,
967
+ borderRadius: "8px",
968
+ marginTop: "0.5rem",
969
+ padding: "10px"
970
+ }, children: /* @__PURE__ */ jsx3(
971
+ "input",
972
+ {
973
+ placeholder: "Full Name on Card",
974
+ autoComplete: "cc-name",
975
+ value: fullName,
976
+ onChange: (e) => handleNameChange(e.target.value),
977
+ disabled: isSubmitting,
978
+ required: true,
979
+ style: {
980
+ width: "100%",
981
+ border: "none",
982
+ outline: "none",
983
+ background: "transparent",
984
+ fontSize: bStyles.cardInputFontSize ?? "16px",
985
+ fontFamily: "Poppins, sans-serif",
986
+ color: bStyles.cardInputColor ?? "#262833",
987
+ ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
988
+ }
989
+ }
990
+ ) }),
991
+ displayError && /* @__PURE__ */ jsxs("div", { role: "alert", "data-testid": "flopay-error", style: {
992
+ margin: "0.75rem 0",
993
+ padding: "0.625rem 0.875rem",
994
+ background: "#FEF2F2",
995
+ border: "1px solid #FECACA",
996
+ borderRadius: "8px",
997
+ color: "#991B1B",
998
+ fontSize: "0.85rem",
999
+ fontWeight: 600,
1000
+ display: "flex",
1001
+ alignItems: "center",
1002
+ gap: "0.5rem",
1003
+ ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
1004
+ }, children: [
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" }) }),
1006
+ displayError
1007
+ ] }),
1008
+ children ?? /* @__PURE__ */ jsx3(
1009
+ "button",
1010
+ {
1011
+ type: "submit",
1012
+ disabled: !formReady || isSubmitting,
1013
+ "data-testid": "flopay-submit",
1014
+ style: {
1015
+ width: "100%",
1016
+ padding: "0.875rem",
1017
+ marginTop: "1rem",
1018
+ backgroundColor: "#4A49FF",
1019
+ color: "white",
1020
+ border: "none",
1021
+ borderRadius: "8px",
1022
+ fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
1023
+ fontWeight: 600,
1024
+ cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
1025
+ opacity: !formReady || isSubmitting ? 0.5 : 1,
1026
+ ...isButtons ? bStyles.submitButton : {}
1027
+ },
1028
+ children: isSubmitting ? "PROCESSING..." : submitLabel
1029
+ }
1030
+ ),
1031
+ !isButtons && /* @__PURE__ */ jsx3("div", { style: {
1032
+ backgroundColor: "#EFF9F0",
1033
+ borderRadius: "8px",
1034
+ padding: "0.75rem",
1035
+ marginTop: "0.75rem",
1036
+ textAlign: "center",
1037
+ fontSize: "0.85rem",
1038
+ fontWeight: 600,
1039
+ color: "#7DAD3A"
1040
+ }, children: "Secure Card Checkout" })
1041
+ ] });
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;
1047
+ return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
1048
+ /* @__PURE__ */ jsx3(FloPayKeyframes, {}),
1049
+ overlayStatus && /* @__PURE__ */ jsx3(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
1050
+ /* @__PURE__ */ jsxs("div", { style: { display: "grid" }, children: [
1051
+ /* @__PURE__ */ jsxs("div", { style: {
1052
+ gridArea: "1 / 1",
1053
+ display: "flex",
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" } : {}
1059
+ }, children: [
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
+ ] })
1152
+ ] });
1153
+ }
795
1154
  return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
796
- overlayStatus && /* @__PURE__ */ jsx3(ProcessingOverlay, { status: overlayStatus }),
1155
+ /* @__PURE__ */ jsx3(FloPayKeyframes, {}),
1156
+ overlayStatus && /* @__PURE__ */ jsx3(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
797
1157
  showWallets && stripeInstance && /* @__PURE__ */ jsx3(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx3(
798
1158
  WalletButtonInner,
799
1159
  {
@@ -829,100 +1189,15 @@ function SplitCardFormInner({
829
1189
  /* @__PURE__ */ jsx3("span", { children: "or pay with card" }),
830
1190
  /* @__PURE__ */ jsx3("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
831
1191
  ] }),
832
- /* @__PURE__ */ jsxs("div", { style: { backgroundColor: "#EDEDFF", borderRadius: "8px", padding: "1rem" }, children: [
833
- /* @__PURE__ */ jsx3("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
834
- /* @__PURE__ */ jsx3("div", { style: {
835
- backgroundColor: "white",
836
- border: "1px solid #A4A4FF",
837
- borderTopLeftRadius: "8px",
838
- borderTopRightRadius: "8px",
839
- padding: "10px"
840
- }, children: /* @__PURE__ */ jsx3(CardNumberElement, { onReady: () => setFormReady(true) }) }),
841
- /* @__PURE__ */ jsxs("div", { style: { display: "flex" }, children: [
842
- /* @__PURE__ */ jsx3("div", { style: {
843
- flex: 1,
844
- backgroundColor: "white",
845
- border: "1px solid #A4A4FF",
846
- borderTop: "none",
847
- borderRight: "none",
848
- borderBottomLeftRadius: "8px",
849
- padding: "10px"
850
- }, children: /* @__PURE__ */ jsx3(CardExpiryElement, {}) }),
851
- /* @__PURE__ */ jsx3("div", { style: {
852
- flex: 1,
853
- backgroundColor: "white",
854
- border: "1px solid #A4A4FF",
855
- borderTop: "none",
856
- borderBottomRightRadius: "8px",
857
- padding: "10px"
858
- }, children: /* @__PURE__ */ jsx3(CardCvcElement, {}) })
859
- ] }),
860
- /* @__PURE__ */ jsx3("div", { style: {
861
- backgroundColor: "white",
862
- border: "1px solid #A4A4FF",
863
- borderRadius: "8px",
864
- marginTop: "0.5rem",
865
- padding: "10px"
866
- }, children: /* @__PURE__ */ jsx3(
867
- "input",
868
- {
869
- placeholder: "Full Name on Card",
870
- autoComplete: "cc-name",
871
- value: fullName,
872
- onChange: (e) => handleNameChange(e.target.value),
873
- disabled: isSubmitting,
874
- required: true,
875
- style: {
876
- width: "100%",
877
- border: "none",
878
- outline: "none",
879
- fontSize: "16px",
880
- fontFamily: "Poppins, sans-serif",
881
- color: "#262833"
882
- }
883
- }
884
- ) }),
885
- displayError && /* @__PURE__ */ jsx3("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0", fontSize: "0.9rem" }, children: displayError }),
886
- children ?? /* @__PURE__ */ jsx3(
887
- "button",
888
- {
889
- type: "submit",
890
- disabled: !formReady || isSubmitting,
891
- "data-testid": "flopay-submit",
892
- style: {
893
- width: "100%",
894
- padding: "0.875rem",
895
- marginTop: "1rem",
896
- backgroundColor: "#4A49FF",
897
- color: "white",
898
- border: "none",
899
- borderRadius: "8px",
900
- fontSize: "1rem",
901
- fontWeight: 600,
902
- cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
903
- opacity: !formReady || isSubmitting ? 0.5 : 1
904
- },
905
- children: isSubmitting ? "PROCESSING..." : submitLabel
906
- }
907
- ),
908
- /* @__PURE__ */ jsx3("div", { style: {
909
- backgroundColor: "#EFF9F0",
910
- borderRadius: "8px",
911
- padding: "0.75rem",
912
- marginTop: "0.75rem",
913
- textAlign: "center",
914
- fontSize: "0.85rem",
915
- fontWeight: 600,
916
- color: "#7DAD3A"
917
- }, children: "Secure Card Checkout" })
918
- ] })
1192
+ cardFormBlock
919
1193
  ] });
920
1194
  }
921
1195
 
922
1196
  // src/flopay-checkout.tsx
923
1197
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
924
1198
  function FloPayCheckout({
925
- sessionId,
1199
+ sessionId: sessionIdProp,
1200
+ createSession: createSessionParams,
926
1201
  billingApiUrl,
927
1202
  appearance,
928
1203
  locale,
@@ -934,6 +1209,10 @@ function FloPayCheckout({
934
1209
  showPayPal = true,
935
1210
  showApplePay = true,
936
1211
  showGooglePay = true,
1212
+ layout,
1213
+ buttonsTheme,
1214
+ buttonsStyles,
1215
+ onButtonClick,
937
1216
  submitLabel,
938
1217
  className,
939
1218
  children,
@@ -947,6 +1226,7 @@ function FloPayCheckout({
947
1226
  const [flopay, setFloPay] = useState3(null);
948
1227
  const flopayRef = useRef3(null);
949
1228
  const [session, setSession] = useState3(null);
1229
+ const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
950
1230
  const [isLoading, setIsLoading] = useState3(true);
951
1231
  const [loadError, setLoadError] = useState3(null);
952
1232
  const [currentMode, setCurrentMode] = useState3("full");
@@ -969,7 +1249,7 @@ function FloPayCheckout({
969
1249
  "x-user-id": sess.customer?.id ?? ""
970
1250
  },
971
1251
  body: JSON.stringify({
972
- sessionId,
1252
+ sessionId: resolvedSessionId,
973
1253
  tokenizedData: { id: void 0 },
974
1254
  accountData: {
975
1255
  userId: sess.customer?.id ?? "",
@@ -1003,7 +1283,7 @@ function FloPayCheckout({
1003
1283
  "api_error"
1004
1284
  );
1005
1285
  },
1006
- [resolvedBillingUrl, sessionId]
1286
+ [resolvedBillingUrl, resolvedSessionId]
1007
1287
  );
1008
1288
  const handleRedirectResult = useCallback2(
1009
1289
  async (redirectResult, sess, options) => {
@@ -1048,16 +1328,169 @@ function FloPayCheckout({
1048
1328
  }
1049
1329
  return false;
1050
1330
  },
1051
- [resolvedBillingUrl, sessionId]
1331
+ [resolvedBillingUrl, resolvedSessionId]
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
+ ]
1052
1359
  );
1360
+ const createSessionParamsRef = useRef3(createSessionParams);
1361
+ createSessionParamsRef.current = createSessionParams;
1053
1362
  useEffect4(() => {
1054
1363
  let cancelled = false;
1055
- setIsLoading(true);
1056
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);
1057
1490
  async function init() {
1058
1491
  try {
1059
1492
  const api = new PaymentAPI(resolvedBillingUrl);
1060
- const result = await api.getUnifiedCheckoutSession(sessionId);
1493
+ const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
1061
1494
  if (cancelled) return;
1062
1495
  setUnified(result);
1063
1496
  const sess = result.data.session ?? null;
@@ -1075,7 +1508,7 @@ function FloPayCheckout({
1075
1508
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
1076
1509
  if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
1077
1510
  autoCheckoutAttempted.current = true;
1078
- const stripeInitPromise = initStripe(result, sess);
1511
+ const stripeInitPromise = initStripe(result);
1079
1512
  try {
1080
1513
  const redirectResult = await processPaymentForMode(sess);
1081
1514
  if (!redirectResult) {
@@ -1086,9 +1519,7 @@ function FloPayCheckout({
1086
1519
  await stripeInitPromise;
1087
1520
  const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
1088
1521
  if (!cancelled) {
1089
- if (!handled) {
1090
- setCurrentMode("full");
1091
- }
1522
+ if (!handled) setCurrentMode("full");
1092
1523
  setIsLoading(false);
1093
1524
  }
1094
1525
  return;
@@ -1100,19 +1531,16 @@ function FloPayCheckout({
1100
1531
  return;
1101
1532
  }
1102
1533
  }
1103
- await initStripe(result, sess);
1534
+ await initStripe(result);
1104
1535
  if (!cancelled) setIsLoading(false);
1105
1536
  } catch (err) {
1106
1537
  if (cancelled) return;
1107
- const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(
1108
- err instanceof Error ? err.message : "Failed to initialize checkout",
1109
- "api_error"
1110
- );
1538
+ const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1111
1539
  setLoadError(floPayErr);
1112
1540
  setIsLoading(false);
1113
1541
  }
1114
1542
  }
1115
- async function initStripe(result, _sess) {
1543
+ async function initStripe(result) {
1116
1544
  let publishableKey;
1117
1545
  if (result.provider === "stripe") {
1118
1546
  publishableKey = result.data.stripe?.publishableKey;
@@ -1135,7 +1563,7 @@ function FloPayCheckout({
1135
1563
  return () => {
1136
1564
  cancelled = true;
1137
1565
  };
1138
- }, [sessionId, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1566
+ }, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
1139
1567
  const handleConfirmCheckout = useCallback2(async () => {
1140
1568
  if (confirmProcessing || !session) return;
1141
1569
  setConfirmProcessing(true);
@@ -1187,32 +1615,32 @@ function FloPayCheckout({
1187
1615
  [session, isLoading, loadError, currentMode]
1188
1616
  );
1189
1617
  if (isLoading) {
1190
- return /* @__PURE__ */ jsx4(Fragment2, { children: loadingNode ?? /* @__PURE__ */ jsxs2(
1191
- "div",
1192
- {
1193
- style: {
1194
- display: "flex",
1195
- justifyContent: "center",
1196
- padding: 32
1197
- },
1198
- children: [
1199
- /* @__PURE__ */ jsx4(
1200
- "div",
1201
- {
1202
- style: {
1203
- width: 24,
1204
- height: 24,
1205
- border: "2px solid #e5e7eb",
1206
- borderTopColor: "#6b7280",
1207
- borderRadius: "50%",
1208
- animation: "spin 0.6s linear infinite"
1209
- }
1210
- }
1211
- ),
1212
- /* @__PURE__ */ jsx4("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1213
- ]
1214
- }
1215
- ) });
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
+ ] });
1216
1644
  }
1217
1645
  if (loadError) {
1218
1646
  if (errorNode) return /* @__PURE__ */ jsx4(Fragment2, { children: errorNode(loadError) });
@@ -1229,6 +1657,19 @@ function FloPayCheckout({
1229
1657
  }
1230
1658
  );
1231
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
+ }
1232
1673
  if (!flopay || !providerOptions) return /* @__PURE__ */ jsx4(Fragment2, {});
1233
1674
  if (currentMode === "confirm") {
1234
1675
  return /* @__PURE__ */ jsx4(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx4(FloPayProvider, { flopay, options: providerOptions, children: /* @__PURE__ */ jsxs2("div", { className, children: [
@@ -1289,7 +1730,7 @@ function FloPayCheckout({
1289
1730
  children ? /* @__PURE__ */ jsx4(
1290
1731
  SessionInjector,
1291
1732
  {
1292
- sessionId,
1733
+ sessionId: resolvedSessionId,
1293
1734
  billingApiUrl: resolvedBillingUrl,
1294
1735
  session,
1295
1736
  children
@@ -1297,7 +1738,7 @@ function FloPayCheckout({
1297
1738
  ) : /* @__PURE__ */ jsx4(
1298
1739
  SplitCardForm,
1299
1740
  {
1300
- sessionId,
1741
+ sessionId: resolvedSessionId,
1301
1742
  email: session?.customer?.email,
1302
1743
  userId: session?.customer?.id,
1303
1744
  firstName: session?.customer?.firstName,
@@ -1309,6 +1750,10 @@ function FloPayCheckout({
1309
1750
  showPayPal,
1310
1751
  showApplePay,
1311
1752
  showGooglePay,
1753
+ layout,
1754
+ buttonsTheme,
1755
+ buttonsStyles,
1756
+ onButtonClick,
1312
1757
  submitLabel,
1313
1758
  className
1314
1759
  }
@@ -1339,6 +1784,162 @@ function SessionInjector({
1339
1784
  return React4.cloneElement(child, injected);
1340
1785
  }) });
1341
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
+ }
1342
1943
 
1343
1944
  // src/checkout-form.tsx
1344
1945
  import { FloPayError as FloPayError3 } from "@flopay/shared";