@flopay/react 0.3.17 → 0.3.20

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/README.md CHANGED
@@ -123,7 +123,7 @@ Theme presets: `'default'`, `'minimal'`, `'rounded'`, `'dark'`. Custom styles vi
123
123
 
124
124
  #### Button Hooks
125
125
 
126
- Use `onButtonClick` to track button interactions, `onDecline` to track declines/cancellations, and `onBeforeButtonClick` to enrich the credit card flow before the card form opens:
126
+ Use `onButtonClick` to track button interactions, `onDecline` to track declines/cancellations, and `onBeforeButtonClick` to enrich any buttons-layout payment method before it continues:
127
127
 
128
128
  ```tsx
129
129
  <FloPayCheckout
@@ -136,7 +136,11 @@ Use `onButtonClick` to track button interactions, `onDecline` to track declines/
136
136
  cancelUrl: '/cancel',
137
137
  }}
138
138
  onBeforeButtonClick={async ({ method, createSession }) => {
139
- if (method !== 'card') return;
139
+ if (method !== 'card') {
140
+ return {
141
+ tagsData: { sessionId: `checkout_${method}_clicked` },
142
+ };
143
+ }
140
144
 
141
145
  const email = await openEmailCaptureModal({
142
146
  initialEmail: createSession?.account.email ?? '',
@@ -158,7 +162,7 @@ Use `onButtonClick` to track button interactions, `onDecline` to track declines/
158
162
  />
159
163
  ```
160
164
 
161
- `onBeforeButtonClick` is **credit card only**. It runs only for the "Credit / Debit Card" button in `layout="buttons"`. It does not run for PayPal, Apple Pay, or Google Pay. If you need required data before every payment method, collect it before rendering checkout.
165
+ `onBeforeButtonClick` runs for every payment button in `layout="buttons"`. Branch on `method` if only some flows need extra work. For PayPal, Apple Pay, and Google Pay, keep the hook fast because it runs during the provider button handshake before the wallet sheet or PayPal window opens.
162
166
 
163
167
  #### Inline Session Creation
164
168
 
@@ -180,7 +184,7 @@ Skip the backend API route — create the session directly in the component:
180
184
 
181
185
  The component POSTs to the billing API, gets the full session back, and renders the form — zero backend code needed.
182
186
 
183
- When you use `onBeforeButtonClick`, `createSession.account.email` should still be present when checkout renders. If you only have a temporary address such as `test@email.com`, pass it first and replace it in `onBeforeButtonClick` before the card flow continues. PayPal and wallet buttons still bootstrap normally, but this hook does not run for them, so collect any data those methods require before the user uses them.
187
+ When you use `onBeforeButtonClick` with `createSession`, any returned `InlineSessionPatch` is merged into the draft session params before the selected buttons-layout flow continues. That lets you add tracking data or update account fields just in time without pre-creating a separate backend session.
184
188
 
185
189
  ### Advanced: Manual Provider Setup
186
190
 
package/dist/index.cjs CHANGED
@@ -394,6 +394,25 @@ var FLOPAY_KEYFRAMES = `
394
394
  100% { opacity: 0; transform: translateX(16px) scale(0.97); }
395
395
  }
396
396
  `;
397
+ var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT = 44;
398
+ function getButtonMethodLabel(method) {
399
+ switch (method) {
400
+ case "paypal":
401
+ return "PayPal";
402
+ case "apple_pay":
403
+ return "Apple Pay";
404
+ case "google_pay":
405
+ return "Google Pay";
406
+ default:
407
+ return "Card";
408
+ }
409
+ }
410
+ function normalizeBeforeButtonClickError(method, err) {
411
+ return err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
412
+ err instanceof Error ? err.message : `${getButtonMethodLabel(method)} before-click hook failed.`,
413
+ "validation_error"
414
+ );
415
+ }
397
416
  function FloPayKeyframes() {
398
417
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: FLOPAY_KEYFRAMES });
399
418
  }
@@ -564,13 +583,15 @@ function PayPalButtonInner({
564
583
  onErrorChange,
565
584
  isProcessing = false,
566
585
  onButtonClick,
567
- onDecline
586
+ onDecline,
587
+ runBeforeButtonClick
568
588
  }) {
569
589
  const stripe = (0, import_react_stripe_js.useStripe)();
570
590
  const elements = (0, import_react_stripe_js.useElements)();
571
591
  const [loadState, setLoadState] = (0, import_react6.useState)("loading");
572
592
  const [submitting, setSubmitting] = (0, import_react6.useState)(false);
573
593
  const paypalResumeAttempted = (0, import_react6.useRef)(false);
594
+ const beforeClickRef = (0, import_react6.useRef)(null);
574
595
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
575
596
  (0, import_react6.useEffect)(() => {
576
597
  if (!stripe || paypalResumeAttempted.current) return;
@@ -619,13 +640,45 @@ function PayPalButtonInner({
619
640
  }
620
641
  })();
621
642
  }, [stripe, onTokenizedBody, onErrorChange, onDecline]);
622
- const handlePayPalConfirm = (0, import_react6.useCallback)(async (_event) => {
643
+ const handlePayPalClick = (0, import_react6.useCallback)(async (event) => {
644
+ if (isProcessing || submitting) {
645
+ event.reject();
646
+ return;
647
+ }
648
+ const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick("paypal") : { proceed: true };
649
+ if (!beforeClick.proceed) {
650
+ beforeClickRef.current = null;
651
+ event.reject();
652
+ return;
653
+ }
654
+ beforeClickRef.current = {
655
+ accountPatch: beforeClick.accountPatch,
656
+ sessionId: beforeClick.sessionId
657
+ };
658
+ event.resolve();
659
+ }, [isProcessing, runBeforeButtonClick, submitting]);
660
+ const handlePayPalConfirm = (0, import_react6.useCallback)(async (event) => {
623
661
  if (!stripe || !elements) return;
662
+ let prepared = beforeClickRef.current;
663
+ beforeClickRef.current = null;
664
+ if (!prepared && runBeforeButtonClick) {
665
+ const beforeClick = await runBeforeButtonClick("paypal");
666
+ if (!beforeClick.proceed) {
667
+ event.paymentFailed({ reason: "fail", message: "PayPal checkout was cancelled." });
668
+ return;
669
+ }
670
+ prepared = {
671
+ accountPatch: beforeClick.accountPatch,
672
+ sessionId: beforeClick.sessionId
673
+ };
674
+ }
675
+ const effectiveSessionId = prepared?.sessionId ?? sessionId;
676
+ const effectiveEmail = prepared?.accountPatch?.email ?? email;
624
677
  onButtonClick?.("paypal");
625
678
  try {
626
679
  setSubmitting(true);
627
680
  onErrorChange?.(null);
628
- if (!sessionId || !email) {
681
+ if (!effectiveSessionId || !effectiveEmail) {
629
682
  throw new Error("Missing sessionId or email for PayPal payment");
630
683
  }
631
684
  const createPM = stripe.createPaymentMethod;
@@ -637,8 +690,8 @@ function PayPalButtonInner({
637
690
  method: "POST",
638
691
  headers: { "Content-Type": "application/json" },
639
692
  body: JSON.stringify({
640
- sessionId,
641
- email,
693
+ sessionId: effectiveSessionId,
694
+ email: effectiveEmail,
642
695
  paymentMethodType: paymentMethod?.id ?? "paypal",
643
696
  isPaypal: "true"
644
697
  })
@@ -672,21 +725,26 @@ function PayPalButtonInner({
672
725
  type: "card",
673
726
  threeDSecureActionResultTokenId: paymentIntent?.id,
674
727
  isPaypal: true
728
+ }, {
729
+ accountPatch: prepared?.accountPatch,
730
+ sessionId: effectiveSessionId
675
731
  });
676
732
  } catch (err) {
677
733
  onErrorChange?.(err instanceof Error ? err.message : "PayPal payment failed. Please try again.");
678
734
  } finally {
679
735
  setSubmitting(false);
680
736
  }
681
- }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]);
737
+ }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]);
682
738
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
683
739
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
684
740
  import_react_stripe_js.ExpressCheckoutElement,
685
741
  {
686
742
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
687
743
  onLoadError: () => setLoadState("unavailable"),
744
+ onClick: handlePayPalClick,
688
745
  onConfirm: handlePayPalConfirm,
689
746
  onCancel: () => {
747
+ beforeClickRef.current = null;
690
748
  onDecline?.(buildDeclineEvent("paypal", "PayPal checkout was cancelled."));
691
749
  },
692
750
  options: {
@@ -712,7 +770,8 @@ function WalletButtonInner({
712
770
  onTokenizedBody,
713
771
  onErrorChange,
714
772
  onButtonClick,
715
- onDecline
773
+ onDecline,
774
+ runBeforeButtonClick
716
775
  }) {
717
776
  const stripe = (0, import_react_stripe_js.useStripe)();
718
777
  const elements = (0, import_react_stripe_js.useElements)();
@@ -720,10 +779,29 @@ function WalletButtonInner({
720
779
  const [submitting, setSubmitting] = (0, import_react6.useState)(false);
721
780
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
722
781
  const lastWalletMethodRef = (0, import_react6.useRef)("card");
782
+ const beforeClickRef = (0, import_react6.useRef)(null);
723
783
  const handleWalletConfirm = (0, import_react6.useCallback)(
724
- async (_event) => {
784
+ async (event) => {
725
785
  if (!stripe || !elements) return;
726
- const walletType = _event.expressPaymentType;
786
+ const walletType = event.expressPaymentType;
787
+ let prepared = beforeClickRef.current;
788
+ beforeClickRef.current = null;
789
+ if (!prepared && runBeforeButtonClick) {
790
+ const beforeClick = await runBeforeButtonClick(
791
+ walletType === "apple_pay" ? "apple_pay" : "google_pay"
792
+ );
793
+ if (!beforeClick.proceed) {
794
+ event.paymentFailed({ reason: "fail", message: "Wallet checkout was cancelled." });
795
+ return;
796
+ }
797
+ prepared = {
798
+ accountPatch: beforeClick.accountPatch,
799
+ sessionId: beforeClick.sessionId
800
+ };
801
+ }
802
+ const method = walletType === "apple_pay" ? "apple_pay" : "google_pay";
803
+ const effectiveSessionId = prepared?.sessionId ?? sessionId;
804
+ const effectiveEmail = prepared?.accountPatch?.email ?? email;
727
805
  onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
728
806
  try {
729
807
  setSubmitting(true);
@@ -738,15 +816,15 @@ function WalletButtonInner({
738
816
  onErrorChange?.(pmError?.message ?? "Failed to create payment method.");
739
817
  return;
740
818
  }
741
- if (!sessionId || !email) {
819
+ if (!effectiveSessionId || !effectiveEmail) {
742
820
  throw new Error("Missing sessionId or email for wallet payment");
743
821
  }
744
822
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
745
823
  method: "POST",
746
824
  headers: { "Content-Type": "application/json" },
747
825
  body: JSON.stringify({
748
- sessionId,
749
- email,
826
+ sessionId: effectiveSessionId,
827
+ email: effectiveEmail,
750
828
  paymentMethodType: paymentMethod.id,
751
829
  isPaypal: false
752
830
  })
@@ -760,7 +838,6 @@ function WalletButtonInner({
760
838
  { payment_method: paymentMethod.id }
761
839
  );
762
840
  if (confirmError) {
763
- const method = walletType === "apple_pay" ? "apple_pay" : "google_pay";
764
841
  const message = confirmError.message ?? "Wallet payment failed.";
765
842
  onErrorChange?.(message);
766
843
  onDecline?.(buildDeclineEvent(method, message, {
@@ -772,6 +849,9 @@ function WalletButtonInner({
772
849
  id: paymentMethod.id,
773
850
  type: "card",
774
851
  threeDSecureActionResultTokenId: paymentIntent?.id
852
+ }, {
853
+ accountPatch: prepared?.accountPatch,
854
+ sessionId: effectiveSessionId
775
855
  });
776
856
  } catch (err) {
777
857
  onErrorChange?.(err instanceof Error ? err.message : "Wallet payment failed. Please try again.");
@@ -779,7 +859,7 @@ function WalletButtonInner({
779
859
  setSubmitting(false);
780
860
  }
781
861
  },
782
- [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]
862
+ [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]
783
863
  );
784
864
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
785
865
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -787,12 +867,23 @@ function WalletButtonInner({
787
867
  {
788
868
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["applePay", "googlePay"])),
789
869
  onLoadError: () => setLoadState("unavailable"),
790
- onClick: (event) => {
870
+ onClick: async (event) => {
791
871
  lastWalletMethodRef.current = event.expressPaymentType === "apple_pay" ? "apple_pay" : "google_pay";
872
+ const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick(lastWalletMethodRef.current) : { proceed: true };
873
+ if (!beforeClick.proceed) {
874
+ beforeClickRef.current = null;
875
+ event.reject();
876
+ return;
877
+ }
878
+ beforeClickRef.current = {
879
+ accountPatch: beforeClick.accountPatch,
880
+ sessionId: beforeClick.sessionId
881
+ };
792
882
  event.resolve();
793
883
  },
794
884
  onConfirm: handleWalletConfirm,
795
885
  onCancel: () => {
886
+ beforeClickRef.current = null;
796
887
  onDecline?.(buildDeclineEvent(lastWalletMethodRef.current, "Wallet checkout was cancelled."));
797
888
  },
798
889
  options: {
@@ -954,55 +1045,83 @@ function SplitCardFormInner({
954
1045
  onFirstNameChange?.(parts[0] ?? "");
955
1046
  onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
956
1047
  }, [onFullNameChange, onFirstNameChange, onLastNameChange]);
957
- const runBeforeCardButtonClick = (0, import_react6.useCallback)(async () => {
958
- if (!onBeforeButtonClick) return true;
1048
+ const applyInlineSessionPatch = (0, import_react6.useCallback)(
1049
+ (patch, method) => {
1050
+ if (!checkout.applyInlineSessionPatch) {
1051
+ return Promise.resolve({ error: null, sessionId });
1052
+ }
1053
+ return checkout.applyInlineSessionPatch(patch).then((result) => ({
1054
+ error: null,
1055
+ sessionId: result.sessionId || sessionId
1056
+ })).catch((err) => {
1057
+ const floPayErr = normalizeBeforeButtonClickError(method, err);
1058
+ updateError(floPayErr.message);
1059
+ onError?.(floPayErr);
1060
+ return { error: floPayErr, sessionId };
1061
+ });
1062
+ },
1063
+ [checkout.applyInlineSessionPatch, onError, sessionId, updateError]
1064
+ );
1065
+ const runBeforeButtonClick = (0, import_react6.useCallback)(async (method) => {
1066
+ if (!onBeforeButtonClick) return { proceed: true };
959
1067
  try {
960
1068
  const result = await onBeforeButtonClick({
961
- method: "card",
962
- sessionId: sessionId || void 0
1069
+ method,
1070
+ sessionId: sessionId || void 0,
1071
+ createSession: checkout.inlineSessionDraft
963
1072
  });
964
1073
  if (result === false) {
965
- return false;
1074
+ return { proceed: false };
966
1075
  }
967
1076
  if (result && typeof result === "object") {
968
- await checkout.applyInlineSessionPatch?.(result);
969
1077
  if (result.account) {
970
1078
  setAccountPatch((prev) => ({ ...prev ?? {}, ...result.account }));
971
1079
  }
1080
+ const patchResult = await applyInlineSessionPatch(result, method);
1081
+ if (patchResult.error) {
1082
+ return {
1083
+ proceed: false,
1084
+ accountPatch: result.account
1085
+ };
1086
+ }
1087
+ return {
1088
+ proceed: true,
1089
+ accountPatch: result.account,
1090
+ sessionId: patchResult.sessionId
1091
+ };
972
1092
  }
973
- return true;
1093
+ return { proceed: true };
974
1094
  } catch (err) {
975
- const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
976
- err instanceof Error ? err.message : "Card before-click hook failed.",
977
- "validation_error"
978
- );
1095
+ const floPayErr = normalizeBeforeButtonClickError(method, err);
979
1096
  updateError(floPayErr.message);
980
1097
  onError?.(floPayErr);
981
- return false;
1098
+ return { proceed: false };
982
1099
  }
983
- }, [checkout.applyInlineSessionPatch, onBeforeButtonClick, sessionId, updateError, onError]);
1100
+ }, [applyInlineSessionPatch, checkout.inlineSessionDraft, onBeforeButtonClick, onError, sessionId, updateError]);
984
1101
  const processPaymentInternal = (0, import_react6.useCallback)(
985
- async (tokenizedBody) => {
1102
+ async (tokenizedBody, overrides) => {
986
1103
  if (processingRef.current) return;
987
1104
  processingRef.current = true;
988
1105
  setProcessing(true);
989
1106
  setOverlayStatus("processing");
990
1107
  updateError(null);
1108
+ const effectiveSessionId = overrides?.sessionId ?? sessionId;
1109
+ const effectiveAccount = mergeAccountPatch(resolvedAccount, overrides?.accountPatch);
991
1110
  try {
992
1111
  const response = await fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
993
1112
  method: "POST",
994
1113
  headers: {
995
1114
  "Content-Type": "application/json",
996
- "x-user-id": resolvedAccount.userId ?? ""
1115
+ "x-user-id": effectiveAccount.userId ?? ""
997
1116
  },
998
1117
  body: JSON.stringify({
999
- sessionId,
1118
+ sessionId: effectiveSessionId,
1000
1119
  tokenizedData: tokenizedBody,
1001
1120
  accountData: {
1002
- userId: resolvedAccount.userId ?? "",
1003
- email: resolvedAccount.email ?? "",
1004
- firstName: resolvedAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
1005
- lastName: resolvedAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
1121
+ userId: effectiveAccount.userId ?? "",
1122
+ email: effectiveAccount.email ?? "",
1123
+ firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
1124
+ lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
1006
1125
  ...enableAVS ? { zip: zipCodeRef.current, country: selectedCountryRef.current } : {}
1007
1126
  },
1008
1127
  chv
@@ -1111,11 +1230,11 @@ function SplitCardFormInner({
1111
1230
  [baseUrl, sessionId, resolvedAccount, fullName, chv, flopay, onComplete, onError, updateError, emitDecline]
1112
1231
  );
1113
1232
  const dispatchTokenizedBody = (0, import_react6.useCallback)(
1114
- (tokenizedBody) => {
1233
+ (tokenizedBody, overrides) => {
1115
1234
  if (onTokenizedBody) {
1116
1235
  onTokenizedBody(tokenizedBody);
1117
1236
  } else {
1118
- processPaymentInternal(tokenizedBody);
1237
+ processPaymentInternal(tokenizedBody, overrides);
1119
1238
  }
1120
1239
  },
1121
1240
  [onTokenizedBody, processPaymentInternal]
@@ -1542,6 +1661,7 @@ function SplitCardFormInner({
1542
1661
  if (layout === "buttons") {
1543
1662
  const isButtonsView = viewState === "buttons" || viewState === "expanding";
1544
1663
  const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
1664
+ const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
1545
1665
  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;
1546
1666
  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;
1547
1667
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
@@ -1567,9 +1687,10 @@ function SplitCardFormInner({
1567
1687
  onErrorChange: updateError,
1568
1688
  isProcessing: isSubmitting,
1569
1689
  onButtonClick,
1570
- onDecline
1690
+ onDecline,
1691
+ runBeforeButtonClick
1571
1692
  }
1572
- ) }) : showPayPal ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1693
+ ) }) : showPayPal ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1573
1694
  showWallets && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1574
1695
  WalletButtonInner,
1575
1696
  {
@@ -1581,24 +1702,25 @@ function SplitCardFormInner({
1581
1702
  onTokenizedBody: dispatchTokenizedBody,
1582
1703
  onErrorChange: updateError,
1583
1704
  onButtonClick,
1584
- onDecline
1705
+ onDecline,
1706
+ runBeforeButtonClick
1585
1707
  }
1586
- ) }) : showWallets ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1708
+ ) }) : showWallets ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1587
1709
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1588
1710
  "button",
1589
1711
  {
1590
1712
  type: "button",
1591
1713
  onClick: async () => {
1592
1714
  if (isSubmitting) return;
1593
- const shouldContinue = await runBeforeCardButtonClick();
1594
- if (!shouldContinue) return;
1715
+ const beforeClick = await runBeforeButtonClick("card");
1716
+ if (!beforeClick.proceed) return;
1595
1717
  onButtonClick?.("card");
1596
1718
  expandToCard();
1597
1719
  },
1598
1720
  disabled: isSubmitting,
1599
1721
  style: {
1600
1722
  width: "100%",
1601
- padding: "0.9rem 1rem",
1723
+ ...cardButtonSizing,
1602
1724
  backgroundColor: "white",
1603
1725
  color: "#262833",
1604
1726
  border: "1px solid #d1d5db",
@@ -1697,6 +1819,7 @@ function SplitCardFormInner({
1697
1819
 
1698
1820
  // src/flopay-checkout.tsx
1699
1821
  var import_jsx_runtime5 = require("react/jsx-runtime");
1822
+ var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
1700
1823
  function hasInlineSessionPatchData(patch) {
1701
1824
  if (!patch) return false;
1702
1825
  return Boolean(
@@ -2003,16 +2126,27 @@ function FloPayCheckout({
2003
2126
  [fallbackPublishableKey, locale, resolvedBillingUrl]
2004
2127
  );
2005
2128
  const handleInlineSessionPatch = (0, import_react7.useCallback)(async (patch) => {
2006
- if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) return;
2129
+ if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) {
2130
+ return {
2131
+ sessionId: resolvedSessionId,
2132
+ session
2133
+ };
2134
+ }
2007
2135
  setCardBootstrapPending(true);
2008
2136
  try {
2009
- await bootstrapInlineSession(patch);
2137
+ const resolved = await bootstrapInlineSession(patch);
2138
+ return {
2139
+ sessionId: resolved.sid,
2140
+ session: resolved.result.data.session ?? null
2141
+ };
2010
2142
  } finally {
2011
2143
  setCardBootstrapPending(false);
2012
2144
  }
2013
2145
  }, [
2014
2146
  bootstrapInlineSession,
2015
- cardBootstrapPending
2147
+ cardBootstrapPending,
2148
+ resolvedSessionId,
2149
+ session
2016
2150
  ]);
2017
2151
  (0, import_react7.useEffect)(() => {
2018
2152
  let cancelled = false;
@@ -2165,6 +2299,7 @@ function FloPayCheckout({
2165
2299
  loading: isLoading,
2166
2300
  error: loadError,
2167
2301
  checkoutMode: currentMode,
2302
+ inlineSessionDraft: effectiveCreateSession,
2168
2303
  applyInlineSessionPatch: shouldHandleInlineSessionPatch ? handleInlineSessionPatch : void 0,
2169
2304
  inlineSessionPatchProcessing: cardBootstrapPending
2170
2305
  }),
@@ -2173,6 +2308,7 @@ function FloPayCheckout({
2173
2308
  isLoading,
2174
2309
  loadError,
2175
2310
  currentMode,
2311
+ effectiveCreateSession,
2176
2312
  shouldHandleInlineSessionPatch,
2177
2313
  handleInlineSessionPatch,
2178
2314
  cardBootstrapPending
@@ -2189,9 +2325,9 @@ function FloPayCheckout({
2189
2325
  animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
2190
2326
  } });
2191
2327
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2192
- showPayPal && skeletonBar(44),
2193
- (showApplePay || showGooglePay) && skeletonBar(44),
2194
- skeletonBar(48),
2328
+ showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2329
+ (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2330
+ skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2195
2331
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2196
2332
  ] });
2197
2333
  }
@@ -2405,6 +2541,7 @@ function InterimButtonsView({
2405
2541
  background: "#e5e7eb",
2406
2542
  animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
2407
2543
  } });
2544
+ const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
2408
2545
  if (showCardForm) {
2409
2546
  const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
2410
2547
  const inputBg = bStyles.cardInputBackground ?? "white";
@@ -2495,8 +2632,8 @@ function InterimButtonsView({
2495
2632
  ] });
2496
2633
  }
2497
2634
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2498
- showPayPal && skeleton(44),
2499
- (showApplePay || showGooglePay) && skeleton(44),
2635
+ showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2636
+ (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
2500
2637
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2501
2638
  "button",
2502
2639
  {
@@ -2513,7 +2650,7 @@ function InterimButtonsView({
2513
2650
  disabled: cardLoading,
2514
2651
  style: {
2515
2652
  width: "100%",
2516
- padding: "0.9rem 1rem",
2653
+ ...cardButtonSizing,
2517
2654
  backgroundColor: "white",
2518
2655
  color: "#262833",
2519
2656
  border: "1px solid #d1d5db",