@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/README.md +19 -6
- package/dist/index.cjs +1400 -550
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -11
- package/dist/index.d.ts +141 -11
- package/dist/index.mjs +1310 -461
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -100,9 +100,9 @@ function FloPayProvider({
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// src/flopay-checkout.tsx
|
|
103
|
-
import
|
|
104
|
-
import { PaymentAPI as
|
|
105
|
-
import { SDK_VERSION, FloPayError as
|
|
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
|
|
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
|
|
461
|
-
...(params.
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
|
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
|
|
656
|
-
import { Fragment as Fragment2, jsx as
|
|
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
|
|
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__ */
|
|
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__ */
|
|
720
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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] =
|
|
777
|
-
|
|
1346
|
+
const [loadState, setLoadState] = useState3("loading");
|
|
1347
|
+
useEffect4(() => {
|
|
778
1348
|
onLoadStateChange?.(loadState);
|
|
779
1349
|
}, [loadState, onLoadStateChange]);
|
|
780
|
-
const [submitting, setSubmitting] =
|
|
781
|
-
const paypalResumeAttempted =
|
|
782
|
-
const beforeClickRef =
|
|
1350
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
1351
|
+
const paypalResumeAttempted = useRef3(false);
|
|
1352
|
+
const beforeClickRef = useRef3(null);
|
|
783
1353
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
784
|
-
|
|
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__ */
|
|
942
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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] =
|
|
986
|
-
|
|
1555
|
+
const [loadState, setLoadState] = useState3("loading");
|
|
1556
|
+
useEffect4(() => {
|
|
987
1557
|
onLoadStateChange?.(loadState);
|
|
988
1558
|
}, [loadState, onLoadStateChange]);
|
|
989
|
-
const [submitting, setSubmitting] =
|
|
1559
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
990
1560
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
991
|
-
const lastWalletMethodRef =
|
|
992
|
-
const beforeClickRef =
|
|
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__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
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) =>
|
|
1087
|
-
|
|
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__ */
|
|
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] =
|
|
1182
|
-
const [error, setError] =
|
|
1183
|
-
const [is3DSActive, setIs3DSActive] =
|
|
1184
|
-
const [selectedCountry, setSelectedCountry] =
|
|
1185
|
-
const [zipCode, setZipCode] =
|
|
1186
|
-
const [addressLine1, setAddressLine1] =
|
|
1187
|
-
const [addressLine2, setAddressLine2] =
|
|
1188
|
-
const [city, setCity] =
|
|
1189
|
-
const [stateValue, setStateValue] =
|
|
1190
|
-
const [accountPatch, setAccountPatch] =
|
|
1191
|
-
const zipCodeRef =
|
|
1192
|
-
const selectedCountryRef =
|
|
1193
|
-
const addressLine1Ref =
|
|
1194
|
-
const addressLine2Ref =
|
|
1195
|
-
const cityRef =
|
|
1196
|
-
const stateRef =
|
|
1197
|
-
const avsConfig =
|
|
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] =
|
|
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
|
-
|
|
1788
|
+
useEffect4(() => {
|
|
1211
1789
|
if (layout === "buttons" && initialCardOpen) {
|
|
1212
1790
|
setViewState("card");
|
|
1213
1791
|
}
|
|
1214
1792
|
}, [layout, initialCardOpen]);
|
|
1215
|
-
const [fullName, setFullName] =
|
|
1216
|
-
const [formReady, setFormReady] =
|
|
1217
|
-
const [overlayStatus, setOverlayStatus] =
|
|
1218
|
-
const processingRef =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1830
|
+
const stripeInstance = useMemo3(() => {
|
|
1248
1831
|
if (!flopay) return null;
|
|
1249
1832
|
return flopay.getRawProvider();
|
|
1250
1833
|
}, [flopay]);
|
|
1251
|
-
const paypalStripeInstance =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
1285
|
-
const [walletLoadState, setWalletLoadState] =
|
|
1286
|
-
const [
|
|
1287
|
-
|
|
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
|
|
1291
|
-
const
|
|
1292
|
-
const
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2452
|
+
}, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
|
|
1848
2453
|
] }),
|
|
1849
|
-
!isButtons && !hideTitle && /* @__PURE__ */
|
|
1850
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2479
|
+
}, children: /* @__PURE__ */ jsx6(CardCvcElement, { options: stripeElementStyle }) })
|
|
1875
2480
|
] }),
|
|
1876
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1922
|
-
isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2010
|
-
stateOpts.map((s) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2152
|
-
/* @__PURE__ */
|
|
2153
|
-
overlayStatus && /* @__PURE__ */
|
|
2154
|
-
/* @__PURE__ */
|
|
2155
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
2195
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2906
|
+
children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
|
|
2234
2907
|
}
|
|
2235
2908
|
),
|
|
2236
|
-
displayError && viewState === "buttons" && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
2263
|
-
/* @__PURE__ */
|
|
2264
|
-
overlayStatus && /* @__PURE__ */
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
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__ */
|
|
2301
|
-
/* @__PURE__ */
|
|
2302
|
-
/* @__PURE__ */
|
|
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
|
|
2310
|
-
import { FloPayError as
|
|
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
|
|
2329
|
-
|
|
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
|
|
2384
|
-
if (
|
|
2385
|
-
const
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
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
|
|
3169
|
+
const api = new PaymentAPI3(billingApiUrl);
|
|
2418
3170
|
const unified = await api.getUnifiedCheckoutSession(sessionId);
|
|
2419
|
-
const refreshedToken = unified.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3531
|
+
throw new FloPayError4("Unsupported payment redirect state.", "api_error");
|
|
2780
3532
|
}
|
|
2781
3533
|
function normalizeSavedPaymentError(err) {
|
|
2782
|
-
if (err instanceof
|
|
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
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
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
|
-
|
|
2792
|
-
|
|
2793
|
-
if (
|
|
2794
|
-
|
|
2795
|
-
|
|
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
|
-
|
|
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
|
|
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] =
|
|
2939
|
-
const [flopay, setFloPay] =
|
|
2940
|
-
const flopayRef =
|
|
2941
|
-
const [paypalFlopay, setPaypalFloPay] =
|
|
2942
|
-
const paypalFlopayRef =
|
|
2943
|
-
const [session, setSession] =
|
|
2944
|
-
const [resolvedSessionId, setResolvedSessionId] =
|
|
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] =
|
|
2948
|
-
const [loadError, setLoadError] =
|
|
2949
|
-
const [currentMode, setCurrentMode] =
|
|
2950
|
-
const [confirmProcessing, setConfirmProcessing] =
|
|
2951
|
-
const [modeError, setModeError] =
|
|
2952
|
-
const [modeOverlayStatus, setModeOverlayStatus] =
|
|
2953
|
-
const [modeOverlayError, setModeOverlayError] =
|
|
2954
|
-
const [createSessionPatch, setCreateSessionPatch] =
|
|
2955
|
-
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] =
|
|
2956
|
-
const [cardBootstrapPending, setCardBootstrapPending] =
|
|
2957
|
-
const autoCheckoutAttempted =
|
|
2958
|
-
const paypalResumeAttempted =
|
|
2959
|
-
const savedPaymentKeysRef =
|
|
2960
|
-
const onCompleteRef =
|
|
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 =
|
|
3746
|
+
const onErrorRef = useRef4(onError);
|
|
2963
3747
|
onErrorRef.current = onError;
|
|
2964
|
-
const onDeclineRef =
|
|
3748
|
+
const onDeclineRef = useRef4(onDecline);
|
|
2965
3749
|
onDeclineRef.current = onDecline;
|
|
2966
|
-
const onSessionCompletedRef =
|
|
3750
|
+
const onSessionCompletedRef = useRef4(onSessionCompleted);
|
|
2967
3751
|
onSessionCompletedRef.current = onSessionCompleted;
|
|
2968
|
-
|
|
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 =
|
|
3760
|
+
const baseCreateSessionHash = useMemo4(
|
|
2977
3761
|
() => createSessionParams ? hashCreateParams(createSessionParams) : "",
|
|
2978
3762
|
[createSessionParams]
|
|
2979
3763
|
);
|
|
2980
|
-
const activeCreateSessionPatch =
|
|
3764
|
+
const activeCreateSessionPatch = useMemo4(
|
|
2981
3765
|
() => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
|
|
2982
3766
|
[createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
|
|
2983
3767
|
);
|
|
2984
|
-
const effectiveCreateSessionBase =
|
|
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 =
|
|
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
|
-
|
|
3780
|
+
useEffect5(() => {
|
|
2997
3781
|
setCreateSessionPatch(void 0);
|
|
2998
3782
|
setCreateSessionPatchBaseHash(baseCreateSessionHash);
|
|
2999
3783
|
}, [baseCreateSessionHash]);
|
|
3000
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
3946
|
+
const paypalStripe = (resumePaypalFlopay ?? resumeFlopay)?.getRawProvider();
|
|
3163
3947
|
if (!paypalStripe) {
|
|
3164
3948
|
throw Object.assign(
|
|
3165
|
-
new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
4053
|
+
const createSessionHash = useMemo4(
|
|
3270
4054
|
() => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
|
|
3271
4055
|
[effectiveCreateSession]
|
|
3272
4056
|
);
|
|
3273
|
-
const createSessionParamsRef =
|
|
4057
|
+
const createSessionParamsRef = useRef4(effectiveCreateSession);
|
|
3274
4058
|
createSessionParamsRef.current = effectiveCreateSession;
|
|
3275
|
-
|
|
4059
|
+
useEffect5(() => {
|
|
3276
4060
|
setResolvedSessionId(sessionIdProp ?? "");
|
|
3277
4061
|
}, [sessionIdProp]);
|
|
3278
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
3579
|
-
|
|
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.
|
|
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 =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
3641
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
3651
|
-
/* @__PURE__ */
|
|
3652
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
4458
|
+
return /* @__PURE__ */ jsx7(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
|
|
3668
4459
|
}
|
|
3669
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
3684
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4535
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: modeOverlay });
|
|
3703
4536
|
}
|
|
3704
4537
|
if (currentMode === "confirm") {
|
|
3705
|
-
return /* @__PURE__ */
|
|
3706
|
-
/* @__PURE__ */
|
|
3707
|
-
modeError && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
3748
|
-
/* @__PURE__ */
|
|
3749
|
-
modeError && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
3825
|
-
if (!
|
|
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
|
|
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] =
|
|
4694
|
+
const [showCardForm, setShowCardForm] = useState4(false);
|
|
3858
4695
|
const isCardOpenControlled = typeof cardOpen === "boolean";
|
|
3859
|
-
|
|
4696
|
+
useEffect5(() => {
|
|
3860
4697
|
if (isCardOpenControlled) {
|
|
3861
4698
|
setShowCardForm(cardOpen);
|
|
3862
4699
|
}
|
|
3863
4700
|
}, [cardOpen, isCardOpenControlled]);
|
|
3864
|
-
const bStyles =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
3898
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
3935
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
4784
|
+
}, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
|
|
3948
4785
|
] }),
|
|
3949
|
-
/* @__PURE__ */
|
|
3950
|
-
/* @__PURE__ */
|
|
3951
|
-
/* @__PURE__ */
|
|
3952
|
-
/* @__PURE__ */
|
|
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__ */
|
|
3955
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
4854
|
+
children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
|
|
4018
4855
|
}
|
|
4019
4856
|
),
|
|
4020
|
-
errorMessage && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
4043
|
-
import { FloPayError as
|
|
4044
|
-
import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as
|
|
4045
|
-
import { Fragment as Fragment4, jsx as
|
|
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__ */
|
|
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] =
|
|
4079
|
-
const [error, setError] =
|
|
4080
|
-
const [is3DSActive, setIs3DSActive] =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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__ */
|
|
4355
|
-
(is3DSActive || isSubmitting) && /* @__PURE__ */
|
|
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__ */
|
|
4365
|
-
isReady && /* @__PURE__ */
|
|
4366
|
-
/* @__PURE__ */
|
|
4367
|
-
showAddress && /* @__PURE__ */
|
|
4368
|
-
displayError && /* @__PURE__ */
|
|
4369
|
-
children ?? /* @__PURE__ */
|
|
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
|
|
4384
|
-
import { FloPayError as
|
|
4385
|
-
import { useCallback as useCallback4, useEffect as
|
|
4386
|
-
import { Fragment as Fragment5, jsx as
|
|
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] =
|
|
4404
|
-
const [submitting, setSubmitting] =
|
|
4405
|
-
const paypalResumeAttempted =
|
|
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
|
|
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
|
-
|
|
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
|
|
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__ */
|
|
5362
|
+
return /* @__PURE__ */ jsx9("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
|
|
4526
5363
|
}
|
|
4527
|
-
return /* @__PURE__ */
|
|
4528
|
-
!ready && /* @__PURE__ */
|
|
4529
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
4572
|
-
import { PaymentAPI as
|
|
4573
|
-
import { FloPayError as
|
|
4574
|
-
import { Fragment as Fragment6, jsx as
|
|
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
|
|
5418
|
+
if (err instanceof FloPayError8) {
|
|
4582
5419
|
return err;
|
|
4583
5420
|
}
|
|
4584
|
-
return new
|
|
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 =
|
|
5528
|
+
const resolvedBillingUrl = useMemo5(
|
|
4692
5529
|
() => resolveBillingApiUrl4(billingApiUrl),
|
|
4693
5530
|
[billingApiUrl]
|
|
4694
5531
|
);
|
|
4695
|
-
const createSessionDraft =
|
|
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] =
|
|
4722
|
-
const [overlayStatus, setOverlayStatus] =
|
|
4723
|
-
const [overlayError, setOverlayError] =
|
|
4724
|
-
const [fallbackSession, setFallbackSession] =
|
|
4725
|
-
const automaticPaymentToken =
|
|
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
|
-
|
|
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 =
|
|
4734
|
-
const resumeAttemptedRef =
|
|
4735
|
-
const fallbackSessionRef =
|
|
4736
|
-
const onSuccessRef =
|
|
4737
|
-
const onErrorRef =
|
|
4738
|
-
const onDeclineRef =
|
|
4739
|
-
|
|
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
|
-
|
|
5583
|
+
useEffect8(() => {
|
|
4743
5584
|
onSuccessRef.current = onSuccess;
|
|
4744
5585
|
}, [onSuccess]);
|
|
4745
|
-
|
|
5586
|
+
useEffect8(() => {
|
|
4746
5587
|
onErrorRef.current = onError;
|
|
4747
5588
|
}, [onError]);
|
|
4748
|
-
|
|
5589
|
+
useEffect8(() => {
|
|
4749
5590
|
onDeclineRef.current = onDecline;
|
|
4750
5591
|
}, [onDecline]);
|
|
4751
|
-
|
|
5592
|
+
useEffect8(() => {
|
|
4752
5593
|
isMountedRef.current = true;
|
|
4753
5594
|
return () => {
|
|
4754
5595
|
isMountedRef.current = false;
|
|
4755
5596
|
};
|
|
4756
5597
|
}, []);
|
|
4757
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
5998
|
+
const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
|
|
5151
5999
|
if (!paypalStripe) {
|
|
5152
6000
|
throw Object.assign(
|
|
5153
|
-
new
|
|
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
|
|
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
|
|
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
|
|
6026
|
+
new FloPayError8("Missing session id on PayPal resume.", "api_error"),
|
|
5179
6027
|
{ checkoutMethod: "paypal" }
|
|
5180
6028
|
);
|
|
5181
6029
|
}
|
|
5182
|
-
const api = new
|
|
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
|
|
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
|
|
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 =
|
|
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__ */
|
|
5258
|
-
/* @__PURE__ */
|
|
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__ */
|
|
6147
|
+
children: /* @__PURE__ */ jsx10(CardButtonContentSlot, { content: children })
|
|
5300
6148
|
}
|
|
5301
6149
|
),
|
|
5302
|
-
overlayStatus && /* @__PURE__ */
|
|
6150
|
+
overlayStatus && /* @__PURE__ */ jsx10(
|
|
5303
6151
|
ProcessingOverlay,
|
|
5304
6152
|
{
|
|
5305
6153
|
status: overlayStatus,
|
|
5306
6154
|
errorMessage: overlayError
|
|
5307
6155
|
}
|
|
5308
6156
|
),
|
|
5309
|
-
fallbackSession && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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,
|