@flopay/react 0.3.5 → 0.3.12

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 React5, { 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, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
72
+ import { FloPayError as FloPayError3, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
73
73
 
74
74
  // src/card-button-content.tsx
75
75
  import "react";
@@ -113,10 +113,29 @@ function DefaultCardButtonContent() {
113
113
  )
114
114
  ] });
115
115
  }
116
+ function DefaultBackButtonContent() {
117
+ return /* @__PURE__ */ jsx2(Fragment, { children: "Go back" });
118
+ }
119
+ function DefaultTitleContent() {
120
+ return /* @__PURE__ */ jsx2(Fragment, { children: "Secure card checkout" });
121
+ }
122
+ function isEmptySlotContent(content) {
123
+ return content !== void 0 && (content === "" || content === null || content === false);
124
+ }
116
125
  function CardButtonContentSlot({
117
126
  content
118
127
  }) {
119
- return /* @__PURE__ */ jsx2(Fragment, { children: content ?? /* @__PURE__ */ jsx2(DefaultCardButtonContent, {}) });
128
+ return /* @__PURE__ */ jsx2(Fragment, { children: content === void 0 ? /* @__PURE__ */ jsx2(DefaultCardButtonContent, {}) : content });
129
+ }
130
+ function BackButtonContentSlot({
131
+ content
132
+ }) {
133
+ return /* @__PURE__ */ jsx2(Fragment, { children: content === void 0 ? /* @__PURE__ */ jsx2(DefaultBackButtonContent, {}) : content });
134
+ }
135
+ function TitleContentSlot({
136
+ content
137
+ }) {
138
+ return /* @__PURE__ */ jsx2(Fragment, { children: content === void 0 ? /* @__PURE__ */ jsx2(DefaultTitleContent, {}) : content });
120
139
  }
121
140
 
122
141
  // src/elements.tsx
@@ -208,8 +227,116 @@ function useBillingApiUrl() {
208
227
  return ctx.billingApiUrl || resolveBillingApiUrl2();
209
228
  }
210
229
 
211
- // src/split-card-form.tsx
230
+ // src/checkout-utils.ts
212
231
  import { FloPayError } from "@flopay/shared";
232
+ function mergeInlineSessionPatches(base, patch) {
233
+ if (!patch) return base;
234
+ if (!base) return patch;
235
+ return {
236
+ account: { ...base.account ?? {}, ...patch.account ?? {} },
237
+ couponCodes: patch.couponCodes ?? base.couponCodes,
238
+ tagsData: {
239
+ ...base.tagsData ?? {},
240
+ ...patch.tagsData ?? {}
241
+ },
242
+ utmMetadata: patch.utmMetadata ?? base.utmMetadata
243
+ };
244
+ }
245
+ function mergeInlineSessionPatch(params, patch) {
246
+ if (!patch) return params;
247
+ return {
248
+ ...params,
249
+ account: {
250
+ ...params.account,
251
+ ...patch.account ?? {}
252
+ },
253
+ couponCodes: patch.couponCodes ?? params.couponCodes,
254
+ tagsData: {
255
+ ...params.tagsData ?? {},
256
+ ...patch.tagsData ?? {}
257
+ },
258
+ utmMetadata: patch.utmMetadata ?? params.utmMetadata
259
+ };
260
+ }
261
+ function buildSyntheticSession(params, checkoutModeOverride) {
262
+ const totalAmount = [
263
+ ...(params.items ?? []).map((item) => item.overrideAmount ?? item.totalAmount ?? 0),
264
+ ...(params.subscriptions ?? []).map((subscription) => subscription.overrideAmount ?? subscription.totalAmount ?? 0)
265
+ ].reduce((sum, value) => sum + value, 0);
266
+ const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
267
+ return {
268
+ id: "",
269
+ clientSecret: "",
270
+ mode: "payment",
271
+ amount: Math.round(totalAmount * 100),
272
+ currency,
273
+ status: "open",
274
+ customer: {
275
+ id: params.account.userId,
276
+ email: params.account.email ?? "",
277
+ firstName: params.account.firstName,
278
+ lastName: params.account.lastName,
279
+ gender: params.account.gender ?? void 0,
280
+ city: params.account.city ?? void 0,
281
+ state: params.account.state ?? void 0,
282
+ country: params.account.country ?? void 0,
283
+ zip: params.account.zip ?? void 0
284
+ },
285
+ successUrl: params.successUrl,
286
+ cancelUrl: params.cancelUrl,
287
+ checkoutMode: params.checkoutMode ?? checkoutModeOverride ?? "full",
288
+ items: (params.items ?? []).map((item, idx) => ({
289
+ uuid: `synthetic-item-${idx}`,
290
+ checkoutSessionId: "",
291
+ providerItemId: item.providerItemId,
292
+ providerItemName: item.providerItemName ?? item.providerItemId,
293
+ quantity: item.quantity ?? 1,
294
+ totalAmount: item.totalAmount,
295
+ overrideAmount: item.overrideAmount ?? null,
296
+ currency: item.currency ?? currency
297
+ })),
298
+ subscriptions: (params.subscriptions ?? []).map((subscription, idx) => ({
299
+ uuid: `synthetic-sub-${idx}`,
300
+ checkoutSessionId: "",
301
+ providerPlanId: subscription.providerPlanId,
302
+ providerPlanName: subscription.providerPlanName ?? subscription.providerPlanId,
303
+ quantity: subscription.quantity ?? 1,
304
+ totalAmount: subscription.totalAmount,
305
+ overrideAmount: subscription.overrideAmount ?? null,
306
+ currency: subscription.currency ?? currency
307
+ }))
308
+ };
309
+ }
310
+ function ensureInlineSessionReady(params) {
311
+ if (!params.account.email?.trim()) {
312
+ throw new FloPayError(
313
+ "Email is required before continuing with card checkout.",
314
+ "validation_error",
315
+ { param: "createSession.account.email" }
316
+ );
317
+ }
318
+ }
319
+ function mergeAccountPatch(base, patch) {
320
+ if (!patch) return base;
321
+ return {
322
+ ...base,
323
+ ...patch
324
+ };
325
+ }
326
+ function buildDeclineEvent(method, input, overrides) {
327
+ const message = typeof input === "string" ? input : input.message;
328
+ const code = typeof input === "string" ? overrides?.code : overrides?.code ?? input.code;
329
+ const declineCode = typeof input === "string" ? overrides?.declineCode : overrides?.declineCode ?? input.declineCode;
330
+ return {
331
+ method,
332
+ message,
333
+ ...code ? { code } : {},
334
+ ...declineCode ? { declineCode } : {}
335
+ };
336
+ }
337
+
338
+ // src/split-card-form.tsx
339
+ import { FloPayError as FloPayError2 } from "@flopay/shared";
213
340
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
214
341
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
215
342
  var FLOPAY_KEYFRAMES = `
@@ -347,7 +474,8 @@ function PayPalButtonInner({
347
474
  onTokenizedBody,
348
475
  onErrorChange,
349
476
  isProcessing = false,
350
- onButtonClick
477
+ onButtonClick,
478
+ onDecline
351
479
  }) {
352
480
  const stripe = useStripeRaw();
353
481
  const elements = useStripeElements();
@@ -367,7 +495,9 @@ function PayPalButtonInner({
367
495
  try {
368
496
  setSubmitting(true);
369
497
  if (redirectStatus === "failed") {
370
- onErrorChange?.("PayPal payment was declined. Please try again.");
498
+ const message = "PayPal payment was declined. Please try again.";
499
+ onErrorChange?.(message);
500
+ onDecline?.(buildDeclineEvent("paypal", message));
371
501
  return;
372
502
  }
373
503
  const { paymentIntent, error } = await stripe.retrievePaymentIntent(clientSecret);
@@ -389,7 +519,9 @@ function PayPalButtonInner({
389
519
  url.searchParams.delete("redirect_status");
390
520
  window.history.replaceState({}, "", url.toString());
391
521
  } else {
392
- onErrorChange?.("PayPal payment was not completed. Please try again.");
522
+ const message = "PayPal payment was not completed. Please try again.";
523
+ onErrorChange?.(message);
524
+ onDecline?.(buildDeclineEvent("paypal", message));
393
525
  }
394
526
  } catch (err) {
395
527
  onErrorChange?.(err instanceof Error ? err.message : "Failed to complete PayPal payment.");
@@ -397,7 +529,7 @@ function PayPalButtonInner({
397
529
  setSubmitting(false);
398
530
  }
399
531
  })();
400
- }, [stripe, onTokenizedBody, onErrorChange]);
532
+ }, [stripe, onTokenizedBody, onErrorChange, onDecline]);
401
533
  const handlePayPalConfirm = useCallback(async (_event) => {
402
534
  if (!stripe || !elements) return;
403
535
  onButtonClick?.("paypal");
@@ -438,7 +570,11 @@ function PayPalButtonInner({
438
570
  redirect: "if_required"
439
571
  });
440
572
  if (confirmError) {
441
- onErrorChange?.(confirmError.message ?? "PayPal payment failed.");
573
+ const message = confirmError.message ?? "PayPal payment failed.";
574
+ onErrorChange?.(message);
575
+ onDecline?.(buildDeclineEvent("paypal", message, {
576
+ code: confirmError.code
577
+ }));
442
578
  return;
443
579
  }
444
580
  const confirmedPmId = typeof paymentIntent?.payment_method === "string" ? paymentIntent.payment_method : paymentIntent?.payment_method?.id;
@@ -453,7 +589,7 @@ function PayPalButtonInner({
453
589
  } finally {
454
590
  setSubmitting(false);
455
591
  }
456
- }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
592
+ }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]);
457
593
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
458
594
  /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(
459
595
  ExpressCheckoutElement,
@@ -462,6 +598,9 @@ function PayPalButtonInner({
462
598
  onLoadError: () => {
463
599
  },
464
600
  onConfirm: handlePayPalConfirm,
601
+ onCancel: () => {
602
+ onDecline?.(buildDeclineEvent("paypal", "PayPal checkout was cancelled."));
603
+ },
465
604
  options: {
466
605
  buttonType: { paypal: "paypal" },
467
606
  paymentMethods: {
@@ -484,13 +623,15 @@ function WalletButtonInner({
484
623
  showGooglePay = true,
485
624
  onTokenizedBody,
486
625
  onErrorChange,
487
- onButtonClick
626
+ onButtonClick,
627
+ onDecline
488
628
  }) {
489
629
  const stripe = useStripeRaw();
490
630
  const elements = useStripeElements();
491
631
  const [ready, setReady] = useState2(false);
492
632
  const [submitting, setSubmitting] = useState2(false);
493
633
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
634
+ const lastWalletMethodRef = useRef2("card");
494
635
  const handleWalletConfirm = useCallback(
495
636
  async (_event) => {
496
637
  if (!stripe || !elements) return;
@@ -531,7 +672,12 @@ function WalletButtonInner({
531
672
  { payment_method: paymentMethod.id }
532
673
  );
533
674
  if (confirmError) {
534
- onErrorChange?.(confirmError.message ?? "Wallet payment failed.");
675
+ const method = walletType === "apple_pay" ? "apple_pay" : "google_pay";
676
+ const message = confirmError.message ?? "Wallet payment failed.";
677
+ onErrorChange?.(message);
678
+ onDecline?.(buildDeclineEvent(method, message, {
679
+ code: confirmError.code
680
+ }));
535
681
  return;
536
682
  }
537
683
  onTokenizedBody({
@@ -545,7 +691,7 @@ function WalletButtonInner({
545
691
  setSubmitting(false);
546
692
  }
547
693
  },
548
- [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
694
+ [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]
549
695
  );
550
696
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
551
697
  /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(
@@ -554,7 +700,14 @@ function WalletButtonInner({
554
700
  onReady: () => setReady(true),
555
701
  onLoadError: () => {
556
702
  },
703
+ onClick: (event) => {
704
+ lastWalletMethodRef.current = event.expressPaymentType === "apple_pay" ? "apple_pay" : "google_pay";
705
+ event.resolve();
706
+ },
557
707
  onConfirm: handleWalletConfirm,
708
+ onCancel: () => {
709
+ onDecline?.(buildDeclineEvent(lastWalletMethodRef.current, "Wallet checkout was cancelled."));
710
+ },
558
711
  options: {
559
712
  buttonType: { applePay: "plain", googlePay: "plain" },
560
713
  paymentMethods: {
@@ -579,6 +732,7 @@ function SplitCardFormInner({
579
732
  userId,
580
733
  onComplete,
581
734
  onError,
735
+ onDecline,
582
736
  onTokenizedBody,
583
737
  firstName,
584
738
  lastName,
@@ -598,7 +752,10 @@ function SplitCardFormInner({
598
752
  buttonsTheme,
599
753
  buttonsStyles: buttonsStylesOverride,
600
754
  cardButtonContent,
755
+ cardBackButtonContent,
756
+ cardTitleContent,
601
757
  onButtonClick,
758
+ onBeforeButtonClick,
602
759
  enableAVS = false,
603
760
  avsLayout: avsLayoutProp = "row",
604
761
  country: countryProp,
@@ -607,6 +764,7 @@ function SplitCardFormInner({
607
764
  onZipChange,
608
765
  totalAmount = 0,
609
766
  currency = "usd",
767
+ initialCardOpen = false,
610
768
  innerRef
611
769
  }) {
612
770
  const flopay = useFloPay();
@@ -617,9 +775,10 @@ function SplitCardFormInner({
617
775
  const [is3DSActive, setIs3DSActive] = useState2(false);
618
776
  const [selectedCountry, setSelectedCountry] = useState2(countryProp ?? "US");
619
777
  const [zipCode, setZipCode] = useState2(zipProp ?? "");
778
+ const [accountPatch, setAccountPatch] = useState2({});
620
779
  const zipCodeRef = useRef2(zipProp ?? "");
621
780
  const selectedCountryRef = useRef2(countryProp ?? "US");
622
- const [viewState, setViewState] = useState2("buttons");
781
+ const [viewState, setViewState] = useState2(initialCardOpen ? "card" : "buttons");
623
782
  const showCardForm = viewState === "expanding" || viewState === "card";
624
783
  const TRANSITION_MS = 280;
625
784
  const expandToCard = useCallback(() => {
@@ -630,6 +789,11 @@ function SplitCardFormInner({
630
789
  setViewState("collapsing");
631
790
  setTimeout(() => setViewState("buttons"), TRANSITION_MS);
632
791
  }, []);
792
+ useEffect3(() => {
793
+ if (layout === "buttons" && initialCardOpen) {
794
+ setViewState("card");
795
+ }
796
+ }, [layout, initialCardOpen]);
633
797
  const [fullName, setFullName] = useState2("");
634
798
  const [formReady, setFormReady] = useState2(false);
635
799
  const [overlayStatus, setOverlayStatus] = useState2(null);
@@ -653,6 +817,14 @@ function SplitCardFormInner({
653
817
  const isSubmitting = externalProcessing ?? processing;
654
818
  const isSelfContained = !onTokenizedBody;
655
819
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
820
+ const resolvedAccount = useMemo2(() => mergeAccountPatch({
821
+ userId,
822
+ email,
823
+ firstName,
824
+ lastName,
825
+ country: countryProp,
826
+ zip: zipProp
827
+ }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
656
828
  const stripeInstance = useMemo2(() => {
657
829
  if (!flopay) return null;
658
830
  return flopay.getRawProvider();
@@ -678,6 +850,12 @@ function SplitCardFormInner({
678
850
  },
679
851
  [onErrorChange]
680
852
  );
853
+ const emitDecline = useCallback(
854
+ (method, input, overrides) => {
855
+ onDecline?.(buildDeclineEvent(method, input, overrides));
856
+ },
857
+ [onDecline]
858
+ );
681
859
  const showWallets = showApplePay || showGooglePay;
682
860
  const handleNameChange = useCallback((value) => {
683
861
  setFullName(value);
@@ -685,6 +863,30 @@ function SplitCardFormInner({
685
863
  onFirstNameChange?.(parts[0] ?? "");
686
864
  onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
687
865
  }, [onFirstNameChange, onLastNameChange]);
866
+ const runBeforeCardButtonClick = useCallback(async () => {
867
+ if (!onBeforeButtonClick) return true;
868
+ try {
869
+ const result = await onBeforeButtonClick({
870
+ method: "card",
871
+ sessionId: sessionId || void 0
872
+ });
873
+ if (result === false) {
874
+ return false;
875
+ }
876
+ if (result?.account) {
877
+ setAccountPatch((prev) => ({ ...prev ?? {}, ...result.account }));
878
+ }
879
+ return true;
880
+ } catch (err) {
881
+ const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(
882
+ err instanceof Error ? err.message : "Card before-click hook failed.",
883
+ "validation_error"
884
+ );
885
+ updateError(floPayErr.message);
886
+ onError?.(floPayErr);
887
+ return false;
888
+ }
889
+ }, [onBeforeButtonClick, sessionId, updateError, onError]);
688
890
  const processPaymentInternal = useCallback(
689
891
  async (tokenizedBody) => {
690
892
  if (processingRef.current) return;
@@ -697,16 +899,16 @@ function SplitCardFormInner({
697
899
  method: "POST",
698
900
  headers: {
699
901
  "Content-Type": "application/json",
700
- "x-user-id": userId ?? ""
902
+ "x-user-id": resolvedAccount.userId ?? ""
701
903
  },
702
904
  body: JSON.stringify({
703
905
  sessionId,
704
906
  tokenizedData: tokenizedBody,
705
907
  accountData: {
706
- userId: userId ?? "",
707
- email: email ?? "",
708
- firstName: firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
709
- lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
908
+ userId: resolvedAccount.userId ?? "",
909
+ email: resolvedAccount.email ?? "",
910
+ firstName: resolvedAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
911
+ lastName: resolvedAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
710
912
  ...enableAVS ? { zip: zipCodeRef.current, country: selectedCountryRef.current } : {}
711
913
  },
712
914
  chv
@@ -739,6 +941,7 @@ function SplitCardFormInner({
739
941
  setOverlayStatus("error");
740
942
  updateError(result.error.message);
741
943
  onError?.(result.error);
944
+ emitDecline("card", result.error);
742
945
  return;
743
946
  }
744
947
  if (result.status === "succeeded" || result.status === "processing") {
@@ -781,7 +984,11 @@ function SplitCardFormInner({
781
984
  });
782
985
  if (confirmError) {
783
986
  setOverlayStatus("error");
784
- updateError(confirmError.message ?? "PayPal payment failed.");
987
+ const message2 = confirmError.message ?? "PayPal payment failed.";
988
+ updateError(message2);
989
+ emitDecline("paypal", message2, {
990
+ code: confirmError.code
991
+ });
785
992
  }
786
993
  } catch (err) {
787
994
  setOverlayStatus("error");
@@ -790,7 +997,12 @@ function SplitCardFormInner({
790
997
  return;
791
998
  }
792
999
  setOverlayStatus("error");
793
- updateError(json?.message ?? "Payment failed. Please try again.");
1000
+ const message = json?.message ?? "Payment failed. Please try again.";
1001
+ updateError(message);
1002
+ emitDecline(tokenizedBody.isPaypal ? "paypal" : "card", message, {
1003
+ code: json?.code ?? json?.gatewayErrorCode,
1004
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1005
+ });
794
1006
  await new Promise((r) => setTimeout(r, 1500));
795
1007
  } catch (err) {
796
1008
  setOverlayStatus("error");
@@ -802,7 +1014,7 @@ function SplitCardFormInner({
802
1014
  processingRef.current = false;
803
1015
  }
804
1016
  },
805
- [baseUrl, sessionId, userId, email, firstName, lastName, fullName, chv, flopay, onComplete, onError, updateError]
1017
+ [baseUrl, sessionId, resolvedAccount, fullName, chv, flopay, onComplete, onError, updateError, emitDecline]
806
1018
  );
807
1019
  const dispatchTokenizedBody = useCallback(
808
1020
  (tokenizedBody) => {
@@ -826,6 +1038,7 @@ function SplitCardFormInner({
826
1038
  if (result.error) {
827
1039
  updateError(result.error.message);
828
1040
  onError?.(result.error);
1041
+ emitDecline("card", result.error);
829
1042
  } else if (result.status === "succeeded" || result.status === "processing") {
830
1043
  dispatchTokenizedBody({
831
1044
  id: result.paymentIntentId,
@@ -839,7 +1052,7 @@ function SplitCardFormInner({
839
1052
  setIs3DSActive(false);
840
1053
  }
841
1054
  }
842
- }), [flopay, dispatchTokenizedBody, onError, updateError]);
1055
+ }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
843
1056
  useEffect3(() => {
844
1057
  if (typeof window === "undefined") return;
845
1058
  const stored = localStorage.getItem(WALLET_RESUME_KEY);
@@ -862,7 +1075,9 @@ function SplitCardFormInner({
862
1075
  async (e) => {
863
1076
  e.preventDefault();
864
1077
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
865
- onButtonClick?.("card");
1078
+ if (layout !== "buttons") {
1079
+ onButtonClick?.("card");
1080
+ }
866
1081
  setProcessing(true);
867
1082
  setOverlayStatus("processing");
868
1083
  updateError(null);
@@ -890,23 +1105,23 @@ function SplitCardFormInner({
890
1105
  updateError(pmResult.error?.message ?? "Failed to create payment method.");
891
1106
  return;
892
1107
  }
893
- if (!sessionId || !email) {
894
- throw new FloPayError("Missing sessionId or email", "validation_error");
1108
+ if (!sessionId || !resolvedAccount.email) {
1109
+ throw new FloPayError2("Missing sessionId or email", "validation_error");
895
1110
  }
896
1111
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
897
1112
  method: "POST",
898
1113
  headers: { "Content-Type": "application/json" },
899
1114
  body: JSON.stringify({
900
1115
  sessionId,
901
- email,
1116
+ email: resolvedAccount.email,
902
1117
  paymentMethodType: pmResult.paymentMethodId,
903
1118
  isPaypal: false
904
1119
  })
905
1120
  });
906
- if (!intentResponse.ok) throw new FloPayError("Failed to create payment intent", "api_error");
1121
+ if (!intentResponse.ok) throw new FloPayError2("Failed to create payment intent", "api_error");
907
1122
  const intentJson = await intentResponse.json();
908
1123
  const intentClientSecret = intentJson.data?.id;
909
- if (!intentClientSecret) throw new FloPayError("No client_secret in payment intent response", "api_error");
1124
+ if (!intentClientSecret) throw new FloPayError2("No client_secret in payment intent response", "api_error");
910
1125
  const confirmResult = await flopay.confirmCardPayment({
911
1126
  clientSecret: intentClientSecret,
912
1127
  paymentMethodId: pmResult.paymentMethodId
@@ -914,6 +1129,8 @@ function SplitCardFormInner({
914
1129
  if (confirmResult.error) {
915
1130
  setOverlayStatus("error");
916
1131
  updateError(confirmResult.error.message);
1132
+ onError?.(confirmResult.error);
1133
+ emitDecline("card", confirmResult.error);
917
1134
  await new Promise((r) => setTimeout(r, 1500));
918
1135
  return;
919
1136
  }
@@ -934,7 +1151,7 @@ function SplitCardFormInner({
934
1151
  }
935
1152
  }
936
1153
  },
937
- [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError]
1154
+ [flopay, elements, isSubmitting, sessionId, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
938
1155
  );
939
1156
  const isReady = flopay !== null && elements !== null;
940
1157
  if (!isReady) {
@@ -944,6 +1161,8 @@ function SplitCardFormInner({
944
1161
  const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
945
1162
  const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
946
1163
  const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
1164
+ const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
1165
+ const hideTitle = isEmptySlotContent(cardTitleContent);
947
1166
  const stripeElementStyle = isButtons && (bStyles.cardInputColor || bStyles.cardInputPlaceholderColor || bStyles.cardInputFontSize) ? {
948
1167
  style: {
949
1168
  base: {
@@ -974,7 +1193,7 @@ function SplitCardFormInner({
974
1193
  style: {
975
1194
  display: "inline-flex",
976
1195
  alignItems: "center",
977
- gap: "0.5rem",
1196
+ gap: hideBackButtonLabel ? 0 : "0.5rem",
978
1197
  background: "none",
979
1198
  border: "none",
980
1199
  cursor: "pointer",
@@ -999,11 +1218,11 @@ function SplitCardFormInner({
999
1218
  transition: "background-color 0.15s",
1000
1219
  ...bStyles.backButtonIcon
1001
1220
  }, 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" }) }) }),
1002
- "Go back"
1221
+ /* @__PURE__ */ jsx4(BackButtonContentSlot, { content: cardBackButtonContent })
1003
1222
  ]
1004
1223
  }
1005
1224
  ),
1006
- /* @__PURE__ */ jsx4("div", { style: {
1225
+ hideTitle ? /* @__PURE__ */ jsx4("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx4("div", { style: {
1007
1226
  flex: 1,
1008
1227
  textAlign: "center",
1009
1228
  fontWeight: 600,
@@ -1011,9 +1230,9 @@ function SplitCardFormInner({
1011
1230
  color: "#262833",
1012
1231
  paddingRight: 80,
1013
1232
  ...bStyles.title
1014
- }, children: "Secure card checkout" })
1233
+ }, children: /* @__PURE__ */ jsx4(TitleContentSlot, { content: cardTitleContent }) })
1015
1234
  ] }),
1016
- !isButtons && /* @__PURE__ */ jsx4("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
1235
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx4("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx4(TitleContentSlot, { content: cardTitleContent }) }),
1017
1236
  /* @__PURE__ */ jsx4("div", { style: {
1018
1237
  backgroundColor: cardInputBg,
1019
1238
  border: `1px solid ${resolvedBorder}`,
@@ -1217,32 +1436,36 @@ function SplitCardFormInner({
1217
1436
  PayPalButtonInner,
1218
1437
  {
1219
1438
  sessionId,
1220
- email,
1439
+ email: resolvedAccount.email,
1221
1440
  billingApiUrl: resolvedBillingApiUrl,
1222
1441
  onTokenizedBody: dispatchTokenizedBody,
1223
1442
  onErrorChange: updateError,
1224
1443
  isProcessing: isSubmitting,
1225
- onButtonClick
1444
+ onButtonClick,
1445
+ onDecline
1226
1446
  }
1227
1447
  ) }) : showPayPal ? /* @__PURE__ */ jsx4("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1228
1448
  showWallets && stripeInstance ? /* @__PURE__ */ jsx4(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx4(
1229
1449
  WalletButtonInner,
1230
1450
  {
1231
1451
  sessionId,
1232
- email,
1452
+ email: resolvedAccount.email,
1233
1453
  billingApiUrl: resolvedBillingApiUrl,
1234
1454
  showApplePay,
1235
1455
  showGooglePay,
1236
1456
  onTokenizedBody: dispatchTokenizedBody,
1237
1457
  onErrorChange: updateError,
1238
- onButtonClick
1458
+ onButtonClick,
1459
+ onDecline
1239
1460
  }
1240
1461
  ) }) : showWallets ? /* @__PURE__ */ jsx4("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
1241
1462
  /* @__PURE__ */ jsx4(
1242
1463
  "button",
1243
1464
  {
1244
1465
  type: "button",
1245
- onClick: () => {
1466
+ onClick: async () => {
1467
+ const shouldContinue = await runBeforeCardButtonClick();
1468
+ if (!shouldContinue) return;
1246
1469
  onButtonClick?.("card");
1247
1470
  expandToCard();
1248
1471
  },
@@ -1307,23 +1530,25 @@ function SplitCardFormInner({
1307
1530
  WalletButtonInner,
1308
1531
  {
1309
1532
  sessionId,
1310
- email,
1533
+ email: resolvedAccount.email,
1311
1534
  billingApiUrl: resolvedBillingApiUrl,
1312
1535
  showApplePay,
1313
1536
  showGooglePay,
1314
1537
  onTokenizedBody: dispatchTokenizedBody,
1315
- onErrorChange: updateError
1538
+ onErrorChange: updateError,
1539
+ onDecline
1316
1540
  }
1317
1541
  ) }),
1318
1542
  showPayPal && stripeInstance && /* @__PURE__ */ jsx4(StripeElements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx4(
1319
1543
  PayPalButtonInner,
1320
1544
  {
1321
1545
  sessionId,
1322
- email,
1546
+ email: resolvedAccount.email,
1323
1547
  billingApiUrl: resolvedBillingApiUrl,
1324
1548
  onTokenizedBody: dispatchTokenizedBody,
1325
1549
  onErrorChange: updateError,
1326
- isProcessing: isSubmitting
1550
+ isProcessing: isSubmitting,
1551
+ onDecline
1327
1552
  }
1328
1553
  ) }),
1329
1554
  (showWallets && stripeInstance || showPayPal && stripeInstance) && /* @__PURE__ */ jsxs2("div", { style: {
@@ -1355,6 +1580,7 @@ function FloPayCheckout({
1355
1580
  error: errorNode,
1356
1581
  onComplete,
1357
1582
  onError,
1583
+ onDecline,
1358
1584
  showPayPal = true,
1359
1585
  showApplePay = true,
1360
1586
  showGooglePay = true,
@@ -1362,7 +1588,10 @@ function FloPayCheckout({
1362
1588
  buttonsTheme,
1363
1589
  buttonsStyles,
1364
1590
  cardButtonContent,
1591
+ cardBackButtonContent,
1592
+ cardTitleContent,
1365
1593
  onButtonClick,
1594
+ onBeforeButtonClick,
1366
1595
  enableAVS,
1367
1596
  avsLayout,
1368
1597
  submitLabel,
@@ -1384,13 +1613,28 @@ function FloPayCheckout({
1384
1613
  const [currentMode, setCurrentMode] = useState3("full");
1385
1614
  const [confirmProcessing, setConfirmProcessing] = useState3(false);
1386
1615
  const [modeError, setModeError] = useState3(null);
1616
+ const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
1617
+ const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
1618
+ const [deferredCardOpen, setDeferredCardOpen] = useState3(false);
1387
1619
  const autoCheckoutAttempted = useRef3(false);
1388
1620
  const onCompleteRef = useRef3(onComplete);
1389
1621
  onCompleteRef.current = onComplete;
1390
1622
  const onErrorRef = useRef3(onError);
1391
1623
  onErrorRef.current = onError;
1624
+ const onDeclineRef = useRef3(onDecline);
1625
+ onDeclineRef.current = onDecline;
1392
1626
  const onSessionCompletedRef = useRef3(onSessionCompleted);
1393
1627
  onSessionCompletedRef.current = onSessionCompleted;
1628
+ const effectiveCreateSession = useMemo3(
1629
+ () => createSessionParams ? mergeInlineSessionPatch(createSessionParams, createSessionPatch) : void 0,
1630
+ [createSessionParams, createSessionPatch]
1631
+ );
1632
+ const emitDecline = useCallback2(
1633
+ (method, input, overrides) => {
1634
+ onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
1635
+ },
1636
+ []
1637
+ );
1394
1638
  const processPaymentForMode = useCallback2(
1395
1639
  async (sess) => {
1396
1640
  const baseUrl = resolvedBillingUrl.replace(/\/+$/, "");
@@ -1430,9 +1674,13 @@ function FloPayCheckout({
1430
1674
  // No client secret available
1431
1675
  };
1432
1676
  }
1433
- throw new FloPayError2(
1677
+ throw new FloPayError3(
1434
1678
  json?.message ?? "Payment failed. Please try again.",
1435
- "api_error"
1679
+ "api_error",
1680
+ {
1681
+ code: json?.code ?? json?.gatewayErrorCode,
1682
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1683
+ }
1436
1684
  );
1437
1685
  },
1438
1686
  [resolvedBillingUrl, resolvedSessionId]
@@ -1451,6 +1699,9 @@ function FloPayCheckout({
1451
1699
  });
1452
1700
  if (nextActionError) {
1453
1701
  setModeError(nextActionError.message ?? "3DS authentication failed.");
1702
+ emitDecline("card", nextActionError.message ?? "3DS authentication failed.", {
1703
+ code: nextActionError.code
1704
+ });
1454
1705
  return false;
1455
1706
  }
1456
1707
  if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded")) {
@@ -1473,6 +1724,9 @@ function FloPayCheckout({
1473
1724
  });
1474
1725
  if (error) {
1475
1726
  setModeError(error.message ?? "PayPal authorization failed.");
1727
+ emitDecline("paypal", error.message ?? "PayPal authorization failed.", {
1728
+ code: error.code
1729
+ });
1476
1730
  return false;
1477
1731
  }
1478
1732
  onCompleteRef.current?.({ status: "succeeded" });
@@ -1480,16 +1734,24 @@ function FloPayCheckout({
1480
1734
  }
1481
1735
  return false;
1482
1736
  },
1483
- [resolvedBillingUrl, resolvedSessionId]
1737
+ [resolvedBillingUrl, resolvedSessionId, emitDecline]
1484
1738
  );
1485
1739
  const inflightRef = useRef3(/* @__PURE__ */ new Map());
1486
1740
  const initializedHashRef = useRef3(null);
1741
+ const deferInlineSessionUntilCardClick = Boolean(
1742
+ effectiveCreateSession && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession.checkoutMode ?? checkoutModeProp ?? "full") === "full"
1743
+ );
1487
1744
  function hashCreateParams(params) {
1488
1745
  const key = JSON.stringify({
1489
1746
  c: params?.clientId,
1747
+ successUrl: params?.successUrl,
1748
+ cancelUrl: params?.cancelUrl,
1490
1749
  i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1491
1750
  s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1492
- e: params?.account.email,
1751
+ account: params?.account,
1752
+ couponCodes: params?.couponCodes,
1753
+ tagsData: params?.tagsData,
1754
+ utmMetadata: params?.utmMetadata,
1493
1755
  m: params?.checkoutMode ?? "full"
1494
1756
  });
1495
1757
  let h = 0;
@@ -1499,138 +1761,167 @@ function FloPayCheckout({
1499
1761
  return `flopay_session_${Math.abs(h).toString(36)}`;
1500
1762
  }
1501
1763
  const createSessionHash = useMemo3(
1502
- () => createSessionParams ? hashCreateParams(createSessionParams) : "",
1503
- // eslint-disable-next-line react-hooks/exhaustive-deps
1504
- [
1505
- createSessionParams?.clientId,
1506
- createSessionParams?.account?.email,
1507
- createSessionParams?.checkoutMode,
1508
- JSON.stringify(createSessionParams?.items),
1509
- JSON.stringify(createSessionParams?.subscriptions)
1510
- ]
1764
+ () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
1765
+ [effectiveCreateSession]
1766
+ );
1767
+ const createSessionParamsRef = useRef3(effectiveCreateSession);
1768
+ createSessionParamsRef.current = effectiveCreateSession;
1769
+ useEffect4(() => {
1770
+ setResolvedSessionId(sessionIdProp ?? "");
1771
+ }, [sessionIdProp]);
1772
+ async function resolveInlineSession(params, cacheKey) {
1773
+ ensureInlineSessionReady(params);
1774
+ const api = new PaymentAPI(resolvedBillingUrl);
1775
+ let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1776
+ let realResult = null;
1777
+ if (sid) {
1778
+ try {
1779
+ realResult = await api.getUnifiedCheckoutSession(sid);
1780
+ if (realResult.data.session?.status === "complete") {
1781
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1782
+ sid = null;
1783
+ realResult = null;
1784
+ }
1785
+ } catch {
1786
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1787
+ sid = null;
1788
+ }
1789
+ }
1790
+ if (!sid) {
1791
+ realResult = await api.createAndFetchSession(params);
1792
+ sid = realResult.data.session?.id ?? "";
1793
+ if (sid && typeof window !== "undefined") {
1794
+ window.sessionStorage.setItem(cacheKey, sid);
1795
+ }
1796
+ }
1797
+ return { sid: sid ?? "", result: realResult };
1798
+ }
1799
+ const bootstrapInlineSession = useCallback2(
1800
+ async (patch) => {
1801
+ const baseParams = createSessionParamsRef.current;
1802
+ if (!baseParams) {
1803
+ throw new FloPayError3("createSession is required to bootstrap checkout.", "validation_error");
1804
+ }
1805
+ const mergedParams = mergeInlineSessionPatch(baseParams, patch);
1806
+ const cacheKey = hashCreateParams(mergedParams);
1807
+ let promise = inflightRef.current.get(cacheKey);
1808
+ if (!promise) {
1809
+ promise = resolveInlineSession(mergedParams, cacheKey);
1810
+ inflightRef.current.set(cacheKey, promise);
1811
+ }
1812
+ let resolved;
1813
+ try {
1814
+ resolved = await promise;
1815
+ } finally {
1816
+ inflightRef.current.delete(cacheKey);
1817
+ }
1818
+ if (patch) {
1819
+ setCreateSessionPatch((prev) => mergeInlineSessionPatches(prev, patch));
1820
+ }
1821
+ const { sid, result: realResult } = resolved;
1822
+ setUnified(realResult);
1823
+ if (realResult.data.session) {
1824
+ setSession(realResult.data.session);
1825
+ }
1826
+ if (sid) {
1827
+ setResolvedSessionId(sid);
1828
+ }
1829
+ let publishableKey;
1830
+ if (realResult.provider === "stripe") {
1831
+ publishableKey = realResult.data.stripe?.publishableKey;
1832
+ }
1833
+ if (!publishableKey) publishableKey = fallbackPublishableKey;
1834
+ if (!publishableKey) {
1835
+ throw new FloPayError3(
1836
+ "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1837
+ "validation_error"
1838
+ );
1839
+ }
1840
+ const instance = await loadFloPay(publishableKey, {
1841
+ billingApiUrl: resolvedBillingUrl,
1842
+ locale
1843
+ });
1844
+ flopayRef.current = instance;
1845
+ setFloPay(instance);
1846
+ initializedHashRef.current = cacheKey;
1847
+ setIsLoading(false);
1848
+ return resolved;
1849
+ },
1850
+ [fallbackPublishableKey, locale, resolvedBillingUrl]
1511
1851
  );
1512
- const createSessionParamsRef = useRef3(createSessionParams);
1513
- createSessionParamsRef.current = createSessionParams;
1852
+ const handleDeferredCardButtonClick = useCallback2(async () => {
1853
+ if (cardBootstrapPending) return;
1854
+ setLoadError(null);
1855
+ setCardBootstrapPending(true);
1856
+ try {
1857
+ const beforeClickResult = await onBeforeButtonClick?.({
1858
+ method: "card",
1859
+ createSession: effectiveCreateSession
1860
+ });
1861
+ if (beforeClickResult === false) {
1862
+ setDeferredCardOpen(false);
1863
+ return;
1864
+ }
1865
+ const patch = beforeClickResult && typeof beforeClickResult === "object" ? beforeClickResult : void 0;
1866
+ const baseParams = createSessionParamsRef.current;
1867
+ if (!baseParams) {
1868
+ throw new FloPayError3(
1869
+ "createSession is required to bootstrap checkout.",
1870
+ "validation_error"
1871
+ );
1872
+ }
1873
+ ensureInlineSessionReady(mergeInlineSessionPatch(baseParams, patch));
1874
+ setDeferredCardOpen(true);
1875
+ onButtonClick?.("card");
1876
+ await bootstrapInlineSession(patch);
1877
+ } catch (err) {
1878
+ setDeferredCardOpen(false);
1879
+ const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(
1880
+ err instanceof Error ? err.message : "Failed to start card checkout.",
1881
+ "api_error"
1882
+ );
1883
+ setLoadError(floPayErr);
1884
+ onErrorRef.current?.(floPayErr);
1885
+ } finally {
1886
+ setCardBootstrapPending(false);
1887
+ }
1888
+ }, [
1889
+ bootstrapInlineSession,
1890
+ cardBootstrapPending,
1891
+ effectiveCreateSession,
1892
+ onButtonClick,
1893
+ onBeforeButtonClick
1894
+ ]);
1514
1895
  useEffect4(() => {
1515
1896
  let cancelled = false;
1516
1897
  setLoadError(null);
1517
1898
  if (createSessionHash) {
1899
+ if (deferInlineSessionUntilCardClick) {
1900
+ if (initializedHashRef.current !== createSessionHash) {
1901
+ setSession(null);
1902
+ setUnified(null);
1903
+ setFloPay(null);
1904
+ setResolvedSessionId("");
1905
+ setDeferredCardOpen(false);
1906
+ flopayRef.current = null;
1907
+ }
1908
+ setCurrentMode(createSessionParamsRef.current?.checkoutMode ?? checkoutModeProp ?? "full");
1909
+ setIsLoading(false);
1910
+ return () => {
1911
+ cancelled = true;
1912
+ };
1913
+ }
1518
1914
  if (initializedHashRef.current === createSessionHash) return;
1519
1915
  const params = createSessionParamsRef.current;
1520
- const totalAmount = [
1521
- ...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
1522
- ...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
1523
- ].reduce((sum, v) => sum + v, 0);
1524
- const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
1525
- const syntheticSession = {
1526
- id: "",
1527
- clientSecret: "",
1528
- mode: "payment",
1529
- amount: Math.round(totalAmount * 100),
1530
- currency,
1531
- status: "open",
1532
- customer: {
1533
- id: params.account.userId,
1534
- email: params.account.email,
1535
- firstName: params.account.firstName,
1536
- lastName: params.account.lastName
1537
- },
1538
- successUrl: params.successUrl,
1539
- cancelUrl: params.cancelUrl,
1540
- checkoutMode: params.checkoutMode ?? "full",
1541
- items: (params.items ?? []).map((i, idx) => ({
1542
- uuid: `synthetic-item-${idx}`,
1543
- checkoutSessionId: "",
1544
- providerItemId: i.providerItemId,
1545
- providerItemName: i.providerItemName ?? i.providerItemId,
1546
- quantity: i.quantity ?? 1,
1547
- totalAmount: i.totalAmount,
1548
- overrideAmount: i.overrideAmount ?? null,
1549
- currency: i.currency ?? currency
1550
- })),
1551
- subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
1552
- uuid: `synthetic-sub-${idx}`,
1553
- checkoutSessionId: "",
1554
- providerPlanId: s.providerPlanId,
1555
- providerPlanName: s.providerPlanName ?? s.providerPlanId,
1556
- quantity: s.quantity ?? 1,
1557
- totalAmount: s.totalAmount,
1558
- overrideAmount: s.overrideAmount ?? null,
1559
- currency: s.currency ?? currency
1560
- }))
1561
- };
1562
- setSession(syntheticSession);
1916
+ setSession(buildSyntheticSession(params, checkoutModeProp));
1563
1917
  setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
1564
1918
  setIsLoading(false);
1565
- const cacheKey = createSessionHash;
1566
- async function resolveSession() {
1567
- const api = new PaymentAPI(resolvedBillingUrl);
1568
- let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1569
- let realResult = null;
1570
- if (sid) {
1571
- try {
1572
- realResult = await api.getUnifiedCheckoutSession(sid);
1573
- if (realResult.data.session?.status === "complete") {
1574
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1575
- sid = null;
1576
- realResult = null;
1577
- }
1578
- } catch {
1579
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1580
- sid = null;
1581
- }
1582
- }
1583
- if (!sid) {
1584
- realResult = await api.createAndFetchSession(createSessionParamsRef.current);
1585
- sid = realResult.data.session?.id ?? "";
1586
- if (sid && typeof window !== "undefined") {
1587
- window.sessionStorage.setItem(cacheKey, sid);
1588
- }
1589
- }
1590
- return { sid: sid ?? "", result: realResult };
1591
- }
1592
1919
  (async () => {
1593
1920
  try {
1594
- let promise = inflightRef.current.get(cacheKey);
1595
- if (!promise) {
1596
- promise = resolveSession();
1597
- inflightRef.current.set(cacheKey, promise);
1598
- }
1599
- let resolved;
1600
- try {
1601
- resolved = await promise;
1602
- } finally {
1603
- inflightRef.current.delete(cacheKey);
1604
- }
1605
- if (cancelled) return;
1606
- const { sid, result: realResult } = resolved;
1607
- if (realResult) {
1608
- setUnified(realResult);
1609
- if (realResult.data.session) setSession(realResult.data.session);
1610
- }
1611
- if (sid) {
1612
- setResolvedSessionId(sid);
1613
- }
1614
- let pk;
1615
- if (realResult?.provider === "stripe") {
1616
- pk = realResult.data.stripe?.publishableKey;
1617
- }
1618
- if (!pk) pk = fallbackPublishableKey;
1619
- if (!pk) {
1620
- throw new FloPayError2(
1621
- "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1622
- "validation_error"
1623
- );
1624
- }
1625
- const instance = await loadFloPay(pk, { billingApiUrl: resolvedBillingUrl, locale });
1626
- if (!cancelled) {
1627
- flopayRef.current = instance;
1628
- setFloPay(instance);
1629
- initializedHashRef.current = cacheKey;
1630
- }
1921
+ await bootstrapInlineSession();
1631
1922
  } catch (err) {
1632
1923
  if (cancelled) return;
1633
- const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(err instanceof Error ? err.message : "Failed to create session", "api_error");
1924
+ const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to create session", "api_error");
1634
1925
  setLoadError(floPayErr);
1635
1926
  }
1636
1927
  })();
@@ -1648,7 +1939,7 @@ function FloPayCheckout({
1648
1939
  const sess = result.data.session ?? null;
1649
1940
  setSession(sess);
1650
1941
  if (!sess) {
1651
- throw new FloPayError2("No session data returned", "api_error");
1942
+ throw new FloPayError3("No session data returned", "api_error");
1652
1943
  }
1653
1944
  if (sess.status === "complete") {
1654
1945
  setIsLoading(false);
@@ -1687,7 +1978,7 @@ function FloPayCheckout({
1687
1978
  if (!cancelled) setIsLoading(false);
1688
1979
  } catch (err) {
1689
1980
  if (cancelled) return;
1690
- const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1981
+ const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1691
1982
  setLoadError(floPayErr);
1692
1983
  setIsLoading(false);
1693
1984
  }
@@ -1699,7 +1990,7 @@ function FloPayCheckout({
1699
1990
  }
1700
1991
  if (!publishableKey) publishableKey = fallbackPublishableKey;
1701
1992
  if (!publishableKey) {
1702
- throw new FloPayError2(
1993
+ throw new FloPayError3(
1703
1994
  "No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
1704
1995
  "validation_error"
1705
1996
  );
@@ -1715,7 +2006,7 @@ function FloPayCheckout({
1715
2006
  return () => {
1716
2007
  cancelled = true;
1717
2008
  };
1718
- }, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
2009
+ }, [resolvedSessionId, createSessionHash, checkoutModeProp, deferInlineSessionUntilCardClick, bootstrapInlineSession]);
1719
2010
  const handleConfirmCheckout = useCallback2(async () => {
1720
2011
  if (confirmProcessing || !session) return;
1721
2012
  setConfirmProcessing(true);
@@ -1730,17 +2021,18 @@ function FloPayCheckout({
1730
2021
  setCurrentMode("full");
1731
2022
  }
1732
2023
  } catch (err) {
1733
- const floPayErr = err instanceof FloPayError2 ? err : new FloPayError2(
2024
+ const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(
1734
2025
  err instanceof Error ? err.message : "Payment failed",
1735
2026
  "api_error"
1736
2027
  );
1737
2028
  setModeError(floPayErr.message);
1738
2029
  onError?.(floPayErr);
2030
+ emitDecline("card", floPayErr);
1739
2031
  setCurrentMode("full");
1740
2032
  } finally {
1741
2033
  setConfirmProcessing(false);
1742
2034
  }
1743
- }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError]);
2035
+ }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError, emitDecline]);
1744
2036
  const providerOptions = useMemo3(() => {
1745
2037
  if (!unified || !session) return void 0;
1746
2038
  const opts = {
@@ -1766,6 +2058,8 @@ function FloPayCheckout({
1766
2058
  }),
1767
2059
  [session, isLoading, loadError, currentMode]
1768
2060
  );
2061
+ const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
2062
+ const shouldKeepDeferredInterimVisible = shouldShowInterimButtons && deferInlineSessionUntilCardClick;
1769
2063
  if (isLoading) {
1770
2064
  if (loadingNode) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingNode });
1771
2065
  if (layout === "buttons") {
@@ -1794,7 +2088,7 @@ function FloPayCheckout({
1794
2088
  /* @__PURE__ */ jsx5("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1795
2089
  ] });
1796
2090
  }
1797
- if (loadError) {
2091
+ if (loadError && !shouldKeepDeferredInterimVisible) {
1798
2092
  if (errorNode) return /* @__PURE__ */ jsx5(Fragment3, { children: errorNode(loadError) });
1799
2093
  return /* @__PURE__ */ jsx5(
1800
2094
  "div",
@@ -1809,17 +2103,23 @@ function FloPayCheckout({
1809
2103
  }
1810
2104
  );
1811
2105
  }
1812
- if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
2106
+ if (shouldShowInterimButtons) {
1813
2107
  return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx5(
1814
2108
  InterimButtonsView,
1815
2109
  {
1816
2110
  onButtonClick,
1817
- showPayPal,
1818
- showApplePay,
1819
- showGooglePay,
2111
+ onCardButtonClick: deferInlineSessionUntilCardClick ? handleDeferredCardButtonClick : void 0,
2112
+ cardLoading: cardBootstrapPending,
2113
+ cardOpen: deferInlineSessionUntilCardClick ? deferredCardOpen : void 0,
2114
+ errorMessage: shouldKeepDeferredInterimVisible ? loadError?.message ?? null : null,
2115
+ showPayPal: deferInlineSessionUntilCardClick ? false : showPayPal,
2116
+ showApplePay: deferInlineSessionUntilCardClick ? false : showApplePay,
2117
+ showGooglePay: deferInlineSessionUntilCardClick ? false : showGooglePay,
1820
2118
  buttonsTheme,
1821
2119
  buttonsStyles,
1822
- cardButtonContent
2120
+ cardButtonContent,
2121
+ cardBackButtonContent,
2122
+ cardTitleContent
1823
2123
  }
1824
2124
  ) });
1825
2125
  }
@@ -1900,6 +2200,7 @@ function FloPayCheckout({
1900
2200
  currency: session?.currency?.toLowerCase() ?? "usd",
1901
2201
  onComplete,
1902
2202
  onError,
2203
+ onDecline,
1903
2204
  showPayPal,
1904
2205
  showApplePay,
1905
2206
  showGooglePay,
@@ -1907,11 +2208,15 @@ function FloPayCheckout({
1907
2208
  buttonsTheme,
1908
2209
  buttonsStyles,
1909
2210
  cardButtonContent,
2211
+ cardBackButtonContent,
2212
+ cardTitleContent,
1910
2213
  onButtonClick,
2214
+ onBeforeButtonClick,
1911
2215
  enableAVS,
1912
2216
  avsLayout,
1913
2217
  country: session?.customer?.country,
1914
2218
  zip: session?.customer?.zip,
2219
+ initialCardOpen: deferredCardOpen,
1915
2220
  submitLabel,
1916
2221
  className
1917
2222
  }
@@ -1944,14 +2249,26 @@ function SessionInjector({
1944
2249
  }
1945
2250
  function InterimButtonsView({
1946
2251
  onButtonClick,
2252
+ onCardButtonClick,
2253
+ cardLoading = false,
2254
+ cardOpen,
2255
+ errorMessage,
1947
2256
  showPayPal,
1948
2257
  showApplePay,
1949
2258
  showGooglePay,
1950
2259
  buttonsTheme,
1951
2260
  buttonsStyles: stylesOverride,
1952
- cardButtonContent
2261
+ cardButtonContent,
2262
+ cardBackButtonContent,
2263
+ cardTitleContent
1953
2264
  }) {
1954
2265
  const [showCardForm, setShowCardForm] = useState3(false);
2266
+ const isCardOpenControlled = typeof cardOpen === "boolean";
2267
+ useEffect4(() => {
2268
+ if (isCardOpenControlled) {
2269
+ setShowCardForm(cardOpen);
2270
+ }
2271
+ }, [cardOpen, isCardOpenControlled]);
1955
2272
  const bStyles = useMemo3(() => {
1956
2273
  const base = resolveButtonsLayoutTheme2(buttonsTheme);
1957
2274
  if (!stylesOverride) return base;
@@ -1960,6 +2277,8 @@ function InterimButtonsView({
1960
2277
  ...stylesOverride,
1961
2278
  cardButton: { ...base.cardButton, ...stylesOverride.cardButton },
1962
2279
  cardFormContainer: { ...base.cardFormContainer, ...stylesOverride.cardFormContainer },
2280
+ backButton: { ...base.backButton, ...stylesOverride.backButton },
2281
+ backButtonIcon: { ...base.backButtonIcon, ...stylesOverride.backButtonIcon },
1963
2282
  submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
1964
2283
  title: { ...base.title, ...stylesOverride.title }
1965
2284
  };
@@ -1973,6 +2292,8 @@ function InterimButtonsView({
1973
2292
  if (showCardForm) {
1974
2293
  const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
1975
2294
  const inputBg = bStyles.cardInputBackground ?? "white";
2295
+ const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
2296
+ const hideTitle = isEmptySlotContent(cardTitleContent);
1976
2297
  return /* @__PURE__ */ jsxs3("div", { style: {
1977
2298
  backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
1978
2299
  borderRadius: "8px",
@@ -1985,19 +2306,26 @@ function InterimButtonsView({
1985
2306
  "button",
1986
2307
  {
1987
2308
  type: "button",
1988
- onClick: () => setShowCardForm(false),
2309
+ onClick: () => {
2310
+ if (!isCardOpenControlled) {
2311
+ setShowCardForm(false);
2312
+ }
2313
+ },
2314
+ "aria-label": "Back to payment methods",
2315
+ disabled: isCardOpenControlled || cardLoading,
1989
2316
  style: {
1990
2317
  display: "inline-flex",
1991
2318
  alignItems: "center",
1992
- gap: "0.5rem",
2319
+ gap: hideBackButtonLabel ? 0 : "0.5rem",
1993
2320
  background: "none",
1994
2321
  border: "none",
1995
- cursor: "pointer",
1996
2322
  color: "#4b5563",
1997
2323
  fontSize: "0.85rem",
1998
2324
  fontWeight: 500,
1999
2325
  padding: 0,
2000
2326
  flexShrink: 0,
2327
+ opacity: isCardOpenControlled || cardLoading ? 0.6 : 1,
2328
+ cursor: isCardOpenControlled || cardLoading ? "not-allowed" : "pointer",
2001
2329
  ...bStyles.backButton
2002
2330
  },
2003
2331
  children: [
@@ -2011,11 +2339,11 @@ function InterimButtonsView({
2011
2339
  backgroundColor: "#f3f4f6",
2012
2340
  ...bStyles.backButtonIcon
2013
2341
  }, children: /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M15 18l-6-6 6-6" }) }) }),
2014
- "Go back"
2342
+ /* @__PURE__ */ jsx5(BackButtonContentSlot, { content: cardBackButtonContent })
2015
2343
  ]
2016
2344
  }
2017
2345
  ),
2018
- /* @__PURE__ */ jsx5("div", { style: {
2346
+ hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
2019
2347
  flex: 1,
2020
2348
  textAlign: "center",
2021
2349
  fontWeight: 600,
@@ -2023,7 +2351,7 @@ function InterimButtonsView({
2023
2351
  color: "#262833",
2024
2352
  paddingRight: 80,
2025
2353
  ...bStyles.title
2026
- }, children: "Secure card checkout" })
2354
+ }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
2027
2355
  ] }),
2028
2356
  /* @__PURE__ */ jsx5("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx5("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
2029
2357
  /* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
@@ -2057,10 +2385,16 @@ function InterimButtonsView({
2057
2385
  "button",
2058
2386
  {
2059
2387
  type: "button",
2060
- onClick: () => {
2388
+ onClick: async () => {
2389
+ if (cardLoading) return;
2390
+ if (onCardButtonClick) {
2391
+ await onCardButtonClick();
2392
+ return;
2393
+ }
2061
2394
  onButtonClick?.("card");
2062
2395
  setShowCardForm(true);
2063
2396
  },
2397
+ disabled: cardLoading,
2064
2398
  style: {
2065
2399
  width: "100%",
2066
2400
  padding: "0.9rem 1rem",
@@ -2070,7 +2404,7 @@ function InterimButtonsView({
2070
2404
  borderRadius: "8px",
2071
2405
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
2072
2406
  fontWeight: 600,
2073
- cursor: "pointer",
2407
+ cursor: cardLoading ? "not-allowed" : "pointer",
2074
2408
  display: "flex",
2075
2409
  alignItems: "center",
2076
2410
  justifyContent: "center",
@@ -2078,6 +2412,7 @@ function InterimButtonsView({
2078
2412
  boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
2079
2413
  transition: "transform 0.1s",
2080
2414
  position: "relative",
2415
+ opacity: cardLoading ? 0.6 : 1,
2081
2416
  ...bStyles.cardButton
2082
2417
  },
2083
2418
  onMouseDown: (e) => {
@@ -2089,12 +2424,29 @@ function InterimButtonsView({
2089
2424
  children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
2090
2425
  }
2091
2426
  ),
2427
+ errorMessage && /* @__PURE__ */ jsxs3("div", { style: {
2428
+ margin: "0.25rem 0",
2429
+ padding: "0.625rem 0.875rem",
2430
+ background: "#FEF2F2",
2431
+ border: "1px solid #FECACA",
2432
+ borderRadius: "8px",
2433
+ color: "#991B1B",
2434
+ fontSize: "0.85rem",
2435
+ fontWeight: 600,
2436
+ display: "flex",
2437
+ alignItems: "center",
2438
+ gap: "0.5rem",
2439
+ ...bStyles.errorBanner
2440
+ }, children: [
2441
+ /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("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" }) }),
2442
+ errorMessage
2443
+ ] }),
2092
2444
  /* @__PURE__ */ jsx5("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2093
2445
  ] });
2094
2446
  }
2095
2447
 
2096
2448
  // src/checkout-form.tsx
2097
- import { FloPayError as FloPayError3 } from "@flopay/shared";
2449
+ import { FloPayError as FloPayError4 } from "@flopay/shared";
2098
2450
  import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useState as useState4 } from "react";
2099
2451
  import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2100
2452
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
@@ -2110,6 +2462,7 @@ function CheckoutFormInner({
2110
2462
  userId,
2111
2463
  onComplete,
2112
2464
  onError,
2465
+ onDecline,
2113
2466
  onTokenizedBody,
2114
2467
  layout = "auto",
2115
2468
  submitLabel = "Pay",
@@ -2141,6 +2494,12 @@ function CheckoutFormInner({
2141
2494
  },
2142
2495
  [onErrorChange]
2143
2496
  );
2497
+ const emitDecline = useCallback3(
2498
+ (input, overrides) => {
2499
+ onDecline?.(buildDeclineEvent("card", input, overrides));
2500
+ },
2501
+ [onDecline]
2502
+ );
2144
2503
  const processPaymentInternal = useCallback3(
2145
2504
  async (tokenizedBody) => {
2146
2505
  setProcessing(true);
@@ -2187,6 +2546,7 @@ function CheckoutFormInner({
2187
2546
  if (result.error) {
2188
2547
  updateError(result.error.message);
2189
2548
  onError?.(result.error);
2549
+ emitDecline(result.error);
2190
2550
  return;
2191
2551
  }
2192
2552
  if (result.status === "succeeded" || result.status === "processing") {
@@ -2221,13 +2581,17 @@ function CheckoutFormInner({
2221
2581
  }
2222
2582
  const errorMessage = json?.message ?? "Payment failed. Please try again.";
2223
2583
  updateError(errorMessage);
2584
+ emitDecline(errorMessage, {
2585
+ code: json?.code,
2586
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
2587
+ });
2224
2588
  } catch (err) {
2225
2589
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
2226
2590
  } finally {
2227
2591
  setProcessing(false);
2228
2592
  }
2229
2593
  },
2230
- [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError]
2594
+ [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError, emitDecline]
2231
2595
  );
2232
2596
  const dispatchTokenizedBody = useCallback3(
2233
2597
  (tokenizedBody) => {
@@ -2251,6 +2615,7 @@ function CheckoutFormInner({
2251
2615
  if (result.error) {
2252
2616
  updateError(result.error.message);
2253
2617
  onError?.(result.error);
2618
+ emitDecline(result.error);
2254
2619
  } else if (result.status === "succeeded" || result.status === "processing") {
2255
2620
  dispatchTokenizedBody({
2256
2621
  id: result.paymentIntentId,
@@ -2264,7 +2629,7 @@ function CheckoutFormInner({
2264
2629
  setIs3DSActive(false);
2265
2630
  }
2266
2631
  }
2267
- }), [flopay, dispatchTokenizedBody, onError, updateError]);
2632
+ }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
2268
2633
  useEffect5(() => {
2269
2634
  if (typeof window === "undefined") return;
2270
2635
  const stored = localStorage.getItem(WALLET_RESUME_KEY2);
@@ -2302,7 +2667,7 @@ function CheckoutFormInner({
2302
2667
  return;
2303
2668
  }
2304
2669
  if (!sessionId || !email) {
2305
- throw new FloPayError3("Missing sessionId or email", "validation_error");
2670
+ throw new FloPayError4("Missing sessionId or email", "validation_error");
2306
2671
  }
2307
2672
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2308
2673
  method: "POST",
@@ -2314,16 +2679,18 @@ function CheckoutFormInner({
2314
2679
  isPaypal: false
2315
2680
  })
2316
2681
  });
2317
- if (!intentResponse.ok) throw new FloPayError3("Failed to create payment intent", "api_error");
2682
+ if (!intentResponse.ok) throw new FloPayError4("Failed to create payment intent", "api_error");
2318
2683
  const intentJson = await intentResponse.json();
2319
2684
  const intentClientSecret = intentJson.data?.id;
2320
- if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
2685
+ if (!intentClientSecret) throw new FloPayError4("No client_secret in payment intent response", "api_error");
2321
2686
  const confirmResult = await flopay.confirmCardPayment({
2322
2687
  clientSecret: intentClientSecret,
2323
2688
  paymentMethodId: pmResult.paymentMethodId
2324
2689
  });
2325
2690
  if (confirmResult.error) {
2326
2691
  updateError(confirmResult.error.message);
2692
+ onError?.(confirmResult.error);
2693
+ emitDecline(confirmResult.error);
2327
2694
  return;
2328
2695
  }
2329
2696
  dispatchTokenizedBody({
@@ -2340,7 +2707,7 @@ function CheckoutFormInner({
2340
2707
  }
2341
2708
  }
2342
2709
  },
2343
- [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError]
2710
+ [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
2344
2711
  );
2345
2712
  const isReady = flopay !== null && elements !== null;
2346
2713
  return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
@@ -2372,7 +2739,7 @@ function CheckoutFormInner({
2372
2739
  }
2373
2740
 
2374
2741
  // src/paypal-button.tsx
2375
- import { FloPayError as FloPayError4 } from "@flopay/shared";
2742
+ import { FloPayError as FloPayError5 } from "@flopay/shared";
2376
2743
  import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
2377
2744
  import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2378
2745
  function PayPalButton({
@@ -2492,7 +2859,7 @@ function PayPalButton({
2492
2859
  setSubmitting(true);
2493
2860
  onErrorChange?.(null);
2494
2861
  if (!sessionId || !email) {
2495
- throw new FloPayError4("Missing sessionId or email for PayPal payment", "validation_error");
2862
+ throw new FloPayError5("Missing sessionId or email for PayPal payment", "validation_error");
2496
2863
  }
2497
2864
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2498
2865
  method: "POST",
@@ -2504,10 +2871,10 @@ function PayPalButton({
2504
2871
  isPaypal: "true"
2505
2872
  })
2506
2873
  });
2507
- if (!intentResponse.ok) throw new FloPayError4("Failed to create payment intent", "api_error");
2874
+ if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
2508
2875
  const intentJson = await intentResponse.json();
2509
2876
  const intentClientSecret = intentJson.data?.id;
2510
- if (!intentClientSecret) throw new FloPayError4("No client_secret in response", "api_error");
2877
+ if (!intentClientSecret) throw new FloPayError5("No client_secret in response", "api_error");
2511
2878
  const result = await flopay.confirmPayment({
2512
2879
  clientSecret: intentClientSecret,
2513
2880
  returnUrl: window.location.href