@flopay/react 0.3.11 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -118,7 +118,7 @@ function FloPayProvider({
118
118
  // src/flopay-checkout.tsx
119
119
  var import_react7 = __toESM(require("react"), 1);
120
120
  var import_js = require("@flopay/js");
121
- var import_shared5 = require("@flopay/shared");
121
+ var import_shared6 = require("@flopay/shared");
122
122
 
123
123
  // src/card-button-content.tsx
124
124
  var import_react3 = require("react");
@@ -249,7 +249,7 @@ var AddressElement = createElementComponent("address", "AddressElement");
249
249
 
250
250
  // src/split-card-form.tsx
251
251
  var import_react_stripe_js = require("@stripe/react-stripe-js");
252
- var import_shared3 = require("@flopay/shared");
252
+ var import_shared4 = require("@flopay/shared");
253
253
  var import_react6 = require("react");
254
254
 
255
255
  // src/hooks.ts
@@ -271,8 +271,116 @@ function useBillingApiUrl() {
271
271
  return ctx.billingApiUrl || (0, import_shared2.resolveBillingApiUrl)();
272
272
  }
273
273
 
274
+ // src/checkout-utils.ts
275
+ var import_shared3 = require("@flopay/shared");
276
+ function mergeInlineSessionPatches(base, patch) {
277
+ if (!patch) return base;
278
+ if (!base) return patch;
279
+ return {
280
+ account: { ...base.account ?? {}, ...patch.account ?? {} },
281
+ couponCodes: patch.couponCodes ?? base.couponCodes,
282
+ tagsData: {
283
+ ...base.tagsData ?? {},
284
+ ...patch.tagsData ?? {}
285
+ },
286
+ utmMetadata: patch.utmMetadata ?? base.utmMetadata
287
+ };
288
+ }
289
+ function mergeInlineSessionPatch(params, patch) {
290
+ if (!patch) return params;
291
+ return {
292
+ ...params,
293
+ account: {
294
+ ...params.account,
295
+ ...patch.account ?? {}
296
+ },
297
+ couponCodes: patch.couponCodes ?? params.couponCodes,
298
+ tagsData: {
299
+ ...params.tagsData ?? {},
300
+ ...patch.tagsData ?? {}
301
+ },
302
+ utmMetadata: patch.utmMetadata ?? params.utmMetadata
303
+ };
304
+ }
305
+ function buildSyntheticSession(params, checkoutModeOverride) {
306
+ const totalAmount = [
307
+ ...(params.items ?? []).map((item) => item.overrideAmount ?? item.totalAmount ?? 0),
308
+ ...(params.subscriptions ?? []).map((subscription) => subscription.overrideAmount ?? subscription.totalAmount ?? 0)
309
+ ].reduce((sum, value) => sum + value, 0);
310
+ const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
311
+ return {
312
+ id: "",
313
+ clientSecret: "",
314
+ mode: "payment",
315
+ amount: Math.round(totalAmount * 100),
316
+ currency,
317
+ status: "open",
318
+ customer: {
319
+ id: params.account.userId,
320
+ email: params.account.email ?? "",
321
+ firstName: params.account.firstName,
322
+ lastName: params.account.lastName,
323
+ gender: params.account.gender ?? void 0,
324
+ city: params.account.city ?? void 0,
325
+ state: params.account.state ?? void 0,
326
+ country: params.account.country ?? void 0,
327
+ zip: params.account.zip ?? void 0
328
+ },
329
+ successUrl: params.successUrl,
330
+ cancelUrl: params.cancelUrl,
331
+ checkoutMode: params.checkoutMode ?? checkoutModeOverride ?? "full",
332
+ items: (params.items ?? []).map((item, idx) => ({
333
+ uuid: `synthetic-item-${idx}`,
334
+ checkoutSessionId: "",
335
+ providerItemId: item.providerItemId,
336
+ providerItemName: item.providerItemName ?? item.providerItemId,
337
+ quantity: item.quantity ?? 1,
338
+ totalAmount: item.totalAmount,
339
+ overrideAmount: item.overrideAmount ?? null,
340
+ currency: item.currency ?? currency
341
+ })),
342
+ subscriptions: (params.subscriptions ?? []).map((subscription, idx) => ({
343
+ uuid: `synthetic-sub-${idx}`,
344
+ checkoutSessionId: "",
345
+ providerPlanId: subscription.providerPlanId,
346
+ providerPlanName: subscription.providerPlanName ?? subscription.providerPlanId,
347
+ quantity: subscription.quantity ?? 1,
348
+ totalAmount: subscription.totalAmount,
349
+ overrideAmount: subscription.overrideAmount ?? null,
350
+ currency: subscription.currency ?? currency
351
+ }))
352
+ };
353
+ }
354
+ function ensureInlineSessionReady(params) {
355
+ if (!params.account.email?.trim()) {
356
+ throw new import_shared3.FloPayError(
357
+ "Email is required before continuing with card checkout.",
358
+ "validation_error",
359
+ { param: "createSession.account.email" }
360
+ );
361
+ }
362
+ }
363
+ function mergeAccountPatch(base, patch) {
364
+ if (!patch) return base;
365
+ return {
366
+ ...base,
367
+ ...patch
368
+ };
369
+ }
370
+ function buildDeclineEvent(method, input, overrides) {
371
+ const message = typeof input === "string" ? input : input.message;
372
+ const code = typeof input === "string" ? overrides?.code : overrides?.code ?? input.code;
373
+ const declineCode = typeof input === "string" ? overrides?.declineCode : overrides?.declineCode ?? input.declineCode;
374
+ return {
375
+ method,
376
+ message,
377
+ ...code ? { code } : {},
378
+ ...declineCode ? { declineCode } : {}
379
+ };
380
+ }
381
+
274
382
  // src/split-card-form.tsx
275
- var import_shared4 = require("@flopay/shared");
383
+ var import_shared5 = require("@flopay/shared");
276
384
  var import_jsx_runtime4 = require("react/jsx-runtime");
277
385
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
278
386
  var FLOPAY_KEYFRAMES = `
@@ -410,7 +518,8 @@ function PayPalButtonInner({
410
518
  onTokenizedBody,
411
519
  onErrorChange,
412
520
  isProcessing = false,
413
- onButtonClick
521
+ onButtonClick,
522
+ onDecline
414
523
  }) {
415
524
  const stripe = (0, import_react_stripe_js.useStripe)();
416
525
  const elements = (0, import_react_stripe_js.useElements)();
@@ -430,7 +539,9 @@ function PayPalButtonInner({
430
539
  try {
431
540
  setSubmitting(true);
432
541
  if (redirectStatus === "failed") {
433
- onErrorChange?.("PayPal payment was declined. Please try again.");
542
+ const message = "PayPal payment was declined. Please try again.";
543
+ onErrorChange?.(message);
544
+ onDecline?.(buildDeclineEvent("paypal", message));
434
545
  return;
435
546
  }
436
547
  const { paymentIntent, error } = await stripe.retrievePaymentIntent(clientSecret);
@@ -452,7 +563,9 @@ function PayPalButtonInner({
452
563
  url.searchParams.delete("redirect_status");
453
564
  window.history.replaceState({}, "", url.toString());
454
565
  } else {
455
- onErrorChange?.("PayPal payment was not completed. Please try again.");
566
+ const message = "PayPal payment was not completed. Please try again.";
567
+ onErrorChange?.(message);
568
+ onDecline?.(buildDeclineEvent("paypal", message));
456
569
  }
457
570
  } catch (err) {
458
571
  onErrorChange?.(err instanceof Error ? err.message : "Failed to complete PayPal payment.");
@@ -460,7 +573,7 @@ function PayPalButtonInner({
460
573
  setSubmitting(false);
461
574
  }
462
575
  })();
463
- }, [stripe, onTokenizedBody, onErrorChange]);
576
+ }, [stripe, onTokenizedBody, onErrorChange, onDecline]);
464
577
  const handlePayPalConfirm = (0, import_react6.useCallback)(async (_event) => {
465
578
  if (!stripe || !elements) return;
466
579
  onButtonClick?.("paypal");
@@ -501,7 +614,11 @@ function PayPalButtonInner({
501
614
  redirect: "if_required"
502
615
  });
503
616
  if (confirmError) {
504
- onErrorChange?.(confirmError.message ?? "PayPal payment failed.");
617
+ const message = confirmError.message ?? "PayPal payment failed.";
618
+ onErrorChange?.(message);
619
+ onDecline?.(buildDeclineEvent("paypal", message, {
620
+ code: confirmError.code
621
+ }));
505
622
  return;
506
623
  }
507
624
  const confirmedPmId = typeof paymentIntent?.payment_method === "string" ? paymentIntent.payment_method : paymentIntent?.payment_method?.id;
@@ -516,7 +633,7 @@ function PayPalButtonInner({
516
633
  } finally {
517
634
  setSubmitting(false);
518
635
  }
519
- }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
636
+ }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]);
520
637
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
521
638
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
522
639
  import_react_stripe_js.ExpressCheckoutElement,
@@ -525,6 +642,9 @@ function PayPalButtonInner({
525
642
  onLoadError: () => {
526
643
  },
527
644
  onConfirm: handlePayPalConfirm,
645
+ onCancel: () => {
646
+ onDecline?.(buildDeclineEvent("paypal", "PayPal checkout was cancelled."));
647
+ },
528
648
  options: {
529
649
  buttonType: { paypal: "paypal" },
530
650
  paymentMethods: {
@@ -547,13 +667,15 @@ function WalletButtonInner({
547
667
  showGooglePay = true,
548
668
  onTokenizedBody,
549
669
  onErrorChange,
550
- onButtonClick
670
+ onButtonClick,
671
+ onDecline
551
672
  }) {
552
673
  const stripe = (0, import_react_stripe_js.useStripe)();
553
674
  const elements = (0, import_react_stripe_js.useElements)();
554
675
  const [ready, setReady] = (0, import_react6.useState)(false);
555
676
  const [submitting, setSubmitting] = (0, import_react6.useState)(false);
556
677
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
678
+ const lastWalletMethodRef = (0, import_react6.useRef)("card");
557
679
  const handleWalletConfirm = (0, import_react6.useCallback)(
558
680
  async (_event) => {
559
681
  if (!stripe || !elements) return;
@@ -594,7 +716,12 @@ function WalletButtonInner({
594
716
  { payment_method: paymentMethod.id }
595
717
  );
596
718
  if (confirmError) {
597
- onErrorChange?.(confirmError.message ?? "Wallet payment failed.");
719
+ const method = walletType === "apple_pay" ? "apple_pay" : "google_pay";
720
+ const message = confirmError.message ?? "Wallet payment failed.";
721
+ onErrorChange?.(message);
722
+ onDecline?.(buildDeclineEvent(method, message, {
723
+ code: confirmError.code
724
+ }));
598
725
  return;
599
726
  }
600
727
  onTokenizedBody({
@@ -608,7 +735,7 @@ function WalletButtonInner({
608
735
  setSubmitting(false);
609
736
  }
610
737
  },
611
- [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
738
+ [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline]
612
739
  );
613
740
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
614
741
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -617,7 +744,14 @@ function WalletButtonInner({
617
744
  onReady: () => setReady(true),
618
745
  onLoadError: () => {
619
746
  },
747
+ onClick: (event) => {
748
+ lastWalletMethodRef.current = event.expressPaymentType === "apple_pay" ? "apple_pay" : "google_pay";
749
+ event.resolve();
750
+ },
620
751
  onConfirm: handleWalletConfirm,
752
+ onCancel: () => {
753
+ onDecline?.(buildDeclineEvent(lastWalletMethodRef.current, "Wallet checkout was cancelled."));
754
+ },
621
755
  options: {
622
756
  buttonType: { applePay: "plain", googlePay: "plain" },
623
757
  paymentMethods: {
@@ -628,7 +762,7 @@ function WalletButtonInner({
628
762
  amazonPay: "never",
629
763
  klarna: "never"
630
764
  },
631
- layout: { overflow: "never" }
765
+ layout: { maxColumns: 1, overflow: "never" }
632
766
  }
633
767
  }
634
768
  ) }),
@@ -642,6 +776,7 @@ function SplitCardFormInner({
642
776
  userId,
643
777
  onComplete,
644
778
  onError,
779
+ onDecline,
645
780
  onTokenizedBody,
646
781
  firstName,
647
782
  lastName,
@@ -664,6 +799,7 @@ function SplitCardFormInner({
664
799
  cardBackButtonContent,
665
800
  cardTitleContent,
666
801
  onButtonClick,
802
+ onBeforeButtonClick,
667
803
  enableAVS = false,
668
804
  avsLayout: avsLayoutProp = "row",
669
805
  country: countryProp,
@@ -672,6 +808,7 @@ function SplitCardFormInner({
672
808
  onZipChange,
673
809
  totalAmount = 0,
674
810
  currency = "usd",
811
+ initialCardOpen = false,
675
812
  innerRef
676
813
  }) {
677
814
  const flopay = useFloPay();
@@ -682,9 +819,10 @@ function SplitCardFormInner({
682
819
  const [is3DSActive, setIs3DSActive] = (0, import_react6.useState)(false);
683
820
  const [selectedCountry, setSelectedCountry] = (0, import_react6.useState)(countryProp ?? "US");
684
821
  const [zipCode, setZipCode] = (0, import_react6.useState)(zipProp ?? "");
822
+ const [accountPatch, setAccountPatch] = (0, import_react6.useState)({});
685
823
  const zipCodeRef = (0, import_react6.useRef)(zipProp ?? "");
686
824
  const selectedCountryRef = (0, import_react6.useRef)(countryProp ?? "US");
687
- const [viewState, setViewState] = (0, import_react6.useState)("buttons");
825
+ const [viewState, setViewState] = (0, import_react6.useState)(initialCardOpen ? "card" : "buttons");
688
826
  const showCardForm = viewState === "expanding" || viewState === "card";
689
827
  const TRANSITION_MS = 280;
690
828
  const expandToCard = (0, import_react6.useCallback)(() => {
@@ -695,6 +833,11 @@ function SplitCardFormInner({
695
833
  setViewState("collapsing");
696
834
  setTimeout(() => setViewState("buttons"), TRANSITION_MS);
697
835
  }, []);
836
+ (0, import_react6.useEffect)(() => {
837
+ if (layout === "buttons" && initialCardOpen) {
838
+ setViewState("card");
839
+ }
840
+ }, [layout, initialCardOpen]);
698
841
  const [fullName, setFullName] = (0, import_react6.useState)("");
699
842
  const [formReady, setFormReady] = (0, import_react6.useState)(false);
700
843
  const [overlayStatus, setOverlayStatus] = (0, import_react6.useState)(null);
@@ -702,7 +845,7 @@ function SplitCardFormInner({
702
845
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
703
846
  const displayError = externalError ?? error;
704
847
  const bStyles = (0, import_react6.useMemo)(() => {
705
- const base = (0, import_shared3.resolveButtonsLayoutTheme)(buttonsTheme);
848
+ const base = (0, import_shared4.resolveButtonsLayoutTheme)(buttonsTheme);
706
849
  if (!buttonsStylesOverride) return base;
707
850
  return {
708
851
  ...base,
@@ -718,6 +861,14 @@ function SplitCardFormInner({
718
861
  const isSubmitting = externalProcessing ?? processing;
719
862
  const isSelfContained = !onTokenizedBody;
720
863
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
864
+ const resolvedAccount = (0, import_react6.useMemo)(() => mergeAccountPatch({
865
+ userId,
866
+ email,
867
+ firstName,
868
+ lastName,
869
+ country: countryProp,
870
+ zip: zipProp
871
+ }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
721
872
  const stripeInstance = (0, import_react6.useMemo)(() => {
722
873
  if (!flopay) return null;
723
874
  return flopay.getRawProvider();
@@ -743,6 +894,12 @@ function SplitCardFormInner({
743
894
  },
744
895
  [onErrorChange]
745
896
  );
897
+ const emitDecline = (0, import_react6.useCallback)(
898
+ (method, input, overrides) => {
899
+ onDecline?.(buildDeclineEvent(method, input, overrides));
900
+ },
901
+ [onDecline]
902
+ );
746
903
  const showWallets = showApplePay || showGooglePay;
747
904
  const handleNameChange = (0, import_react6.useCallback)((value) => {
748
905
  setFullName(value);
@@ -750,6 +907,30 @@ function SplitCardFormInner({
750
907
  onFirstNameChange?.(parts[0] ?? "");
751
908
  onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
752
909
  }, [onFirstNameChange, onLastNameChange]);
910
+ const runBeforeCardButtonClick = (0, import_react6.useCallback)(async () => {
911
+ if (!onBeforeButtonClick) return true;
912
+ try {
913
+ const result = await onBeforeButtonClick({
914
+ method: "card",
915
+ sessionId: sessionId || void 0
916
+ });
917
+ if (result === false) {
918
+ return false;
919
+ }
920
+ if (result?.account) {
921
+ setAccountPatch((prev) => ({ ...prev ?? {}, ...result.account }));
922
+ }
923
+ return true;
924
+ } catch (err) {
925
+ const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
926
+ err instanceof Error ? err.message : "Card before-click hook failed.",
927
+ "validation_error"
928
+ );
929
+ updateError(floPayErr.message);
930
+ onError?.(floPayErr);
931
+ return false;
932
+ }
933
+ }, [onBeforeButtonClick, sessionId, updateError, onError]);
753
934
  const processPaymentInternal = (0, import_react6.useCallback)(
754
935
  async (tokenizedBody) => {
755
936
  if (processingRef.current) return;
@@ -762,16 +943,16 @@ function SplitCardFormInner({
762
943
  method: "POST",
763
944
  headers: {
764
945
  "Content-Type": "application/json",
765
- "x-user-id": userId ?? ""
946
+ "x-user-id": resolvedAccount.userId ?? ""
766
947
  },
767
948
  body: JSON.stringify({
768
949
  sessionId,
769
950
  tokenizedData: tokenizedBody,
770
951
  accountData: {
771
- userId: userId ?? "",
772
- email: email ?? "",
773
- firstName: firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
774
- lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
952
+ userId: resolvedAccount.userId ?? "",
953
+ email: resolvedAccount.email ?? "",
954
+ firstName: resolvedAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
955
+ lastName: resolvedAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
775
956
  ...enableAVS ? { zip: zipCodeRef.current, country: selectedCountryRef.current } : {}
776
957
  },
777
958
  chv
@@ -804,6 +985,7 @@ function SplitCardFormInner({
804
985
  setOverlayStatus("error");
805
986
  updateError(result.error.message);
806
987
  onError?.(result.error);
988
+ emitDecline("card", result.error);
807
989
  return;
808
990
  }
809
991
  if (result.status === "succeeded" || result.status === "processing") {
@@ -846,7 +1028,11 @@ function SplitCardFormInner({
846
1028
  });
847
1029
  if (confirmError) {
848
1030
  setOverlayStatus("error");
849
- updateError(confirmError.message ?? "PayPal payment failed.");
1031
+ const message2 = confirmError.message ?? "PayPal payment failed.";
1032
+ updateError(message2);
1033
+ emitDecline("paypal", message2, {
1034
+ code: confirmError.code
1035
+ });
850
1036
  }
851
1037
  } catch (err) {
852
1038
  setOverlayStatus("error");
@@ -855,7 +1041,12 @@ function SplitCardFormInner({
855
1041
  return;
856
1042
  }
857
1043
  setOverlayStatus("error");
858
- updateError(json?.message ?? "Payment failed. Please try again.");
1044
+ const message = json?.message ?? "Payment failed. Please try again.";
1045
+ updateError(message);
1046
+ emitDecline(tokenizedBody.isPaypal ? "paypal" : "card", message, {
1047
+ code: json?.code ?? json?.gatewayErrorCode,
1048
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1049
+ });
859
1050
  await new Promise((r) => setTimeout(r, 1500));
860
1051
  } catch (err) {
861
1052
  setOverlayStatus("error");
@@ -867,7 +1058,7 @@ function SplitCardFormInner({
867
1058
  processingRef.current = false;
868
1059
  }
869
1060
  },
870
- [baseUrl, sessionId, userId, email, firstName, lastName, fullName, chv, flopay, onComplete, onError, updateError]
1061
+ [baseUrl, sessionId, resolvedAccount, fullName, chv, flopay, onComplete, onError, updateError, emitDecline]
871
1062
  );
872
1063
  const dispatchTokenizedBody = (0, import_react6.useCallback)(
873
1064
  (tokenizedBody) => {
@@ -891,6 +1082,7 @@ function SplitCardFormInner({
891
1082
  if (result.error) {
892
1083
  updateError(result.error.message);
893
1084
  onError?.(result.error);
1085
+ emitDecline("card", result.error);
894
1086
  } else if (result.status === "succeeded" || result.status === "processing") {
895
1087
  dispatchTokenizedBody({
896
1088
  id: result.paymentIntentId,
@@ -904,7 +1096,7 @@ function SplitCardFormInner({
904
1096
  setIs3DSActive(false);
905
1097
  }
906
1098
  }
907
- }), [flopay, dispatchTokenizedBody, onError, updateError]);
1099
+ }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
908
1100
  (0, import_react6.useEffect)(() => {
909
1101
  if (typeof window === "undefined") return;
910
1102
  const stored = localStorage.getItem(WALLET_RESUME_KEY);
@@ -927,14 +1119,16 @@ function SplitCardFormInner({
927
1119
  async (e) => {
928
1120
  e.preventDefault();
929
1121
  if (!flopay || !elements || isSubmitting || processingRef.current) return;
930
- onButtonClick?.("card");
1122
+ if (layout !== "buttons") {
1123
+ onButtonClick?.("card");
1124
+ }
931
1125
  setProcessing(true);
932
1126
  setOverlayStatus("processing");
933
1127
  updateError(null);
934
1128
  let handedOff = false;
935
1129
  try {
936
1130
  if (enableAVS && !zipCodeRef.current.trim()) {
937
- updateError((0, import_shared3.getPostalCodeLabel)(selectedCountryRef.current) + " is required");
1131
+ updateError((0, import_shared4.getPostalCodeLabel)(selectedCountryRef.current) + " is required");
938
1132
  return;
939
1133
  }
940
1134
  const submitResult = await flopay.submitElements();
@@ -955,23 +1149,23 @@ function SplitCardFormInner({
955
1149
  updateError(pmResult.error?.message ?? "Failed to create payment method.");
956
1150
  return;
957
1151
  }
958
- if (!sessionId || !email) {
959
- throw new import_shared4.FloPayError("Missing sessionId or email", "validation_error");
1152
+ if (!sessionId || !resolvedAccount.email) {
1153
+ throw new import_shared5.FloPayError("Missing sessionId or email", "validation_error");
960
1154
  }
961
1155
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
962
1156
  method: "POST",
963
1157
  headers: { "Content-Type": "application/json" },
964
1158
  body: JSON.stringify({
965
1159
  sessionId,
966
- email,
1160
+ email: resolvedAccount.email,
967
1161
  paymentMethodType: pmResult.paymentMethodId,
968
1162
  isPaypal: false
969
1163
  })
970
1164
  });
971
- if (!intentResponse.ok) throw new import_shared4.FloPayError("Failed to create payment intent", "api_error");
1165
+ if (!intentResponse.ok) throw new import_shared5.FloPayError("Failed to create payment intent", "api_error");
972
1166
  const intentJson = await intentResponse.json();
973
1167
  const intentClientSecret = intentJson.data?.id;
974
- if (!intentClientSecret) throw new import_shared4.FloPayError("No client_secret in payment intent response", "api_error");
1168
+ if (!intentClientSecret) throw new import_shared5.FloPayError("No client_secret in payment intent response", "api_error");
975
1169
  const confirmResult = await flopay.confirmCardPayment({
976
1170
  clientSecret: intentClientSecret,
977
1171
  paymentMethodId: pmResult.paymentMethodId
@@ -979,6 +1173,8 @@ function SplitCardFormInner({
979
1173
  if (confirmResult.error) {
980
1174
  setOverlayStatus("error");
981
1175
  updateError(confirmResult.error.message);
1176
+ onError?.(confirmResult.error);
1177
+ emitDecline("card", confirmResult.error);
982
1178
  await new Promise((r) => setTimeout(r, 1500));
983
1179
  return;
984
1180
  }
@@ -999,7 +1195,7 @@ function SplitCardFormInner({
999
1195
  }
1000
1196
  }
1001
1197
  },
1002
- [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError]
1198
+ [flopay, elements, isSubmitting, sessionId, resolvedAccount.email, baseUrl, isSelfContained, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
1003
1199
  );
1004
1200
  const isReady = flopay !== null && elements !== null;
1005
1201
  if (!isReady) {
@@ -1170,7 +1366,7 @@ function SplitCardFormInner({
1170
1366
  cursor: "pointer",
1171
1367
  ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
1172
1368
  },
1173
- children: import_shared3.COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("option", { value: c.code, children: [
1369
+ children: import_shared4.COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("option", { value: c.code, children: [
1174
1370
  c.flag,
1175
1371
  " ",
1176
1372
  c.name
@@ -1187,7 +1383,7 @@ function SplitCardFormInner({
1187
1383
  }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1188
1384
  "input",
1189
1385
  {
1190
- placeholder: (0, import_shared3.getPostalCodeLabel)(selectedCountry),
1386
+ placeholder: (0, import_shared4.getPostalCodeLabel)(selectedCountry),
1191
1387
  autoComplete: "postal-code",
1192
1388
  value: zipCode,
1193
1389
  onChange: (e) => {
@@ -1284,32 +1480,36 @@ function SplitCardFormInner({
1284
1480
  PayPalButtonInner,
1285
1481
  {
1286
1482
  sessionId,
1287
- email,
1483
+ email: resolvedAccount.email,
1288
1484
  billingApiUrl: resolvedBillingApiUrl,
1289
1485
  onTokenizedBody: dispatchTokenizedBody,
1290
1486
  onErrorChange: updateError,
1291
1487
  isProcessing: isSubmitting,
1292
- onButtonClick
1488
+ onButtonClick,
1489
+ onDecline
1293
1490
  }
1294
1491
  ) }) : 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,
1295
1492
  showWallets && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1296
1493
  WalletButtonInner,
1297
1494
  {
1298
1495
  sessionId,
1299
- email,
1496
+ email: resolvedAccount.email,
1300
1497
  billingApiUrl: resolvedBillingApiUrl,
1301
1498
  showApplePay,
1302
1499
  showGooglePay,
1303
1500
  onTokenizedBody: dispatchTokenizedBody,
1304
1501
  onErrorChange: updateError,
1305
- onButtonClick
1502
+ onButtonClick,
1503
+ onDecline
1306
1504
  }
1307
1505
  ) }) : 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,
1308
1506
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1309
1507
  "button",
1310
1508
  {
1311
1509
  type: "button",
1312
- onClick: () => {
1510
+ onClick: async () => {
1511
+ const shouldContinue = await runBeforeCardButtonClick();
1512
+ if (!shouldContinue) return;
1313
1513
  onButtonClick?.("card");
1314
1514
  expandToCard();
1315
1515
  },
@@ -1374,23 +1574,25 @@ function SplitCardFormInner({
1374
1574
  WalletButtonInner,
1375
1575
  {
1376
1576
  sessionId,
1377
- email,
1577
+ email: resolvedAccount.email,
1378
1578
  billingApiUrl: resolvedBillingApiUrl,
1379
1579
  showApplePay,
1380
1580
  showGooglePay,
1381
1581
  onTokenizedBody: dispatchTokenizedBody,
1382
- onErrorChange: updateError
1582
+ onErrorChange: updateError,
1583
+ onDecline
1383
1584
  }
1384
1585
  ) }),
1385
1586
  showPayPal && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1386
1587
  PayPalButtonInner,
1387
1588
  {
1388
1589
  sessionId,
1389
- email,
1590
+ email: resolvedAccount.email,
1390
1591
  billingApiUrl: resolvedBillingApiUrl,
1391
1592
  onTokenizedBody: dispatchTokenizedBody,
1392
1593
  onErrorChange: updateError,
1393
- isProcessing: isSubmitting
1594
+ isProcessing: isSubmitting,
1595
+ onDecline
1394
1596
  }
1395
1597
  ) }),
1396
1598
  (showWallets && stripeInstance || showPayPal && stripeInstance) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
@@ -1422,6 +1624,7 @@ function FloPayCheckout({
1422
1624
  error: errorNode,
1423
1625
  onComplete,
1424
1626
  onError,
1627
+ onDecline,
1425
1628
  showPayPal = true,
1426
1629
  showApplePay = true,
1427
1630
  showGooglePay = true,
@@ -1432,6 +1635,7 @@ function FloPayCheckout({
1432
1635
  cardBackButtonContent,
1433
1636
  cardTitleContent,
1434
1637
  onButtonClick,
1638
+ onBeforeButtonClick,
1435
1639
  enableAVS,
1436
1640
  avsLayout,
1437
1641
  submitLabel,
@@ -1442,7 +1646,7 @@ function FloPayCheckout({
1442
1646
  renderConfirmButton,
1443
1647
  onSessionCompleted
1444
1648
  }) {
1445
- const resolvedBillingUrl = (0, import_shared5.resolveBillingApiUrl)(billingApiUrl);
1649
+ const resolvedBillingUrl = (0, import_shared6.resolveBillingApiUrl)(billingApiUrl);
1446
1650
  const [unified, setUnified] = (0, import_react7.useState)(null);
1447
1651
  const [flopay, setFloPay] = (0, import_react7.useState)(null);
1448
1652
  const flopayRef = (0, import_react7.useRef)(null);
@@ -1453,13 +1657,41 @@ function FloPayCheckout({
1453
1657
  const [currentMode, setCurrentMode] = (0, import_react7.useState)("full");
1454
1658
  const [confirmProcessing, setConfirmProcessing] = (0, import_react7.useState)(false);
1455
1659
  const [modeError, setModeError] = (0, import_react7.useState)(null);
1660
+ const [createSessionPatch, setCreateSessionPatch] = (0, import_react7.useState)(void 0);
1661
+ const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = (0, import_react7.useState)("");
1662
+ const [cardBootstrapPending, setCardBootstrapPending] = (0, import_react7.useState)(false);
1663
+ const [deferredCardOpen, setDeferredCardOpen] = (0, import_react7.useState)(false);
1456
1664
  const autoCheckoutAttempted = (0, import_react7.useRef)(false);
1457
1665
  const onCompleteRef = (0, import_react7.useRef)(onComplete);
1458
1666
  onCompleteRef.current = onComplete;
1459
1667
  const onErrorRef = (0, import_react7.useRef)(onError);
1460
1668
  onErrorRef.current = onError;
1669
+ const onDeclineRef = (0, import_react7.useRef)(onDecline);
1670
+ onDeclineRef.current = onDecline;
1461
1671
  const onSessionCompletedRef = (0, import_react7.useRef)(onSessionCompleted);
1462
1672
  onSessionCompletedRef.current = onSessionCompleted;
1673
+ const baseCreateSessionHash = (0, import_react7.useMemo)(
1674
+ () => createSessionParams ? hashCreateParams(createSessionParams) : "",
1675
+ [createSessionParams]
1676
+ );
1677
+ const activeCreateSessionPatch = (0, import_react7.useMemo)(
1678
+ () => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
1679
+ [createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
1680
+ );
1681
+ const effectiveCreateSession = (0, import_react7.useMemo)(
1682
+ () => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
1683
+ [createSessionParams, activeCreateSessionPatch]
1684
+ );
1685
+ (0, import_react7.useEffect)(() => {
1686
+ setCreateSessionPatch(void 0);
1687
+ setCreateSessionPatchBaseHash(baseCreateSessionHash);
1688
+ }, [baseCreateSessionHash]);
1689
+ const emitDecline = (0, import_react7.useCallback)(
1690
+ (method, input, overrides) => {
1691
+ onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
1692
+ },
1693
+ []
1694
+ );
1463
1695
  const processPaymentForMode = (0, import_react7.useCallback)(
1464
1696
  async (sess) => {
1465
1697
  const baseUrl = resolvedBillingUrl.replace(/\/+$/, "");
@@ -1499,9 +1731,13 @@ function FloPayCheckout({
1499
1731
  // No client secret available
1500
1732
  };
1501
1733
  }
1502
- throw new import_shared5.FloPayError(
1734
+ throw new import_shared6.FloPayError(
1503
1735
  json?.message ?? "Payment failed. Please try again.",
1504
- "api_error"
1736
+ "api_error",
1737
+ {
1738
+ code: json?.code ?? json?.gatewayErrorCode,
1739
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
1740
+ }
1505
1741
  );
1506
1742
  },
1507
1743
  [resolvedBillingUrl, resolvedSessionId]
@@ -1520,6 +1756,9 @@ function FloPayCheckout({
1520
1756
  });
1521
1757
  if (nextActionError) {
1522
1758
  setModeError(nextActionError.message ?? "3DS authentication failed.");
1759
+ emitDecline("card", nextActionError.message ?? "3DS authentication failed.", {
1760
+ code: nextActionError.code
1761
+ });
1523
1762
  return false;
1524
1763
  }
1525
1764
  if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded")) {
@@ -1542,6 +1781,9 @@ function FloPayCheckout({
1542
1781
  });
1543
1782
  if (error) {
1544
1783
  setModeError(error.message ?? "PayPal authorization failed.");
1784
+ emitDecline("paypal", error.message ?? "PayPal authorization failed.", {
1785
+ code: error.code
1786
+ });
1545
1787
  return false;
1546
1788
  }
1547
1789
  onCompleteRef.current?.({ status: "succeeded" });
@@ -1549,16 +1791,24 @@ function FloPayCheckout({
1549
1791
  }
1550
1792
  return false;
1551
1793
  },
1552
- [resolvedBillingUrl, resolvedSessionId]
1794
+ [resolvedBillingUrl, resolvedSessionId, emitDecline]
1553
1795
  );
1554
1796
  const inflightRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
1555
1797
  const initializedHashRef = (0, import_react7.useRef)(null);
1798
+ const deferInlineSessionUntilCardClick = Boolean(
1799
+ effectiveCreateSession && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession.checkoutMode ?? checkoutModeProp ?? "full") === "full"
1800
+ );
1556
1801
  function hashCreateParams(params) {
1557
1802
  const key = JSON.stringify({
1558
1803
  c: params?.clientId,
1804
+ successUrl: params?.successUrl,
1805
+ cancelUrl: params?.cancelUrl,
1559
1806
  i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1560
1807
  s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
1561
- e: params?.account.email,
1808
+ account: params?.account,
1809
+ couponCodes: params?.couponCodes,
1810
+ tagsData: params?.tagsData,
1811
+ utmMetadata: params?.utmMetadata,
1562
1812
  m: params?.checkoutMode ?? "full"
1563
1813
  });
1564
1814
  let h = 0;
@@ -1568,138 +1818,169 @@ function FloPayCheckout({
1568
1818
  return `flopay_session_${Math.abs(h).toString(36)}`;
1569
1819
  }
1570
1820
  const createSessionHash = (0, import_react7.useMemo)(
1571
- () => createSessionParams ? hashCreateParams(createSessionParams) : "",
1572
- // eslint-disable-next-line react-hooks/exhaustive-deps
1573
- [
1574
- createSessionParams?.clientId,
1575
- createSessionParams?.account?.email,
1576
- createSessionParams?.checkoutMode,
1577
- JSON.stringify(createSessionParams?.items),
1578
- JSON.stringify(createSessionParams?.subscriptions)
1579
- ]
1821
+ () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
1822
+ [effectiveCreateSession]
1823
+ );
1824
+ const createSessionParamsRef = (0, import_react7.useRef)(effectiveCreateSession);
1825
+ createSessionParamsRef.current = effectiveCreateSession;
1826
+ (0, import_react7.useEffect)(() => {
1827
+ setResolvedSessionId(sessionIdProp ?? "");
1828
+ }, [sessionIdProp]);
1829
+ async function resolveInlineSession(params, cacheKey) {
1830
+ ensureInlineSessionReady(params);
1831
+ const api = new import_js.PaymentAPI(resolvedBillingUrl);
1832
+ let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1833
+ let realResult = null;
1834
+ if (sid) {
1835
+ try {
1836
+ realResult = await api.getUnifiedCheckoutSession(sid);
1837
+ if (realResult.data.session?.status === "complete") {
1838
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1839
+ sid = null;
1840
+ realResult = null;
1841
+ }
1842
+ } catch {
1843
+ if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1844
+ sid = null;
1845
+ }
1846
+ }
1847
+ if (!sid) {
1848
+ realResult = await api.createAndFetchSession(params);
1849
+ sid = realResult.data.session?.id ?? "";
1850
+ if (sid && typeof window !== "undefined") {
1851
+ window.sessionStorage.setItem(cacheKey, sid);
1852
+ }
1853
+ }
1854
+ return { sid: sid ?? "", result: realResult };
1855
+ }
1856
+ const bootstrapInlineSession = (0, import_react7.useCallback)(
1857
+ async (patch) => {
1858
+ const baseParams = createSessionParamsRef.current;
1859
+ if (!baseParams) {
1860
+ throw new import_shared6.FloPayError("createSession is required to bootstrap checkout.", "validation_error");
1861
+ }
1862
+ const mergedParams = mergeInlineSessionPatch(baseParams, patch);
1863
+ const cacheKey = hashCreateParams(mergedParams);
1864
+ let promise = inflightRef.current.get(cacheKey);
1865
+ if (!promise) {
1866
+ promise = resolveInlineSession(mergedParams, cacheKey);
1867
+ inflightRef.current.set(cacheKey, promise);
1868
+ }
1869
+ let resolved;
1870
+ try {
1871
+ resolved = await promise;
1872
+ } finally {
1873
+ inflightRef.current.delete(cacheKey);
1874
+ }
1875
+ if (patch) {
1876
+ setCreateSessionPatchBaseHash(baseCreateSessionHash);
1877
+ setCreateSessionPatch((prev) => mergeInlineSessionPatches(prev, patch));
1878
+ }
1879
+ const { sid, result: realResult } = resolved;
1880
+ setUnified(realResult);
1881
+ if (realResult.data.session) {
1882
+ setSession(realResult.data.session);
1883
+ }
1884
+ if (sid) {
1885
+ setResolvedSessionId(sid);
1886
+ }
1887
+ let publishableKey;
1888
+ if (realResult.provider === "stripe") {
1889
+ publishableKey = realResult.data.stripe?.publishableKey;
1890
+ }
1891
+ if (!publishableKey) publishableKey = fallbackPublishableKey;
1892
+ if (!publishableKey) {
1893
+ throw new import_shared6.FloPayError(
1894
+ "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1895
+ "validation_error"
1896
+ );
1897
+ }
1898
+ const instance = await (0, import_js.loadFloPay)(publishableKey, {
1899
+ billingApiUrl: resolvedBillingUrl,
1900
+ locale
1901
+ });
1902
+ flopayRef.current = instance;
1903
+ setFloPay(instance);
1904
+ initializedHashRef.current = cacheKey;
1905
+ setIsLoading(false);
1906
+ return resolved;
1907
+ },
1908
+ [fallbackPublishableKey, locale, resolvedBillingUrl]
1580
1909
  );
1581
- const createSessionParamsRef = (0, import_react7.useRef)(createSessionParams);
1582
- createSessionParamsRef.current = createSessionParams;
1910
+ const handleDeferredCardButtonClick = (0, import_react7.useCallback)(async () => {
1911
+ if (cardBootstrapPending) return;
1912
+ setLoadError(null);
1913
+ setCardBootstrapPending(true);
1914
+ try {
1915
+ const beforeClickResult = await onBeforeButtonClick?.({
1916
+ method: "card",
1917
+ createSession: effectiveCreateSession
1918
+ });
1919
+ if (beforeClickResult === false) {
1920
+ setDeferredCardOpen(false);
1921
+ return;
1922
+ }
1923
+ const patch = beforeClickResult && typeof beforeClickResult === "object" ? beforeClickResult : void 0;
1924
+ const baseParams = createSessionParamsRef.current;
1925
+ if (!baseParams) {
1926
+ throw new import_shared6.FloPayError(
1927
+ "createSession is required to bootstrap checkout.",
1928
+ "validation_error"
1929
+ );
1930
+ }
1931
+ ensureInlineSessionReady(mergeInlineSessionPatch(baseParams, patch));
1932
+ setDeferredCardOpen(true);
1933
+ onButtonClick?.("card");
1934
+ await bootstrapInlineSession(patch);
1935
+ } catch (err) {
1936
+ setDeferredCardOpen(false);
1937
+ const floPayErr = err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(
1938
+ err instanceof Error ? err.message : "Failed to start card checkout.",
1939
+ "api_error"
1940
+ );
1941
+ setLoadError(floPayErr);
1942
+ onErrorRef.current?.(floPayErr);
1943
+ } finally {
1944
+ setCardBootstrapPending(false);
1945
+ }
1946
+ }, [
1947
+ bootstrapInlineSession,
1948
+ baseCreateSessionHash,
1949
+ cardBootstrapPending,
1950
+ effectiveCreateSession,
1951
+ onButtonClick,
1952
+ onBeforeButtonClick
1953
+ ]);
1583
1954
  (0, import_react7.useEffect)(() => {
1584
1955
  let cancelled = false;
1585
1956
  setLoadError(null);
1586
1957
  if (createSessionHash) {
1958
+ if (deferInlineSessionUntilCardClick) {
1959
+ if (initializedHashRef.current !== createSessionHash) {
1960
+ setSession(null);
1961
+ setUnified(null);
1962
+ setFloPay(null);
1963
+ setResolvedSessionId("");
1964
+ setDeferredCardOpen(false);
1965
+ flopayRef.current = null;
1966
+ }
1967
+ setCurrentMode(createSessionParamsRef.current?.checkoutMode ?? checkoutModeProp ?? "full");
1968
+ setIsLoading(false);
1969
+ return () => {
1970
+ cancelled = true;
1971
+ };
1972
+ }
1587
1973
  if (initializedHashRef.current === createSessionHash) return;
1588
1974
  const params = createSessionParamsRef.current;
1589
- const totalAmount = [
1590
- ...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
1591
- ...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
1592
- ].reduce((sum, v) => sum + v, 0);
1593
- const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
1594
- const syntheticSession = {
1595
- id: "",
1596
- clientSecret: "",
1597
- mode: "payment",
1598
- amount: Math.round(totalAmount * 100),
1599
- currency,
1600
- status: "open",
1601
- customer: {
1602
- id: params.account.userId,
1603
- email: params.account.email,
1604
- firstName: params.account.firstName,
1605
- lastName: params.account.lastName
1606
- },
1607
- successUrl: params.successUrl,
1608
- cancelUrl: params.cancelUrl,
1609
- checkoutMode: params.checkoutMode ?? "full",
1610
- items: (params.items ?? []).map((i, idx) => ({
1611
- uuid: `synthetic-item-${idx}`,
1612
- checkoutSessionId: "",
1613
- providerItemId: i.providerItemId,
1614
- providerItemName: i.providerItemName ?? i.providerItemId,
1615
- quantity: i.quantity ?? 1,
1616
- totalAmount: i.totalAmount,
1617
- overrideAmount: i.overrideAmount ?? null,
1618
- currency: i.currency ?? currency
1619
- })),
1620
- subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
1621
- uuid: `synthetic-sub-${idx}`,
1622
- checkoutSessionId: "",
1623
- providerPlanId: s.providerPlanId,
1624
- providerPlanName: s.providerPlanName ?? s.providerPlanId,
1625
- quantity: s.quantity ?? 1,
1626
- totalAmount: s.totalAmount,
1627
- overrideAmount: s.overrideAmount ?? null,
1628
- currency: s.currency ?? currency
1629
- }))
1630
- };
1631
- setSession(syntheticSession);
1975
+ setSession(buildSyntheticSession(params, checkoutModeProp));
1632
1976
  setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
1633
1977
  setIsLoading(false);
1634
- const cacheKey = createSessionHash;
1635
- async function resolveSession() {
1636
- const api = new import_js.PaymentAPI(resolvedBillingUrl);
1637
- let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
1638
- let realResult = null;
1639
- if (sid) {
1640
- try {
1641
- realResult = await api.getUnifiedCheckoutSession(sid);
1642
- if (realResult.data.session?.status === "complete") {
1643
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1644
- sid = null;
1645
- realResult = null;
1646
- }
1647
- } catch {
1648
- if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
1649
- sid = null;
1650
- }
1651
- }
1652
- if (!sid) {
1653
- realResult = await api.createAndFetchSession(createSessionParamsRef.current);
1654
- sid = realResult.data.session?.id ?? "";
1655
- if (sid && typeof window !== "undefined") {
1656
- window.sessionStorage.setItem(cacheKey, sid);
1657
- }
1658
- }
1659
- return { sid: sid ?? "", result: realResult };
1660
- }
1661
1978
  (async () => {
1662
1979
  try {
1663
- let promise = inflightRef.current.get(cacheKey);
1664
- if (!promise) {
1665
- promise = resolveSession();
1666
- inflightRef.current.set(cacheKey, promise);
1667
- }
1668
- let resolved;
1669
- try {
1670
- resolved = await promise;
1671
- } finally {
1672
- inflightRef.current.delete(cacheKey);
1673
- }
1674
- if (cancelled) return;
1675
- const { sid, result: realResult } = resolved;
1676
- if (realResult) {
1677
- setUnified(realResult);
1678
- if (realResult.data.session) setSession(realResult.data.session);
1679
- }
1680
- if (sid) {
1681
- setResolvedSessionId(sid);
1682
- }
1683
- let pk;
1684
- if (realResult?.provider === "stripe") {
1685
- pk = realResult.data.stripe?.publishableKey;
1686
- }
1687
- if (!pk) pk = fallbackPublishableKey;
1688
- if (!pk) {
1689
- throw new import_shared5.FloPayError(
1690
- "No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
1691
- "validation_error"
1692
- );
1693
- }
1694
- const instance = await (0, import_js.loadFloPay)(pk, { billingApiUrl: resolvedBillingUrl, locale });
1695
- if (!cancelled) {
1696
- flopayRef.current = instance;
1697
- setFloPay(instance);
1698
- initializedHashRef.current = cacheKey;
1699
- }
1980
+ await bootstrapInlineSession();
1700
1981
  } catch (err) {
1701
1982
  if (cancelled) return;
1702
- const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to create session", "api_error");
1983
+ const floPayErr = err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(err instanceof Error ? err.message : "Failed to create session", "api_error");
1703
1984
  setLoadError(floPayErr);
1704
1985
  }
1705
1986
  })();
@@ -1717,7 +1998,7 @@ function FloPayCheckout({
1717
1998
  const sess = result.data.session ?? null;
1718
1999
  setSession(sess);
1719
2000
  if (!sess) {
1720
- throw new import_shared5.FloPayError("No session data returned", "api_error");
2001
+ throw new import_shared6.FloPayError("No session data returned", "api_error");
1721
2002
  }
1722
2003
  if (sess.status === "complete") {
1723
2004
  setIsLoading(false);
@@ -1756,7 +2037,7 @@ function FloPayCheckout({
1756
2037
  if (!cancelled) setIsLoading(false);
1757
2038
  } catch (err) {
1758
2039
  if (cancelled) return;
1759
- const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
2040
+ const floPayErr = err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
1760
2041
  setLoadError(floPayErr);
1761
2042
  setIsLoading(false);
1762
2043
  }
@@ -1768,7 +2049,7 @@ function FloPayCheckout({
1768
2049
  }
1769
2050
  if (!publishableKey) publishableKey = fallbackPublishableKey;
1770
2051
  if (!publishableKey) {
1771
- throw new import_shared5.FloPayError(
2052
+ throw new import_shared6.FloPayError(
1772
2053
  "No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
1773
2054
  "validation_error"
1774
2055
  );
@@ -1784,7 +2065,7 @@ function FloPayCheckout({
1784
2065
  return () => {
1785
2066
  cancelled = true;
1786
2067
  };
1787
- }, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
2068
+ }, [resolvedSessionId, createSessionHash, checkoutModeProp, deferInlineSessionUntilCardClick, bootstrapInlineSession]);
1788
2069
  const handleConfirmCheckout = (0, import_react7.useCallback)(async () => {
1789
2070
  if (confirmProcessing || !session) return;
1790
2071
  setConfirmProcessing(true);
@@ -1799,17 +2080,18 @@ function FloPayCheckout({
1799
2080
  setCurrentMode("full");
1800
2081
  }
1801
2082
  } catch (err) {
1802
- const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
2083
+ const floPayErr = err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(
1803
2084
  err instanceof Error ? err.message : "Payment failed",
1804
2085
  "api_error"
1805
2086
  );
1806
2087
  setModeError(floPayErr.message);
1807
2088
  onError?.(floPayErr);
2089
+ emitDecline("card", floPayErr);
1808
2090
  setCurrentMode("full");
1809
2091
  } finally {
1810
2092
  setConfirmProcessing(false);
1811
2093
  }
1812
- }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError]);
2094
+ }, [confirmProcessing, session, processPaymentForMode, handleRedirectResult, onError, emitDecline]);
1813
2095
  const providerOptions = (0, import_react7.useMemo)(() => {
1814
2096
  if (!unified || !session) return void 0;
1815
2097
  const opts = {
@@ -1820,7 +2102,7 @@ function FloPayCheckout({
1820
2102
  if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
1821
2103
  opts.clientSecret = unified.data.stripe.clientSecret;
1822
2104
  } else {
1823
- const displayTotal = (0, import_shared5.buildCheckoutDisplayData)(session).total;
2105
+ const displayTotal = (0, import_shared6.buildCheckoutDisplayData)(session).total;
1824
2106
  opts.amount = Math.round(displayTotal * 100) || session.amount;
1825
2107
  opts.currency = session.currency?.toLowerCase();
1826
2108
  }
@@ -1835,6 +2117,8 @@ function FloPayCheckout({
1835
2117
  }),
1836
2118
  [session, isLoading, loadError, currentMode]
1837
2119
  );
2120
+ const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
2121
+ const shouldKeepDeferredInterimVisible = shouldShowInterimButtons && deferInlineSessionUntilCardClick;
1838
2122
  if (isLoading) {
1839
2123
  if (loadingNode) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: loadingNode });
1840
2124
  if (layout === "buttons") {
@@ -1863,7 +2147,7 @@ function FloPayCheckout({
1863
2147
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
1864
2148
  ] });
1865
2149
  }
1866
- if (loadError) {
2150
+ if (loadError && !shouldKeepDeferredInterimVisible) {
1867
2151
  if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: errorNode(loadError) });
1868
2152
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1869
2153
  "div",
@@ -1878,14 +2162,18 @@ function FloPayCheckout({
1878
2162
  }
1879
2163
  );
1880
2164
  }
1881
- if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
2165
+ if (shouldShowInterimButtons) {
1882
2166
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1883
2167
  InterimButtonsView,
1884
2168
  {
1885
2169
  onButtonClick,
1886
- showPayPal,
1887
- showApplePay,
1888
- showGooglePay,
2170
+ onCardButtonClick: deferInlineSessionUntilCardClick ? handleDeferredCardButtonClick : void 0,
2171
+ cardLoading: cardBootstrapPending,
2172
+ cardOpen: deferInlineSessionUntilCardClick ? deferredCardOpen : void 0,
2173
+ errorMessage: shouldKeepDeferredInterimVisible ? loadError?.message ?? null : null,
2174
+ showPayPal: deferInlineSessionUntilCardClick ? false : showPayPal,
2175
+ showApplePay: deferInlineSessionUntilCardClick ? false : showApplePay,
2176
+ showGooglePay: deferInlineSessionUntilCardClick ? false : showGooglePay,
1889
2177
  buttonsTheme,
1890
2178
  buttonsStyles,
1891
2179
  cardButtonContent,
@@ -1967,10 +2255,11 @@ function FloPayCheckout({
1967
2255
  userId: session?.customer?.id,
1968
2256
  firstName: session?.customer?.firstName,
1969
2257
  lastName: session?.customer?.lastName,
1970
- totalAmount: session ? Math.round((0, import_shared5.buildCheckoutDisplayData)(session).total * 100) : 0,
2258
+ totalAmount: session ? Math.round((0, import_shared6.buildCheckoutDisplayData)(session).total * 100) : 0,
1971
2259
  currency: session?.currency?.toLowerCase() ?? "usd",
1972
2260
  onComplete,
1973
2261
  onError,
2262
+ onDecline,
1974
2263
  showPayPal,
1975
2264
  showApplePay,
1976
2265
  showGooglePay,
@@ -1981,10 +2270,11 @@ function FloPayCheckout({
1981
2270
  cardBackButtonContent,
1982
2271
  cardTitleContent,
1983
2272
  onButtonClick,
2273
+ onBeforeButtonClick,
1984
2274
  enableAVS,
1985
2275
  avsLayout,
1986
2276
  country: session?.customer?.country,
1987
- zip: session?.customer?.zip,
2277
+ initialCardOpen: deferredCardOpen,
1988
2278
  submitLabel,
1989
2279
  className
1990
2280
  }
@@ -2017,6 +2307,10 @@ function SessionInjector({
2017
2307
  }
2018
2308
  function InterimButtonsView({
2019
2309
  onButtonClick,
2310
+ onCardButtonClick,
2311
+ cardLoading = false,
2312
+ cardOpen,
2313
+ errorMessage,
2020
2314
  showPayPal,
2021
2315
  showApplePay,
2022
2316
  showGooglePay,
@@ -2027,8 +2321,14 @@ function InterimButtonsView({
2027
2321
  cardTitleContent
2028
2322
  }) {
2029
2323
  const [showCardForm, setShowCardForm] = (0, import_react7.useState)(false);
2324
+ const isCardOpenControlled = typeof cardOpen === "boolean";
2325
+ (0, import_react7.useEffect)(() => {
2326
+ if (isCardOpenControlled) {
2327
+ setShowCardForm(cardOpen);
2328
+ }
2329
+ }, [cardOpen, isCardOpenControlled]);
2030
2330
  const bStyles = (0, import_react7.useMemo)(() => {
2031
- const base = (0, import_shared5.resolveButtonsLayoutTheme)(buttonsTheme);
2331
+ const base = (0, import_shared6.resolveButtonsLayoutTheme)(buttonsTheme);
2032
2332
  if (!stylesOverride) return base;
2033
2333
  return {
2034
2334
  ...base,
@@ -2064,20 +2364,26 @@ function InterimButtonsView({
2064
2364
  "button",
2065
2365
  {
2066
2366
  type: "button",
2067
- onClick: () => setShowCardForm(false),
2367
+ onClick: () => {
2368
+ if (!isCardOpenControlled) {
2369
+ setShowCardForm(false);
2370
+ }
2371
+ },
2068
2372
  "aria-label": "Back to payment methods",
2373
+ disabled: isCardOpenControlled || cardLoading,
2069
2374
  style: {
2070
2375
  display: "inline-flex",
2071
2376
  alignItems: "center",
2072
2377
  gap: hideBackButtonLabel ? 0 : "0.5rem",
2073
2378
  background: "none",
2074
2379
  border: "none",
2075
- cursor: "pointer",
2076
2380
  color: "#4b5563",
2077
2381
  fontSize: "0.85rem",
2078
2382
  fontWeight: 500,
2079
2383
  padding: 0,
2080
2384
  flexShrink: 0,
2385
+ opacity: isCardOpenControlled || cardLoading ? 0.6 : 1,
2386
+ cursor: isCardOpenControlled || cardLoading ? "not-allowed" : "pointer",
2081
2387
  ...bStyles.backButton
2082
2388
  },
2083
2389
  children: [
@@ -2137,10 +2443,16 @@ function InterimButtonsView({
2137
2443
  "button",
2138
2444
  {
2139
2445
  type: "button",
2140
- onClick: () => {
2446
+ onClick: async () => {
2447
+ if (cardLoading) return;
2448
+ if (onCardButtonClick) {
2449
+ await onCardButtonClick();
2450
+ return;
2451
+ }
2141
2452
  onButtonClick?.("card");
2142
2453
  setShowCardForm(true);
2143
2454
  },
2455
+ disabled: cardLoading,
2144
2456
  style: {
2145
2457
  width: "100%",
2146
2458
  padding: "0.9rem 1rem",
@@ -2150,7 +2462,7 @@ function InterimButtonsView({
2150
2462
  borderRadius: "8px",
2151
2463
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
2152
2464
  fontWeight: 600,
2153
- cursor: "pointer",
2465
+ cursor: cardLoading ? "not-allowed" : "pointer",
2154
2466
  display: "flex",
2155
2467
  alignItems: "center",
2156
2468
  justifyContent: "center",
@@ -2158,6 +2470,7 @@ function InterimButtonsView({
2158
2470
  boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
2159
2471
  transition: "transform 0.1s",
2160
2472
  position: "relative",
2473
+ opacity: cardLoading ? 0.6 : 1,
2161
2474
  ...bStyles.cardButton
2162
2475
  },
2163
2476
  onMouseDown: (e) => {
@@ -2169,12 +2482,29 @@ function InterimButtonsView({
2169
2482
  children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardButtonContentSlot, { content: cardButtonContent })
2170
2483
  }
2171
2484
  ),
2485
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
2486
+ margin: "0.25rem 0",
2487
+ padding: "0.625rem 0.875rem",
2488
+ background: "#FEF2F2",
2489
+ border: "1px solid #FECACA",
2490
+ borderRadius: "8px",
2491
+ color: "#991B1B",
2492
+ fontSize: "0.85rem",
2493
+ fontWeight: 600,
2494
+ display: "flex",
2495
+ alignItems: "center",
2496
+ gap: "0.5rem",
2497
+ ...bStyles.errorBanner
2498
+ }, children: [
2499
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2500
+ errorMessage
2501
+ ] }),
2172
2502
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
2173
2503
  ] });
2174
2504
  }
2175
2505
 
2176
2506
  // src/checkout-form.tsx
2177
- var import_shared6 = require("@flopay/shared");
2507
+ var import_shared7 = require("@flopay/shared");
2178
2508
  var import_react8 = require("react");
2179
2509
  var import_jsx_runtime6 = require("react/jsx-runtime");
2180
2510
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
@@ -2190,6 +2520,7 @@ function CheckoutFormInner({
2190
2520
  userId,
2191
2521
  onComplete,
2192
2522
  onError,
2523
+ onDecline,
2193
2524
  onTokenizedBody,
2194
2525
  layout = "auto",
2195
2526
  submitLabel = "Pay",
@@ -2221,6 +2552,12 @@ function CheckoutFormInner({
2221
2552
  },
2222
2553
  [onErrorChange]
2223
2554
  );
2555
+ const emitDecline = (0, import_react8.useCallback)(
2556
+ (input, overrides) => {
2557
+ onDecline?.(buildDeclineEvent("card", input, overrides));
2558
+ },
2559
+ [onDecline]
2560
+ );
2224
2561
  const processPaymentInternal = (0, import_react8.useCallback)(
2225
2562
  async (tokenizedBody) => {
2226
2563
  setProcessing(true);
@@ -2267,6 +2604,7 @@ function CheckoutFormInner({
2267
2604
  if (result.error) {
2268
2605
  updateError(result.error.message);
2269
2606
  onError?.(result.error);
2607
+ emitDecline(result.error);
2270
2608
  return;
2271
2609
  }
2272
2610
  if (result.status === "succeeded" || result.status === "processing") {
@@ -2301,13 +2639,17 @@ function CheckoutFormInner({
2301
2639
  }
2302
2640
  const errorMessage = json?.message ?? "Payment failed. Please try again.";
2303
2641
  updateError(errorMessage);
2642
+ emitDecline(errorMessage, {
2643
+ code: json?.code,
2644
+ declineCode: json?.declineCode ?? json?.gatewayDeclineReason
2645
+ });
2304
2646
  } catch (err) {
2305
2647
  updateError(err instanceof Error ? err.message : "An unexpected error occurred");
2306
2648
  } finally {
2307
2649
  setProcessing(false);
2308
2650
  }
2309
2651
  },
2310
- [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError]
2652
+ [baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError, emitDecline]
2311
2653
  );
2312
2654
  const dispatchTokenizedBody = (0, import_react8.useCallback)(
2313
2655
  (tokenizedBody) => {
@@ -2331,6 +2673,7 @@ function CheckoutFormInner({
2331
2673
  if (result.error) {
2332
2674
  updateError(result.error.message);
2333
2675
  onError?.(result.error);
2676
+ emitDecline(result.error);
2334
2677
  } else if (result.status === "succeeded" || result.status === "processing") {
2335
2678
  dispatchTokenizedBody({
2336
2679
  id: result.paymentIntentId,
@@ -2344,7 +2687,7 @@ function CheckoutFormInner({
2344
2687
  setIs3DSActive(false);
2345
2688
  }
2346
2689
  }
2347
- }), [flopay, dispatchTokenizedBody, onError, updateError]);
2690
+ }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
2348
2691
  (0, import_react8.useEffect)(() => {
2349
2692
  if (typeof window === "undefined") return;
2350
2693
  const stored = localStorage.getItem(WALLET_RESUME_KEY2);
@@ -2382,7 +2725,7 @@ function CheckoutFormInner({
2382
2725
  return;
2383
2726
  }
2384
2727
  if (!sessionId || !email) {
2385
- throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
2728
+ throw new import_shared7.FloPayError("Missing sessionId or email", "validation_error");
2386
2729
  }
2387
2730
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2388
2731
  method: "POST",
@@ -2394,16 +2737,18 @@ function CheckoutFormInner({
2394
2737
  isPaypal: false
2395
2738
  })
2396
2739
  });
2397
- if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
2740
+ if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
2398
2741
  const intentJson = await intentResponse.json();
2399
2742
  const intentClientSecret = intentJson.data?.id;
2400
- if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
2743
+ if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in payment intent response", "api_error");
2401
2744
  const confirmResult = await flopay.confirmCardPayment({
2402
2745
  clientSecret: intentClientSecret,
2403
2746
  paymentMethodId: pmResult.paymentMethodId
2404
2747
  });
2405
2748
  if (confirmResult.error) {
2406
2749
  updateError(confirmResult.error.message);
2750
+ onError?.(confirmResult.error);
2751
+ emitDecline(confirmResult.error);
2407
2752
  return;
2408
2753
  }
2409
2754
  dispatchTokenizedBody({
@@ -2420,7 +2765,7 @@ function CheckoutFormInner({
2420
2765
  }
2421
2766
  }
2422
2767
  },
2423
- [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError]
2768
+ [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
2424
2769
  );
2425
2770
  const isReady = flopay !== null && elements !== null;
2426
2771
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
@@ -2452,7 +2797,7 @@ function CheckoutFormInner({
2452
2797
  }
2453
2798
 
2454
2799
  // src/paypal-button.tsx
2455
- var import_shared7 = require("@flopay/shared");
2800
+ var import_shared8 = require("@flopay/shared");
2456
2801
  var import_react9 = require("react");
2457
2802
  var import_jsx_runtime7 = require("react/jsx-runtime");
2458
2803
  function PayPalButton({
@@ -2572,7 +2917,7 @@ function PayPalButton({
2572
2917
  setSubmitting(true);
2573
2918
  onErrorChange?.(null);
2574
2919
  if (!sessionId || !email) {
2575
- throw new import_shared7.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
2920
+ throw new import_shared8.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
2576
2921
  }
2577
2922
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2578
2923
  method: "POST",
@@ -2584,10 +2929,10 @@ function PayPalButton({
2584
2929
  isPaypal: "true"
2585
2930
  })
2586
2931
  });
2587
- if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
2932
+ if (!intentResponse.ok) throw new import_shared8.FloPayError("Failed to create payment intent", "api_error");
2588
2933
  const intentJson = await intentResponse.json();
2589
2934
  const intentClientSecret = intentJson.data?.id;
2590
- if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in response", "api_error");
2935
+ if (!intentClientSecret) throw new import_shared8.FloPayError("No client_secret in response", "api_error");
2591
2936
  const result = await flopay.confirmPayment({
2592
2937
  clientSecret: intentClientSecret,
2593
2938
  returnUrl: window.location.href