@flopay/react 1.0.3 → 1.1.3

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
@@ -100,9 +100,9 @@ function FloPayProvider({
100
100
  }
101
101
 
102
102
  // src/flopay-checkout.tsx
103
- import React6, { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
104
- import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
105
- import { SDK_VERSION, FloPayError as FloPayError4, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
103
+ import React7, { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
104
+ import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
105
+ import { SDK_VERSION, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
106
106
 
107
107
  // src/card-button-content.tsx
108
108
  import "react";
@@ -248,8 +248,8 @@ import {
248
248
  isAVSFieldVisible,
249
249
  getStateFromPostalCode
250
250
  } from "@flopay/shared";
251
- import { PaymentAPI } from "@flopay/js";
252
- import { forwardRef, useCallback, useContext as useContext3, useEffect as useEffect3, useImperativeHandle, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
251
+ import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
252
+ import { forwardRef, useCallback, useContext as useContext3, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
253
253
 
254
254
  // src/hooks.ts
255
255
  import { useContext as useContext2 } from "react";
@@ -457,15 +457,37 @@ function mergeInlineSessionPatch(params, patch) {
457
457
  };
458
458
  }
459
459
  function buildSyntheticSession(params, checkoutModeOverride) {
460
- const totalAmount = [
461
- ...(params.items ?? []).map((item) => item.overrideAmount ?? item.totalAmount ?? 0),
462
- ...(params.subscriptions ?? []).map((subscription) => subscription.overrideAmount ?? subscription.totalAmount ?? 0)
463
- ].reduce((sum, value) => sum + value, 0);
464
- const currency = params.currency ?? params.items?.find((i) => i.currency)?.currency ?? params.subscriptions?.find((s) => s.currency)?.currency ?? "USD";
460
+ const inputProducts = params.products ?? [
461
+ ...(params.subscriptions ?? []).map((s) => ({
462
+ type: "subscription",
463
+ code: s.code ?? s.providerPlanId,
464
+ name: s.subscriptionName ?? s.providerPlanName ?? s.code ?? s.providerPlanId ?? null,
465
+ quantity: s.quantity ?? 1,
466
+ totalAmount: s.totalAmount,
467
+ overrideAmount: s.overrideAmount,
468
+ currency: s.currency,
469
+ metadata: s.metadata
470
+ })),
471
+ ...(params.items ?? []).map((i) => ({
472
+ type: "item",
473
+ code: i.code ?? i.providerItemId,
474
+ name: i.itemName ?? i.providerItemName ?? i.code ?? i.providerItemId ?? null,
475
+ quantity: i.quantity ?? 1,
476
+ totalAmount: i.totalAmount,
477
+ overrideAmount: i.overrideAmount,
478
+ currency: i.currency,
479
+ metadata: i.metadata
480
+ }))
481
+ ];
482
+ const totalAmount = inputProducts.reduce(
483
+ (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),
484
+ 0
485
+ );
486
+ const currency = params.currency ?? inputProducts.find((p) => p.currency)?.currency ?? "USD";
465
487
  return {
466
488
  id: "",
467
489
  clientSecret: "",
468
- mode: "payment",
490
+ mode: inputProducts.some((p) => p.type === "subscription") ? "subscription" : "payment",
469
491
  amount: Math.round(totalAmount * 100),
470
492
  currency,
471
493
  status: "open",
@@ -483,40 +505,18 @@ function buildSyntheticSession(params, checkoutModeOverride) {
483
505
  successUrl: params.successUrl,
484
506
  cancelUrl: params.cancelUrl,
485
507
  checkoutMode: checkoutModeOverride ?? params.checkoutMode ?? "full",
486
- items: (params.items ?? []).map((item, idx) => {
487
- const code = item.code ?? item.providerItemId;
488
- const itemName = item.itemName ?? item.providerItemName ?? code;
489
- return {
490
- uuid: `synthetic-item-${idx}`,
491
- checkoutSessionId: "",
492
- code,
493
- providerItemId: code,
494
- itemName,
495
- providerItemName: itemName,
496
- quantity: item.quantity ?? 1,
497
- totalAmount: item.totalAmount,
498
- overrideAmount: item.overrideAmount ?? null,
499
- currency: item.currency ?? currency,
500
- metadata: item.metadata ?? null
501
- };
502
- }),
503
- subscriptions: (params.subscriptions ?? []).map((subscription, idx) => {
504
- const code = subscription.code ?? subscription.providerPlanId;
505
- const subscriptionName = subscription.subscriptionName ?? subscription.providerPlanName ?? code;
506
- return {
507
- uuid: `synthetic-sub-${idx}`,
508
- checkoutSessionId: "",
509
- code,
510
- providerPlanId: code,
511
- subscriptionName,
512
- providerPlanName: subscriptionName,
513
- quantity: subscription.quantity ?? 1,
514
- totalAmount: subscription.totalAmount,
515
- overrideAmount: subscription.overrideAmount ?? null,
516
- currency: subscription.currency ?? currency,
517
- metadata: subscription.metadata ?? null
518
- };
519
- })
508
+ products: inputProducts.map((p, idx) => ({
509
+ uuid: `synthetic-${p.type}-${idx}`,
510
+ checkoutSessionId: "",
511
+ type: p.type,
512
+ code: p.code,
513
+ name: p.name ?? null,
514
+ quantity: p.quantity ?? 1,
515
+ totalAmount: p.totalAmount,
516
+ overrideAmount: p.overrideAmount ?? null,
517
+ currency: p.currency ?? currency,
518
+ metadata: p.metadata ?? null
519
+ }))
520
520
  };
521
521
  }
522
522
  function mergeAccountPatch(base, patch) {
@@ -544,9 +544,25 @@ function readString(payload, key) {
544
544
  const value = payload?.[key];
545
545
  return typeof value === "string" && value.trim() ? value : void 0;
546
546
  }
547
+ var FRIENDLY_MESSAGE_OVERRIDES = [
548
+ {
549
+ match: /^paypal authorization required\.?$/i,
550
+ replacement: "For your additional security, please re-authenticate this payment via PayPal."
551
+ }
552
+ ];
553
+ function applyFriendlyMessageOverride(message) {
554
+ if (typeof message !== "string") return message ?? void 0;
555
+ const trimmed = message.trim();
556
+ if (!trimmed) return message;
557
+ for (const { match, replacement } of FRIENDLY_MESSAGE_OVERRIDES) {
558
+ if (match.test(trimmed)) return replacement;
559
+ }
560
+ return message;
561
+ }
547
562
  function buildFloPayApiError(payload, fallbackMessage) {
548
563
  const nestedError = isRecord(payload?.error) ? payload.error : null;
549
- const message = readString(payload, "message") ?? readString(nestedError, "message") ?? fallbackMessage;
564
+ const rawMessage = readString(payload, "message") ?? readString(nestedError, "message") ?? fallbackMessage;
565
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
550
566
  const code = readString(payload, "code") ?? readString(payload, "gatewayErrorCode") ?? readString(nestedError, "code");
551
567
  const declineCode = readString(payload, "declineCode") ?? readString(payload, "gatewayDeclineReason") ?? readString(payload, "decline_code") ?? readString(nestedError, "decline_code");
552
568
  return new FloPayError(message, "api_error", {
@@ -651,11 +667,565 @@ function isInAppBrowser(userAgent) {
651
667
  return false;
652
668
  }
653
669
 
670
+ // src/direct-paypal-button.tsx
671
+ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
672
+ import { loadScript } from "@paypal/paypal-js";
673
+ import { PaymentAPI } from "@flopay/js";
674
+ import { FloPayError as FloPayError2, normalizeGatewayEnvironment } from "@flopay/shared";
675
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
676
+ var DEFAULT_BUTTON_HEIGHT = 45;
677
+ function DirectPayPalButton({
678
+ sessionId,
679
+ billingApiUrl,
680
+ email,
681
+ clientId,
682
+ environment,
683
+ currency,
684
+ isSubscription,
685
+ onTokenizedBody,
686
+ onComplete,
687
+ onErrorChange,
688
+ onDecline,
689
+ isProcessing = false,
690
+ onLoadStateChange,
691
+ onButtonClick,
692
+ runBeforeButtonClick,
693
+ session,
694
+ existingOrderId,
695
+ debug = false
696
+ }) {
697
+ const containerRef = useRef2(null);
698
+ const [ready, setReady] = useState2(false);
699
+ const [failed, setFailed] = useState2(false);
700
+ const [submitting, setSubmitting] = useState2(false);
701
+ const baseUrl = useMemo2(() => billingApiUrl.replace(/\/+$/, ""), [billingApiUrl]);
702
+ const [debugLines, setDebugLines] = useState2([]);
703
+ const appendDebug = (line) => {
704
+ if (!debug) return;
705
+ setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
706
+ };
707
+ const onTokenizedBodyRef = useRef2(onTokenizedBody);
708
+ const onCompleteRef = useRef2(onComplete);
709
+ const onErrorChangeRef = useRef2(onErrorChange);
710
+ const onDeclineRef = useRef2(onDecline);
711
+ const onButtonClickRef = useRef2(onButtonClick);
712
+ const onLoadStateChangeRef = useRef2(onLoadStateChange);
713
+ const runBeforeButtonClickRef = useRef2(runBeforeButtonClick);
714
+ const sessionRef = useRef2(session);
715
+ const emailRef = useRef2(email);
716
+ const beforeClickRef = useRef2(null);
717
+ useEffect3(() => {
718
+ onTokenizedBodyRef.current = onTokenizedBody;
719
+ }, [onTokenizedBody]);
720
+ useEffect3(() => {
721
+ onCompleteRef.current = onComplete;
722
+ }, [onComplete]);
723
+ useEffect3(() => {
724
+ onErrorChangeRef.current = onErrorChange;
725
+ }, [onErrorChange]);
726
+ useEffect3(() => {
727
+ onDeclineRef.current = onDecline;
728
+ }, [onDecline]);
729
+ useEffect3(() => {
730
+ onButtonClickRef.current = onButtonClick;
731
+ }, [onButtonClick]);
732
+ useEffect3(() => {
733
+ onLoadStateChangeRef.current = onLoadStateChange;
734
+ }, [onLoadStateChange]);
735
+ useEffect3(() => {
736
+ runBeforeButtonClickRef.current = runBeforeButtonClick;
737
+ }, [runBeforeButtonClick]);
738
+ useEffect3(() => {
739
+ sessionRef.current = session;
740
+ }, [session]);
741
+ useEffect3(() => {
742
+ emailRef.current = email;
743
+ }, [email]);
744
+ useEffect3(() => {
745
+ onLoadStateChangeRef.current?.(ready && !failed);
746
+ }, [ready, failed]);
747
+ const normalizedEnv = normalizeGatewayEnvironment(environment);
748
+ useEffect3(() => {
749
+ const maskedClient = clientId ? `${clientId.slice(0, 6)}\u2026(len ${clientId.length})` : "(empty)";
750
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "(no navigator)";
751
+ appendDebug(`mount clientId=${maskedClient} env=${environment ?? "(unset)"}\u2192${normalizedEnv ?? "live"} ccy=${currency} sub=${isSubscription}`);
752
+ appendDebug(`ua=${ua.slice(0, 80)}${ua.length > 80 ? "\u2026" : ""}`);
753
+ if (!clientId) {
754
+ appendDebug("FAIL: clientId empty \u2014 gateway misconfigured");
755
+ setFailed(true);
756
+ onErrorChangeRef.current?.("Direct PayPal gateway is misconfigured.");
757
+ return;
758
+ }
759
+ if (!containerRef.current) {
760
+ appendDebug("FAIL: containerRef not attached");
761
+ return;
762
+ }
763
+ let cancelled = false;
764
+ let activeButtons = null;
765
+ let rendered = false;
766
+ const container = containerRef.current;
767
+ let containerObserver = null;
768
+ let perfObserver = null;
769
+ const watchdogTimers = [];
770
+ const formatDims = (el) => {
771
+ if (!el || typeof el.getBoundingClientRect !== "function") return "(no rect)";
772
+ const rect = el.getBoundingClientRect();
773
+ return `${Math.round(rect.width)}\xD7${Math.round(rect.height)}`;
774
+ };
775
+ const classifyIframeSrc = (raw) => {
776
+ if (!raw) return "(empty)";
777
+ try {
778
+ const url = new URL(raw, typeof window !== "undefined" ? window.location.href : "https://localhost");
779
+ const path = url.pathname.toLowerCase();
780
+ if (path.includes("checkcaptcha") || path.includes("captcha")) return `CAPTCHA(${url.host}${path})`;
781
+ if (path.includes("risk") || path.includes("challenge")) return `RISK(${url.host}${path})`;
782
+ if (path.includes("smart/buttons")) return `smart-buttons(${url.host})`;
783
+ return `${url.host}${path}`.slice(0, 100);
784
+ } catch {
785
+ return raw.slice(0, 80);
786
+ }
787
+ };
788
+ const describeIframe = (frame) => {
789
+ const src = frame.getAttribute("src");
790
+ const srcdoc = frame.getAttribute("srcdoc");
791
+ const name = frame.getAttribute("name");
792
+ const sandbox = frame.getAttribute("sandbox");
793
+ const parts = [];
794
+ if (src) parts.push(`src=${classifyIframeSrc(src)}`);
795
+ if (srcdoc) parts.push(`srcdoc[${srcdoc.length}ch]`);
796
+ if (!src && !srcdoc) parts.push("src=(empty) srcdoc=(empty)");
797
+ if (name) parts.push(`name=${name.slice(0, 40)}`);
798
+ if (sandbox !== null) parts.push(`sandbox="${sandbox.slice(0, 40)}"`);
799
+ return parts.join(" ");
800
+ };
801
+ const inspectFrameContent = (frame) => {
802
+ if (!(frame instanceof HTMLIFrameElement)) return "";
803
+ try {
804
+ const cd = frame.contentDocument;
805
+ if (!cd) return "cd=null";
806
+ const bodyChildren = cd.body?.children.length ?? 0;
807
+ const bodyLen = cd.body?.innerHTML.length ?? 0;
808
+ const headLen = cd.head?.innerHTML.length ?? 0;
809
+ return `cd=same-origin readyState=${cd.readyState} body[${bodyChildren}children,${bodyLen}ch] head[${headLen}ch]`;
810
+ } catch (err) {
811
+ return `cd=cross-origin(${err.message.slice(0, 30)})`;
812
+ }
813
+ };
814
+ const errorMessages = [];
815
+ const onWindowError = (ev) => {
816
+ const msg = ev.message ?? String(ev.error ?? "(no message)");
817
+ if (msg && (msg.toLowerCase().includes("paypal") || msg.toLowerCase().includes("zoid") || msg.toLowerCase().includes("postrobot") || msg.toLowerCase().includes("storage"))) {
818
+ appendDebug(`window:error ${msg.slice(0, 140)}`);
819
+ errorMessages.push(msg);
820
+ }
821
+ };
822
+ const onUnhandledRejection = (ev) => {
823
+ const reason = ev.reason instanceof Error ? ev.reason.message : String(ev.reason ?? "(no reason)");
824
+ if (reason && (reason.toLowerCase().includes("paypal") || reason.toLowerCase().includes("zoid") || reason.toLowerCase().includes("postrobot") || reason.toLowerCase().includes("storage"))) {
825
+ appendDebug(`window:rejection ${reason.slice(0, 140)}`);
826
+ errorMessages.push(reason);
827
+ }
828
+ };
829
+ if (debug && typeof window !== "undefined") {
830
+ window.addEventListener("error", onWindowError);
831
+ window.addEventListener("unhandledrejection", onUnhandledRejection);
832
+ }
833
+ const paypalRequestCount = { value: 0 };
834
+ if (debug && typeof PerformanceObserver !== "undefined") {
835
+ try {
836
+ perfObserver = new PerformanceObserver((list) => {
837
+ for (const entry of list.getEntries()) {
838
+ if (!entry.name.toLowerCase().includes("paypal")) continue;
839
+ paypalRequestCount.value += 1;
840
+ const dur = Math.round(entry.duration);
841
+ appendDebug(`net ${dur}ms ${entry.name.slice(0, 90)}`);
842
+ }
843
+ });
844
+ perfObserver.observe({ type: "resource", buffered: true });
845
+ } catch {
846
+ }
847
+ }
848
+ const stopDiagnostics = () => {
849
+ containerObserver?.disconnect();
850
+ containerObserver = null;
851
+ perfObserver?.disconnect();
852
+ perfObserver = null;
853
+ while (watchdogTimers.length) clearTimeout(watchdogTimers.pop());
854
+ if (debug && typeof window !== "undefined") {
855
+ window.removeEventListener("error", onWindowError);
856
+ window.removeEventListener("unhandledrejection", onUnhandledRejection);
857
+ }
858
+ };
859
+ const isZoidLifecycleMessage = (message) => {
860
+ if (!message) return false;
861
+ const lower = message.toLowerCase();
862
+ return lower.includes("zoid destroyed") || lower.includes("destroyed all components") || lower.includes("window closed") || lower.includes("detected container element removed");
863
+ };
864
+ const forwardError = (message) => {
865
+ if (cancelled) return;
866
+ if (isZoidLifecycleMessage(message)) return;
867
+ const friendly = applyFriendlyMessageOverride(message) ?? message;
868
+ onErrorChangeRef.current?.(friendly);
869
+ };
870
+ const markRenderFailed = (message) => {
871
+ if (cancelled) return;
872
+ if (isZoidLifecycleMessage(message)) {
873
+ appendDebug(`markRenderFailed:skip-zoid msg=${message.slice(0, 80)}`);
874
+ return;
875
+ }
876
+ setFailed(true);
877
+ forwardError(message);
878
+ };
879
+ setFailed(false);
880
+ const dispatchTokenizedBody = async (body) => {
881
+ const prepared = beforeClickRef.current;
882
+ const effectiveSessionId = prepared?.sessionId ?? sessionId;
883
+ if (onTokenizedBodyRef.current) {
884
+ onTokenizedBodyRef.current(body, {
885
+ sessionId: effectiveSessionId,
886
+ accountPatch: prepared?.accountPatch
887
+ });
888
+ return;
889
+ }
890
+ try {
891
+ const currentSession = sessionRef.current;
892
+ const currentEmail = prepared?.accountPatch?.email ?? emailRef.current;
893
+ const effectiveUserId = prepared?.accountPatch?.userId ?? currentSession?.customer?.id ?? currentSession?.accountData?.userId ?? "";
894
+ const api = new PaymentAPI(baseUrl);
895
+ const response = await api.processPayment(
896
+ effectiveUserId,
897
+ {
898
+ sessionId: effectiveSessionId,
899
+ tokenizedData: body,
900
+ accountData: {
901
+ userId: effectiveUserId,
902
+ email: currentEmail ?? currentSession?.customer?.email ?? "",
903
+ firstName: prepared?.accountPatch?.firstName ?? currentSession?.customer?.firstName ?? currentSession?.accountData?.firstName ?? "",
904
+ lastName: prepared?.accountPatch?.lastName ?? currentSession?.customer?.lastName ?? currentSession?.accountData?.lastName ?? "",
905
+ country: prepared?.accountPatch?.country ?? currentSession?.customer?.country ?? currentSession?.accountData?.country ?? void 0,
906
+ zip: prepared?.accountPatch?.zip ?? currentSession?.customer?.zip ?? currentSession?.accountData?.zip ?? void 0
907
+ }
908
+ }
909
+ );
910
+ if (response.ok) {
911
+ onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" });
912
+ return;
913
+ }
914
+ const json = await response.json().catch(() => null);
915
+ const rawMessage = json?.["message"] ?? "PayPal payment failed.";
916
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
917
+ forwardError(message);
918
+ onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
919
+ code: json?.["code"],
920
+ declineCode: json?.["declineCode"]
921
+ }));
922
+ } catch (err) {
923
+ const rawMessage = err instanceof Error ? err.message : "PayPal payment failed.";
924
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
925
+ forwardError(message);
926
+ onDeclineRef.current?.(buildDeclineEvent("paypal", message));
927
+ }
928
+ };
929
+ const createPaypalIntent = async (fallbackMessage) => {
930
+ const prepared = beforeClickRef.current;
931
+ const effectiveSessionId = prepared?.sessionId ?? sessionId;
932
+ const effectiveEmail = prepared?.accountPatch?.email ?? emailRef.current;
933
+ const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
934
+ method: "POST",
935
+ headers: { "Content-Type": "application/json" },
936
+ body: JSON.stringify({
937
+ sessionId: effectiveSessionId,
938
+ email: effectiveEmail,
939
+ paymentMethodType: "paypal",
940
+ isPaypal: "true"
941
+ })
942
+ });
943
+ const json = await response.json().catch(() => null);
944
+ if (!response.ok) {
945
+ throw new Error(json?.message ?? fallbackMessage);
946
+ }
947
+ const id = json?.data?.id;
948
+ if (!id) {
949
+ throw new Error(fallbackMessage);
950
+ }
951
+ return id;
952
+ };
953
+ appendDebug("loadScript:scheduled (deferred 1 tick)");
954
+ let loadPromise = null;
955
+ const startTimer = setTimeout(() => {
956
+ if (cancelled) {
957
+ appendDebug("loadScript:skipped (cancelled before defer ran)");
958
+ return;
959
+ }
960
+ appendDebug("loadScript:start");
961
+ const paypalSdkEnv = normalizedEnv === "live" ? "production" : normalizedEnv;
962
+ loadPromise = loadScript({
963
+ clientId,
964
+ currency,
965
+ // Subscriptions need the `subscription` vault intent; one-time payments
966
+ // use a standard order capture.
967
+ intent: isSubscription ? "subscription" : "capture",
968
+ vault: isSubscription ? true : void 0,
969
+ // Tell PayPal which environment the clientId belongs to. Without
970
+ // this, PayPal defaults to live endpoints — and a sandbox clientId
971
+ // sent to live silently stalls in zoid's prerender forever (no
972
+ // error, no rejection).
973
+ ...paypalSdkEnv ? { environment: paypalSdkEnv } : {},
974
+ // Namespace the sandbox SDK so it can coexist with a production SDK on
975
+ // the same page without clobbering `window.paypal`.
976
+ ...paypalSdkEnv === "sandbox" ? { dataNamespace: "paypal_sandbox" } : {}
977
+ });
978
+ loadPromise.then((paypal) => {
979
+ appendDebug(`loadScript:resolved cancelled=${cancelled} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
980
+ if (cancelled || !paypal?.Buttons) {
981
+ if (!cancelled && !paypal?.Buttons) appendDebug("FAIL: namespace missing Buttons factory");
982
+ return;
983
+ }
984
+ const handleApprove = async (data) => {
985
+ try {
986
+ setSubmitting(true);
987
+ onErrorChangeRef.current?.(null);
988
+ const token = data.subscriptionID ?? data.orderID ?? "";
989
+ if (!token) {
990
+ throw new FloPayError2(
991
+ "PayPal did not return an approval token.",
992
+ "api_error",
993
+ { code: "paypal_missing_token" }
994
+ );
995
+ }
996
+ await dispatchTokenizedBody({
997
+ id: token,
998
+ isPaypal: true
999
+ });
1000
+ } catch (err) {
1001
+ forwardError(err instanceof Error ? err.message : "PayPal capture failed.");
1002
+ } finally {
1003
+ setSubmitting(false);
1004
+ }
1005
+ };
1006
+ const buttons = paypal.Buttons({
1007
+ style: { layout: "horizontal", height: DEFAULT_BUTTON_HEIGHT, tagline: false },
1008
+ // PayPal's SDK awaits a Promise returned from `onClick` and aborts
1009
+ // the create-order/create-subscription step when `actions.reject()`
1010
+ // is invoked. Run the consumer's `runBeforeButtonClick` here so
1011
+ // inline-session/account patches land before the order is created,
1012
+ // matching the Stripe-rendered PayPal flow.
1013
+ onClick: async (_data, actions) => {
1014
+ const runner = runBeforeButtonClickRef.current;
1015
+ if (runner) {
1016
+ try {
1017
+ const beforeClick = await runner("paypal");
1018
+ if (!beforeClick.proceed) {
1019
+ beforeClickRef.current = null;
1020
+ await actions.reject();
1021
+ return;
1022
+ }
1023
+ beforeClickRef.current = {
1024
+ sessionId: beforeClick.sessionId,
1025
+ accountPatch: beforeClick.accountPatch
1026
+ };
1027
+ } catch (err) {
1028
+ appendDebug(`onClick:runBeforeButtonClick rejected msg=${(err instanceof Error ? err.message : String(err)).slice(0, 120)}`);
1029
+ beforeClickRef.current = null;
1030
+ await actions.reject();
1031
+ return;
1032
+ }
1033
+ } else {
1034
+ beforeClickRef.current = null;
1035
+ }
1036
+ onButtonClickRef.current?.("paypal");
1037
+ await actions.resolve();
1038
+ },
1039
+ // When the SDK is already holding an order/subscription id from a
1040
+ // prior backend round-trip (the `paypal_direct_required` retry
1041
+ // path), feed it straight to PayPal instead of creating a new one.
1042
+ // Otherwise fall back to the normal create-intent call.
1043
+ createOrder: isSubscription ? void 0 : existingOrderId ? () => Promise.resolve(existingOrderId) : () => createPaypalIntent("Failed to create PayPal order."),
1044
+ createSubscription: isSubscription ? existingOrderId ? () => Promise.resolve(existingOrderId) : () => createPaypalIntent("Failed to create PayPal subscription.") : void 0,
1045
+ onApprove: handleApprove,
1046
+ onCancel: () => {
1047
+ beforeClickRef.current = null;
1048
+ onDeclineRef.current?.(buildDeclineEvent("paypal", "PayPal checkout was cancelled."));
1049
+ },
1050
+ onError: (err) => {
1051
+ const message = err instanceof Error ? err.message : "PayPal failed to render.";
1052
+ appendDebug(`onError rendered=${rendered} msg=${message.slice(0, 120)}`);
1053
+ if (rendered) {
1054
+ forwardError(message);
1055
+ onDeclineRef.current?.(buildDeclineEvent("paypal", message));
1056
+ return;
1057
+ }
1058
+ markRenderFailed(message);
1059
+ }
1060
+ });
1061
+ const eligible = buttons.isEligible();
1062
+ appendDebug(`isEligible=${eligible}`);
1063
+ if (!eligible) {
1064
+ setReady(false);
1065
+ markRenderFailed(
1066
+ "PayPal buttons are not eligible to render in this context (paypal_ineligible)."
1067
+ );
1068
+ return;
1069
+ }
1070
+ const typedButtons = buttons;
1071
+ if (debug) {
1072
+ appendDebug(`container:dims ${formatDims(container)} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"}`);
1073
+ }
1074
+ if (debug && typeof MutationObserver !== "undefined") {
1075
+ containerObserver = new MutationObserver((mutations) => {
1076
+ for (const mutation of mutations) {
1077
+ if (mutation.type === "childList") {
1078
+ mutation.addedNodes.forEach((node) => {
1079
+ if (!(node instanceof Element)) return;
1080
+ const tag = node.tagName.toLowerCase();
1081
+ const title = (node.getAttribute("title") ?? "").slice(0, 40);
1082
+ const detail = tag === "iframe" ? ` ${describeIframe(node)}` : "";
1083
+ appendDebug(`child+ ${tag}${title ? ` title="${title}"` : ""}${detail} dims=${formatDims(node)}`);
1084
+ });
1085
+ } else if (mutation.type === "attributes" && mutation.target instanceof Element) {
1086
+ const target = mutation.target;
1087
+ if (target.tagName.toLowerCase() !== "iframe") continue;
1088
+ const attr = mutation.attributeName;
1089
+ if (attr === "src" || attr === "srcdoc") {
1090
+ appendDebug(`attr~ iframe ${attr}=${attr === "srcdoc" ? `[${(target.getAttribute("srcdoc") ?? "").length}ch]` : classifyIframeSrc(target.getAttribute("src"))} dims=${formatDims(target)}`);
1091
+ }
1092
+ }
1093
+ }
1094
+ });
1095
+ containerObserver.observe(container, {
1096
+ childList: true,
1097
+ subtree: true,
1098
+ attributes: true,
1099
+ attributeFilter: ["src", "srcdoc"]
1100
+ });
1101
+ }
1102
+ const snapshotContainer = (when) => {
1103
+ if (cancelled || rendered) return;
1104
+ const iframes = container.querySelectorAll("iframe");
1105
+ const hasStorageAccess = typeof document !== "undefined" && "hasStorageAccess" in document;
1106
+ appendDebug(`watchdog:${when} container=${formatDims(container)} children=${container.childElementCount} iframes=${iframes.length} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"} cookies=${typeof navigator !== "undefined" ? navigator.cookieEnabled : "?"} hasStorageAccessApi=${hasStorageAccess} paypalNetRequests=${paypalRequestCount.value}`);
1107
+ iframes.forEach((frame, i) => {
1108
+ const title = (frame.getAttribute("title") ?? "").slice(0, 40);
1109
+ appendDebug(` iframe[${i}] ${formatDims(frame)}${title ? ` title="${title}"` : ""} ${describeIframe(frame)}`);
1110
+ const content = inspectFrameContent(frame);
1111
+ if (content) appendDebug(` ${content}`);
1112
+ });
1113
+ if (errorMessages.length === 0) {
1114
+ appendDebug(` (no PayPal/zoid window errors captured)`);
1115
+ }
1116
+ };
1117
+ if (debug) {
1118
+ watchdogTimers.push(setTimeout(() => snapshotContainer("3s"), 3e3));
1119
+ watchdogTimers.push(setTimeout(() => snapshotContainer("8s"), 8e3));
1120
+ watchdogTimers.push(setTimeout(() => snapshotContainer("15s"), 15e3));
1121
+ }
1122
+ appendDebug("render:start");
1123
+ buttons.render(container).then(() => {
1124
+ appendDebug(`render:resolved cancelled=${cancelled}`);
1125
+ stopDiagnostics();
1126
+ if (cancelled) {
1127
+ typedButtons.close().catch(() => {
1128
+ });
1129
+ return;
1130
+ }
1131
+ activeButtons = typedButtons;
1132
+ rendered = true;
1133
+ setReady(true);
1134
+ }).catch((err) => {
1135
+ const message = err instanceof Error ? err.message : "PayPal failed to render.";
1136
+ appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
1137
+ stopDiagnostics();
1138
+ markRenderFailed(message);
1139
+ });
1140
+ }).catch((err) => {
1141
+ const message = err instanceof Error ? err.message : "PayPal SDK failed to load.";
1142
+ appendDebug(`loadScript:rejected msg=${message.slice(0, 120)}`);
1143
+ markRenderFailed(message);
1144
+ });
1145
+ }, 0);
1146
+ return () => {
1147
+ cancelled = true;
1148
+ clearTimeout(startTimer);
1149
+ stopDiagnostics();
1150
+ if (activeButtons) {
1151
+ activeButtons.close().catch(() => {
1152
+ });
1153
+ }
1154
+ };
1155
+ }, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId]);
1156
+ const debugPanel = debug ? /* @__PURE__ */ jsxs3(
1157
+ "pre",
1158
+ {
1159
+ "data-testid": "flopay-direct-paypal-debug",
1160
+ style: {
1161
+ margin: "0 0 8px 0",
1162
+ padding: "6px 8px",
1163
+ background: failed ? "#fef2f2" : "#f3f4f6",
1164
+ border: `1px solid ${failed ? "#fca5a5" : "#d1d5db"}`,
1165
+ borderRadius: 6,
1166
+ color: "#111827",
1167
+ font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
1168
+ whiteSpace: "pre-wrap",
1169
+ wordBreak: "break-word",
1170
+ maxHeight: 220,
1171
+ overflowY: "auto"
1172
+ },
1173
+ children: [
1174
+ `FloPay/DirectPayPal-debug (ready=${ready} failed=${failed})`,
1175
+ debugLines.length === 0 ? "\n(waiting for first lifecycle event\u2026)" : `
1176
+ ${debugLines.join("\n")}`
1177
+ ]
1178
+ }
1179
+ ) : null;
1180
+ if (failed) {
1181
+ return debug ? /* @__PURE__ */ jsx5("div", { children: debugPanel }) : null;
1182
+ }
1183
+ return (
1184
+ // Single wrapper so the parent flex container sees exactly one flex item
1185
+ // (otherwise the fragment's placeholder + container become two siblings
1186
+ // and any spacing-sensitive layout has to reason about both). The wrapper
1187
+ // intentionally has no margin/padding so the parent owns all spacing.
1188
+ /* @__PURE__ */ jsxs3("div", { children: [
1189
+ debugPanel,
1190
+ /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT }, children: [
1191
+ !ready && /* @__PURE__ */ jsx5(
1192
+ "div",
1193
+ {
1194
+ "data-testid": "flopay-direct-paypal-placeholder",
1195
+ style: {
1196
+ position: "absolute",
1197
+ inset: 0,
1198
+ borderRadius: 8,
1199
+ background: "#e5e7eb",
1200
+ animation: "flopay-pulse 1.5s ease-in-out infinite",
1201
+ pointerEvents: "none"
1202
+ }
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsx5(
1206
+ "div",
1207
+ {
1208
+ ref: containerRef,
1209
+ "data-testid": "flopay-direct-paypal-container",
1210
+ style: {
1211
+ minHeight: DEFAULT_BUTTON_HEIGHT,
1212
+ display: "flex",
1213
+ opacity: ready ? 1 : 0
1214
+ },
1215
+ "aria-busy": submitting || isProcessing
1216
+ }
1217
+ )
1218
+ ] })
1219
+ ] })
1220
+ );
1221
+ }
1222
+
654
1223
  // src/split-card-form.tsx
655
- import { FloPayError as FloPayError2 } from "@flopay/shared";
656
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1224
+ import { FloPayError as FloPayError3 } from "@flopay/shared";
1225
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
657
1226
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
658
1227
  var FLOPAY_KEYFRAMES = `
1228
+ .paypal-buttons { margin: 0 !important; vertical-align: top !important; }
659
1229
  @keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
660
1230
  @keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
661
1231
  @keyframes flopay-buttons-enter {
@@ -689,13 +1259,13 @@ function getButtonMethodLabel(method) {
689
1259
  }
690
1260
  }
691
1261
  function normalizeBeforeButtonClickError(method, err) {
692
- return err instanceof FloPayError2 ? err : new FloPayError2(
1262
+ return err instanceof FloPayError3 ? err : new FloPayError3(
693
1263
  err instanceof Error ? err.message : `${getButtonMethodLabel(method)} before-click hook failed.`,
694
1264
  "validation_error"
695
1265
  );
696
1266
  }
697
1267
  function FloPayKeyframes() {
698
- return /* @__PURE__ */ jsx5("style", { children: FLOPAY_KEYFRAMES });
1268
+ return /* @__PURE__ */ jsx6("style", { children: FLOPAY_KEYFRAMES });
699
1269
  }
700
1270
  function toCssSize(value) {
701
1271
  if (typeof value === "number") return `${value}px`;
@@ -716,8 +1286,8 @@ function ExpressCheckoutReadySwap({
716
1286
  children
717
1287
  }) {
718
1288
  if (state === "unavailable" || state === "load_error") return null;
719
- return /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: 44 }, children: [
720
- /* @__PURE__ */ jsx5(
1289
+ return /* @__PURE__ */ jsxs4("div", { style: { position: "relative", minHeight: 44 }, children: [
1290
+ /* @__PURE__ */ jsx6(
721
1291
  "div",
722
1292
  {
723
1293
  "data-testid": placeholderTestId,
@@ -736,7 +1306,7 @@ function ExpressCheckoutReadySwap({
736
1306
  }
737
1307
  }
738
1308
  ),
739
- /* @__PURE__ */ jsx5(
1309
+ /* @__PURE__ */ jsx6(
740
1310
  "div",
741
1311
  {
742
1312
  style: {
@@ -756,7 +1326,7 @@ function isExpressCheckoutRowVisible(state) {
756
1326
  }
757
1327
  var SplitCardForm = forwardRef(
758
1328
  function SplitCardForm2(props, ref) {
759
- return /* @__PURE__ */ jsx5(SplitCardFormInner, { ...props, innerRef: ref });
1329
+ return /* @__PURE__ */ jsx6(SplitCardFormInner, { ...props, innerRef: ref });
760
1330
  }
761
1331
  );
762
1332
  function PayPalButtonInner({
@@ -773,15 +1343,15 @@ function PayPalButtonInner({
773
1343
  }) {
774
1344
  const stripe = useStripeRaw();
775
1345
  const elements = useStripeElements();
776
- const [loadState, setLoadState] = useState2("loading");
777
- useEffect3(() => {
1346
+ const [loadState, setLoadState] = useState3("loading");
1347
+ useEffect4(() => {
778
1348
  onLoadStateChange?.(loadState);
779
1349
  }, [loadState, onLoadStateChange]);
780
- const [submitting, setSubmitting] = useState2(false);
781
- const paypalResumeAttempted = useRef2(false);
782
- const beforeClickRef = useRef2(null);
1350
+ const [submitting, setSubmitting] = useState3(false);
1351
+ const paypalResumeAttempted = useRef3(false);
1352
+ const beforeClickRef = useRef3(null);
783
1353
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
784
- useEffect3(() => {
1354
+ useEffect4(() => {
785
1355
  if (!stripe || paypalResumeAttempted.current) return;
786
1356
  const params = new URLSearchParams(window.location.search);
787
1357
  const paymentIntentId = params.get("payment_intent");
@@ -938,8 +1508,8 @@ function PayPalButtonInner({
938
1508
  setSubmitting(false);
939
1509
  }
940
1510
  }, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
941
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
942
- /* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ jsx5(
1511
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1512
+ /* @__PURE__ */ jsx6(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ jsx6(
943
1513
  ExpressCheckoutElement,
944
1514
  {
945
1515
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -964,7 +1534,7 @@ function PayPalButtonInner({
964
1534
  }
965
1535
  }
966
1536
  ) }),
967
- submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
1537
+ submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
968
1538
  ] });
969
1539
  }
970
1540
  function WalletButtonInner({
@@ -982,14 +1552,14 @@ function WalletButtonInner({
982
1552
  }) {
983
1553
  const stripe = useStripeRaw();
984
1554
  const elements = useStripeElements();
985
- const [loadState, setLoadState] = useState2("loading");
986
- useEffect3(() => {
1555
+ const [loadState, setLoadState] = useState3("loading");
1556
+ useEffect4(() => {
987
1557
  onLoadStateChange?.(loadState);
988
1558
  }, [loadState, onLoadStateChange]);
989
- const [submitting, setSubmitting] = useState2(false);
1559
+ const [submitting, setSubmitting] = useState3(false);
990
1560
  const baseUrl = billingApiUrl.replace(/\/+$/, "");
991
- const lastWalletMethodRef = useRef2("card");
992
- const beforeClickRef = useRef2(null);
1561
+ const lastWalletMethodRef = useRef3("card");
1562
+ const beforeClickRef = useRef3(null);
993
1563
  const handleWalletConfirm = useCallback(
994
1564
  async (event) => {
995
1565
  if (!stripe || !elements) return;
@@ -1079,12 +1649,16 @@ function WalletButtonInner({
1079
1649
  },
1080
1650
  [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]
1081
1651
  );
1082
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1083
- /* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ jsx5(
1652
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1653
+ /* @__PURE__ */ jsx6(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ jsx6(
1084
1654
  ExpressCheckoutElement,
1085
1655
  {
1086
- onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["applePay", "googlePay"])),
1087
- onLoadError: () => setLoadState("load_error"),
1656
+ onReady: (event) => {
1657
+ setLoadState(resolveExpressCheckoutLoadState(event, ["applePay", "googlePay"]));
1658
+ },
1659
+ onLoadError: (e) => {
1660
+ setLoadState("load_error");
1661
+ },
1088
1662
  onClick: async (event) => {
1089
1663
  lastWalletMethodRef.current = event.expressPaymentType === "apple_pay" ? "apple_pay" : "google_pay";
1090
1664
  const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick(lastWalletMethodRef.current) : { proceed: true };
@@ -1119,7 +1693,7 @@ function WalletButtonInner({
1119
1693
  }
1120
1694
  }
1121
1695
  ) }),
1122
- submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
1696
+ submitting && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: "processing" })
1123
1697
  ] });
1124
1698
  }
1125
1699
  function SplitCardFormInner({
@@ -1171,6 +1745,10 @@ function SplitCardFormInner({
1171
1745
  totalAmount = 0,
1172
1746
  currency = "usd",
1173
1747
  initialCardOpen = false,
1748
+ directPaypal,
1749
+ isSubscription = false,
1750
+ session,
1751
+ debug = false,
1174
1752
  innerRef
1175
1753
  }) {
1176
1754
  const flopay = useFloPay();
@@ -1178,25 +1756,25 @@ function SplitCardFormInner({
1178
1756
  const elements = useElements();
1179
1757
  const checkout = useContext3(CheckoutContext);
1180
1758
  const contextBillingUrl = useBillingApiUrl();
1181
- const [processing, setProcessing] = useState2(false);
1182
- const [error, setError] = useState2(null);
1183
- const [is3DSActive, setIs3DSActive] = useState2(false);
1184
- const [selectedCountry, setSelectedCountry] = useState2(countryProp ?? "US");
1185
- const [zipCode, setZipCode] = useState2(zipProp ?? "");
1186
- const [addressLine1, setAddressLine1] = useState2(addressLine1Prop ?? "");
1187
- const [addressLine2, setAddressLine2] = useState2(addressLine2Prop ?? "");
1188
- const [city, setCity] = useState2(cityProp ?? "");
1189
- const [stateValue, setStateValue] = useState2(stateProp ?? "");
1190
- const [accountPatch, setAccountPatch] = useState2({});
1191
- const zipCodeRef = useRef2(zipProp ?? "");
1192
- const selectedCountryRef = useRef2(countryProp ?? "US");
1193
- const addressLine1Ref = useRef2(addressLine1Prop ?? "");
1194
- const addressLine2Ref = useRef2(addressLine2Prop ?? "");
1195
- const cityRef = useRef2(cityProp ?? "");
1196
- const stateRef = useRef2(stateProp ?? "");
1197
- const avsConfig = useMemo2(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
1759
+ const [processing, setProcessing] = useState3(false);
1760
+ const [error, setError] = useState3(null);
1761
+ const [is3DSActive, setIs3DSActive] = useState3(false);
1762
+ const [selectedCountry, setSelectedCountry] = useState3(countryProp ?? "US");
1763
+ const [zipCode, setZipCode] = useState3(zipProp ?? "");
1764
+ const [addressLine1, setAddressLine1] = useState3(addressLine1Prop ?? "");
1765
+ const [addressLine2, setAddressLine2] = useState3(addressLine2Prop ?? "");
1766
+ const [city, setCity] = useState3(cityProp ?? "");
1767
+ const [stateValue, setStateValue] = useState3(stateProp ?? "");
1768
+ const [accountPatch, setAccountPatch] = useState3({});
1769
+ const zipCodeRef = useRef3(zipProp ?? "");
1770
+ const selectedCountryRef = useRef3(countryProp ?? "US");
1771
+ const addressLine1Ref = useRef3(addressLine1Prop ?? "");
1772
+ const addressLine2Ref = useRef3(addressLine2Prop ?? "");
1773
+ const cityRef = useRef3(cityProp ?? "");
1774
+ const stateRef = useRef3(stateProp ?? "");
1775
+ const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
1198
1776
  const enableAVS = avsConfig !== null;
1199
- const [viewState, setViewState] = useState2(initialCardOpen ? "card" : "buttons");
1777
+ const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
1200
1778
  const showCardForm = viewState === "expanding" || viewState === "card";
1201
1779
  const TRANSITION_MS = 280;
1202
1780
  const expandToCard = useCallback(() => {
@@ -1207,18 +1785,23 @@ function SplitCardFormInner({
1207
1785
  setViewState("collapsing");
1208
1786
  setTimeout(() => setViewState("buttons"), TRANSITION_MS);
1209
1787
  }, []);
1210
- useEffect3(() => {
1788
+ useEffect4(() => {
1211
1789
  if (layout === "buttons" && initialCardOpen) {
1212
1790
  setViewState("card");
1213
1791
  }
1214
1792
  }, [layout, initialCardOpen]);
1215
- const [fullName, setFullName] = useState2("");
1216
- const [formReady, setFormReady] = useState2(false);
1217
- const [overlayStatus, setOverlayStatus] = useState2(null);
1218
- const processingRef = useRef2(false);
1793
+ const [fullName, setFullName] = useState3("");
1794
+ const [formReady, setFormReady] = useState3(false);
1795
+ const [overlayStatus, setOverlayStatus] = useState3(null);
1796
+ const processingRef = useRef3(false);
1797
+ const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
1798
+ const paypalDirectRetryRef = useRef3(paypalDirectRetry);
1799
+ useEffect4(() => {
1800
+ paypalDirectRetryRef.current = paypalDirectRetry;
1801
+ }, [paypalDirectRetry]);
1219
1802
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
1220
1803
  const displayError = externalError ?? error;
1221
- const bStyles = useMemo2(() => {
1804
+ const bStyles = useMemo3(() => {
1222
1805
  const base = resolveButtonsLayoutTheme(buttonsTheme);
1223
1806
  if (!buttonsStylesOverride) return base;
1224
1807
  return {
@@ -1236,7 +1819,7 @@ function SplitCardFormInner({
1236
1819
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
1237
1820
  const isSelfContained = !onTokenizedBody;
1238
1821
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
1239
- const resolvedAccount = useMemo2(() => mergeAccountPatch({
1822
+ const resolvedAccount = useMemo3(() => mergeAccountPatch({
1240
1823
  userId,
1241
1824
  email,
1242
1825
  firstName,
@@ -1244,23 +1827,23 @@ function SplitCardFormInner({
1244
1827
  country: countryProp,
1245
1828
  zip: zipProp
1246
1829
  }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
1247
- const stripeInstance = useMemo2(() => {
1830
+ const stripeInstance = useMemo3(() => {
1248
1831
  if (!flopay) return null;
1249
1832
  return flopay.getRawProvider();
1250
1833
  }, [flopay]);
1251
- const paypalStripeInstance = useMemo2(() => {
1834
+ const paypalStripeInstance = useMemo3(() => {
1252
1835
  if (!paypalFlopay) return null;
1253
1836
  return paypalFlopay.getRawProvider();
1254
1837
  }, [paypalFlopay]);
1255
1838
  const amountInCents = totalAmount || 100;
1256
- const walletOptions = useMemo2(() => ({
1839
+ const walletOptions = useMemo3(() => ({
1257
1840
  mode: "payment",
1258
1841
  amount: amountInCents,
1259
1842
  currency: currency.toLowerCase(),
1260
1843
  paymentMethodCreation: "manual",
1261
1844
  captureMethod: "manual"
1262
1845
  }), [amountInCents, currency]);
1263
- const paypalOptions = useMemo2(() => ({
1846
+ const paypalOptions = useMemo3(() => ({
1264
1847
  mode: "payment",
1265
1848
  amount: amountInCents,
1266
1849
  currency: currency.toLowerCase(),
@@ -1281,17 +1864,20 @@ function SplitCardFormInner({
1281
1864
  [onDecline]
1282
1865
  );
1283
1866
  const showWallets = showApplePay || showGooglePay;
1284
- const [paypalLoadState, setPaypalLoadState] = useState2("loading");
1285
- const [walletLoadState, setWalletLoadState] = useState2("loading");
1286
- const [inAppBrowserDetected, setInAppBrowserDetected] = useState2();
1287
- useEffect3(() => {
1867
+ const [paypalLoadState, setPaypalLoadState] = useState3("loading");
1868
+ const [walletLoadState, setWalletLoadState] = useState3("loading");
1869
+ const [directPaypalReady, setDirectPaypalReady] = useState3(false);
1870
+ const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
1871
+ useEffect4(() => {
1288
1872
  setInAppBrowserDetected(isInAppBrowser());
1289
1873
  }, []);
1290
- const shouldShowPayPal = showPayPal && inAppBrowserDetected === false;
1291
- const shouldShowWallets = showWallets && inAppBrowserDetected === false;
1292
- const shouldRenderPayPal = shouldShowPayPal && !!paypalStripeInstance;
1874
+ const directPaypalConfigured = !!directPaypal?.clientId;
1875
+ const shouldShowPayPal = showPayPal && (directPaypalConfigured || inAppBrowserDetected === false);
1876
+ const shouldShowWallets = showWallets;
1877
+ const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
1878
+ const shouldRenderStripePayPal = shouldShowPayPal && !directPaypalConfigured && !!paypalStripeInstance;
1293
1879
  const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
1294
- const shouldDisplayPayPalRow = shouldRenderPayPal && isExpressCheckoutRowVisible(paypalLoadState);
1880
+ const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
1295
1881
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
1296
1882
  const handleNameChange = useCallback((value) => {
1297
1883
  setFullName(value);
@@ -1365,7 +1951,7 @@ function SplitCardFormInner({
1365
1951
  const resolvedCompletionPaymentMethodId = overrides?.completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
1366
1952
  const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
1367
1953
  try {
1368
- const api = new PaymentAPI(baseUrl);
1954
+ const api = new PaymentAPI2(baseUrl);
1369
1955
  const response = await api.processPayment(effectiveAccount.userId ?? "", {
1370
1956
  sessionId: effectiveSessionId,
1371
1957
  tokenizedData: requestTokenizedBody,
@@ -1410,6 +1996,7 @@ function SplitCardFormInner({
1410
1996
  if (response.ok) {
1411
1997
  markSessionRecentlyCompleted(effectiveSessionId);
1412
1998
  setOverlayStatus("success");
1999
+ setPaypalDirectRetry(null);
1413
2000
  await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
1414
2001
  onComplete?.({
1415
2002
  status: "succeeded",
@@ -1517,6 +2104,24 @@ function SplitCardFormInner({
1517
2104
  }
1518
2105
  return;
1519
2106
  }
2107
+ if (json?.type === "paypal_direct_required") {
2108
+ const orderId = json["orderId"];
2109
+ if (!orderId) {
2110
+ setOverlayStatus("error");
2111
+ updateError("PayPal retry required but no order id provided.");
2112
+ return;
2113
+ }
2114
+ const prevAttempts = paypalDirectRetryRef.current?.attempts ?? 0;
2115
+ if (prevAttempts >= 2) {
2116
+ setOverlayStatus("error");
2117
+ updateError("PayPal payment could not be completed after multiple attempts.");
2118
+ emitDecline("paypal", "paypal_direct_required retry limit exceeded");
2119
+ return;
2120
+ }
2121
+ setPaypalDirectRetry({ orderId, attempts: prevAttempts + 1 });
2122
+ setOverlayStatus(null);
2123
+ return;
2124
+ }
1520
2125
  setOverlayStatus("error");
1521
2126
  const message = json?.message ?? "Payment failed. Please try again.";
1522
2127
  updateError(message);
@@ -1574,7 +2179,7 @@ function SplitCardFormInner({
1574
2179
  }
1575
2180
  }
1576
2181
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
1577
- useEffect3(() => {
2182
+ useEffect4(() => {
1578
2183
  if (typeof window === "undefined") return;
1579
2184
  const stored = localStorage.getItem(WALLET_RESUME_KEY);
1580
2185
  if (!stored) return;
@@ -1658,7 +2263,7 @@ function SplitCardFormInner({
1658
2263
  return;
1659
2264
  }
1660
2265
  if (!sessionId || !resolvedAccount.email) {
1661
- throw new FloPayError2("Missing sessionId or email", "validation_error");
2266
+ throw new FloPayError3("Missing sessionId or email", "validation_error");
1662
2267
  }
1663
2268
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1664
2269
  method: "POST",
@@ -1684,7 +2289,7 @@ function SplitCardFormInner({
1684
2289
  }
1685
2290
  const intentJson = await intentResponse.json();
1686
2291
  const intentClientSecret = intentJson.data?.id;
1687
- if (!intentClientSecret) throw new FloPayError2("No client_secret in payment intent response", "api_error");
2292
+ if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
1688
2293
  const confirmResult = await flopay.confirmCardPayment({
1689
2294
  clientSecret: intentClientSecret,
1690
2295
  paymentMethodId: pmResult.paymentMethodId
@@ -1705,7 +2310,7 @@ function SplitCardFormInner({
1705
2310
  const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
1706
2311
  const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
1707
2312
  if (!paymentIntentId) {
1708
- const error2 = new FloPayError2("No payment intent returned after confirmation.", "api_error");
2313
+ const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
1709
2314
  setOverlayStatus("error");
1710
2315
  updateError(error2.message);
1711
2316
  onError?.(error2);
@@ -1734,7 +2339,7 @@ function SplitCardFormInner({
1734
2339
  );
1735
2340
  const isReady = flopay !== null && elements !== null;
1736
2341
  if (!isReady) {
1737
- return /* @__PURE__ */ jsx5("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
2342
+ return /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
1738
2343
  }
1739
2344
  const isButtons = layout === "buttons";
1740
2345
  const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
@@ -1777,7 +2382,7 @@ function SplitCardFormInner({
1777
2382
  invalid: { color: "#ef4444" }
1778
2383
  }
1779
2384
  };
1780
- const cardFormBlock = /* @__PURE__ */ jsxs3("div", { style: {
2385
+ const cardFormBlock = /* @__PURE__ */ jsxs4("div", { style: {
1781
2386
  backgroundColor: cardBg,
1782
2387
  borderRadius: "8px",
1783
2388
  padding: isButtons ? "0" : "1rem",
@@ -1785,7 +2390,7 @@ function SplitCardFormInner({
1785
2390
  ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {},
1786
2391
  ...sharedInputPlaceholderVars
1787
2392
  }, children: [
1788
- /* @__PURE__ */ jsx5("style", { children: `
2393
+ /* @__PURE__ */ jsx6("style", { children: `
1789
2394
  .flopay-shared-input::placeholder {
1790
2395
  color: var(--flopay-input-placeholder-color);
1791
2396
  opacity: 1;
@@ -1794,12 +2399,12 @@ function SplitCardFormInner({
1794
2399
  font-weight: var(--flopay-input-font-weight);
1795
2400
  }
1796
2401
  ` }),
1797
- isButtons && showCardForm && /* @__PURE__ */ jsxs3("div", { style: {
2402
+ isButtons && showCardForm && /* @__PURE__ */ jsxs4("div", { style: {
1798
2403
  display: "flex",
1799
2404
  alignItems: "center",
1800
2405
  padding: "0.75rem 0 0.625rem"
1801
2406
  }, children: [
1802
- /* @__PURE__ */ jsxs3(
2407
+ /* @__PURE__ */ jsxs4(
1803
2408
  "button",
1804
2409
  {
1805
2410
  type: "button",
@@ -1821,7 +2426,7 @@ function SplitCardFormInner({
1821
2426
  },
1822
2427
  "aria-label": "Back to payment methods",
1823
2428
  children: [
1824
- /* @__PURE__ */ jsx5("span", { style: {
2429
+ /* @__PURE__ */ jsx6("span", { style: {
1825
2430
  display: "inline-flex",
1826
2431
  alignItems: "center",
1827
2432
  justifyContent: "center",
@@ -1831,12 +2436,12 @@ function SplitCardFormInner({
1831
2436
  backgroundColor: "#f3f4f6",
1832
2437
  transition: "background-color 0.15s",
1833
2438
  ...bStyles.backButtonIcon
1834
- }, 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" }) }) }),
1835
- /* @__PURE__ */ jsx5(BackButtonContentSlot, { content: cardBackButtonContent })
2439
+ }, children: /* @__PURE__ */ jsx6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "M15 18l-6-6 6-6" }) }) }),
2440
+ /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
1836
2441
  ]
1837
2442
  }
1838
2443
  ),
1839
- hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
2444
+ hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
1840
2445
  flex: 1,
1841
2446
  textAlign: "center",
1842
2447
  fontWeight: 600,
@@ -1844,18 +2449,18 @@ function SplitCardFormInner({
1844
2449
  color: "#262833",
1845
2450
  paddingRight: 80,
1846
2451
  ...bStyles.title
1847
- }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
2452
+ }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
1848
2453
  ] }),
1849
- !isButtons && !hideTitle && /* @__PURE__ */ jsx5("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) }),
1850
- /* @__PURE__ */ jsx5("div", { style: {
2454
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) }),
2455
+ /* @__PURE__ */ jsx6("div", { style: {
1851
2456
  backgroundColor: cardInputBg,
1852
2457
  border: `1px solid ${resolvedBorder}`,
1853
2458
  borderTopLeftRadius: "8px",
1854
2459
  borderTopRightRadius: "8px",
1855
2460
  padding: "10px"
1856
- }, children: /* @__PURE__ */ jsx5(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
1857
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
1858
- /* @__PURE__ */ jsx5("div", { style: {
2461
+ }, children: /* @__PURE__ */ jsx6(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
2462
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
2463
+ /* @__PURE__ */ jsx6("div", { style: {
1859
2464
  flex: 1,
1860
2465
  backgroundColor: cardInputBg,
1861
2466
  border: `1px solid ${resolvedBorder}`,
@@ -1863,23 +2468,23 @@ function SplitCardFormInner({
1863
2468
  borderRight: "none",
1864
2469
  borderBottomLeftRadius: "8px",
1865
2470
  padding: "10px"
1866
- }, children: /* @__PURE__ */ jsx5(CardExpiryElement, { options: stripeElementStyle }) }),
1867
- /* @__PURE__ */ jsx5("div", { style: {
2471
+ }, children: /* @__PURE__ */ jsx6(CardExpiryElement, { options: stripeElementStyle }) }),
2472
+ /* @__PURE__ */ jsx6("div", { style: {
1868
2473
  flex: 1,
1869
2474
  backgroundColor: cardInputBg,
1870
2475
  border: `1px solid ${resolvedBorder}`,
1871
2476
  borderTop: "none",
1872
2477
  borderBottomRightRadius: "8px",
1873
2478
  padding: "10px"
1874
- }, children: /* @__PURE__ */ jsx5(CardCvcElement, { options: stripeElementStyle }) })
2479
+ }, children: /* @__PURE__ */ jsx6(CardCvcElement, { options: stripeElementStyle }) })
1875
2480
  ] }),
1876
- /* @__PURE__ */ jsx5("div", { style: {
2481
+ /* @__PURE__ */ jsx6("div", { style: {
1877
2482
  backgroundColor: cardInputBg,
1878
2483
  border: `1px solid ${resolvedBorder}`,
1879
2484
  borderRadius: "8px",
1880
2485
  marginTop: "0.5rem",
1881
2486
  padding: "10px"
1882
- }, children: /* @__PURE__ */ jsx5(
2487
+ }, children: /* @__PURE__ */ jsx6(
1883
2488
  "input",
1884
2489
  {
1885
2490
  className: "flopay-shared-input",
@@ -1918,8 +2523,8 @@ function SplitCardFormInner({
1918
2523
  ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
1919
2524
  });
1920
2525
  const stateOpts = getStateOptions(cc);
1921
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1922
- isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx5("div", { style: inputWrapStyle(bStyles.addressLine1Input), children: /* @__PURE__ */ jsx5(
2526
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2527
+ isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine1Input), children: /* @__PURE__ */ jsx6(
1923
2528
  "input",
1924
2529
  {
1925
2530
  className: "flopay-shared-input",
@@ -1936,7 +2541,7 @@ function SplitCardFormInner({
1936
2541
  style: inputFieldStyle()
1937
2542
  }
1938
2543
  ) }),
1939
- isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx5("div", { style: inputWrapStyle(bStyles.addressLine2Input), children: /* @__PURE__ */ jsx5(
2544
+ isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx6("div", { style: inputWrapStyle(bStyles.addressLine2Input), children: /* @__PURE__ */ jsx6(
1940
2545
  "input",
1941
2546
  {
1942
2547
  className: "flopay-shared-input",
@@ -1952,12 +2557,12 @@ function SplitCardFormInner({
1952
2557
  style: inputFieldStyle()
1953
2558
  }
1954
2559
  ) }),
1955
- (isAVSFieldVisible(avsConfig.city, cc) || isAVSFieldVisible(avsConfig.state, cc)) && /* @__PURE__ */ jsxs3("div", { style: {
2560
+ (isAVSFieldVisible(avsConfig.city, cc) || isAVSFieldVisible(avsConfig.state, cc)) && /* @__PURE__ */ jsxs4("div", { style: {
1956
2561
  display: "flex",
1957
2562
  gap: "0",
1958
2563
  marginTop: "0.5rem"
1959
2564
  }, children: [
1960
- isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx5("div", { style: {
2565
+ isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx6("div", { style: {
1961
2566
  flex: 1,
1962
2567
  backgroundColor: cardInputBg,
1963
2568
  border: `1px solid ${resolvedBorder}`,
@@ -1966,7 +2571,7 @@ function SplitCardFormInner({
1966
2571
  borderBottomLeftRadius: "8px",
1967
2572
  ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: "8px" },
1968
2573
  ...isButtons && bStyles.cityInput ? bStyles.cityInput : {}
1969
- }, children: /* @__PURE__ */ jsx5(
2574
+ }, children: /* @__PURE__ */ jsx6(
1970
2575
  "input",
1971
2576
  {
1972
2577
  className: "flopay-shared-input",
@@ -1983,7 +2588,7 @@ function SplitCardFormInner({
1983
2588
  style: inputFieldStyle()
1984
2589
  }
1985
2590
  ) }),
1986
- isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx5("div", { style: {
2591
+ isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx6("div", { style: {
1987
2592
  flex: 1,
1988
2593
  backgroundColor: cardInputBg,
1989
2594
  border: `1px solid ${resolvedBorder}`,
@@ -1992,7 +2597,7 @@ function SplitCardFormInner({
1992
2597
  borderBottomRightRadius: "8px",
1993
2598
  ...isAVSFieldVisible(avsConfig.city, cc) ? { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 } : { borderRadius: "8px" },
1994
2599
  ...isButtons && bStyles.stateInput ? bStyles.stateInput : {}
1995
- }, children: stateOpts ? /* @__PURE__ */ jsxs3(
2600
+ }, children: stateOpts ? /* @__PURE__ */ jsxs4(
1996
2601
  "select",
1997
2602
  {
1998
2603
  value: stateValue,
@@ -2006,11 +2611,11 @@ function SplitCardFormInner({
2006
2611
  "data-testid": "flopay-state",
2007
2612
  style: { ...inputFieldStyle(), cursor: "pointer" },
2008
2613
  children: [
2009
- /* @__PURE__ */ jsx5("option", { value: "", children: getStateLabel(cc) }),
2010
- stateOpts.map((s) => /* @__PURE__ */ jsx5("option", { value: s.code, children: s.name }, s.code))
2614
+ /* @__PURE__ */ jsx6("option", { value: "", children: getStateLabel(cc) }),
2615
+ stateOpts.map((s) => /* @__PURE__ */ jsx6("option", { value: s.code, children: s.name }, s.code))
2011
2616
  ]
2012
2617
  }
2013
- ) : /* @__PURE__ */ jsx5(
2618
+ ) : /* @__PURE__ */ jsx6(
2014
2619
  "input",
2015
2620
  {
2016
2621
  className: "flopay-shared-input",
@@ -2028,20 +2633,20 @@ function SplitCardFormInner({
2028
2633
  }
2029
2634
  ) })
2030
2635
  ] }),
2031
- (isAVSFieldVisible(avsConfig.country, cc) || isAVSFieldVisible(avsConfig.postal_code, cc)) && /* @__PURE__ */ jsxs3("div", { style: {
2636
+ (isAVSFieldVisible(avsConfig.country, cc) || isAVSFieldVisible(avsConfig.postal_code, cc)) && /* @__PURE__ */ jsxs4("div", { style: {
2032
2637
  display: "flex",
2033
2638
  flexDirection: avsLayoutProp === "column" ? "column" : "row",
2034
2639
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
2035
2640
  marginTop: "0.5rem"
2036
2641
  }, children: [
2037
- isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx5("div", { style: {
2642
+ isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx6("div", { style: {
2038
2643
  flex: avsLayoutProp === "row" ? 1 : void 0,
2039
2644
  backgroundColor: cardInputBg,
2040
2645
  border: `1px solid ${resolvedBorder}`,
2041
2646
  padding: "10px",
2042
2647
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
2043
2648
  ...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
2044
- }, children: /* @__PURE__ */ jsx5(
2649
+ }, children: /* @__PURE__ */ jsx6(
2045
2650
  "select",
2046
2651
  {
2047
2652
  value: selectedCountry,
@@ -2056,21 +2661,21 @@ function SplitCardFormInner({
2056
2661
  autoComplete: "country",
2057
2662
  "data-testid": "flopay-country",
2058
2663
  style: { ...inputFieldStyle(), cursor: "pointer" },
2059
- children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs3("option", { value: c.code, children: [
2664
+ children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs4("option", { value: c.code, children: [
2060
2665
  c.flag,
2061
2666
  " ",
2062
2667
  c.name
2063
2668
  ] }, c.code))
2064
2669
  }
2065
2670
  ) }),
2066
- isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx5("div", { style: {
2671
+ isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx6("div", { style: {
2067
2672
  flex: avsLayoutProp === "row" ? 1 : void 0,
2068
2673
  backgroundColor: cardInputBg,
2069
2674
  border: `1px solid ${resolvedBorder}`,
2070
2675
  padding: "10px",
2071
2676
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
2072
2677
  ...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
2073
- }, children: /* @__PURE__ */ jsx5(
2678
+ }, children: /* @__PURE__ */ jsx6(
2074
2679
  "input",
2075
2680
  {
2076
2681
  className: "flopay-shared-input",
@@ -2091,7 +2696,7 @@ function SplitCardFormInner({
2091
2696
  ] })
2092
2697
  ] });
2093
2698
  })(),
2094
- displayError && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
2699
+ displayError && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
2095
2700
  margin: "0.75rem 0",
2096
2701
  padding: "0.625rem 0.875rem",
2097
2702
  background: "#FEF2F2",
@@ -2105,10 +2710,10 @@ function SplitCardFormInner({
2105
2710
  gap: "0.5rem",
2106
2711
  ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
2107
2712
  }, children: [
2108
- /* @__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" }) }),
2713
+ /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("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" }) }),
2109
2714
  displayError
2110
2715
  ] }),
2111
- children ?? /* @__PURE__ */ jsx5(
2716
+ children ?? /* @__PURE__ */ jsx6(
2112
2717
  "button",
2113
2718
  {
2114
2719
  type: "submit",
@@ -2131,7 +2736,7 @@ function SplitCardFormInner({
2131
2736
  children: isSubmitting ? "PROCESSING..." : submitLabel
2132
2737
  }
2133
2738
  ),
2134
- !isButtons && showSecurityFooter && /* @__PURE__ */ jsx5("div", { style: {
2739
+ !isButtons && showSecurityFooter && /* @__PURE__ */ jsx6("div", { style: {
2135
2740
  backgroundColor: "#EFF9F0",
2136
2741
  borderRadius: "8px",
2137
2742
  padding: "0.75rem",
@@ -2148,11 +2753,11 @@ function SplitCardFormInner({
2148
2753
  const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
2149
2754
  const buttonsAnim = viewState === "expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
2150
2755
  const cardAnim = viewState === "expanding" ? `flopay-card-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-card-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : void 0;
2151
- return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
2152
- /* @__PURE__ */ jsx5(FloPayKeyframes, {}),
2153
- overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
2154
- /* @__PURE__ */ jsxs3("div", { style: { display: "grid" }, children: [
2155
- /* @__PURE__ */ jsxs3("div", { style: {
2756
+ return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
2757
+ /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
2758
+ overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
2759
+ /* @__PURE__ */ jsxs4("div", { style: { display: "grid" }, children: [
2760
+ /* @__PURE__ */ jsxs4("div", { style: {
2156
2761
  gridArea: "1 / 1",
2157
2762
  display: "flex",
2158
2763
  flexDirection: "column",
@@ -2161,7 +2766,75 @@ function SplitCardFormInner({
2161
2766
  ...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
2162
2767
  ...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
2163
2768
  }, children: [
2164
- shouldRenderPayPal && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
2769
+ debug && showPayPal && /* @__PURE__ */ jsx6(
2770
+ "pre",
2771
+ {
2772
+ "data-testid": "flopay-direct-paypal-gate-debug",
2773
+ style: {
2774
+ margin: 0,
2775
+ padding: "6px 8px",
2776
+ background: "#eef2ff",
2777
+ border: "1px solid #c7d2fe",
2778
+ borderRadius: 6,
2779
+ color: "#111827",
2780
+ font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
2781
+ whiteSpace: "pre-wrap",
2782
+ wordBreak: "break-word"
2783
+ },
2784
+ children: [
2785
+ "FloPay/DirectPayPal-debug (parent gate)",
2786
+ ` showPayPal=${showPayPal}`,
2787
+ ` directPaypalConfigured=${directPaypalConfigured}`,
2788
+ ` inAppBrowserDetected=${String(inAppBrowserDetected)}`,
2789
+ ` shouldRenderDirectPayPal=${shouldRenderDirectPayPal}`,
2790
+ ` shouldRenderStripePayPal=${shouldRenderStripePayPal}`,
2791
+ ` hasPaypalStripeInstance=${!!paypalStripeInstance}`
2792
+ ].join("\n")
2793
+ }
2794
+ ),
2795
+ shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
2796
+ paypalDirectRetry && /* @__PURE__ */ jsx6(
2797
+ "div",
2798
+ {
2799
+ "data-testid": "flopay-paypal-direct-retry-notice",
2800
+ style: {
2801
+ padding: "8px 10px",
2802
+ background: "#fef3c7",
2803
+ border: "1px solid #fcd34d",
2804
+ borderRadius: 6,
2805
+ color: "#78350f",
2806
+ fontSize: 13,
2807
+ lineHeight: 1.4
2808
+ },
2809
+ children: "Please confirm your PayPal payment to complete checkout."
2810
+ }
2811
+ ),
2812
+ /* @__PURE__ */ jsx6(
2813
+ DirectPayPalButton,
2814
+ {
2815
+ sessionId,
2816
+ billingApiUrl: resolvedBillingApiUrl,
2817
+ email: resolvedAccount.email,
2818
+ clientId: directPaypal.clientId,
2819
+ environment: directPaypal.environment,
2820
+ currency: currency.toUpperCase(),
2821
+ isSubscription,
2822
+ onTokenizedBody: dispatchTokenizedBody,
2823
+ onComplete,
2824
+ onErrorChange: updateError,
2825
+ onDecline,
2826
+ onButtonClick,
2827
+ runBeforeButtonClick,
2828
+ isProcessing: isSubmitting,
2829
+ onLoadStateChange: setDirectPaypalReady,
2830
+ session: session ?? null,
2831
+ existingOrderId: paypalDirectRetry?.orderId,
2832
+ debug
2833
+ },
2834
+ paypalDirectRetry?.orderId ?? "fresh"
2835
+ )
2836
+ ] }),
2837
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
2165
2838
  PayPalButtonInner,
2166
2839
  {
2167
2840
  sessionId,
@@ -2176,7 +2849,7 @@ function SplitCardFormInner({
2176
2849
  onLoadStateChange: setPaypalLoadState
2177
2850
  }
2178
2851
  ) }),
2179
- shouldRenderWallets ? /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
2852
+ shouldRenderWallets ? /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
2180
2853
  WalletButtonInner,
2181
2854
  {
2182
2855
  sessionId,
@@ -2191,8 +2864,8 @@ function SplitCardFormInner({
2191
2864
  runBeforeButtonClick,
2192
2865
  onLoadStateChange: setWalletLoadState
2193
2866
  }
2194
- ) }) : shouldShowWallets ? /* @__PURE__ */ jsx5("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
2195
- /* @__PURE__ */ jsx5(
2867
+ ) }) : shouldShowWallets ? /* @__PURE__ */ jsx6("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
2868
+ /* @__PURE__ */ jsx6(
2196
2869
  "button",
2197
2870
  {
2198
2871
  type: "button",
@@ -2230,10 +2903,10 @@ function SplitCardFormInner({
2230
2903
  onMouseUp: (e) => {
2231
2904
  e.currentTarget.style.transform = "scale(1)";
2232
2905
  },
2233
- children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
2906
+ children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
2234
2907
  }
2235
2908
  ),
2236
- displayError && viewState === "buttons" && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
2909
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
2237
2910
  margin: "0.25rem 0",
2238
2911
  padding: "0.625rem 0.875rem",
2239
2912
  background: "#FEF2F2",
@@ -2247,11 +2920,11 @@ function SplitCardFormInner({
2247
2920
  gap: "0.5rem",
2248
2921
  ...bStyles.errorBanner ? bStyles.errorBanner : {}
2249
2922
  }, children: [
2250
- /* @__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" }) }),
2923
+ /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("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" }) }),
2251
2924
  displayError
2252
2925
  ] })
2253
2926
  ] }),
2254
- isCardView && /* @__PURE__ */ jsx5("div", { style: {
2927
+ isCardView && /* @__PURE__ */ jsx6("div", { style: {
2255
2928
  gridArea: "1 / 1",
2256
2929
  ...cardAnim ? { animation: cardAnim } : {},
2257
2930
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
@@ -2259,37 +2932,111 @@ function SplitCardFormInner({
2259
2932
  ] })
2260
2933
  ] });
2261
2934
  }
2262
- return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
2263
- /* @__PURE__ */ jsx5(FloPayKeyframes, {}),
2264
- overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
2265
- shouldRenderWallets && /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
2266
- WalletButtonInner,
2267
- {
2268
- sessionId,
2269
- email: resolvedAccount.email,
2270
- billingApiUrl: resolvedBillingApiUrl,
2271
- showApplePay,
2272
- showGooglePay,
2273
- onTokenizedBody: dispatchTokenizedBody,
2274
- onErrorChange: updateError,
2275
- onDecline,
2276
- onLoadStateChange: setWalletLoadState
2277
- }
2278
- ) }),
2279
- shouldRenderPayPal && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
2280
- PayPalButtonInner,
2281
- {
2282
- sessionId,
2283
- email: resolvedAccount.email,
2284
- billingApiUrl: resolvedBillingApiUrl,
2285
- onTokenizedBody: dispatchTokenizedBody,
2286
- onErrorChange: updateError,
2287
- isProcessing: isSubmitting,
2288
- onDecline,
2289
- onLoadStateChange: setPaypalLoadState
2290
- }
2291
- ) }),
2292
- (shouldDisplayWalletRow || shouldDisplayPayPalRow) && /* @__PURE__ */ jsxs3("div", { style: {
2935
+ return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
2936
+ /* @__PURE__ */ jsx6(FloPayKeyframes, {}),
2937
+ overlayStatus && /* @__PURE__ */ jsx6(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
2938
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2939
+ debug && showPayPal && /* @__PURE__ */ jsx6(
2940
+ "pre",
2941
+ {
2942
+ "data-testid": "flopay-direct-paypal-gate-debug-default",
2943
+ style: {
2944
+ margin: 0,
2945
+ padding: "6px 8px",
2946
+ background: "#eef2ff",
2947
+ border: "1px solid #c7d2fe",
2948
+ borderRadius: 6,
2949
+ color: "#111827",
2950
+ font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
2951
+ whiteSpace: "pre-wrap",
2952
+ wordBreak: "break-word"
2953
+ },
2954
+ children: [
2955
+ "FloPay/DirectPayPal-debug (parent gate)",
2956
+ ` showPayPal=${showPayPal}`,
2957
+ ` directPaypalConfigured=${directPaypalConfigured}`,
2958
+ ` inAppBrowserDetected=${String(inAppBrowserDetected)}`,
2959
+ ` shouldRenderDirectPayPal=${shouldRenderDirectPayPal}`,
2960
+ ` shouldRenderStripePayPal=${shouldRenderStripePayPal}`,
2961
+ ` hasPaypalStripeInstance=${!!paypalStripeInstance}`
2962
+ ].join("\n")
2963
+ }
2964
+ ),
2965
+ shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
2966
+ paypalDirectRetry && /* @__PURE__ */ jsx6(
2967
+ "div",
2968
+ {
2969
+ "data-testid": "flopay-paypal-direct-retry-notice-default",
2970
+ style: {
2971
+ padding: "8px 10px",
2972
+ background: "#fef3c7",
2973
+ border: "1px solid #fcd34d",
2974
+ borderRadius: 6,
2975
+ color: "#78350f",
2976
+ fontSize: 13,
2977
+ lineHeight: 1.4
2978
+ },
2979
+ children: "Please confirm your PayPal payment to complete checkout."
2980
+ }
2981
+ ),
2982
+ /* @__PURE__ */ jsx6(
2983
+ DirectPayPalButton,
2984
+ {
2985
+ sessionId,
2986
+ billingApiUrl: resolvedBillingApiUrl,
2987
+ email: resolvedAccount.email,
2988
+ clientId: directPaypal.clientId,
2989
+ environment: directPaypal.environment,
2990
+ currency: currency.toUpperCase(),
2991
+ isSubscription,
2992
+ onTokenizedBody: dispatchTokenizedBody,
2993
+ onComplete,
2994
+ onErrorChange: updateError,
2995
+ onDecline,
2996
+ onButtonClick,
2997
+ runBeforeButtonClick,
2998
+ isProcessing: isSubmitting,
2999
+ onLoadStateChange: setDirectPaypalReady,
3000
+ session: session ?? null,
3001
+ existingOrderId: paypalDirectRetry?.orderId,
3002
+ debug
3003
+ },
3004
+ paypalDirectRetry?.orderId ?? "fresh"
3005
+ )
3006
+ ] }),
3007
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx6(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx6(
3008
+ PayPalButtonInner,
3009
+ {
3010
+ sessionId,
3011
+ email: resolvedAccount.email,
3012
+ billingApiUrl: resolvedBillingApiUrl,
3013
+ onTokenizedBody: dispatchTokenizedBody,
3014
+ onErrorChange: updateError,
3015
+ isProcessing: isSubmitting,
3016
+ onButtonClick,
3017
+ onDecline,
3018
+ runBeforeButtonClick,
3019
+ onLoadStateChange: setPaypalLoadState
3020
+ }
3021
+ ) }),
3022
+ shouldRenderWallets && /* @__PURE__ */ jsx6(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx6(
3023
+ WalletButtonInner,
3024
+ {
3025
+ sessionId,
3026
+ email: resolvedAccount.email,
3027
+ billingApiUrl: resolvedBillingApiUrl,
3028
+ showApplePay,
3029
+ showGooglePay,
3030
+ onTokenizedBody: dispatchTokenizedBody,
3031
+ onErrorChange: updateError,
3032
+ onButtonClick,
3033
+ onDecline,
3034
+ runBeforeButtonClick,
3035
+ onLoadStateChange: setWalletLoadState
3036
+ }
3037
+ ) })
3038
+ ] }),
3039
+ (shouldDisplayWalletRow || shouldDisplayPayPalRow) && /* @__PURE__ */ jsxs4("div", { style: {
2293
3040
  display: "flex",
2294
3041
  alignItems: "center",
2295
3042
  gap: "0.75rem",
@@ -2297,17 +3044,17 @@ function SplitCardFormInner({
2297
3044
  color: "#999",
2298
3045
  fontSize: "0.85rem"
2299
3046
  }, children: [
2300
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
2301
- /* @__PURE__ */ jsx5("span", { children: "or pay with card" }),
2302
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
3047
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
3048
+ /* @__PURE__ */ jsx6("span", { children: "or pay with card" }),
3049
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
2303
3050
  ] }),
2304
3051
  cardFormBlock
2305
3052
  ] });
2306
3053
  }
2307
3054
 
2308
3055
  // src/saved-payment-flow.ts
2309
- import { loadFloPay, PaymentAPI as PaymentAPI2 } from "@flopay/js";
2310
- import { FloPayError as FloPayError3 } from "@flopay/shared";
3056
+ import { loadFloPay, PaymentAPI as PaymentAPI3 } from "@flopay/js";
3057
+ import { FloPayError as FloPayError4 } from "@flopay/shared";
2311
3058
  var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
2312
3059
  function getRedirectResultFromCheckoutProcessError(error) {
2313
3060
  if (!error?.type || !error.threeDSecureToken) {
@@ -2324,9 +3071,11 @@ function getRedirectResultFromCheckoutProcessError(error) {
2324
3071
  }
2325
3072
  function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment failed. Please try again.", options) {
2326
3073
  const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
3074
+ const rawMessage = error?.message ?? fallbackMessage;
3075
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
2327
3076
  return Object.assign(
2328
- new FloPayError3(
2329
- error?.message ?? fallbackMessage,
3077
+ new FloPayError4(
3078
+ message,
2330
3079
  "api_error",
2331
3080
  {
2332
3081
  code: error?.gatewayErrorCode
@@ -2380,16 +3129,19 @@ function getRedirectTokenFromProcessResponse(json, options) {
2380
3129
  return candidate;
2381
3130
  }
2382
3131
  }
2383
- const nestedGatewayData = nestedRecord.gatewayData;
2384
- if (nestedGatewayData && typeof nestedGatewayData === "object") {
2385
- const nestedGatewayDataRecord = nestedGatewayData;
2386
- const gatewayCandidates = [
2387
- nestedGatewayDataRecord.stripeClientSecret,
2388
- nestedGatewayDataRecord.clientSecret
2389
- ];
2390
- for (const candidate of gatewayCandidates) {
2391
- if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
2392
- return candidate;
3132
+ const nestedGateways = nestedRecord.gateways;
3133
+ if (nestedGateways && typeof nestedGateways === "object") {
3134
+ const stripeGateway = nestedGateways.stripe;
3135
+ if (stripeGateway && typeof stripeGateway === "object") {
3136
+ const stripeRecord = stripeGateway;
3137
+ const gatewayCandidates = [
3138
+ stripeRecord.stripeClientSecret,
3139
+ stripeRecord.clientSecret
3140
+ ];
3141
+ for (const candidate of gatewayCandidates) {
3142
+ if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
3143
+ return candidate;
3144
+ }
2393
3145
  }
2394
3146
  }
2395
3147
  }
@@ -2414,9 +3166,9 @@ async function recover3DSRedirectResult({
2414
3166
  return null;
2415
3167
  }
2416
3168
  try {
2417
- const api = new PaymentAPI2(billingApiUrl);
3169
+ const api = new PaymentAPI3(billingApiUrl);
2418
3170
  const unified = await api.getUnifiedCheckoutSession(sessionId);
2419
- const refreshedToken = unified.provider === "stripe" ? unified.data.stripe?.clientSecret : void 0;
3171
+ const refreshedToken = unified.data.stripe?.clientSecret;
2420
3172
  if (isStripePaymentIntentClientSecret(refreshedToken)) {
2421
3173
  return {
2422
3174
  type: "3ds_required",
@@ -2442,7 +3194,7 @@ async function processSavedPaymentForMode({
2442
3194
  const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
2443
3195
  const country = session.customer?.country ?? session.accountData?.country ?? void 0;
2444
3196
  const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
2445
- const api = new PaymentAPI2(baseUrl);
3197
+ const api = new PaymentAPI3(baseUrl);
2446
3198
  const response = await retryOnceOnFetchFailure(() => api.processPayment(customerId, {
2447
3199
  sessionId: resolvedSessionId,
2448
3200
  tokenizedData,
@@ -2471,7 +3223,7 @@ async function processSavedPaymentForMode({
2471
3223
  if (json?.type === "paypal_redirect_required" || json?.type === "3ds_required") {
2472
3224
  const redirectToken = getRedirectTokenFromProcessResponse(json);
2473
3225
  if (!redirectToken) {
2474
- throw new FloPayError3(
3226
+ throw new FloPayError4(
2475
3227
  "Authentication is required but no redirect token was provided.",
2476
3228
  "api_error",
2477
3229
  { code: "authentication_required" }
@@ -2493,7 +3245,7 @@ async function processSavedPaymentForMode({
2493
3245
  return recoveredRedirect;
2494
3246
  }
2495
3247
  throw Object.assign(
2496
- new FloPayError3(
3248
+ new FloPayError4(
2497
3249
  "Your card requires authentication. Please enter your payment details below.",
2498
3250
  "api_error",
2499
3251
  { code: "authentication_required" }
@@ -2501,7 +3253,7 @@ async function processSavedPaymentForMode({
2501
3253
  { checkoutMethod: "card" }
2502
3254
  );
2503
3255
  }
2504
- throw new FloPayError3(
3256
+ throw new FloPayError4(
2505
3257
  json?.message ?? "Payment failed. Please try again.",
2506
3258
  "api_error",
2507
3259
  {
@@ -2521,13 +3273,13 @@ async function processSavedPaymentWithIntent({
2521
3273
  const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
2522
3274
  if (!customerEmail) {
2523
3275
  throw Object.assign(
2524
- new FloPayError3("Customer email is required to create a payment intent.", "validation_error", {
3276
+ new FloPayError4("Customer email is required to create a payment intent.", "validation_error", {
2525
3277
  param: "email"
2526
3278
  }),
2527
3279
  { checkoutMethod: "card" }
2528
3280
  );
2529
3281
  }
2530
- const api = new PaymentAPI2(billingApiUrl);
3282
+ const api = new PaymentAPI3(billingApiUrl);
2531
3283
  const intentResponse = await retryOnceOnFetchFailure(() => api.createPaymentIntent(
2532
3284
  sessionId,
2533
3285
  customerEmail,
@@ -2549,7 +3301,7 @@ async function processSavedPaymentWithIntent({
2549
3301
  });
2550
3302
  if (!intentClientSecret) {
2551
3303
  throw Object.assign(
2552
- new FloPayError3("No client secret in payment intent response.", "api_error"),
3304
+ new FloPayError4("No client secret in payment intent response.", "api_error"),
2553
3305
  { checkoutMethod: "card" }
2554
3306
  );
2555
3307
  }
@@ -2572,7 +3324,7 @@ async function processSavedPaymentWithIntent({
2572
3324
  const confirmedPaymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? paymentMethodId;
2573
3325
  if (!confirmedPaymentIntentId || confirmResult.status !== "succeeded" && confirmResult.status !== "processing" && confirmResult.status !== "requires_capture") {
2574
3326
  throw Object.assign(
2575
- new FloPayError3(
3327
+ new FloPayError4(
2576
3328
  `Unfortunately, your payment could not be processed. Please try again using a different payment method or contact your bank for assistance. If the issue persists, feel free to reach out to us for support.`,
2577
3329
  "api_error",
2578
3330
  {
@@ -2620,12 +3372,12 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2620
3372
  }) {
2621
3373
  const stripe = flopay?.getRawProvider();
2622
3374
  if (!stripe) {
2623
- throw new FloPayError3("Payment provider is not available.", "api_error");
3375
+ throw new FloPayError4("Payment provider is not available.", "api_error");
2624
3376
  }
2625
3377
  if (redirectResult.type === "3ds_required") {
2626
3378
  if (!attempt3DS || !redirectResult.threeDSecureToken) {
2627
3379
  throw Object.assign(
2628
- new FloPayError3(
3380
+ new FloPayError4(
2629
3381
  "Your card requires authentication. Please enter your payment details below.",
2630
3382
  "api_error",
2631
3383
  { code: "authentication_required" }
@@ -2641,7 +3393,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2641
3393
  );
2642
3394
  if (retrieveError) {
2643
3395
  throw Object.assign(
2644
- new FloPayError3(
3396
+ new FloPayError4(
2645
3397
  retrieveError.message ?? "Failed to retrieve 3DS payment status.",
2646
3398
  "api_error",
2647
3399
  { code: retrieveError.code }
@@ -2667,7 +3419,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2667
3419
  );
2668
3420
  if (confirmError) {
2669
3421
  throw Object.assign(
2670
- new FloPayError3(
3422
+ new FloPayError4(
2671
3423
  confirmError.message ?? "3DS authentication failed.",
2672
3424
  "api_error",
2673
3425
  { code: confirmError.code }
@@ -2682,7 +3434,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2682
3434
  });
2683
3435
  if (nextActionError) {
2684
3436
  throw Object.assign(
2685
- new FloPayError3(
3437
+ new FloPayError4(
2686
3438
  nextActionError.message ?? "3DS authentication failed.",
2687
3439
  "api_error",
2688
3440
  { code: nextActionError.code }
@@ -2722,7 +3474,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2722
3474
  });
2723
3475
  }
2724
3476
  throw Object.assign(
2725
- new FloPayError3("3DS authentication did not complete successfully.", "api_error"),
3477
+ new FloPayError4("3DS authentication did not complete successfully.", "api_error"),
2726
3478
  { checkoutMethod: "card" }
2727
3479
  );
2728
3480
  }
@@ -2730,7 +3482,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2730
3482
  const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
2731
3483
  if (!paypalStripe) {
2732
3484
  throw Object.assign(
2733
- new FloPayError3("PayPal is not available.", "api_error"),
3485
+ new FloPayError4("PayPal is not available.", "api_error"),
2734
3486
  { checkoutMethod: "paypal" }
2735
3487
  );
2736
3488
  }
@@ -2740,7 +3492,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2740
3492
  });
2741
3493
  if (error2) {
2742
3494
  throw Object.assign(
2743
- new FloPayError3(
3495
+ new FloPayError4(
2744
3496
  error2.message ?? "PayPal authorization failed.",
2745
3497
  "api_error",
2746
3498
  { code: error2.code }
@@ -2763,7 +3515,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2763
3515
  });
2764
3516
  if (error) {
2765
3517
  throw Object.assign(
2766
- new FloPayError3(
3518
+ new FloPayError4(
2767
3519
  error.message ?? "PayPal authorization failed.",
2768
3520
  "api_error",
2769
3521
  { code: error.code }
@@ -2776,33 +3528,48 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
2776
3528
  checkoutMethod: "paypal"
2777
3529
  };
2778
3530
  }
2779
- throw new FloPayError3("Unsupported payment redirect state.", "api_error");
3531
+ throw new FloPayError4("Unsupported payment redirect state.", "api_error");
2780
3532
  }
2781
3533
  function normalizeSavedPaymentError(err) {
2782
- if (err instanceof FloPayError3) {
3534
+ if (err instanceof FloPayError4) {
3535
+ const friendly = applyFriendlyMessageOverride(err.message);
3536
+ if (friendly && friendly !== err.message) {
3537
+ return Object.assign(
3538
+ new FloPayError4(friendly, err.type, {
3539
+ code: err.code,
3540
+ declineCode: err.declineCode,
3541
+ param: err.param,
3542
+ statusCode: err.statusCode
3543
+ }),
3544
+ { checkoutMethod: err.checkoutMethod }
3545
+ );
3546
+ }
2783
3547
  return err;
2784
3548
  }
2785
- return new FloPayError3(
2786
- err instanceof Error ? err.message : "Payment failed. Please try again.",
2787
- "api_error"
2788
- );
3549
+ const rawMessage = err instanceof Error ? err.message : "Payment failed. Please try again.";
3550
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
3551
+ return new FloPayError4(message, "api_error");
2789
3552
  }
2790
3553
  function resolveSavedPaymentPublishableKeys(unified) {
2791
- let publishableKey;
2792
- let paypalPublishableKey;
2793
- if (unified.provider === "stripe") {
2794
- publishableKey = unified.data.stripe?.publishableKey;
2795
- paypalPublishableKey = unified.data.stripe?.paypalPublishableKey ?? void 0;
2796
- }
2797
- if (!publishableKey) {
2798
- throw new FloPayError3(
2799
- "No publishable key found in the checkout session. Ensure the session includes gatewayData.publishableKey.",
3554
+ const publishableKey = unified.data.stripe?.publishableKey;
3555
+ const hasDirectPaypal = Boolean(unified.data.paypal?.publishableKey);
3556
+ if (!publishableKey && !hasDirectPaypal) {
3557
+ throw new FloPayError4(
3558
+ "Session advertises no supported gateways (expected `gateways.stripe` and/or `gateways.paypal`).",
2800
3559
  "validation_error"
2801
3560
  );
2802
3561
  }
3562
+ if (!publishableKey) {
3563
+ return {};
3564
+ }
2803
3565
  return {
2804
3566
  publishableKey,
2805
- paypalPublishableKey
3567
+ // Direct-PayPal sessions can still use Stripe's PayPal Element for the
3568
+ // saved-PM redirect leg. Prefer the dedicated Stripe-PayPal sub-account
3569
+ // publishable key when the backend advertises one; fall back to the
3570
+ // primary Stripe publishable key so the resume flow still has a Stripe
3571
+ // instance to drive Stripe's PayPal PI.
3572
+ paypalPublishableKey: unified.data.stripe?.paypalPublishableKey ?? publishableKey
2806
3573
  };
2807
3574
  }
2808
3575
  async function loadSavedPaymentProviders({
@@ -2811,6 +3578,9 @@ async function loadSavedPaymentProviders({
2811
3578
  billingApiUrl,
2812
3579
  locale
2813
3580
  }) {
3581
+ if (!publishableKey) {
3582
+ return { flopay: null, paypalFlopay: null };
3583
+ }
2814
3584
  const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
2815
3585
  const [instance, paypalInstanceOrError] = await Promise.all([
2816
3586
  loadFloPay(publishableKey, {
@@ -2832,13 +3602,26 @@ async function loadSavedPaymentProviders({
2832
3602
  }
2833
3603
 
2834
3604
  // src/flopay-checkout.tsx
2835
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
3605
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2836
3606
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
2837
3607
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
2838
3608
  var sessionInflightMap = /* @__PURE__ */ new Map();
2839
3609
  function sleep(ms) {
2840
3610
  return new Promise((resolve) => setTimeout(resolve, ms));
2841
3611
  }
3612
+ function resolveDirectPaypalConfig(unified) {
3613
+ const clientId = unified?.data.paypal?.publishableKey;
3614
+ if (!clientId) return void 0;
3615
+ return {
3616
+ clientId,
3617
+ environment: unified?.data.paypal?.environment
3618
+ };
3619
+ }
3620
+ function isPayPalOnlyUnified(unified) {
3621
+ if (!unified) return false;
3622
+ if (unified.data.stripe?.publishableKey) return false;
3623
+ return Boolean(resolveDirectPaypalConfig(unified)?.clientId);
3624
+ }
2842
3625
  function canUseStorage() {
2843
3626
  return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
2844
3627
  }
@@ -2912,6 +3695,7 @@ function FloPayCheckout({
2912
3695
  showPayPal = true,
2913
3696
  showApplePay = true,
2914
3697
  showGooglePay = true,
3698
+ debug = false,
2915
3699
  layout,
2916
3700
  buttonsTheme,
2917
3701
  buttonsStyles,
@@ -2935,37 +3719,37 @@ function FloPayCheckout({
2935
3719
  const resolvedBillingUrl = resolveBillingApiUrl3(billingApiUrl);
2936
3720
  const checkoutType = createSessionParams ? "embedded_checkout" : "standard_checkout";
2937
3721
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
2938
- const [unified, setUnified] = useState3(null);
2939
- const [flopay, setFloPay] = useState3(null);
2940
- const flopayRef = useRef3(null);
2941
- const [paypalFlopay, setPaypalFloPay] = useState3(null);
2942
- const paypalFlopayRef = useRef3(null);
2943
- const [session, setSession] = useState3(null);
2944
- const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
3722
+ const [unified, setUnified] = useState4(null);
3723
+ const [flopay, setFloPay] = useState4(null);
3724
+ const flopayRef = useRef4(null);
3725
+ const [paypalFlopay, setPaypalFloPay] = useState4(null);
3726
+ const paypalFlopayRef = useRef4(null);
3727
+ const [session, setSession] = useState4(null);
3728
+ const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
2945
3729
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
2946
3730
  const initSessionDependency = createSessionParams ? "" : activeSessionId;
2947
- const [isLoading, setIsLoading] = useState3(true);
2948
- const [loadError, setLoadError] = useState3(null);
2949
- const [currentMode, setCurrentMode] = useState3("full");
2950
- const [confirmProcessing, setConfirmProcessing] = useState3(false);
2951
- const [modeError, setModeError] = useState3(initialErrorMessage);
2952
- const [modeOverlayStatus, setModeOverlayStatus] = useState3(null);
2953
- const [modeOverlayError, setModeOverlayError] = useState3(null);
2954
- const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
2955
- const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState3("");
2956
- const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
2957
- const autoCheckoutAttempted = useRef3(false);
2958
- const paypalResumeAttempted = useRef3(false);
2959
- const savedPaymentKeysRef = useRef3(null);
2960
- const onCompleteRef = useRef3(onComplete);
3731
+ const [isLoading, setIsLoading] = useState4(true);
3732
+ const [loadError, setLoadError] = useState4(null);
3733
+ const [currentMode, setCurrentMode] = useState4("full");
3734
+ const [confirmProcessing, setConfirmProcessing] = useState4(false);
3735
+ const [modeError, setModeError] = useState4(initialErrorMessage);
3736
+ const [modeOverlayStatus, setModeOverlayStatus] = useState4(null);
3737
+ const [modeOverlayError, setModeOverlayError] = useState4(null);
3738
+ const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
3739
+ const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
3740
+ const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
3741
+ const autoCheckoutAttempted = useRef4(false);
3742
+ const paypalResumeAttempted = useRef4(false);
3743
+ const savedPaymentKeysRef = useRef4(null);
3744
+ const onCompleteRef = useRef4(onComplete);
2961
3745
  onCompleteRef.current = onComplete;
2962
- const onErrorRef = useRef3(onError);
3746
+ const onErrorRef = useRef4(onError);
2963
3747
  onErrorRef.current = onError;
2964
- const onDeclineRef = useRef3(onDecline);
3748
+ const onDeclineRef = useRef4(onDecline);
2965
3749
  onDeclineRef.current = onDecline;
2966
- const onSessionCompletedRef = useRef3(onSessionCompleted);
3750
+ const onSessionCompletedRef = useRef4(onSessionCompleted);
2967
3751
  onSessionCompletedRef.current = onSessionCompleted;
2968
- useEffect4(() => {
3752
+ useEffect5(() => {
2969
3753
  console.info("[FloPay] Checkout initialized", {
2970
3754
  sdk_version: SDK_VERSION,
2971
3755
  checkout_type: checkoutType,
@@ -2973,31 +3757,31 @@ function FloPayCheckout({
2973
3757
  billing_api_url: resolvedBillingUrl
2974
3758
  });
2975
3759
  }, [checkoutLayout, checkoutType, resolvedBillingUrl]);
2976
- const baseCreateSessionHash = useMemo3(
3760
+ const baseCreateSessionHash = useMemo4(
2977
3761
  () => createSessionParams ? hashCreateParams(createSessionParams) : "",
2978
3762
  [createSessionParams]
2979
3763
  );
2980
- const activeCreateSessionPatch = useMemo3(
3764
+ const activeCreateSessionPatch = useMemo4(
2981
3765
  () => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
2982
3766
  [createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
2983
3767
  );
2984
- const effectiveCreateSessionBase = useMemo3(
3768
+ const effectiveCreateSessionBase = useMemo4(
2985
3769
  () => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
2986
3770
  [createSessionParams, activeCreateSessionPatch]
2987
3771
  );
2988
3772
  const effectiveCreateSessionMode = checkoutModeProp ?? effectiveCreateSessionBase?.checkoutMode ?? "full";
2989
- const effectiveCreateSession = useMemo3(
3773
+ const effectiveCreateSession = useMemo4(
2990
3774
  () => effectiveCreateSessionBase ? {
2991
3775
  ...effectiveCreateSessionBase,
2992
3776
  checkoutMode: effectiveCreateSessionMode
2993
3777
  } : void 0,
2994
3778
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
2995
3779
  );
2996
- useEffect4(() => {
3780
+ useEffect5(() => {
2997
3781
  setCreateSessionPatch(void 0);
2998
3782
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
2999
3783
  }, [baseCreateSessionHash]);
3000
- useEffect4(() => {
3784
+ useEffect5(() => {
3001
3785
  setModeError(initialErrorMessage);
3002
3786
  }, [initialErrorMessage]);
3003
3787
  const emitDecline = useCallback2(
@@ -3016,7 +3800,7 @@ function FloPayCheckout({
3016
3800
  const redirectResult = getRedirectResultFromCheckoutProcessError(options?.initialAutoProcessingError);
3017
3801
  let paymentResult;
3018
3802
  if (redirectResult) {
3019
- if (redirectResult.type === "paypal_redirect_required" && savedPaymentKeysRef.current) {
3803
+ if (redirectResult.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
3020
3804
  persistPayPalResumeState({
3021
3805
  sessionId: activeSessionId2,
3022
3806
  publishableKey: savedPaymentKeysRef.current.publishableKey,
@@ -3035,12 +3819,12 @@ function FloPayCheckout({
3035
3819
  clearPayPalResumeState();
3036
3820
  }
3037
3821
  } else if (options?.initialAutoProcessingPending) {
3038
- const api = new PaymentAPI3(resolvedBillingUrl);
3822
+ const api = new PaymentAPI4(resolvedBillingUrl);
3039
3823
  const completed = await api.waitForCheckoutSessionCompletion(options.initialAutoProcessingPending.sessionId, {
3040
3824
  initialDelayMs: options.initialAutoProcessingPending.retryAfterMs
3041
3825
  });
3042
3826
  if (completed.data.session?.status !== "complete") {
3043
- throw new FloPayError4("Automatic payment failed. Please try again.", "api_error", {
3827
+ throw new FloPayError5("Automatic payment failed. Please try again.", "api_error", {
3044
3828
  code: completed.data.session?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
3045
3829
  });
3046
3830
  }
@@ -3068,7 +3852,7 @@ function FloPayCheckout({
3068
3852
  if (result.type === "success") {
3069
3853
  paymentResult = result.result;
3070
3854
  } else {
3071
- if (result.type === "paypal_redirect_required" && savedPaymentKeysRef.current) {
3855
+ if (result.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
3072
3856
  persistPayPalResumeState({
3073
3857
  sessionId: activeSessionId2,
3074
3858
  publishableKey: savedPaymentKeysRef.current.publishableKey,
@@ -3124,7 +3908,7 @@ function FloPayCheckout({
3124
3908
  resolvedBillingUrl
3125
3909
  ]
3126
3910
  );
3127
- useEffect4(() => {
3911
+ useEffect5(() => {
3128
3912
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
3129
3913
  return;
3130
3914
  }
@@ -3146,7 +3930,7 @@ function FloPayCheckout({
3146
3930
  try {
3147
3931
  if (params.get("redirect_status") === "failed") {
3148
3932
  throw Object.assign(
3149
- new FloPayError4("PayPal payment was declined. Please try again.", "api_error"),
3933
+ new FloPayError5("PayPal payment was declined. Please try again.", "api_error"),
3150
3934
  { checkoutMethod: "paypal" }
3151
3935
  );
3152
3936
  }
@@ -3159,17 +3943,17 @@ function FloPayCheckout({
3159
3943
  billingApiUrl: resolvedBillingUrl,
3160
3944
  locale
3161
3945
  });
3162
- const paypalStripe = (resumePaypalFlopay ?? resumeFlopay).getRawProvider();
3946
+ const paypalStripe = (resumePaypalFlopay ?? resumeFlopay)?.getRawProvider();
3163
3947
  if (!paypalStripe) {
3164
3948
  throw Object.assign(
3165
- new FloPayError4("PayPal is not available.", "api_error"),
3949
+ new FloPayError5("PayPal is not available.", "api_error"),
3166
3950
  { checkoutMethod: "paypal" }
3167
3951
  );
3168
3952
  }
3169
3953
  const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
3170
3954
  if (error) {
3171
3955
  throw Object.assign(
3172
- new FloPayError4(
3956
+ new FloPayError5(
3173
3957
  error.message ?? "Failed to retrieve PayPal payment status.",
3174
3958
  "api_error",
3175
3959
  { code: error.code }
@@ -3180,14 +3964,14 @@ function FloPayCheckout({
3180
3964
  const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
3181
3965
  if (!paymentIntent || resultStatus === "failed") {
3182
3966
  throw Object.assign(
3183
- new FloPayError4("PayPal payment was not completed. Please try again.", "api_error"),
3967
+ new FloPayError5("PayPal payment was not completed. Please try again.", "api_error"),
3184
3968
  { checkoutMethod: "paypal" }
3185
3969
  );
3186
3970
  }
3187
3971
  const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
3188
3972
  let finalResultStatus = resultStatus;
3189
3973
  if (resumeState.sessionId) {
3190
- const resumeApi = new PaymentAPI3(resolvedBillingUrl);
3974
+ const resumeApi = new PaymentAPI4(resolvedBillingUrl);
3191
3975
  const resumeSessionResult = await resumeApi.getUnifiedCheckoutSession(resumeState.sessionId);
3192
3976
  const resumeSession = resumeSessionResult.data.session;
3193
3977
  if (resumeSession && resumeSession.status !== "complete") {
@@ -3204,7 +3988,7 @@ function FloPayCheckout({
3204
3988
  });
3205
3989
  if (processResult.type !== "success") {
3206
3990
  throw Object.assign(
3207
- new FloPayError4("Failed to finalize PayPal payment.", "api_error"),
3991
+ new FloPayError5("Failed to finalize PayPal payment.", "api_error"),
3208
3992
  { checkoutMethod: "paypal" }
3209
3993
  );
3210
3994
  }
@@ -3241,7 +4025,7 @@ function FloPayCheckout({
3241
4025
  }
3242
4026
  })();
3243
4027
  }, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
3244
- const initializedHashRef = useRef3(null);
4028
+ const initializedHashRef = useRef4(null);
3245
4029
  function hashCreateParams(params) {
3246
4030
  const key = JSON.stringify({
3247
4031
  c: params?.clientId,
@@ -3266,23 +4050,23 @@ function FloPayCheckout({
3266
4050
  }
3267
4051
  return `flopay_session_${Math.abs(h).toString(36)}`;
3268
4052
  }
3269
- const createSessionHash = useMemo3(
4053
+ const createSessionHash = useMemo4(
3270
4054
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
3271
4055
  [effectiveCreateSession]
3272
4056
  );
3273
- const createSessionParamsRef = useRef3(effectiveCreateSession);
4057
+ const createSessionParamsRef = useRef4(effectiveCreateSession);
3274
4058
  createSessionParamsRef.current = effectiveCreateSession;
3275
- useEffect4(() => {
4059
+ useEffect5(() => {
3276
4060
  setResolvedSessionId(sessionIdProp ?? "");
3277
4061
  }, [sessionIdProp]);
3278
- useEffect4(() => {
4062
+ useEffect5(() => {
3279
4063
  autoCheckoutAttempted.current = false;
3280
4064
  setModeError(initialErrorMessage);
3281
4065
  setModeOverlayError(null);
3282
4066
  setModeOverlayStatus(null);
3283
4067
  }, [createSessionHash, initialErrorMessage, sessionIdProp]);
3284
4068
  async function resolveInlineSession(params, cacheKey) {
3285
- const api = new PaymentAPI3(resolvedBillingUrl);
4069
+ const api = new PaymentAPI4(resolvedBillingUrl);
3286
4070
  let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
3287
4071
  let realResult = null;
3288
4072
  if (sid) {
@@ -3322,7 +4106,7 @@ function FloPayCheckout({
3322
4106
  async (patch) => {
3323
4107
  const baseParams = createSessionParamsRef.current;
3324
4108
  if (!baseParams) {
3325
- throw new FloPayError4("createSession is required to bootstrap checkout.", "validation_error");
4109
+ throw new FloPayError5("createSession is required to bootstrap checkout.", "validation_error");
3326
4110
  }
3327
4111
  const mergedParams = mergeInlineSessionPatch(baseParams, patch);
3328
4112
  const cacheKey = hashCreateParams(mergedParams);
@@ -3402,7 +4186,7 @@ function FloPayCheckout({
3402
4186
  resolvedSessionId,
3403
4187
  session
3404
4188
  ]);
3405
- useEffect4(() => {
4189
+ useEffect5(() => {
3406
4190
  let cancelled = false;
3407
4191
  setLoadError(null);
3408
4192
  if (createSessionHash) {
@@ -3427,7 +4211,8 @@ function FloPayCheckout({
3427
4211
  const resolved = await bootstrapInlineSession();
3428
4212
  const resolvedSession = resolved.result.data.session ?? null;
3429
4213
  savedPaymentKeysRef.current = resolveSavedPaymentPublishableKeys(resolved.result);
3430
- if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
4214
+ const resolvedPaypalOnly = isPayPalOnlyUnified(resolved.result);
4215
+ if (!cancelled && shouldAutoProcessInlineSession && resolvedSession && !resolvedPaypalOnly) {
3431
4216
  autoCheckoutAttempted.current = true;
3432
4217
  await runSavedPaymentFlow(resolvedSession, {
3433
4218
  attempt3DS: true,
@@ -3441,12 +4226,15 @@ function FloPayCheckout({
3441
4226
  return;
3442
4227
  }
3443
4228
  if (!cancelled && shouldAutoProcessInlineSession) {
4229
+ if (resolvedPaypalOnly) {
4230
+ autoCheckoutAttempted.current = true;
4231
+ }
3444
4232
  setModeOverlayStatus(null);
3445
4233
  setModeOverlayError(null);
3446
4234
  }
3447
4235
  } catch (err) {
3448
4236
  if (cancelled) return;
3449
- const errorCode = err instanceof FloPayError4 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
4237
+ const errorCode = err instanceof FloPayError5 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
3450
4238
  if (shouldAutoProcessInlineSession && errorCode === "session_auto_completed") {
3451
4239
  autoCheckoutAttempted.current = true;
3452
4240
  setModeOverlayStatus("success");
@@ -3462,7 +4250,7 @@ function FloPayCheckout({
3462
4250
  setModeOverlayStatus(null);
3463
4251
  setModeOverlayError(null);
3464
4252
  }
3465
- const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to create session", "api_error");
4253
+ const floPayErr = err instanceof FloPayError5 ? err : new FloPayError5(err instanceof Error ? err.message : "Failed to create session", "api_error");
3466
4254
  setLoadError(floPayErr);
3467
4255
  }
3468
4256
  })();
@@ -3473,14 +4261,14 @@ function FloPayCheckout({
3473
4261
  setIsLoading(true);
3474
4262
  async function init() {
3475
4263
  try {
3476
- const api = new PaymentAPI3(resolvedBillingUrl);
4264
+ const api = new PaymentAPI4(resolvedBillingUrl);
3477
4265
  const result = await api.getUnifiedCheckoutSession(activeSessionId);
3478
4266
  if (cancelled) return;
3479
4267
  setUnified(result);
3480
4268
  const sess = result.data.session ?? null;
3481
4269
  setSession(sess);
3482
4270
  if (!sess) {
3483
- throw new FloPayError4("No session data returned", "api_error");
4271
+ throw new FloPayError5("No session data returned", "api_error");
3484
4272
  }
3485
4273
  if (sess.status === "complete") {
3486
4274
  setIsLoading(false);
@@ -3488,14 +4276,15 @@ function FloPayCheckout({
3488
4276
  return;
3489
4277
  }
3490
4278
  if (sess.status === "expired") {
3491
- throw new FloPayError4("Checkout session has expired.", "api_error", {
4279
+ throw new FloPayError5("Checkout session has expired.", "api_error", {
3492
4280
  code: "checkout_session_expired"
3493
4281
  });
3494
4282
  }
3495
4283
  const effectiveMode = checkoutModeProp ?? sess.checkoutMode ?? "full";
3496
4284
  setCurrentMode(effectiveMode);
3497
4285
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
3498
- if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
4286
+ const paypalOnly = isPayPalOnlyUnified(result);
4287
+ if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams && !paypalOnly) {
3499
4288
  autoCheckoutAttempted.current = true;
3500
4289
  const stripeInitPromise = initStripe(result);
3501
4290
  try {
@@ -3522,7 +4311,7 @@ function FloPayCheckout({
3522
4311
  if (!cancelled) setIsLoading(false);
3523
4312
  } catch (err) {
3524
4313
  if (cancelled) return;
3525
- const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
4314
+ const floPayErr = err instanceof FloPayError5 ? err : new FloPayError5(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
3526
4315
  setLoadError(floPayErr);
3527
4316
  setIsLoading(false);
3528
4317
  }
@@ -3575,14 +4364,16 @@ function FloPayCheckout({
3575
4364
  setConfirmProcessing(false);
3576
4365
  }
3577
4366
  }, [activeSessionId, confirmProcessing, runSavedPaymentFlow, session]);
3578
- const providerOptions = useMemo3(() => {
3579
- if (!unified || !session) return void 0;
4367
+ const directPaypalConfig = resolveDirectPaypalConfig(unified);
4368
+ const isPaypalOnlySession = isPayPalOnlyUnified(unified);
4369
+ const providerOptions = useMemo4(() => {
4370
+ if (!unified || !session || !unified.data.stripe?.publishableKey) return void 0;
3580
4371
  const opts = {
3581
4372
  appearance,
3582
4373
  paymentMethodCreation: "manual",
3583
4374
  billingApiUrl: resolvedBillingUrl
3584
4375
  };
3585
- if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
4376
+ if (unified.data.stripe?.clientSecret) {
3586
4377
  opts.clientSecret = unified.data.stripe.clientSecret;
3587
4378
  } else {
3588
4379
  const displayTotal = buildCheckoutDisplayData(session).total;
@@ -3594,7 +4385,7 @@ function FloPayCheckout({
3594
4385
  const shouldHandleInlineSessionPatch = Boolean(
3595
4386
  createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && effectiveCreateSessionMode === "full"
3596
4387
  );
3597
- const checkoutValue = useMemo3(
4388
+ const checkoutValue = useMemo4(
3598
4389
  () => ({
3599
4390
  session,
3600
4391
  loading: isLoading,
@@ -3615,8 +4406,8 @@ function FloPayCheckout({
3615
4406
  cardBootstrapPending
3616
4407
  ]
3617
4408
  );
3618
- const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
3619
- const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx6(
4409
+ const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && !isPaypalOnlySession && (!flopay || !providerOptions);
4410
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx7(
3620
4411
  ProcessingOverlay,
3621
4412
  {
3622
4413
  status: modeOverlayStatus,
@@ -3625,31 +4416,31 @@ function FloPayCheckout({
3625
4416
  ) : null;
3626
4417
  if (isLoading) {
3627
4418
  if (loadingNode) {
3628
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
4419
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
3629
4420
  loadingNode,
3630
4421
  modeOverlay
3631
4422
  ] });
3632
4423
  }
3633
4424
  if (layout === "buttons") {
3634
- const skeletonBar = (h) => /* @__PURE__ */ jsx6("div", { style: {
4425
+ const skeletonBar = (h) => /* @__PURE__ */ jsx7("div", { style: {
3635
4426
  height: h,
3636
4427
  borderRadius: 8,
3637
4428
  background: "#e5e7eb",
3638
4429
  animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
3639
4430
  } });
3640
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3641
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
4431
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4432
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
3642
4433
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
3643
4434
  (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
3644
4435
  skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
3645
- /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
4436
+ /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
3646
4437
  ] }),
3647
4438
  modeOverlay
3648
4439
  ] });
3649
4440
  }
3650
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3651
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
3652
- /* @__PURE__ */ jsx6("div", { style: {
4441
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4442
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
4443
+ /* @__PURE__ */ jsx7("div", { style: {
3653
4444
  width: 24,
3654
4445
  height: 24,
3655
4446
  border: "2px solid #e5e7eb",
@@ -3657,16 +4448,16 @@ function FloPayCheckout({
3657
4448
  borderRadius: "50%",
3658
4449
  animation: "spin 0.6s linear infinite"
3659
4450
  } }),
3660
- /* @__PURE__ */ jsx6("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
4451
+ /* @__PURE__ */ jsx7("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
3661
4452
  ] }),
3662
4453
  modeOverlay
3663
4454
  ] });
3664
4455
  }
3665
4456
  if (loadError) {
3666
4457
  if (errorNode) {
3667
- return /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
4458
+ return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
3668
4459
  }
3669
- return /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
4460
+ return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
3670
4461
  "div",
3671
4462
  {
3672
4463
  style: {
@@ -3680,8 +4471,8 @@ function FloPayCheckout({
3680
4471
  ) });
3681
4472
  }
3682
4473
  if (shouldShowInterimButtons) {
3683
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3684
- /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
4474
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4475
+ /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(
3685
4476
  InterimButtonsView,
3686
4477
  {
3687
4478
  onButtonClick,
@@ -3698,13 +4489,55 @@ function FloPayCheckout({
3698
4489
  modeOverlay
3699
4490
  ] });
3700
4491
  }
4492
+ if (isPaypalOnlySession && session && directPaypalConfig) {
4493
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4494
+ /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs5("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
4495
+ modeError && /* @__PURE__ */ jsx7(
4496
+ "div",
4497
+ {
4498
+ role: "alert",
4499
+ "data-testid": "flopay-error",
4500
+ style: {
4501
+ padding: "0.625rem 0.875rem",
4502
+ background: "#FEF2F2",
4503
+ border: "1px solid #FECACA",
4504
+ borderRadius: "8px",
4505
+ color: "#991B1B",
4506
+ fontSize: "0.85rem",
4507
+ fontWeight: 600
4508
+ },
4509
+ children: modeError
4510
+ }
4511
+ ),
4512
+ /* @__PURE__ */ jsx7(
4513
+ DirectPayPalButton,
4514
+ {
4515
+ sessionId: activeSessionId,
4516
+ billingApiUrl: resolvedBillingUrl,
4517
+ email: session.customer?.email,
4518
+ clientId: directPaypalConfig.clientId,
4519
+ environment: directPaypalConfig.environment,
4520
+ currency: (session.currency ?? "usd").toUpperCase(),
4521
+ isSubscription: session.mode === "subscription",
4522
+ onComplete,
4523
+ onErrorChange: setModeError,
4524
+ onDecline,
4525
+ onButtonClick,
4526
+ session,
4527
+ debug
4528
+ }
4529
+ )
4530
+ ] }) }),
4531
+ modeOverlay
4532
+ ] });
4533
+ }
3701
4534
  if (!flopay || !providerOptions) {
3702
- return /* @__PURE__ */ jsx6(Fragment3, { children: modeOverlay });
4535
+ return /* @__PURE__ */ jsx7(Fragment3, { children: modeOverlay });
3703
4536
  }
3704
4537
  if (currentMode === "confirm") {
3705
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3706
- /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs4("div", { className, children: [
3707
- modeError && /* @__PURE__ */ jsx6(
4538
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4539
+ /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs5("div", { className, children: [
4540
+ modeError && /* @__PURE__ */ jsx7(
3708
4541
  "div",
3709
4542
  {
3710
4543
  style: {
@@ -3719,7 +4552,7 @@ function FloPayCheckout({
3719
4552
  renderConfirmButton ? renderConfirmButton({
3720
4553
  onConfirm: handleConfirmCheckout,
3721
4554
  isProcessing: confirmProcessing
3722
- }) : /* @__PURE__ */ jsx6(
4555
+ }) : /* @__PURE__ */ jsx7(
3723
4556
  "button",
3724
4557
  {
3725
4558
  type: "button",
@@ -3744,9 +4577,9 @@ function FloPayCheckout({
3744
4577
  modeOverlay
3745
4578
  ] });
3746
4579
  }
3747
- return /* @__PURE__ */ jsxs4(Fragment3, { children: [
3748
- /* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
3749
- modeError && /* @__PURE__ */ jsx6(
4580
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
4581
+ /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx7(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
4582
+ modeError && /* @__PURE__ */ jsx7(
3750
4583
  "div",
3751
4584
  {
3752
4585
  style: {
@@ -3761,7 +4594,7 @@ function FloPayCheckout({
3761
4594
  children: modeError
3762
4595
  }
3763
4596
  ),
3764
- /* @__PURE__ */ jsx6(
4597
+ /* @__PURE__ */ jsx7(
3765
4598
  SessionInjector,
3766
4599
  {
3767
4600
  sessionId: activeSessionId,
@@ -3770,7 +4603,7 @@ function FloPayCheckout({
3770
4603
  children
3771
4604
  }
3772
4605
  )
3773
- ] }) : /* @__PURE__ */ jsx6(
4606
+ ] }) : /* @__PURE__ */ jsx7(
3774
4607
  SplitCardForm,
3775
4608
  {
3776
4609
  sessionId: activeSessionId,
@@ -3809,7 +4642,11 @@ function FloPayCheckout({
3809
4642
  checkoutType: createSessionParams ? "embedded_checkout" : "standard_checkout",
3810
4643
  checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout",
3811
4644
  submitLabel,
3812
- className
4645
+ className,
4646
+ directPaypal: resolveDirectPaypalConfig(unified),
4647
+ isSubscription: session?.mode === "subscription",
4648
+ session,
4649
+ debug
3813
4650
  }
3814
4651
  ) }) }),
3815
4652
  modeOverlay
@@ -3821,8 +4658,8 @@ function SessionInjector({
3821
4658
  session,
3822
4659
  children
3823
4660
  }) {
3824
- return /* @__PURE__ */ jsx6(Fragment3, { children: React6.Children.map(children, (child) => {
3825
- if (!React6.isValidElement(child)) return child;
4661
+ return /* @__PURE__ */ jsx7(Fragment3, { children: React7.Children.map(children, (child) => {
4662
+ if (!React7.isValidElement(child)) return child;
3826
4663
  const existing = child.props;
3827
4664
  const injected = {};
3828
4665
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -3836,7 +4673,7 @@ function SessionInjector({
3836
4673
  injected.lastName = session.customer.lastName;
3837
4674
  }
3838
4675
  if (Object.keys(injected).length === 0) return child;
3839
- return React6.cloneElement(child, injected);
4676
+ return React7.cloneElement(child, injected);
3840
4677
  }) });
3841
4678
  }
3842
4679
  function InterimButtonsView({
@@ -3854,14 +4691,14 @@ function InterimButtonsView({
3854
4691
  cardBackButtonContent,
3855
4692
  cardTitleContent
3856
4693
  }) {
3857
- const [showCardForm, setShowCardForm] = useState3(false);
4694
+ const [showCardForm, setShowCardForm] = useState4(false);
3858
4695
  const isCardOpenControlled = typeof cardOpen === "boolean";
3859
- useEffect4(() => {
4696
+ useEffect5(() => {
3860
4697
  if (isCardOpenControlled) {
3861
4698
  setShowCardForm(cardOpen);
3862
4699
  }
3863
4700
  }, [cardOpen, isCardOpenControlled]);
3864
- const bStyles = useMemo3(() => {
4701
+ const bStyles = useMemo4(() => {
3865
4702
  const base = resolveButtonsLayoutTheme2(buttonsTheme);
3866
4703
  if (!stylesOverride) return base;
3867
4704
  return {
@@ -3875,7 +4712,7 @@ function InterimButtonsView({
3875
4712
  title: { ...base.title, ...stylesOverride.title }
3876
4713
  };
3877
4714
  }, [buttonsTheme, stylesOverride]);
3878
- const skeleton = (h) => /* @__PURE__ */ jsx6("div", { style: {
4715
+ const skeleton = (h) => /* @__PURE__ */ jsx7("div", { style: {
3879
4716
  height: h,
3880
4717
  borderRadius: 8,
3881
4718
  background: "#e5e7eb",
@@ -3887,15 +4724,15 @@ function InterimButtonsView({
3887
4724
  const inputBg = bStyles.cardInputBackground ?? "white";
3888
4725
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
3889
4726
  const hideTitle = isEmptySlotContent(cardTitleContent);
3890
- return /* @__PURE__ */ jsxs4("div", { style: {
4727
+ return /* @__PURE__ */ jsxs5("div", { style: {
3891
4728
  backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
3892
4729
  borderRadius: "8px",
3893
4730
  animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
3894
4731
  overflow: "hidden",
3895
4732
  ...bStyles.cardFormContainer
3896
4733
  }, children: [
3897
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
3898
- /* @__PURE__ */ jsxs4(
4734
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
4735
+ /* @__PURE__ */ jsxs5(
3899
4736
  "button",
3900
4737
  {
3901
4738
  type: "button",
@@ -3922,7 +4759,7 @@ function InterimButtonsView({
3922
4759
  ...bStyles.backButton
3923
4760
  },
3924
4761
  children: [
3925
- /* @__PURE__ */ jsx6("span", { style: {
4762
+ /* @__PURE__ */ jsx7("span", { style: {
3926
4763
  display: "inline-flex",
3927
4764
  alignItems: "center",
3928
4765
  justifyContent: "center",
@@ -3931,12 +4768,12 @@ function InterimButtonsView({
3931
4768
  borderRadius: "50%",
3932
4769
  backgroundColor: "#f3f4f6",
3933
4770
  ...bStyles.backButtonIcon
3934
- }, children: /* @__PURE__ */ jsx6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "M15 18l-6-6 6-6" }) }) }),
3935
- /* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
4771
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
4772
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
3936
4773
  ]
3937
4774
  }
3938
4775
  ),
3939
- hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
4776
+ hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
3940
4777
  flex: 1,
3941
4778
  textAlign: "center",
3942
4779
  fontWeight: 600,
@@ -3944,15 +4781,15 @@ function InterimButtonsView({
3944
4781
  color: "#262833",
3945
4782
  paddingRight: 80,
3946
4783
  ...bStyles.title
3947
- }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
4784
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
3948
4785
  ] }),
3949
- /* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3950
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
3951
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3952
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
4786
+ /* @__PURE__ */ jsx7("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
4787
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex" }, children: [
4788
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
4789
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
3953
4790
  ] }),
3954
- /* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
3955
- /* @__PURE__ */ jsx6("div", { style: {
4791
+ /* @__PURE__ */ jsx7("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx7("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
4792
+ /* @__PURE__ */ jsx7("div", { style: {
3956
4793
  height: 50,
3957
4794
  borderRadius: 8,
3958
4795
  marginTop: 16,
@@ -3961,7 +4798,7 @@ function InterimButtonsView({
3961
4798
  ...bStyles.submitButton,
3962
4799
  opacity: 0.5
3963
4800
  } }),
3964
- /* @__PURE__ */ jsx6("style", { children: `
4801
+ /* @__PURE__ */ jsx7("style", { children: `
3965
4802
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
3966
4803
  @keyframes flopay-interim-expand {
3967
4804
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -3971,10 +4808,10 @@ function InterimButtonsView({
3971
4808
  ` })
3972
4809
  ] });
3973
4810
  }
3974
- return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
4811
+ return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
3975
4812
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
3976
4813
  (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
3977
- /* @__PURE__ */ jsx6(
4814
+ /* @__PURE__ */ jsx7(
3978
4815
  "button",
3979
4816
  {
3980
4817
  type: "button",
@@ -4014,10 +4851,10 @@ function InterimButtonsView({
4014
4851
  onMouseUp: (e) => {
4015
4852
  e.currentTarget.style.transform = "scale(1)";
4016
4853
  },
4017
- children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
4854
+ children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
4018
4855
  }
4019
4856
  ),
4020
- errorMessage && /* @__PURE__ */ jsxs4("div", { style: {
4857
+ errorMessage && /* @__PURE__ */ jsxs5("div", { style: {
4021
4858
  margin: "0.25rem 0",
4022
4859
  padding: "0.625rem 0.875rem",
4023
4860
  background: "#FEF2F2",
@@ -4031,22 +4868,22 @@ function InterimButtonsView({
4031
4868
  gap: "0.5rem",
4032
4869
  ...bStyles.errorBanner
4033
4870
  }, children: [
4034
- /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("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" }) }),
4871
+ /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("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" }) }),
4035
4872
  errorMessage
4036
4873
  ] }),
4037
- /* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
4874
+ /* @__PURE__ */ jsx7("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
4038
4875
  ] });
4039
4876
  }
4040
4877
 
4041
4878
  // src/checkout-form.tsx
4042
- import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
4043
- import { FloPayError as FloPayError5 } from "@flopay/shared";
4044
- import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useState as useState4 } from "react";
4045
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4879
+ import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
4880
+ import { FloPayError as FloPayError6 } from "@flopay/shared";
4881
+ import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect6, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
4882
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4046
4883
  var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
4047
4884
  var CheckoutForm = forwardRef2(
4048
4885
  function CheckoutForm2(props, ref) {
4049
- return /* @__PURE__ */ jsx7(CheckoutFormInner, { ...props, innerRef: ref });
4886
+ return /* @__PURE__ */ jsx8(CheckoutFormInner, { ...props, innerRef: ref });
4050
4887
  }
4051
4888
  );
4052
4889
  function CheckoutFormInner({
@@ -4075,9 +4912,9 @@ function CheckoutFormInner({
4075
4912
  const paypalFlopay = usePayPalFloPay();
4076
4913
  const elements = useElements();
4077
4914
  const contextBillingUrl = useBillingApiUrl();
4078
- const [processing, setProcessing] = useState4(false);
4079
- const [error, setError] = useState4(null);
4080
- const [is3DSActive, setIs3DSActive] = useState4(false);
4915
+ const [processing, setProcessing] = useState5(false);
4916
+ const [error, setError] = useState5(null);
4917
+ const [is3DSActive, setIs3DSActive] = useState5(false);
4081
4918
  const displayError = externalError ?? error;
4082
4919
  const isSubmitting = externalProcessing ?? processing;
4083
4920
  const isSelfContained = !onTokenizedBody;
@@ -4102,7 +4939,7 @@ function CheckoutFormInner({
4102
4939
  const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
4103
4940
  const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
4104
4941
  try {
4105
- const api = new PaymentAPI4(baseUrl);
4942
+ const api = new PaymentAPI5(baseUrl);
4106
4943
  const response = await api.processPayment(userId ?? "", {
4107
4944
  sessionId,
4108
4945
  tokenizedData: requestTokenizedBody,
@@ -4243,7 +5080,7 @@ function CheckoutFormInner({
4243
5080
  }
4244
5081
  }
4245
5082
  }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
4246
- useEffect5(() => {
5083
+ useEffect6(() => {
4247
5084
  if (typeof window === "undefined") return;
4248
5085
  const stored = localStorage.getItem(WALLET_RESUME_KEY2);
4249
5086
  if (!stored) return;
@@ -4285,7 +5122,7 @@ function CheckoutFormInner({
4285
5122
  return;
4286
5123
  }
4287
5124
  if (!sessionId || !email) {
4288
- throw new FloPayError5("Missing sessionId or email", "validation_error");
5125
+ throw new FloPayError6("Missing sessionId or email", "validation_error");
4289
5126
  }
4290
5127
  const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
4291
5128
  method: "POST",
@@ -4309,7 +5146,7 @@ function CheckoutFormInner({
4309
5146
  }
4310
5147
  const intentJson = await intentResponse.json();
4311
5148
  const intentClientSecret = intentJson.data?.id;
4312
- if (!intentClientSecret) throw new FloPayError5("No client_secret in payment intent response", "api_error");
5149
+ if (!intentClientSecret) throw new FloPayError6("No client_secret in payment intent response", "api_error");
4313
5150
  const confirmResult = await flopay.confirmCardPayment({
4314
5151
  clientSecret: intentClientSecret,
4315
5152
  paymentMethodId: pmResult.paymentMethodId
@@ -4328,7 +5165,7 @@ function CheckoutFormInner({
4328
5165
  const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
4329
5166
  const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
4330
5167
  if (!paymentIntentId) {
4331
- const error2 = new FloPayError5("No payment intent returned after confirmation.", "api_error");
5168
+ const error2 = new FloPayError6("No payment intent returned after confirmation.", "api_error");
4332
5169
  updateError(error2.message);
4333
5170
  onError?.(error2);
4334
5171
  return;
@@ -4351,8 +5188,8 @@ function CheckoutFormInner({
4351
5188
  [flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
4352
5189
  );
4353
5190
  const isReady = flopay !== null && elements !== null;
4354
- return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
4355
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-overlay", style: {
5191
+ return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5192
+ (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-overlay", style: {
4356
5193
  position: "absolute",
4357
5194
  inset: 0,
4358
5195
  background: "rgba(255,255,255,0.7)",
@@ -4361,12 +5198,12 @@ function CheckoutFormInner({
4361
5198
  justifyContent: "center",
4362
5199
  zIndex: 10
4363
5200
  }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
4364
- !isReady && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
4365
- isReady && /* @__PURE__ */ jsxs5(Fragment4, { children: [
4366
- /* @__PURE__ */ jsx7(PaymentElement, { options: { layout } }),
4367
- showAddress && /* @__PURE__ */ jsx7(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
4368
- displayError && /* @__PURE__ */ jsx7("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
4369
- children ?? /* @__PURE__ */ jsx7(
5201
+ !isReady && /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
5202
+ isReady && /* @__PURE__ */ jsxs6(Fragment4, { children: [
5203
+ /* @__PURE__ */ jsx8(PaymentElement, { options: { layout } }),
5204
+ showAddress && /* @__PURE__ */ jsx8(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
5205
+ displayError && /* @__PURE__ */ jsx8("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
5206
+ children ?? /* @__PURE__ */ jsx8(
4370
5207
  "button",
4371
5208
  {
4372
5209
  type: "submit",
@@ -4380,10 +5217,10 @@ function CheckoutFormInner({
4380
5217
  }
4381
5218
 
4382
5219
  // src/paypal-button.tsx
4383
- import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
4384
- import { FloPayError as FloPayError6 } from "@flopay/shared";
4385
- import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
4386
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
5220
+ import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
5221
+ import { FloPayError as FloPayError7 } from "@flopay/shared";
5222
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState6 } from "react";
5223
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
4387
5224
  function PayPalButton({
4388
5225
  sessionId,
4389
5226
  billingApiUrl,
@@ -4400,14 +5237,14 @@ function PayPalButton({
4400
5237
  const flopay = useFloPay();
4401
5238
  const elements = useElements();
4402
5239
  const contextBillingUrl = useBillingApiUrl();
4403
- const [ready, setReady] = useState5(false);
4404
- const [submitting, setSubmitting] = useState5(false);
4405
- const paypalResumeAttempted = useRef4(false);
5240
+ const [ready, setReady] = useState6(false);
5241
+ const [submitting, setSubmitting] = useState6(false);
5242
+ const paypalResumeAttempted = useRef5(false);
4406
5243
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
4407
5244
  const processPaymentInternal = useCallback4(
4408
5245
  async (tokenizedBody) => {
4409
5246
  try {
4410
- const api = new PaymentAPI5(baseUrl);
5247
+ const api = new PaymentAPI6(baseUrl);
4411
5248
  const response = await api.processPayment(userId ?? "", {
4412
5249
  sessionId,
4413
5250
  tokenizedData: tokenizedBody,
@@ -4441,7 +5278,7 @@ function PayPalButton({
4441
5278
  },
4442
5279
  [onTokenizedBody, processPaymentInternal]
4443
5280
  );
4444
- useEffect6(() => {
5281
+ useEffect7(() => {
4445
5282
  if (!flopay || paypalResumeAttempted.current) return;
4446
5283
  const params = new URLSearchParams(window.location.search);
4447
5284
  const paymentIntentId = params.get("payment_intent");
@@ -4495,7 +5332,7 @@ function PayPalButton({
4495
5332
  setSubmitting(true);
4496
5333
  onErrorChange?.(null);
4497
5334
  if (!sessionId || !email) {
4498
- throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
5335
+ throw new FloPayError7("Missing sessionId or email for PayPal payment", "validation_error");
4499
5336
  }
4500
5337
  const result = await flopay.confirmPayPalPayment({
4501
5338
  billingApiUrl: baseUrl,
@@ -4522,11 +5359,11 @@ function PayPalButton({
4522
5359
  }
4523
5360
  }, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
4524
5361
  if (!flopay || !elements) {
4525
- return /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
5362
+ return /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
4526
5363
  }
4527
- return /* @__PURE__ */ jsxs6(Fragment5, { children: [
4528
- !ready && /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
4529
- /* @__PURE__ */ jsx8("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx8(
5364
+ return /* @__PURE__ */ jsxs7(Fragment5, { children: [
5365
+ !ready && /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
5366
+ /* @__PURE__ */ jsx9("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx9(
4530
5367
  "button",
4531
5368
  {
4532
5369
  type: "button",
@@ -4548,7 +5385,7 @@ function PayPalButton({
4548
5385
  children: submitting ? "Processing..." : "PayPal"
4549
5386
  }
4550
5387
  ) }),
4551
- (submitting || isProcessing) && /* @__PURE__ */ jsx8("div", { style: {
5388
+ (submitting || isProcessing) && /* @__PURE__ */ jsx9("div", { style: {
4552
5389
  position: "fixed",
4553
5390
  inset: 0,
4554
5391
  background: "rgba(0,0,0,0.4)",
@@ -4556,7 +5393,7 @@ function PayPalButton({
4556
5393
  alignItems: "center",
4557
5394
  justifyContent: "center",
4558
5395
  zIndex: 1e3
4559
- }, children: /* @__PURE__ */ jsx8("div", { style: {
5396
+ }, children: /* @__PURE__ */ jsx9("div", { style: {
4560
5397
  background: "white",
4561
5398
  borderRadius: 8,
4562
5399
  padding: "1.5rem",
@@ -4568,20 +5405,20 @@ function PayPalButton({
4568
5405
  }
4569
5406
 
4570
5407
  // src/automatic-payment-button.tsx
4571
- import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
4572
- import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
4573
- import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
4574
- import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
5408
+ import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
5409
+ import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
5410
+ import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
5411
+ import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4575
5412
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
4576
5413
  var PAYPAL_RESUME_STORAGE_KEY2 = "flopay_automatic_payment_button_resume";
4577
5414
  function sleep2(ms) {
4578
5415
  return new Promise((resolve) => setTimeout(resolve, ms));
4579
5416
  }
4580
5417
  function coerceError(err, fallbackMessage) {
4581
- if (err instanceof FloPayError7) {
5418
+ if (err instanceof FloPayError8) {
4582
5419
  return err;
4583
5420
  }
4584
- return new FloPayError7(
5421
+ return new FloPayError8(
4585
5422
  err instanceof Error ? err.message : fallbackMessage,
4586
5423
  "api_error"
4587
5424
  );
@@ -4688,11 +5525,11 @@ function FloPayAutomaticPaymentButton({
4688
5525
  style,
4689
5526
  ...buttonProps
4690
5527
  }) {
4691
- const resolvedBillingUrl = useMemo4(
5528
+ const resolvedBillingUrl = useMemo5(
4692
5529
  () => resolveBillingApiUrl4(billingApiUrl),
4693
5530
  [billingApiUrl]
4694
5531
  );
4695
- const createSessionDraft = useMemo4(
5532
+ const createSessionDraft = useMemo5(
4696
5533
  () => resolveCreateSessionDraft({
4697
5534
  createSession,
4698
5535
  clientId,
@@ -4718,43 +5555,47 @@ function FloPayAutomaticPaymentButton({
4718
5555
  utmMetadata
4719
5556
  ]
4720
5557
  );
4721
- const [isProcessing, setIsProcessing] = useState6(false);
4722
- const [overlayStatus, setOverlayStatus] = useState6(null);
4723
- const [overlayError, setOverlayError] = useState6(null);
4724
- const [fallbackSession, setFallbackSession] = useState6(null);
4725
- const automaticPaymentToken = useMemo4(
5558
+ const [isProcessing, setIsProcessing] = useState7(false);
5559
+ const [overlayStatus, setOverlayStatus] = useState7(null);
5560
+ const [overlayError, setOverlayError] = useState7(null);
5561
+ const [fallbackSession, setFallbackSession] = useState7(null);
5562
+ const automaticPaymentToken = useMemo5(
4726
5563
  () => paymentMethodId ? {
4727
5564
  id: paymentMethodId,
4728
- type: "card",
5565
+ // Saved PayPal `user_payment_method` records are submitted with
5566
+ // `type: 'paypal'` so the backend routes the upsell through the
5567
+ // direct PayPal gateway's vaulted-token flow. Card and wallet PMs
5568
+ // continue to use `type: 'card'`.
5569
+ type: checkoutMethod === "paypal" ? "paypal" : "card",
4729
5570
  ...checkoutMethod === "paypal" ? { isPaypal: true } : {}
4730
5571
  } : void 0,
4731
5572
  [checkoutMethod, paymentMethodId]
4732
5573
  );
4733
- const isMountedRef = useRef5(true);
4734
- const resumeAttemptedRef = useRef5(false);
4735
- const fallbackSessionRef = useRef5(fallbackSession);
4736
- const onSuccessRef = useRef5(onSuccess);
4737
- const onErrorRef = useRef5(onError);
4738
- const onDeclineRef = useRef5(onDecline);
4739
- useEffect7(() => {
5574
+ const isMountedRef = useRef6(true);
5575
+ const resumeAttemptedRef = useRef6(false);
5576
+ const fallbackSessionRef = useRef6(fallbackSession);
5577
+ const onSuccessRef = useRef6(onSuccess);
5578
+ const onErrorRef = useRef6(onError);
5579
+ const onDeclineRef = useRef6(onDecline);
5580
+ useEffect8(() => {
4740
5581
  fallbackSessionRef.current = fallbackSession;
4741
5582
  }, [fallbackSession]);
4742
- useEffect7(() => {
5583
+ useEffect8(() => {
4743
5584
  onSuccessRef.current = onSuccess;
4744
5585
  }, [onSuccess]);
4745
- useEffect7(() => {
5586
+ useEffect8(() => {
4746
5587
  onErrorRef.current = onError;
4747
5588
  }, [onError]);
4748
- useEffect7(() => {
5589
+ useEffect8(() => {
4749
5590
  onDeclineRef.current = onDecline;
4750
5591
  }, [onDecline]);
4751
- useEffect7(() => {
5592
+ useEffect8(() => {
4752
5593
  isMountedRef.current = true;
4753
5594
  return () => {
4754
5595
  isMountedRef.current = false;
4755
5596
  };
4756
5597
  }, []);
4757
- useEffect7(() => {
5598
+ useEffect8(() => {
4758
5599
  if (!fallbackSession || typeof window === "undefined") {
4759
5600
  return;
4760
5601
  }
@@ -4795,7 +5636,7 @@ function FloPayAutomaticPaymentButton({
4795
5636
  const processResolvedSession = useCallback5(async (apiResult, resolvedSessionId, options) => {
4796
5637
  const session = apiResult.data.session ?? null;
4797
5638
  if (!session) {
4798
- throw new FloPayError7("No session data returned", "api_error");
5639
+ throw new FloPayError8("No session data returned", "api_error");
4799
5640
  }
4800
5641
  if (session.status === "complete") {
4801
5642
  await showSuccess({
@@ -4807,7 +5648,7 @@ function FloPayAutomaticPaymentButton({
4807
5648
  return;
4808
5649
  }
4809
5650
  if (session.status === "expired") {
4810
- throw new FloPayError7("Checkout session has expired.", "api_error", {
5651
+ throw new FloPayError8("Checkout session has expired.", "api_error", {
4811
5652
  code: "checkout_session_expired"
4812
5653
  });
4813
5654
  }
@@ -4817,13 +5658,13 @@ function FloPayAutomaticPaymentButton({
4817
5658
  const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && (apiResult.autoProcessingAttempted === true || !!apiResult.autoProcessingError || !!redirectResult);
4818
5659
  const shouldRetryPayPalClientSide = checkoutMethod === "paypal" && automaticPaymentToken?.isPaypal === true && options?.fromCreateSession === true;
4819
5660
  if (apiResult.autoProcessingPending) {
4820
- const api = new PaymentAPI6(resolvedBillingUrl);
5661
+ const api = new PaymentAPI7(resolvedBillingUrl);
4821
5662
  const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
4822
5663
  initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
4823
5664
  });
4824
5665
  const completedSession = completed.data.session;
4825
5666
  if (!completedSession || completedSession.status !== "complete") {
4826
- throw new FloPayError7("Automatic payment failed. Please try again.", "api_error", {
5667
+ throw new FloPayError8("Automatic payment failed. Please try again.", "api_error", {
4827
5668
  code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
4828
5669
  });
4829
5670
  }
@@ -4844,7 +5685,7 @@ function FloPayAutomaticPaymentButton({
4844
5685
  publishableKey,
4845
5686
  paypalPublishableKey
4846
5687
  } = resolveSavedPaymentPublishableKeys(apiResult);
4847
- if (redirectResult.type === "paypal_redirect_required") {
5688
+ if (redirectResult.type === "paypal_redirect_required" && publishableKey) {
4848
5689
  persistPayPalResumeState2({
4849
5690
  sessionId: session.id || resolvedSessionId,
4850
5691
  publishableKey,
@@ -4900,7 +5741,7 @@ function FloPayAutomaticPaymentButton({
4900
5741
  publishableKey,
4901
5742
  paypalPublishableKey
4902
5743
  } = resolveSavedPaymentPublishableKeys(apiResult);
4903
- if (result.type === "paypal_redirect_required") {
5744
+ if (result.type === "paypal_redirect_required" && publishableKey) {
4904
5745
  persistPayPalResumeState2({
4905
5746
  sessionId: session.id || resolvedSessionId,
4906
5747
  publishableKey,
@@ -4961,6 +5802,13 @@ function FloPayAutomaticPaymentButton({
4961
5802
  billingApiUrl: resolvedBillingUrl,
4962
5803
  locale
4963
5804
  });
5805
+ if (!flopay) {
5806
+ throw new FloPayError8(
5807
+ "Stripe is not available for 3DS authentication.",
5808
+ "api_error",
5809
+ { code: "authentication_required" }
5810
+ );
5811
+ }
4964
5812
  const paymentResult = await processSavedPaymentWithIntent({
4965
5813
  billingApiUrl: resolvedBillingUrl,
4966
5814
  sessionId: fallbackSessionId,
@@ -5022,7 +5870,7 @@ function FloPayAutomaticPaymentButton({
5022
5870
  }
5023
5871
  setFallbackSession(null);
5024
5872
  if (sessionId && createSessionDraft) {
5025
- const error = new FloPayError7(
5873
+ const error = new FloPayError8(
5026
5874
  "Provide either sessionId or create-session props, not both.",
5027
5875
  "validation_error"
5028
5876
  );
@@ -5034,7 +5882,7 @@ function FloPayAutomaticPaymentButton({
5034
5882
  return;
5035
5883
  }
5036
5884
  if (!sessionId && !createSessionDraft) {
5037
- const error = new FloPayError7(
5885
+ const error = new FloPayError8(
5038
5886
  "Provide a sessionId or the props required to create an automatic payment session.",
5039
5887
  "validation_error"
5040
5888
  );
@@ -5049,7 +5897,7 @@ function FloPayAutomaticPaymentButton({
5049
5897
  setOverlayError(null);
5050
5898
  setOverlayStatus("processing");
5051
5899
  try {
5052
- const api = new PaymentAPI6(resolvedBillingUrl);
5900
+ const api = new PaymentAPI7(resolvedBillingUrl);
5053
5901
  if (sessionId) {
5054
5902
  const result = await api.getUnifiedCheckoutSession(sessionId);
5055
5903
  await processResolvedSession(result, sessionId);
@@ -5064,7 +5912,7 @@ function FloPayAutomaticPaymentButton({
5064
5912
  fromCreateSession: true
5065
5913
  });
5066
5914
  } catch (err) {
5067
- if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
5915
+ if (err instanceof FloPayError8 && err.code === "session_auto_completed") {
5068
5916
  await showSuccess({
5069
5917
  result: { status: "succeeded" },
5070
5918
  session: null,
@@ -5113,7 +5961,7 @@ function FloPayAutomaticPaymentButton({
5113
5961
  const handleFallbackDecline = useCallback5((decline) => {
5114
5962
  onDeclineRef.current?.(decline);
5115
5963
  }, []);
5116
- useEffect7(() => {
5964
+ useEffect8(() => {
5117
5965
  if (typeof window === "undefined" || resumeAttemptedRef.current) {
5118
5966
  return;
5119
5967
  }
@@ -5134,7 +5982,7 @@ function FloPayAutomaticPaymentButton({
5134
5982
  try {
5135
5983
  if (params.get("redirect_status") === "failed") {
5136
5984
  throw Object.assign(
5137
- new FloPayError7("PayPal payment was declined. Please try again.", "api_error"),
5985
+ new FloPayError8("PayPal payment was declined. Please try again.", "api_error"),
5138
5986
  { checkoutMethod: "paypal" }
5139
5987
  );
5140
5988
  }
@@ -5147,17 +5995,17 @@ function FloPayAutomaticPaymentButton({
5147
5995
  billingApiUrl: resolvedBillingUrl,
5148
5996
  locale
5149
5997
  });
5150
- const paypalStripe = (paypalFlopay ?? flopay).getRawProvider();
5998
+ const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
5151
5999
  if (!paypalStripe) {
5152
6000
  throw Object.assign(
5153
- new FloPayError7("PayPal is not available.", "api_error"),
6001
+ new FloPayError8("PayPal is not available.", "api_error"),
5154
6002
  { checkoutMethod: "paypal" }
5155
6003
  );
5156
6004
  }
5157
6005
  const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
5158
6006
  if (error) {
5159
6007
  throw Object.assign(
5160
- new FloPayError7(
6008
+ new FloPayError8(
5161
6009
  error.message ?? "Failed to retrieve PayPal payment status.",
5162
6010
  "api_error",
5163
6011
  { code: error.code }
@@ -5168,23 +6016,23 @@ function FloPayAutomaticPaymentButton({
5168
6016
  const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
5169
6017
  if (!paymentIntent || resultStatus === "failed") {
5170
6018
  throw Object.assign(
5171
- new FloPayError7("PayPal payment was not completed. Please try again.", "api_error"),
6019
+ new FloPayError8("PayPal payment was not completed. Please try again.", "api_error"),
5172
6020
  { checkoutMethod: "paypal" }
5173
6021
  );
5174
6022
  }
5175
6023
  const paymentMethodId2 = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
5176
6024
  if (!resumeState.sessionId) {
5177
6025
  throw Object.assign(
5178
- new FloPayError7("Missing session id on PayPal resume.", "api_error"),
6026
+ new FloPayError8("Missing session id on PayPal resume.", "api_error"),
5179
6027
  { checkoutMethod: "paypal" }
5180
6028
  );
5181
6029
  }
5182
- const api = new PaymentAPI6(resolvedBillingUrl);
6030
+ const api = new PaymentAPI7(resolvedBillingUrl);
5183
6031
  const sessionResult = await api.getUnifiedCheckoutSession(resumeState.sessionId);
5184
6032
  let resumeSession = sessionResult.data.session;
5185
6033
  if (!resumeSession) {
5186
6034
  throw Object.assign(
5187
- new FloPayError7("Could not load session to capture PayPal payment.", "api_error"),
6035
+ new FloPayError8("Could not load session to capture PayPal payment.", "api_error"),
5188
6036
  { checkoutMethod: "paypal" }
5189
6037
  );
5190
6038
  }
@@ -5203,7 +6051,7 @@ function FloPayAutomaticPaymentButton({
5203
6051
  });
5204
6052
  if (processResult.type !== "success") {
5205
6053
  throw Object.assign(
5206
- new FloPayError7("Failed to finalize PayPal payment.", "api_error"),
6054
+ new FloPayError8("Failed to finalize PayPal payment.", "api_error"),
5207
6055
  { checkoutMethod: "paypal" }
5208
6056
  );
5209
6057
  }
@@ -5244,7 +6092,7 @@ function FloPayAutomaticPaymentButton({
5244
6092
  }
5245
6093
  })();
5246
6094
  }, [locale, resolvedBillingUrl, showError, showSuccess]);
5247
- const bStyles = useMemo4(() => {
6095
+ const bStyles = useMemo5(() => {
5248
6096
  const base = resolveButtonsLayoutTheme3(buttonsTheme);
5249
6097
  if (!stylesOverride) return base;
5250
6098
  return {
@@ -5254,8 +6102,8 @@ function FloPayAutomaticPaymentButton({
5254
6102
  };
5255
6103
  }, [buttonsTheme, stylesOverride]);
5256
6104
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
5257
- return /* @__PURE__ */ jsxs7(Fragment6, { children: [
5258
- /* @__PURE__ */ jsx9(
6105
+ return /* @__PURE__ */ jsxs8(Fragment6, { children: [
6106
+ /* @__PURE__ */ jsx10(
5259
6107
  "button",
5260
6108
  {
5261
6109
  ...buttonProps,
@@ -5296,17 +6144,17 @@ function FloPayAutomaticPaymentButton({
5296
6144
  e.currentTarget.style.transform = "scale(1)";
5297
6145
  }
5298
6146
  },
5299
- children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: children })
6147
+ children: /* @__PURE__ */ jsx10(CardButtonContentSlot, { content: children })
5300
6148
  }
5301
6149
  ),
5302
- overlayStatus && /* @__PURE__ */ jsx9(
6150
+ overlayStatus && /* @__PURE__ */ jsx10(
5303
6151
  ProcessingOverlay,
5304
6152
  {
5305
6153
  status: overlayStatus,
5306
6154
  errorMessage: overlayError
5307
6155
  }
5308
6156
  ),
5309
- fallbackSession && /* @__PURE__ */ jsx9(
6157
+ fallbackSession && /* @__PURE__ */ jsx10(
5310
6158
  "div",
5311
6159
  {
5312
6160
  "data-testid": "flopay-automatic-payment-fallback",
@@ -5327,7 +6175,7 @@ function FloPayAutomaticPaymentButton({
5327
6175
  padding: "1.5rem",
5328
6176
  zIndex: 1100
5329
6177
  },
5330
- children: /* @__PURE__ */ jsx9(
6178
+ children: /* @__PURE__ */ jsx10(
5331
6179
  "div",
5332
6180
  {
5333
6181
  style: {
@@ -5343,7 +6191,7 @@ function FloPayAutomaticPaymentButton({
5343
6191
  flexDirection: "column",
5344
6192
  gap: "1rem"
5345
6193
  },
5346
- children: /* @__PURE__ */ jsx9(
6194
+ children: /* @__PURE__ */ jsx10(
5347
6195
  FloPayCheckout,
5348
6196
  {
5349
6197
  sessionId: fallbackSession.sessionId,
@@ -5370,6 +6218,7 @@ export {
5370
6218
  CardExpiryElement,
5371
6219
  CardNumberElement,
5372
6220
  CheckoutForm,
6221
+ DirectPayPalButton,
5373
6222
  FloPayAutomaticPaymentButton,
5374
6223
  FloPayCheckout,
5375
6224
  FloPayProvider,