@flopay/react 0.1.6 → 0.3.1
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 +42 -0
- package/dist/index.cjs +715 -196
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -4
- package/dist/index.d.ts +61 -4
- package/dist/index.mjs +696 -177
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -118,7 +118,7 @@ function FloPayProvider({
|
|
|
118
118
|
// src/flopay-checkout.tsx
|
|
119
119
|
var import_react6 = __toESM(require("react"), 1);
|
|
120
120
|
var import_js = require("@flopay/js");
|
|
121
|
-
var
|
|
121
|
+
var import_shared5 = require("@flopay/shared");
|
|
122
122
|
|
|
123
123
|
// src/elements.tsx
|
|
124
124
|
var import_react3 = require("react");
|
|
@@ -182,6 +182,7 @@ var AddressElement = createElementComponent("address", "AddressElement");
|
|
|
182
182
|
|
|
183
183
|
// src/split-card-form.tsx
|
|
184
184
|
var import_react_stripe_js = require("@stripe/react-stripe-js");
|
|
185
|
+
var import_shared3 = require("@flopay/shared");
|
|
185
186
|
var import_react5 = require("react");
|
|
186
187
|
|
|
187
188
|
// src/hooks.ts
|
|
@@ -204,9 +205,32 @@ function useBillingApiUrl() {
|
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
// src/split-card-form.tsx
|
|
207
|
-
var
|
|
208
|
+
var import_shared4 = require("@flopay/shared");
|
|
208
209
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
209
210
|
var WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
211
|
+
var FLOPAY_KEYFRAMES = `
|
|
212
|
+
@keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
213
|
+
@keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
|
|
214
|
+
@keyframes flopay-buttons-enter {
|
|
215
|
+
0% { opacity: 0; transform: translateX(-16px) scale(0.97); }
|
|
216
|
+
100% { opacity: 1; transform: translateX(0) scale(1); }
|
|
217
|
+
}
|
|
218
|
+
@keyframes flopay-buttons-exit {
|
|
219
|
+
0% { opacity: 1; transform: translateX(0) scale(1); }
|
|
220
|
+
100% { opacity: 0; transform: translateX(-16px) scale(0.97); }
|
|
221
|
+
}
|
|
222
|
+
@keyframes flopay-card-enter {
|
|
223
|
+
0% { opacity: 0; transform: translateX(16px) scale(0.97); }
|
|
224
|
+
100% { opacity: 1; transform: translateX(0) scale(1); }
|
|
225
|
+
}
|
|
226
|
+
@keyframes flopay-card-exit {
|
|
227
|
+
0% { opacity: 1; transform: translateX(0) scale(1); }
|
|
228
|
+
100% { opacity: 0; transform: translateX(16px) scale(0.97); }
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
function FloPayKeyframes() {
|
|
232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: FLOPAY_KEYFRAMES });
|
|
233
|
+
}
|
|
210
234
|
function ProcessingOverlay({ status, errorMessage }) {
|
|
211
235
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
212
236
|
position: "fixed",
|
|
@@ -296,8 +320,6 @@ function ProcessingOverlay({ status, errorMessage }) {
|
|
|
296
320
|
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
297
321
|
@keyframes flopay-draw { to { stroke-dashoffset: 0; } }
|
|
298
322
|
@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
|
|
299
|
-
@keyframes flopay-expand { 0% { opacity: 0; clip-path: inset(0 0 100% 0); transform: translateY(-8px); } 100% { opacity: 1; clip-path: inset(0 0 0 0); transform: translateY(0); } }
|
|
300
|
-
@keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
|
|
301
323
|
` })
|
|
302
324
|
] }) });
|
|
303
325
|
}
|
|
@@ -312,7 +334,8 @@ function PayPalButtonInner({
|
|
|
312
334
|
billingApiUrl,
|
|
313
335
|
onTokenizedBody,
|
|
314
336
|
onErrorChange,
|
|
315
|
-
isProcessing = false
|
|
337
|
+
isProcessing = false,
|
|
338
|
+
onButtonClick
|
|
316
339
|
}) {
|
|
317
340
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
318
341
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -365,6 +388,7 @@ function PayPalButtonInner({
|
|
|
365
388
|
}, [stripe, onTokenizedBody, onErrorChange]);
|
|
366
389
|
const handlePayPalConfirm = (0, import_react5.useCallback)(async (_event) => {
|
|
367
390
|
if (!stripe || !elements) return;
|
|
391
|
+
onButtonClick?.("paypal");
|
|
368
392
|
try {
|
|
369
393
|
setSubmitting(true);
|
|
370
394
|
onErrorChange?.(null);
|
|
@@ -419,7 +443,7 @@ function PayPalButtonInner({
|
|
|
419
443
|
}
|
|
420
444
|
}, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
|
|
421
445
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
422
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
423
447
|
import_react_stripe_js.ExpressCheckoutElement,
|
|
424
448
|
{
|
|
425
449
|
onReady: () => setReady(true),
|
|
@@ -447,7 +471,8 @@ function WalletButtonInner({
|
|
|
447
471
|
showApplePay = true,
|
|
448
472
|
showGooglePay = true,
|
|
449
473
|
onTokenizedBody,
|
|
450
|
-
onErrorChange
|
|
474
|
+
onErrorChange,
|
|
475
|
+
onButtonClick
|
|
451
476
|
}) {
|
|
452
477
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
453
478
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -457,6 +482,8 @@ function WalletButtonInner({
|
|
|
457
482
|
const handleWalletConfirm = (0, import_react5.useCallback)(
|
|
458
483
|
async (_event) => {
|
|
459
484
|
if (!stripe || !elements) return;
|
|
485
|
+
const walletType = _event.expressPaymentType;
|
|
486
|
+
onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
|
|
460
487
|
try {
|
|
461
488
|
setSubmitting(true);
|
|
462
489
|
onErrorChange?.(null);
|
|
@@ -509,7 +536,7 @@ function WalletButtonInner({
|
|
|
509
536
|
[stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
|
|
510
537
|
);
|
|
511
538
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
512
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
513
540
|
import_react_stripe_js.ExpressCheckoutElement,
|
|
514
541
|
{
|
|
515
542
|
onReady: () => setReady(true),
|
|
@@ -556,6 +583,15 @@ function SplitCardFormInner({
|
|
|
556
583
|
showApplePay = true,
|
|
557
584
|
showGooglePay = true,
|
|
558
585
|
layout = "default",
|
|
586
|
+
buttonsTheme,
|
|
587
|
+
buttonsStyles: buttonsStylesOverride,
|
|
588
|
+
onButtonClick,
|
|
589
|
+
enableAVS = false,
|
|
590
|
+
avsLayout: avsLayoutProp = "row",
|
|
591
|
+
country: countryProp,
|
|
592
|
+
zip: zipProp,
|
|
593
|
+
onCountryChange,
|
|
594
|
+
onZipChange,
|
|
559
595
|
totalAmount = 0,
|
|
560
596
|
currency = "usd",
|
|
561
597
|
innerRef
|
|
@@ -565,14 +601,42 @@ function SplitCardFormInner({
|
|
|
565
601
|
const contextBillingUrl = useBillingApiUrl();
|
|
566
602
|
const [processing, setProcessing] = (0, import_react5.useState)(false);
|
|
567
603
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
568
|
-
const [showCardForm, setShowCardForm] = (0, import_react5.useState)(false);
|
|
569
604
|
const [is3DSActive, setIs3DSActive] = (0, import_react5.useState)(false);
|
|
605
|
+
const [selectedCountry, setSelectedCountry] = (0, import_react5.useState)(countryProp ?? "US");
|
|
606
|
+
const [zipCode, setZipCode] = (0, import_react5.useState)(zipProp ?? "");
|
|
607
|
+
const zipCodeRef = (0, import_react5.useRef)(zipProp ?? "");
|
|
608
|
+
const selectedCountryRef = (0, import_react5.useRef)(countryProp ?? "US");
|
|
609
|
+
const [viewState, setViewState] = (0, import_react5.useState)("buttons");
|
|
610
|
+
const showCardForm = viewState === "expanding" || viewState === "card";
|
|
611
|
+
const TRANSITION_MS = 280;
|
|
612
|
+
const expandToCard = (0, import_react5.useCallback)(() => {
|
|
613
|
+
setViewState("expanding");
|
|
614
|
+
setTimeout(() => setViewState("card"), TRANSITION_MS);
|
|
615
|
+
}, []);
|
|
616
|
+
const collapseToButtons = (0, import_react5.useCallback)(() => {
|
|
617
|
+
setViewState("collapsing");
|
|
618
|
+
setTimeout(() => setViewState("buttons"), TRANSITION_MS);
|
|
619
|
+
}, []);
|
|
570
620
|
const [fullName, setFullName] = (0, import_react5.useState)("");
|
|
571
621
|
const [formReady, setFormReady] = (0, import_react5.useState)(false);
|
|
572
622
|
const [overlayStatus, setOverlayStatus] = (0, import_react5.useState)(null);
|
|
573
623
|
const processingRef = (0, import_react5.useRef)(false);
|
|
574
624
|
const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
|
|
575
625
|
const displayError = externalError ?? error;
|
|
626
|
+
const bStyles = (0, import_react5.useMemo)(() => {
|
|
627
|
+
const base = (0, import_shared3.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
628
|
+
if (!buttonsStylesOverride) return base;
|
|
629
|
+
return {
|
|
630
|
+
...base,
|
|
631
|
+
...buttonsStylesOverride,
|
|
632
|
+
cardButton: { ...base.cardButton, ...buttonsStylesOverride.cardButton },
|
|
633
|
+
cardFormContainer: { ...base.cardFormContainer, ...buttonsStylesOverride.cardFormContainer },
|
|
634
|
+
backButton: { ...base.backButton, ...buttonsStylesOverride.backButton },
|
|
635
|
+
backButtonIcon: { ...base.backButtonIcon, ...buttonsStylesOverride.backButtonIcon },
|
|
636
|
+
submitButton: { ...base.submitButton, ...buttonsStylesOverride.submitButton },
|
|
637
|
+
title: { ...base.title, ...buttonsStylesOverride.title }
|
|
638
|
+
};
|
|
639
|
+
}, [buttonsTheme, buttonsStylesOverride]);
|
|
576
640
|
const isSubmitting = externalProcessing ?? processing;
|
|
577
641
|
const isSelfContained = !onTokenizedBody;
|
|
578
642
|
const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
|
|
@@ -629,7 +693,8 @@ function SplitCardFormInner({
|
|
|
629
693
|
userId: userId ?? "",
|
|
630
694
|
email: email ?? "",
|
|
631
695
|
firstName: firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
|
|
632
|
-
lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? ""
|
|
696
|
+
lastName: lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
|
|
697
|
+
...enableAVS ? { zip: zipCodeRef.current, country: selectedCountryRef.current } : {}
|
|
633
698
|
},
|
|
634
699
|
chv
|
|
635
700
|
})
|
|
@@ -784,24 +849,36 @@ function SplitCardFormInner({
|
|
|
784
849
|
async (e) => {
|
|
785
850
|
e.preventDefault();
|
|
786
851
|
if (!flopay || !elements || isSubmitting || processingRef.current) return;
|
|
852
|
+
onButtonClick?.("card");
|
|
787
853
|
setProcessing(true);
|
|
788
854
|
setOverlayStatus("processing");
|
|
789
855
|
updateError(null);
|
|
790
856
|
let handedOff = false;
|
|
791
857
|
try {
|
|
858
|
+
if (enableAVS && !zipCodeRef.current.trim()) {
|
|
859
|
+
updateError((0, import_shared3.getPostalCodeLabel)(selectedCountryRef.current) + " is required");
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
792
862
|
const submitResult = await flopay.submitElements();
|
|
793
863
|
if (submitResult.error) {
|
|
794
864
|
updateError(submitResult.error.message);
|
|
795
865
|
onError?.(submitResult.error);
|
|
796
866
|
return;
|
|
797
867
|
}
|
|
798
|
-
const
|
|
868
|
+
const billingDetails = enableAVS ? {
|
|
869
|
+
name: fullName,
|
|
870
|
+
address: {
|
|
871
|
+
country: selectedCountryRef.current,
|
|
872
|
+
postal_code: zipCodeRef.current
|
|
873
|
+
}
|
|
874
|
+
} : void 0;
|
|
875
|
+
const pmResult = await flopay.createPaymentMethod(billingDetails);
|
|
799
876
|
if (pmResult.error || !pmResult.paymentMethodId) {
|
|
800
877
|
updateError(pmResult.error?.message ?? "Failed to create payment method.");
|
|
801
878
|
return;
|
|
802
879
|
}
|
|
803
880
|
if (!sessionId || !email) {
|
|
804
|
-
throw new
|
|
881
|
+
throw new import_shared4.FloPayError("Missing sessionId or email", "validation_error");
|
|
805
882
|
}
|
|
806
883
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
807
884
|
method: "POST",
|
|
@@ -813,10 +890,10 @@ function SplitCardFormInner({
|
|
|
813
890
|
isPaypal: false
|
|
814
891
|
})
|
|
815
892
|
});
|
|
816
|
-
if (!intentResponse.ok) throw new
|
|
893
|
+
if (!intentResponse.ok) throw new import_shared4.FloPayError("Failed to create payment intent", "api_error");
|
|
817
894
|
const intentJson = await intentResponse.json();
|
|
818
895
|
const intentClientSecret = intentJson.data?.id;
|
|
819
|
-
if (!intentClientSecret) throw new
|
|
896
|
+
if (!intentClientSecret) throw new import_shared4.FloPayError("No client_secret in payment intent response", "api_error");
|
|
820
897
|
const confirmResult = await flopay.confirmCardPayment({
|
|
821
898
|
clientSecret: intentClientSecret,
|
|
822
899
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -851,15 +928,25 @@ function SplitCardFormInner({
|
|
|
851
928
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
|
|
852
929
|
}
|
|
853
930
|
const isButtons = layout === "buttons";
|
|
854
|
-
const
|
|
855
|
-
const cardBg = isButtons ? "white" : "#EDEDFF";
|
|
931
|
+
const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
|
|
932
|
+
const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
|
|
933
|
+
const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
|
|
934
|
+
const stripeElementStyle = isButtons && (bStyles.cardInputColor || bStyles.cardInputPlaceholderColor || bStyles.cardInputFontSize) ? {
|
|
935
|
+
style: {
|
|
936
|
+
base: {
|
|
937
|
+
...bStyles.cardInputColor ? { color: bStyles.cardInputColor } : {},
|
|
938
|
+
...bStyles.cardInputFontSize ? { fontSize: bStyles.cardInputFontSize } : {},
|
|
939
|
+
...bStyles.cardInputPlaceholderColor ? { "::placeholder": { color: bStyles.cardInputPlaceholderColor } } : {}
|
|
940
|
+
},
|
|
941
|
+
invalid: { color: "#ef4444" }
|
|
942
|
+
}
|
|
943
|
+
} : {};
|
|
856
944
|
const cardFormBlock = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
857
945
|
backgroundColor: cardBg,
|
|
858
946
|
borderRadius: "8px",
|
|
859
947
|
padding: isButtons ? "0" : "1rem",
|
|
860
|
-
...isButtons
|
|
861
|
-
|
|
862
|
-
} : {}
|
|
948
|
+
...isButtons ? bStyles.cardFormContainer : {},
|
|
949
|
+
...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
|
|
863
950
|
}, children: [
|
|
864
951
|
isButtons && showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
865
952
|
display: "flex",
|
|
@@ -870,7 +957,7 @@ function SplitCardFormInner({
|
|
|
870
957
|
"button",
|
|
871
958
|
{
|
|
872
959
|
type: "button",
|
|
873
|
-
onClick:
|
|
960
|
+
onClick: collapseToButtons,
|
|
874
961
|
style: {
|
|
875
962
|
display: "inline-flex",
|
|
876
963
|
alignItems: "center",
|
|
@@ -879,17 +966,12 @@ function SplitCardFormInner({
|
|
|
879
966
|
border: "none",
|
|
880
967
|
cursor: "pointer",
|
|
881
968
|
color: "#4b5563",
|
|
882
|
-
fontSize: "0.85rem",
|
|
969
|
+
fontSize: bStyles.backButtonFontSize ?? "0.85rem",
|
|
883
970
|
fontWeight: 500,
|
|
884
971
|
padding: 0,
|
|
885
972
|
transition: "color 0.15s",
|
|
886
|
-
flexShrink: 0
|
|
887
|
-
|
|
888
|
-
onMouseOver: (e) => {
|
|
889
|
-
e.currentTarget.style.color = "#1f2937";
|
|
890
|
-
},
|
|
891
|
-
onMouseOut: (e) => {
|
|
892
|
-
e.currentTarget.style.color = "#4b5563";
|
|
973
|
+
flexShrink: 0,
|
|
974
|
+
...bStyles.backButton
|
|
893
975
|
},
|
|
894
976
|
"aria-label": "Back to payment methods",
|
|
895
977
|
children: [
|
|
@@ -901,44 +983,53 @@ function SplitCardFormInner({
|
|
|
901
983
|
height: 28,
|
|
902
984
|
borderRadius: "50%",
|
|
903
985
|
backgroundColor: "#f3f4f6",
|
|
904
|
-
transition: "background-color 0.15s"
|
|
986
|
+
transition: "background-color 0.15s",
|
|
987
|
+
...bStyles.backButtonIcon
|
|
905
988
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
906
989
|
"Go back"
|
|
907
990
|
]
|
|
908
991
|
}
|
|
909
992
|
),
|
|
910
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
993
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
994
|
+
flex: 1,
|
|
995
|
+
textAlign: "center",
|
|
996
|
+
fontWeight: 600,
|
|
997
|
+
fontSize: bStyles.titleFontSize ?? "1.05rem",
|
|
998
|
+
color: "#262833",
|
|
999
|
+
paddingRight: 80,
|
|
1000
|
+
...bStyles.title
|
|
1001
|
+
}, children: "Secure card checkout" })
|
|
911
1002
|
] }),
|
|
912
1003
|
!isButtons && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: "Secure card checkout" }),
|
|
913
1004
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
914
|
-
backgroundColor:
|
|
915
|
-
border: `1px solid ${
|
|
1005
|
+
backgroundColor: cardInputBg,
|
|
1006
|
+
border: `1px solid ${resolvedBorder}`,
|
|
916
1007
|
borderTopLeftRadius: "8px",
|
|
917
1008
|
borderTopRightRadius: "8px",
|
|
918
1009
|
padding: "10px"
|
|
919
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true) }) }),
|
|
1010
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
|
|
920
1011
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex" }, children: [
|
|
921
1012
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
922
1013
|
flex: 1,
|
|
923
|
-
backgroundColor:
|
|
924
|
-
border: `1px solid ${
|
|
1014
|
+
backgroundColor: cardInputBg,
|
|
1015
|
+
border: `1px solid ${resolvedBorder}`,
|
|
925
1016
|
borderTop: "none",
|
|
926
1017
|
borderRight: "none",
|
|
927
1018
|
borderBottomLeftRadius: "8px",
|
|
928
1019
|
padding: "10px"
|
|
929
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, {}) }),
|
|
1020
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
930
1021
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
931
1022
|
flex: 1,
|
|
932
|
-
backgroundColor:
|
|
933
|
-
border: `1px solid ${
|
|
1023
|
+
backgroundColor: cardInputBg,
|
|
1024
|
+
border: `1px solid ${resolvedBorder}`,
|
|
934
1025
|
borderTop: "none",
|
|
935
1026
|
borderBottomRightRadius: "8px",
|
|
936
1027
|
padding: "10px"
|
|
937
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, {}) })
|
|
1028
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, { options: stripeElementStyle }) })
|
|
938
1029
|
] }),
|
|
939
1030
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
940
|
-
backgroundColor:
|
|
941
|
-
border: `1px solid ${
|
|
1031
|
+
backgroundColor: cardInputBg,
|
|
1032
|
+
border: `1px solid ${resolvedBorder}`,
|
|
942
1033
|
borderRadius: "8px",
|
|
943
1034
|
marginTop: "0.5rem",
|
|
944
1035
|
padding: "10px"
|
|
@@ -955,12 +1046,91 @@ function SplitCardFormInner({
|
|
|
955
1046
|
width: "100%",
|
|
956
1047
|
border: "none",
|
|
957
1048
|
outline: "none",
|
|
958
|
-
|
|
1049
|
+
background: "transparent",
|
|
1050
|
+
fontSize: bStyles.cardInputFontSize ?? "16px",
|
|
959
1051
|
fontFamily: "Poppins, sans-serif",
|
|
960
|
-
color: "#262833"
|
|
1052
|
+
color: bStyles.cardInputColor ?? "#262833",
|
|
1053
|
+
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
961
1054
|
}
|
|
962
1055
|
}
|
|
963
1056
|
) }),
|
|
1057
|
+
enableAVS && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1058
|
+
display: "flex",
|
|
1059
|
+
flexDirection: avsLayoutProp === "column" ? "column" : "row",
|
|
1060
|
+
gap: avsLayoutProp === "column" ? "0.5rem" : "0",
|
|
1061
|
+
marginTop: "0.5rem"
|
|
1062
|
+
}, children: [
|
|
1063
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1064
|
+
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
1065
|
+
backgroundColor: cardInputBg,
|
|
1066
|
+
border: `1px solid ${resolvedBorder}`,
|
|
1067
|
+
padding: "10px",
|
|
1068
|
+
...avsLayoutProp === "row" ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
|
|
1069
|
+
...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
|
|
1070
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1071
|
+
"select",
|
|
1072
|
+
{
|
|
1073
|
+
value: selectedCountry,
|
|
1074
|
+
onChange: (e) => {
|
|
1075
|
+
selectedCountryRef.current = e.target.value;
|
|
1076
|
+
setSelectedCountry(e.target.value);
|
|
1077
|
+
onCountryChange?.(e.target.value);
|
|
1078
|
+
},
|
|
1079
|
+
disabled: isSubmitting,
|
|
1080
|
+
autoComplete: "country",
|
|
1081
|
+
"data-testid": "flopay-country",
|
|
1082
|
+
style: {
|
|
1083
|
+
width: "100%",
|
|
1084
|
+
border: "none",
|
|
1085
|
+
outline: "none",
|
|
1086
|
+
background: "transparent",
|
|
1087
|
+
fontSize: bStyles.cardInputFontSize ?? "16px",
|
|
1088
|
+
fontFamily: "Poppins, sans-serif",
|
|
1089
|
+
color: bStyles.cardInputColor ?? "#262833",
|
|
1090
|
+
cursor: "pointer",
|
|
1091
|
+
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
1092
|
+
},
|
|
1093
|
+
children: import_shared3.COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("option", { value: c.code, children: [
|
|
1094
|
+
c.flag,
|
|
1095
|
+
" ",
|
|
1096
|
+
c.name
|
|
1097
|
+
] }, c.code))
|
|
1098
|
+
}
|
|
1099
|
+
) }),
|
|
1100
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1101
|
+
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
1102
|
+
backgroundColor: cardInputBg,
|
|
1103
|
+
border: `1px solid ${resolvedBorder}`,
|
|
1104
|
+
padding: "10px",
|
|
1105
|
+
...avsLayoutProp === "row" ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
|
|
1106
|
+
...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
|
|
1107
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1108
|
+
"input",
|
|
1109
|
+
{
|
|
1110
|
+
placeholder: (0, import_shared3.getPostalCodeLabel)(selectedCountry),
|
|
1111
|
+
autoComplete: "postal-code",
|
|
1112
|
+
value: zipCode,
|
|
1113
|
+
onChange: (e) => {
|
|
1114
|
+
zipCodeRef.current = e.target.value;
|
|
1115
|
+
setZipCode(e.target.value);
|
|
1116
|
+
onZipChange?.(e.target.value);
|
|
1117
|
+
},
|
|
1118
|
+
disabled: isSubmitting,
|
|
1119
|
+
required: true,
|
|
1120
|
+
"data-testid": "flopay-zip",
|
|
1121
|
+
style: {
|
|
1122
|
+
width: "100%",
|
|
1123
|
+
border: "none",
|
|
1124
|
+
outline: "none",
|
|
1125
|
+
background: "transparent",
|
|
1126
|
+
fontSize: bStyles.cardInputFontSize ?? "16px",
|
|
1127
|
+
fontFamily: "Poppins, sans-serif",
|
|
1128
|
+
color: bStyles.cardInputColor ?? "#262833",
|
|
1129
|
+
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
) })
|
|
1133
|
+
] }),
|
|
964
1134
|
displayError && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
965
1135
|
margin: "0.75rem 0",
|
|
966
1136
|
padding: "0.625rem 0.875rem",
|
|
@@ -972,7 +1142,8 @@ function SplitCardFormInner({
|
|
|
972
1142
|
fontWeight: 600,
|
|
973
1143
|
display: "flex",
|
|
974
1144
|
alignItems: "center",
|
|
975
|
-
gap: "0.5rem"
|
|
1145
|
+
gap: "0.5rem",
|
|
1146
|
+
...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
976
1147
|
}, children: [
|
|
977
1148
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
978
1149
|
displayError
|
|
@@ -991,10 +1162,11 @@ function SplitCardFormInner({
|
|
|
991
1162
|
color: "white",
|
|
992
1163
|
border: "none",
|
|
993
1164
|
borderRadius: "8px",
|
|
994
|
-
fontSize: "1rem",
|
|
1165
|
+
fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
|
|
995
1166
|
fontWeight: 600,
|
|
996
1167
|
cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
|
|
997
|
-
opacity: !formReady || isSubmitting ? 0.5 : 1
|
|
1168
|
+
opacity: !formReady || isSubmitting ? 0.5 : 1,
|
|
1169
|
+
...isButtons ? bStyles.submitButton : {}
|
|
998
1170
|
},
|
|
999
1171
|
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
1000
1172
|
}
|
|
@@ -1011,103 +1183,119 @@ function SplitCardFormInner({
|
|
|
1011
1183
|
}, children: "Secure Card Checkout" })
|
|
1012
1184
|
] });
|
|
1013
1185
|
if (layout === "buttons") {
|
|
1186
|
+
const isButtonsView = viewState === "buttons" || viewState === "expanding";
|
|
1187
|
+
const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
|
|
1188
|
+
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;
|
|
1189
|
+
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;
|
|
1014
1190
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1015
1192
|
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
1016
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
gap: "0.5rem"
|
|
1020
|
-
}, children: [
|
|
1021
|
-
showPayPal && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1022
|
-
PayPalButtonInner,
|
|
1023
|
-
{
|
|
1024
|
-
sessionId,
|
|
1025
|
-
email,
|
|
1026
|
-
billingApiUrl: resolvedBillingApiUrl,
|
|
1027
|
-
onTokenizedBody: dispatchTokenizedBody,
|
|
1028
|
-
onErrorChange: updateError,
|
|
1029
|
-
isProcessing: isSubmitting
|
|
1030
|
-
}
|
|
1031
|
-
) }),
|
|
1032
|
-
showWallets && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1033
|
-
WalletButtonInner,
|
|
1034
|
-
{
|
|
1035
|
-
sessionId,
|
|
1036
|
-
email,
|
|
1037
|
-
billingApiUrl: resolvedBillingApiUrl,
|
|
1038
|
-
showApplePay,
|
|
1039
|
-
showGooglePay,
|
|
1040
|
-
onTokenizedBody: dispatchTokenizedBody,
|
|
1041
|
-
onErrorChange: updateError
|
|
1042
|
-
}
|
|
1043
|
-
) }),
|
|
1044
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1045
|
-
"button",
|
|
1046
|
-
{
|
|
1047
|
-
type: "button",
|
|
1048
|
-
onClick: () => setShowCardForm(true),
|
|
1049
|
-
style: {
|
|
1050
|
-
width: "100%",
|
|
1051
|
-
padding: "0.9rem 1rem",
|
|
1052
|
-
backgroundColor: "white",
|
|
1053
|
-
color: "#262833",
|
|
1054
|
-
border: "1px solid #d1d5db",
|
|
1055
|
-
borderRadius: "8px",
|
|
1056
|
-
fontSize: "0.95rem",
|
|
1057
|
-
fontWeight: 600,
|
|
1058
|
-
cursor: "pointer",
|
|
1059
|
-
display: "flex",
|
|
1060
|
-
alignItems: "center",
|
|
1061
|
-
justifyContent: "center",
|
|
1062
|
-
gap: "0.625rem",
|
|
1063
|
-
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
1064
|
-
boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
|
|
1065
|
-
},
|
|
1066
|
-
onMouseOver: (e) => {
|
|
1067
|
-
e.currentTarget.style.borderColor = "#A4A4FF";
|
|
1068
|
-
e.currentTarget.style.boxShadow = "0 0 0 1px #A4A4FF, 0 2px 8px rgba(74,73,255,0.08)";
|
|
1069
|
-
},
|
|
1070
|
-
onMouseOut: (e) => {
|
|
1071
|
-
e.currentTarget.style.borderColor = "#d1d5db";
|
|
1072
|
-
e.currentTarget.style.boxShadow = "0 1px 2px rgba(0,0,0,0.04)";
|
|
1073
|
-
},
|
|
1074
|
-
onMouseDown: (e) => {
|
|
1075
|
-
e.currentTarget.style.transform = "scale(0.985)";
|
|
1076
|
-
},
|
|
1077
|
-
onMouseUp: (e) => {
|
|
1078
|
-
e.currentTarget.style.transform = "scale(1)";
|
|
1079
|
-
},
|
|
1080
|
-
children: [
|
|
1081
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1082
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
|
|
1083
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
|
|
1084
|
-
] }),
|
|
1085
|
-
"Credit / Debit Card",
|
|
1086
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { marginLeft: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 18l6-6-6-6" }) })
|
|
1087
|
-
]
|
|
1088
|
-
}
|
|
1089
|
-
),
|
|
1090
|
-
displayError && !showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1091
|
-
margin: "0.25rem 0",
|
|
1092
|
-
padding: "0.625rem 0.875rem",
|
|
1093
|
-
background: "#FEF2F2",
|
|
1094
|
-
border: "1px solid #FECACA",
|
|
1095
|
-
borderRadius: "8px",
|
|
1096
|
-
color: "#991B1B",
|
|
1097
|
-
fontSize: "0.85rem",
|
|
1098
|
-
fontWeight: 600,
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid" }, children: [
|
|
1194
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1195
|
+
gridArea: "1 / 1",
|
|
1099
1196
|
display: "flex",
|
|
1100
|
-
|
|
1101
|
-
gap: "0.5rem"
|
|
1197
|
+
flexDirection: "column",
|
|
1198
|
+
gap: "0.5rem",
|
|
1199
|
+
// When card form is showing (and not transitioning), hide but keep mounted
|
|
1200
|
+
...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
|
|
1201
|
+
...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
|
|
1102
1202
|
}, children: [
|
|
1103
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1203
|
+
showPayPal && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1204
|
+
PayPalButtonInner,
|
|
1205
|
+
{
|
|
1206
|
+
sessionId,
|
|
1207
|
+
email,
|
|
1208
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
1209
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
1210
|
+
onErrorChange: updateError,
|
|
1211
|
+
isProcessing: isSubmitting,
|
|
1212
|
+
onButtonClick
|
|
1213
|
+
}
|
|
1214
|
+
) }) : showPayPal ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
|
|
1215
|
+
showWallets && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1216
|
+
WalletButtonInner,
|
|
1217
|
+
{
|
|
1218
|
+
sessionId,
|
|
1219
|
+
email,
|
|
1220
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
1221
|
+
showApplePay,
|
|
1222
|
+
showGooglePay,
|
|
1223
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
1224
|
+
onErrorChange: updateError,
|
|
1225
|
+
onButtonClick
|
|
1226
|
+
}
|
|
1227
|
+
) }) : showWallets ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { height: 44, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1229
|
+
"button",
|
|
1230
|
+
{
|
|
1231
|
+
type: "button",
|
|
1232
|
+
onClick: () => {
|
|
1233
|
+
onButtonClick?.("card");
|
|
1234
|
+
expandToCard();
|
|
1235
|
+
},
|
|
1236
|
+
style: {
|
|
1237
|
+
width: "100%",
|
|
1238
|
+
padding: "0.9rem 1rem",
|
|
1239
|
+
backgroundColor: "white",
|
|
1240
|
+
color: "#262833",
|
|
1241
|
+
border: "1px solid #d1d5db",
|
|
1242
|
+
borderRadius: "8px",
|
|
1243
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
1244
|
+
fontWeight: 600,
|
|
1245
|
+
cursor: "pointer",
|
|
1246
|
+
display: "flex",
|
|
1247
|
+
alignItems: "center",
|
|
1248
|
+
justifyContent: "center",
|
|
1249
|
+
gap: "0.625rem",
|
|
1250
|
+
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
1251
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
1252
|
+
position: "relative",
|
|
1253
|
+
...bStyles.cardButton
|
|
1254
|
+
},
|
|
1255
|
+
onMouseDown: (e) => {
|
|
1256
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
1257
|
+
},
|
|
1258
|
+
onMouseUp: (e) => {
|
|
1259
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
1260
|
+
},
|
|
1261
|
+
children: [
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
|
|
1265
|
+
] }),
|
|
1266
|
+
"Credit / Debit Card",
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { position: "absolute", right: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 18l6-6-6-6" }) })
|
|
1268
|
+
]
|
|
1269
|
+
}
|
|
1270
|
+
),
|
|
1271
|
+
displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1272
|
+
margin: "0.25rem 0",
|
|
1273
|
+
padding: "0.625rem 0.875rem",
|
|
1274
|
+
background: "#FEF2F2",
|
|
1275
|
+
border: "1px solid #FECACA",
|
|
1276
|
+
borderRadius: "8px",
|
|
1277
|
+
color: "#991B1B",
|
|
1278
|
+
fontSize: "0.85rem",
|
|
1279
|
+
fontWeight: 600,
|
|
1280
|
+
display: "flex",
|
|
1281
|
+
alignItems: "center",
|
|
1282
|
+
gap: "0.5rem",
|
|
1283
|
+
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
1284
|
+
}, children: [
|
|
1285
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
1286
|
+
displayError
|
|
1287
|
+
] })
|
|
1288
|
+
] }),
|
|
1289
|
+
isCardView && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1290
|
+
gridArea: "1 / 1",
|
|
1291
|
+
...cardAnim ? { animation: cardAnim } : {},
|
|
1292
|
+
...viewState === "collapsing" ? { pointerEvents: "none" } : {}
|
|
1293
|
+
}, children: cardFormBlock })
|
|
1294
|
+
] })
|
|
1108
1295
|
] });
|
|
1109
1296
|
}
|
|
1110
1297
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1111
1299
|
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
1112
1300
|
showWallets && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1113
1301
|
WalletButtonInner,
|
|
@@ -1151,7 +1339,8 @@ function SplitCardFormInner({
|
|
|
1151
1339
|
// src/flopay-checkout.tsx
|
|
1152
1340
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1153
1341
|
function FloPayCheckout({
|
|
1154
|
-
sessionId,
|
|
1342
|
+
sessionId: sessionIdProp,
|
|
1343
|
+
createSession: createSessionParams,
|
|
1155
1344
|
billingApiUrl,
|
|
1156
1345
|
appearance,
|
|
1157
1346
|
locale,
|
|
@@ -1164,6 +1353,11 @@ function FloPayCheckout({
|
|
|
1164
1353
|
showApplePay = true,
|
|
1165
1354
|
showGooglePay = true,
|
|
1166
1355
|
layout,
|
|
1356
|
+
buttonsTheme,
|
|
1357
|
+
buttonsStyles,
|
|
1358
|
+
onButtonClick,
|
|
1359
|
+
enableAVS,
|
|
1360
|
+
avsLayout,
|
|
1167
1361
|
submitLabel,
|
|
1168
1362
|
className,
|
|
1169
1363
|
children,
|
|
@@ -1172,11 +1366,12 @@ function FloPayCheckout({
|
|
|
1172
1366
|
renderConfirmButton,
|
|
1173
1367
|
onSessionCompleted
|
|
1174
1368
|
}) {
|
|
1175
|
-
const resolvedBillingUrl = (0,
|
|
1369
|
+
const resolvedBillingUrl = (0, import_shared5.resolveBillingApiUrl)(billingApiUrl);
|
|
1176
1370
|
const [unified, setUnified] = (0, import_react6.useState)(null);
|
|
1177
1371
|
const [flopay, setFloPay] = (0, import_react6.useState)(null);
|
|
1178
1372
|
const flopayRef = (0, import_react6.useRef)(null);
|
|
1179
1373
|
const [session, setSession] = (0, import_react6.useState)(null);
|
|
1374
|
+
const [resolvedSessionId, setResolvedSessionId] = (0, import_react6.useState)(sessionIdProp ?? "");
|
|
1180
1375
|
const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
|
|
1181
1376
|
const [loadError, setLoadError] = (0, import_react6.useState)(null);
|
|
1182
1377
|
const [currentMode, setCurrentMode] = (0, import_react6.useState)("full");
|
|
@@ -1199,7 +1394,7 @@ function FloPayCheckout({
|
|
|
1199
1394
|
"x-user-id": sess.customer?.id ?? ""
|
|
1200
1395
|
},
|
|
1201
1396
|
body: JSON.stringify({
|
|
1202
|
-
sessionId,
|
|
1397
|
+
sessionId: resolvedSessionId,
|
|
1203
1398
|
tokenizedData: { id: void 0 },
|
|
1204
1399
|
accountData: {
|
|
1205
1400
|
userId: sess.customer?.id ?? "",
|
|
@@ -1228,12 +1423,12 @@ function FloPayCheckout({
|
|
|
1228
1423
|
// No client secret available
|
|
1229
1424
|
};
|
|
1230
1425
|
}
|
|
1231
|
-
throw new
|
|
1426
|
+
throw new import_shared5.FloPayError(
|
|
1232
1427
|
json?.message ?? "Payment failed. Please try again.",
|
|
1233
1428
|
"api_error"
|
|
1234
1429
|
);
|
|
1235
1430
|
},
|
|
1236
|
-
[resolvedBillingUrl,
|
|
1431
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
1237
1432
|
);
|
|
1238
1433
|
const handleRedirectResult = (0, import_react6.useCallback)(
|
|
1239
1434
|
async (redirectResult, sess, options) => {
|
|
@@ -1278,22 +1473,175 @@ function FloPayCheckout({
|
|
|
1278
1473
|
}
|
|
1279
1474
|
return false;
|
|
1280
1475
|
},
|
|
1281
|
-
[resolvedBillingUrl,
|
|
1476
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
1477
|
+
);
|
|
1478
|
+
const inflightRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
1479
|
+
const initializedHashRef = (0, import_react6.useRef)(null);
|
|
1480
|
+
function hashCreateParams(params) {
|
|
1481
|
+
const key = JSON.stringify({
|
|
1482
|
+
c: params?.clientId,
|
|
1483
|
+
i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
1484
|
+
s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
1485
|
+
e: params?.account.email,
|
|
1486
|
+
m: params?.checkoutMode ?? "full"
|
|
1487
|
+
});
|
|
1488
|
+
let h = 0;
|
|
1489
|
+
for (let i = 0; i < key.length; i++) {
|
|
1490
|
+
h = (h << 5) - h + key.charCodeAt(i) | 0;
|
|
1491
|
+
}
|
|
1492
|
+
return `flopay_session_${Math.abs(h).toString(36)}`;
|
|
1493
|
+
}
|
|
1494
|
+
const createSessionHash = (0, import_react6.useMemo)(
|
|
1495
|
+
() => createSessionParams ? hashCreateParams(createSessionParams) : "",
|
|
1496
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1497
|
+
[
|
|
1498
|
+
createSessionParams?.clientId,
|
|
1499
|
+
createSessionParams?.account?.email,
|
|
1500
|
+
createSessionParams?.checkoutMode,
|
|
1501
|
+
JSON.stringify(createSessionParams?.items),
|
|
1502
|
+
JSON.stringify(createSessionParams?.subscriptions)
|
|
1503
|
+
]
|
|
1282
1504
|
);
|
|
1505
|
+
const createSessionParamsRef = (0, import_react6.useRef)(createSessionParams);
|
|
1506
|
+
createSessionParamsRef.current = createSessionParams;
|
|
1283
1507
|
(0, import_react6.useEffect)(() => {
|
|
1284
1508
|
let cancelled = false;
|
|
1285
|
-
setIsLoading(true);
|
|
1286
1509
|
setLoadError(null);
|
|
1510
|
+
if (createSessionHash) {
|
|
1511
|
+
if (initializedHashRef.current === createSessionHash) return;
|
|
1512
|
+
const params = createSessionParamsRef.current;
|
|
1513
|
+
const totalAmount = [
|
|
1514
|
+
...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
|
|
1515
|
+
...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
|
|
1516
|
+
].reduce((sum, v) => sum + v, 0);
|
|
1517
|
+
const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
|
|
1518
|
+
const syntheticSession = {
|
|
1519
|
+
id: "",
|
|
1520
|
+
clientSecret: "",
|
|
1521
|
+
mode: "payment",
|
|
1522
|
+
amount: Math.round(totalAmount * 100),
|
|
1523
|
+
currency,
|
|
1524
|
+
status: "open",
|
|
1525
|
+
customer: {
|
|
1526
|
+
id: params.account.userId,
|
|
1527
|
+
email: params.account.email,
|
|
1528
|
+
firstName: params.account.firstName,
|
|
1529
|
+
lastName: params.account.lastName
|
|
1530
|
+
},
|
|
1531
|
+
successUrl: params.successUrl,
|
|
1532
|
+
cancelUrl: params.cancelUrl,
|
|
1533
|
+
checkoutMode: params.checkoutMode ?? "full",
|
|
1534
|
+
items: (params.items ?? []).map((i, idx) => ({
|
|
1535
|
+
uuid: `synthetic-item-${idx}`,
|
|
1536
|
+
checkoutSessionId: "",
|
|
1537
|
+
providerItemId: i.providerItemId,
|
|
1538
|
+
providerItemName: i.providerItemName ?? i.providerItemId,
|
|
1539
|
+
quantity: i.quantity ?? 1,
|
|
1540
|
+
totalAmount: i.totalAmount,
|
|
1541
|
+
overrideAmount: i.overrideAmount ?? null,
|
|
1542
|
+
currency: i.currency ?? currency
|
|
1543
|
+
})),
|
|
1544
|
+
subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
|
|
1545
|
+
uuid: `synthetic-sub-${idx}`,
|
|
1546
|
+
checkoutSessionId: "",
|
|
1547
|
+
providerPlanId: s.providerPlanId,
|
|
1548
|
+
providerPlanName: s.providerPlanName ?? s.providerPlanId,
|
|
1549
|
+
quantity: s.quantity ?? 1,
|
|
1550
|
+
totalAmount: s.totalAmount,
|
|
1551
|
+
overrideAmount: s.overrideAmount ?? null,
|
|
1552
|
+
currency: s.currency ?? currency
|
|
1553
|
+
}))
|
|
1554
|
+
};
|
|
1555
|
+
setSession(syntheticSession);
|
|
1556
|
+
setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
|
|
1557
|
+
setIsLoading(false);
|
|
1558
|
+
const cacheKey = createSessionHash;
|
|
1559
|
+
async function resolveSession() {
|
|
1560
|
+
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1561
|
+
let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
|
|
1562
|
+
let realResult = null;
|
|
1563
|
+
if (sid) {
|
|
1564
|
+
try {
|
|
1565
|
+
realResult = await api.getUnifiedCheckoutSession(sid);
|
|
1566
|
+
if (realResult.data.session?.status === "complete") {
|
|
1567
|
+
if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
|
|
1568
|
+
sid = null;
|
|
1569
|
+
realResult = null;
|
|
1570
|
+
}
|
|
1571
|
+
} catch {
|
|
1572
|
+
if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
|
|
1573
|
+
sid = null;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
if (!sid) {
|
|
1577
|
+
realResult = await api.createAndFetchSession(createSessionParamsRef.current);
|
|
1578
|
+
sid = realResult.data.session?.id ?? "";
|
|
1579
|
+
if (sid && typeof window !== "undefined") {
|
|
1580
|
+
window.sessionStorage.setItem(cacheKey, sid);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return { sid: sid ?? "", result: realResult };
|
|
1584
|
+
}
|
|
1585
|
+
(async () => {
|
|
1586
|
+
try {
|
|
1587
|
+
let promise = inflightRef.current.get(cacheKey);
|
|
1588
|
+
if (!promise) {
|
|
1589
|
+
promise = resolveSession();
|
|
1590
|
+
inflightRef.current.set(cacheKey, promise);
|
|
1591
|
+
}
|
|
1592
|
+
let resolved;
|
|
1593
|
+
try {
|
|
1594
|
+
resolved = await promise;
|
|
1595
|
+
} finally {
|
|
1596
|
+
inflightRef.current.delete(cacheKey);
|
|
1597
|
+
}
|
|
1598
|
+
if (cancelled) return;
|
|
1599
|
+
const { sid, result: realResult } = resolved;
|
|
1600
|
+
if (realResult) {
|
|
1601
|
+
setUnified(realResult);
|
|
1602
|
+
if (realResult.data.session) setSession(realResult.data.session);
|
|
1603
|
+
}
|
|
1604
|
+
if (sid) {
|
|
1605
|
+
setResolvedSessionId(sid);
|
|
1606
|
+
}
|
|
1607
|
+
let pk;
|
|
1608
|
+
if (realResult?.provider === "stripe") {
|
|
1609
|
+
pk = realResult.data.stripe?.publishableKey;
|
|
1610
|
+
}
|
|
1611
|
+
if (!pk) pk = fallbackPublishableKey;
|
|
1612
|
+
if (!pk) {
|
|
1613
|
+
throw new import_shared5.FloPayError(
|
|
1614
|
+
"No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
|
|
1615
|
+
"validation_error"
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
const instance = await (0, import_js.loadFloPay)(pk, { billingApiUrl: resolvedBillingUrl, locale });
|
|
1619
|
+
if (!cancelled) {
|
|
1620
|
+
flopayRef.current = instance;
|
|
1621
|
+
setFloPay(instance);
|
|
1622
|
+
initializedHashRef.current = cacheKey;
|
|
1623
|
+
}
|
|
1624
|
+
} catch (err) {
|
|
1625
|
+
if (cancelled) return;
|
|
1626
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to create session", "api_error");
|
|
1627
|
+
setLoadError(floPayErr);
|
|
1628
|
+
}
|
|
1629
|
+
})();
|
|
1630
|
+
return () => {
|
|
1631
|
+
cancelled = true;
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
setIsLoading(true);
|
|
1287
1635
|
async function init() {
|
|
1288
1636
|
try {
|
|
1289
1637
|
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1290
|
-
const result = await api.getUnifiedCheckoutSession(
|
|
1638
|
+
const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
|
|
1291
1639
|
if (cancelled) return;
|
|
1292
1640
|
setUnified(result);
|
|
1293
1641
|
const sess = result.data.session ?? null;
|
|
1294
1642
|
setSession(sess);
|
|
1295
1643
|
if (!sess) {
|
|
1296
|
-
throw new
|
|
1644
|
+
throw new import_shared5.FloPayError("No session data returned", "api_error");
|
|
1297
1645
|
}
|
|
1298
1646
|
if (sess.status === "complete") {
|
|
1299
1647
|
setIsLoading(false);
|
|
@@ -1305,7 +1653,7 @@ function FloPayCheckout({
|
|
|
1305
1653
|
const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
|
|
1306
1654
|
if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
|
|
1307
1655
|
autoCheckoutAttempted.current = true;
|
|
1308
|
-
const stripeInitPromise = initStripe(result
|
|
1656
|
+
const stripeInitPromise = initStripe(result);
|
|
1309
1657
|
try {
|
|
1310
1658
|
const redirectResult = await processPaymentForMode(sess);
|
|
1311
1659
|
if (!redirectResult) {
|
|
@@ -1316,9 +1664,7 @@ function FloPayCheckout({
|
|
|
1316
1664
|
await stripeInitPromise;
|
|
1317
1665
|
const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
|
|
1318
1666
|
if (!cancelled) {
|
|
1319
|
-
if (!handled)
|
|
1320
|
-
setCurrentMode("full");
|
|
1321
|
-
}
|
|
1667
|
+
if (!handled) setCurrentMode("full");
|
|
1322
1668
|
setIsLoading(false);
|
|
1323
1669
|
}
|
|
1324
1670
|
return;
|
|
@@ -1330,26 +1676,23 @@ function FloPayCheckout({
|
|
|
1330
1676
|
return;
|
|
1331
1677
|
}
|
|
1332
1678
|
}
|
|
1333
|
-
await initStripe(result
|
|
1679
|
+
await initStripe(result);
|
|
1334
1680
|
if (!cancelled) setIsLoading(false);
|
|
1335
1681
|
} catch (err) {
|
|
1336
1682
|
if (cancelled) return;
|
|
1337
|
-
const floPayErr = err instanceof
|
|
1338
|
-
err instanceof Error ? err.message : "Failed to initialize checkout",
|
|
1339
|
-
"api_error"
|
|
1340
|
-
);
|
|
1683
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
1341
1684
|
setLoadError(floPayErr);
|
|
1342
1685
|
setIsLoading(false);
|
|
1343
1686
|
}
|
|
1344
1687
|
}
|
|
1345
|
-
async function initStripe(result
|
|
1688
|
+
async function initStripe(result) {
|
|
1346
1689
|
let publishableKey;
|
|
1347
1690
|
if (result.provider === "stripe") {
|
|
1348
1691
|
publishableKey = result.data.stripe?.publishableKey;
|
|
1349
1692
|
}
|
|
1350
1693
|
if (!publishableKey) publishableKey = fallbackPublishableKey;
|
|
1351
1694
|
if (!publishableKey) {
|
|
1352
|
-
throw new
|
|
1695
|
+
throw new import_shared5.FloPayError(
|
|
1353
1696
|
"No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
|
|
1354
1697
|
"validation_error"
|
|
1355
1698
|
);
|
|
@@ -1365,7 +1708,7 @@ function FloPayCheckout({
|
|
|
1365
1708
|
return () => {
|
|
1366
1709
|
cancelled = true;
|
|
1367
1710
|
};
|
|
1368
|
-
}, [
|
|
1711
|
+
}, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
|
|
1369
1712
|
const handleConfirmCheckout = (0, import_react6.useCallback)(async () => {
|
|
1370
1713
|
if (confirmProcessing || !session) return;
|
|
1371
1714
|
setConfirmProcessing(true);
|
|
@@ -1380,7 +1723,7 @@ function FloPayCheckout({
|
|
|
1380
1723
|
setCurrentMode("full");
|
|
1381
1724
|
}
|
|
1382
1725
|
} catch (err) {
|
|
1383
|
-
const floPayErr = err instanceof
|
|
1726
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
|
|
1384
1727
|
err instanceof Error ? err.message : "Payment failed",
|
|
1385
1728
|
"api_error"
|
|
1386
1729
|
);
|
|
@@ -1401,7 +1744,7 @@ function FloPayCheckout({
|
|
|
1401
1744
|
if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
|
|
1402
1745
|
opts.clientSecret = unified.data.stripe.clientSecret;
|
|
1403
1746
|
} else {
|
|
1404
|
-
const displayTotal = (0,
|
|
1747
|
+
const displayTotal = (0, import_shared5.buildCheckoutDisplayData)(session).total;
|
|
1405
1748
|
opts.amount = Math.round(displayTotal * 100) || session.amount;
|
|
1406
1749
|
opts.currency = session.currency?.toLowerCase();
|
|
1407
1750
|
}
|
|
@@ -1417,32 +1760,32 @@ function FloPayCheckout({
|
|
|
1417
1760
|
[session, isLoading, loadError, currentMode]
|
|
1418
1761
|
);
|
|
1419
1762
|
if (isLoading) {
|
|
1420
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode
|
|
1421
|
-
|
|
1422
|
-
{
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1763
|
+
if (loadingNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode });
|
|
1764
|
+
if (layout === "buttons") {
|
|
1765
|
+
const skeletonBar = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1766
|
+
height: h,
|
|
1767
|
+
borderRadius: 8,
|
|
1768
|
+
background: "#e5e7eb",
|
|
1769
|
+
animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
|
|
1770
|
+
} });
|
|
1771
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1772
|
+
showPayPal && skeletonBar(44),
|
|
1773
|
+
(showApplePay || showGooglePay) && skeletonBar(44),
|
|
1774
|
+
skeletonBar(48),
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
1776
|
+
] });
|
|
1777
|
+
}
|
|
1778
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1780
|
+
width: 24,
|
|
1781
|
+
height: 24,
|
|
1782
|
+
border: "2px solid #e5e7eb",
|
|
1783
|
+
borderTopColor: "#6b7280",
|
|
1784
|
+
borderRadius: "50%",
|
|
1785
|
+
animation: "spin 0.6s linear infinite"
|
|
1786
|
+
} }),
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
|
|
1788
|
+
] });
|
|
1446
1789
|
}
|
|
1447
1790
|
if (loadError) {
|
|
1448
1791
|
if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: errorNode(loadError) });
|
|
@@ -1459,6 +1802,19 @@ function FloPayCheckout({
|
|
|
1459
1802
|
}
|
|
1460
1803
|
);
|
|
1461
1804
|
}
|
|
1805
|
+
if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
|
|
1806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1807
|
+
InterimButtonsView,
|
|
1808
|
+
{
|
|
1809
|
+
onButtonClick,
|
|
1810
|
+
showPayPal,
|
|
1811
|
+
showApplePay,
|
|
1812
|
+
showGooglePay,
|
|
1813
|
+
buttonsTheme,
|
|
1814
|
+
buttonsStyles
|
|
1815
|
+
}
|
|
1816
|
+
) });
|
|
1817
|
+
}
|
|
1462
1818
|
if (!flopay || !providerOptions) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
|
|
1463
1819
|
if (currentMode === "confirm") {
|
|
1464
1820
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FloPayProvider, { flopay, options: providerOptions, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, children: [
|
|
@@ -1519,7 +1875,7 @@ function FloPayCheckout({
|
|
|
1519
1875
|
children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1520
1876
|
SessionInjector,
|
|
1521
1877
|
{
|
|
1522
|
-
sessionId,
|
|
1878
|
+
sessionId: resolvedSessionId,
|
|
1523
1879
|
billingApiUrl: resolvedBillingUrl,
|
|
1524
1880
|
session,
|
|
1525
1881
|
children
|
|
@@ -1527,12 +1883,12 @@ function FloPayCheckout({
|
|
|
1527
1883
|
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1528
1884
|
SplitCardForm,
|
|
1529
1885
|
{
|
|
1530
|
-
sessionId,
|
|
1886
|
+
sessionId: resolvedSessionId,
|
|
1531
1887
|
email: session?.customer?.email,
|
|
1532
1888
|
userId: session?.customer?.id,
|
|
1533
1889
|
firstName: session?.customer?.firstName,
|
|
1534
1890
|
lastName: session?.customer?.lastName,
|
|
1535
|
-
totalAmount: session ? Math.round((0,
|
|
1891
|
+
totalAmount: session ? Math.round((0, import_shared5.buildCheckoutDisplayData)(session).total * 100) : 0,
|
|
1536
1892
|
currency: session?.currency?.toLowerCase() ?? "usd",
|
|
1537
1893
|
onComplete,
|
|
1538
1894
|
onError,
|
|
@@ -1540,6 +1896,13 @@ function FloPayCheckout({
|
|
|
1540
1896
|
showApplePay,
|
|
1541
1897
|
showGooglePay,
|
|
1542
1898
|
layout,
|
|
1899
|
+
buttonsTheme,
|
|
1900
|
+
buttonsStyles,
|
|
1901
|
+
onButtonClick,
|
|
1902
|
+
enableAVS,
|
|
1903
|
+
avsLayout,
|
|
1904
|
+
country: session?.customer?.country,
|
|
1905
|
+
zip: session?.customer?.zip,
|
|
1543
1906
|
submitLabel,
|
|
1544
1907
|
className
|
|
1545
1908
|
}
|
|
@@ -1570,9 +1933,165 @@ function SessionInjector({
|
|
|
1570
1933
|
return import_react6.default.cloneElement(child, injected);
|
|
1571
1934
|
}) });
|
|
1572
1935
|
}
|
|
1936
|
+
function InterimButtonsView({
|
|
1937
|
+
onButtonClick,
|
|
1938
|
+
showPayPal,
|
|
1939
|
+
showApplePay,
|
|
1940
|
+
showGooglePay,
|
|
1941
|
+
buttonsTheme,
|
|
1942
|
+
buttonsStyles: stylesOverride
|
|
1943
|
+
}) {
|
|
1944
|
+
const [showCardForm, setShowCardForm] = (0, import_react6.useState)(false);
|
|
1945
|
+
const bStyles = (0, import_react6.useMemo)(() => {
|
|
1946
|
+
const base = (0, import_shared5.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
1947
|
+
if (!stylesOverride) return base;
|
|
1948
|
+
return {
|
|
1949
|
+
...base,
|
|
1950
|
+
...stylesOverride,
|
|
1951
|
+
cardButton: { ...base.cardButton, ...stylesOverride.cardButton },
|
|
1952
|
+
cardFormContainer: { ...base.cardFormContainer, ...stylesOverride.cardFormContainer },
|
|
1953
|
+
submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
|
|
1954
|
+
title: { ...base.title, ...stylesOverride.title }
|
|
1955
|
+
};
|
|
1956
|
+
}, [buttonsTheme, stylesOverride]);
|
|
1957
|
+
const skeleton = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1958
|
+
height: h,
|
|
1959
|
+
borderRadius: 8,
|
|
1960
|
+
background: "#e5e7eb",
|
|
1961
|
+
animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
|
|
1962
|
+
} });
|
|
1963
|
+
if (showCardForm) {
|
|
1964
|
+
const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
|
|
1965
|
+
const inputBg = bStyles.cardInputBackground ?? "white";
|
|
1966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
|
|
1967
|
+
backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
|
|
1968
|
+
borderRadius: "8px",
|
|
1969
|
+
animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
|
|
1970
|
+
overflow: "hidden",
|
|
1971
|
+
...bStyles.cardFormContainer
|
|
1972
|
+
}, children: [
|
|
1973
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
|
|
1974
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1975
|
+
"button",
|
|
1976
|
+
{
|
|
1977
|
+
type: "button",
|
|
1978
|
+
onClick: () => setShowCardForm(false),
|
|
1979
|
+
style: {
|
|
1980
|
+
display: "inline-flex",
|
|
1981
|
+
alignItems: "center",
|
|
1982
|
+
gap: "0.5rem",
|
|
1983
|
+
background: "none",
|
|
1984
|
+
border: "none",
|
|
1985
|
+
cursor: "pointer",
|
|
1986
|
+
color: "#4b5563",
|
|
1987
|
+
fontSize: "0.85rem",
|
|
1988
|
+
fontWeight: 500,
|
|
1989
|
+
padding: 0,
|
|
1990
|
+
flexShrink: 0,
|
|
1991
|
+
...bStyles.backButton
|
|
1992
|
+
},
|
|
1993
|
+
children: [
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
|
|
1995
|
+
display: "inline-flex",
|
|
1996
|
+
alignItems: "center",
|
|
1997
|
+
justifyContent: "center",
|
|
1998
|
+
width: 28,
|
|
1999
|
+
height: 28,
|
|
2000
|
+
borderRadius: "50%",
|
|
2001
|
+
backgroundColor: "#f3f4f6",
|
|
2002
|
+
...bStyles.backButtonIcon
|
|
2003
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
2004
|
+
"Go back"
|
|
2005
|
+
]
|
|
2006
|
+
}
|
|
2007
|
+
),
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
2009
|
+
flex: 1,
|
|
2010
|
+
textAlign: "center",
|
|
2011
|
+
fontWeight: 600,
|
|
2012
|
+
fontSize: "1.05rem",
|
|
2013
|
+
color: "#262833",
|
|
2014
|
+
paddingRight: 80,
|
|
2015
|
+
...bStyles.title
|
|
2016
|
+
}, children: "Secure card checkout" })
|
|
2017
|
+
] }),
|
|
2018
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
2019
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex" }, children: [
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
2021
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
|
|
2022
|
+
] }),
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
2025
|
+
height: 50,
|
|
2026
|
+
borderRadius: 8,
|
|
2027
|
+
marginTop: 16,
|
|
2028
|
+
background: "#c8c8ff",
|
|
2029
|
+
animation: "flopay-interim-pulse 1.5s ease-in-out infinite",
|
|
2030
|
+
...bStyles.submitButton,
|
|
2031
|
+
opacity: 0.5
|
|
2032
|
+
} }),
|
|
2033
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
|
|
2034
|
+
@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
2035
|
+
@keyframes flopay-interim-expand {
|
|
2036
|
+
0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
|
|
2037
|
+
40% { opacity: 1; }
|
|
2038
|
+
100% { opacity: 1; max-height: 600px; transform: translateY(0); }
|
|
2039
|
+
}
|
|
2040
|
+
` })
|
|
2041
|
+
] });
|
|
2042
|
+
}
|
|
2043
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2044
|
+
showPayPal && skeleton(44),
|
|
2045
|
+
(showApplePay || showGooglePay) && skeleton(44),
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2047
|
+
"button",
|
|
2048
|
+
{
|
|
2049
|
+
type: "button",
|
|
2050
|
+
onClick: () => {
|
|
2051
|
+
onButtonClick?.("card");
|
|
2052
|
+
setShowCardForm(true);
|
|
2053
|
+
},
|
|
2054
|
+
style: {
|
|
2055
|
+
width: "100%",
|
|
2056
|
+
padding: "0.9rem 1rem",
|
|
2057
|
+
backgroundColor: "white",
|
|
2058
|
+
color: "#262833",
|
|
2059
|
+
border: "1px solid #d1d5db",
|
|
2060
|
+
borderRadius: "8px",
|
|
2061
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
2062
|
+
fontWeight: 600,
|
|
2063
|
+
cursor: "pointer",
|
|
2064
|
+
display: "flex",
|
|
2065
|
+
alignItems: "center",
|
|
2066
|
+
justifyContent: "center",
|
|
2067
|
+
gap: "0.625rem",
|
|
2068
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
2069
|
+
transition: "transform 0.1s",
|
|
2070
|
+
position: "relative",
|
|
2071
|
+
...bStyles.cardButton
|
|
2072
|
+
},
|
|
2073
|
+
onMouseDown: (e) => {
|
|
2074
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
2075
|
+
},
|
|
2076
|
+
onMouseUp: (e) => {
|
|
2077
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
2078
|
+
},
|
|
2079
|
+
children: [
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
|
|
2083
|
+
] }),
|
|
2084
|
+
"Credit / Debit Card",
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { position: "absolute", right: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 18l6-6-6-6" }) })
|
|
2086
|
+
]
|
|
2087
|
+
}
|
|
2088
|
+
),
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
2090
|
+
] });
|
|
2091
|
+
}
|
|
1573
2092
|
|
|
1574
2093
|
// src/checkout-form.tsx
|
|
1575
|
-
var
|
|
2094
|
+
var import_shared6 = require("@flopay/shared");
|
|
1576
2095
|
var import_react7 = require("react");
|
|
1577
2096
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1578
2097
|
var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
|
|
@@ -1780,7 +2299,7 @@ function CheckoutFormInner({
|
|
|
1780
2299
|
return;
|
|
1781
2300
|
}
|
|
1782
2301
|
if (!sessionId || !email) {
|
|
1783
|
-
throw new
|
|
2302
|
+
throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
|
|
1784
2303
|
}
|
|
1785
2304
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1786
2305
|
method: "POST",
|
|
@@ -1792,10 +2311,10 @@ function CheckoutFormInner({
|
|
|
1792
2311
|
isPaypal: false
|
|
1793
2312
|
})
|
|
1794
2313
|
});
|
|
1795
|
-
if (!intentResponse.ok) throw new
|
|
2314
|
+
if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
|
|
1796
2315
|
const intentJson = await intentResponse.json();
|
|
1797
2316
|
const intentClientSecret = intentJson.data?.id;
|
|
1798
|
-
if (!intentClientSecret) throw new
|
|
2317
|
+
if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
|
|
1799
2318
|
const confirmResult = await flopay.confirmCardPayment({
|
|
1800
2319
|
clientSecret: intentClientSecret,
|
|
1801
2320
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -1850,7 +2369,7 @@ function CheckoutFormInner({
|
|
|
1850
2369
|
}
|
|
1851
2370
|
|
|
1852
2371
|
// src/paypal-button.tsx
|
|
1853
|
-
var
|
|
2372
|
+
var import_shared7 = require("@flopay/shared");
|
|
1854
2373
|
var import_react8 = require("react");
|
|
1855
2374
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1856
2375
|
function PayPalButton({
|
|
@@ -1970,7 +2489,7 @@ function PayPalButton({
|
|
|
1970
2489
|
setSubmitting(true);
|
|
1971
2490
|
onErrorChange?.(null);
|
|
1972
2491
|
if (!sessionId || !email) {
|
|
1973
|
-
throw new
|
|
2492
|
+
throw new import_shared7.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
|
|
1974
2493
|
}
|
|
1975
2494
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1976
2495
|
method: "POST",
|
|
@@ -1982,10 +2501,10 @@ function PayPalButton({
|
|
|
1982
2501
|
isPaypal: "true"
|
|
1983
2502
|
})
|
|
1984
2503
|
});
|
|
1985
|
-
if (!intentResponse.ok) throw new
|
|
2504
|
+
if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
|
|
1986
2505
|
const intentJson = await intentResponse.json();
|
|
1987
2506
|
const intentClientSecret = intentJson.data?.id;
|
|
1988
|
-
if (!intentClientSecret) throw new
|
|
2507
|
+
if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in response", "api_error");
|
|
1989
2508
|
const result = await flopay.confirmPayment({
|
|
1990
2509
|
clientSecret: intentClientSecret,
|
|
1991
2510
|
returnUrl: window.location.href
|