@flopay/react 0.1.6 → 0.2.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 +608 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -4
- package/dist/index.d.ts +39 -4
- package/dist/index.mjs +589 -175
- 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,9 @@ function SplitCardFormInner({
|
|
|
556
583
|
showApplePay = true,
|
|
557
584
|
showGooglePay = true,
|
|
558
585
|
layout = "default",
|
|
586
|
+
buttonsTheme,
|
|
587
|
+
buttonsStyles: buttonsStylesOverride,
|
|
588
|
+
onButtonClick,
|
|
559
589
|
totalAmount = 0,
|
|
560
590
|
currency = "usd",
|
|
561
591
|
innerRef
|
|
@@ -565,14 +595,38 @@ function SplitCardFormInner({
|
|
|
565
595
|
const contextBillingUrl = useBillingApiUrl();
|
|
566
596
|
const [processing, setProcessing] = (0, import_react5.useState)(false);
|
|
567
597
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
568
|
-
const [showCardForm, setShowCardForm] = (0, import_react5.useState)(false);
|
|
569
598
|
const [is3DSActive, setIs3DSActive] = (0, import_react5.useState)(false);
|
|
599
|
+
const [viewState, setViewState] = (0, import_react5.useState)("buttons");
|
|
600
|
+
const showCardForm = viewState === "expanding" || viewState === "card";
|
|
601
|
+
const TRANSITION_MS = 280;
|
|
602
|
+
const expandToCard = (0, import_react5.useCallback)(() => {
|
|
603
|
+
setViewState("expanding");
|
|
604
|
+
setTimeout(() => setViewState("card"), TRANSITION_MS);
|
|
605
|
+
}, []);
|
|
606
|
+
const collapseToButtons = (0, import_react5.useCallback)(() => {
|
|
607
|
+
setViewState("collapsing");
|
|
608
|
+
setTimeout(() => setViewState("buttons"), TRANSITION_MS);
|
|
609
|
+
}, []);
|
|
570
610
|
const [fullName, setFullName] = (0, import_react5.useState)("");
|
|
571
611
|
const [formReady, setFormReady] = (0, import_react5.useState)(false);
|
|
572
612
|
const [overlayStatus, setOverlayStatus] = (0, import_react5.useState)(null);
|
|
573
613
|
const processingRef = (0, import_react5.useRef)(false);
|
|
574
614
|
const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
|
|
575
615
|
const displayError = externalError ?? error;
|
|
616
|
+
const bStyles = (0, import_react5.useMemo)(() => {
|
|
617
|
+
const base = (0, import_shared3.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
618
|
+
if (!buttonsStylesOverride) return base;
|
|
619
|
+
return {
|
|
620
|
+
...base,
|
|
621
|
+
...buttonsStylesOverride,
|
|
622
|
+
cardButton: { ...base.cardButton, ...buttonsStylesOverride.cardButton },
|
|
623
|
+
cardFormContainer: { ...base.cardFormContainer, ...buttonsStylesOverride.cardFormContainer },
|
|
624
|
+
backButton: { ...base.backButton, ...buttonsStylesOverride.backButton },
|
|
625
|
+
backButtonIcon: { ...base.backButtonIcon, ...buttonsStylesOverride.backButtonIcon },
|
|
626
|
+
submitButton: { ...base.submitButton, ...buttonsStylesOverride.submitButton },
|
|
627
|
+
title: { ...base.title, ...buttonsStylesOverride.title }
|
|
628
|
+
};
|
|
629
|
+
}, [buttonsTheme, buttonsStylesOverride]);
|
|
576
630
|
const isSubmitting = externalProcessing ?? processing;
|
|
577
631
|
const isSelfContained = !onTokenizedBody;
|
|
578
632
|
const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
|
|
@@ -784,6 +838,7 @@ function SplitCardFormInner({
|
|
|
784
838
|
async (e) => {
|
|
785
839
|
e.preventDefault();
|
|
786
840
|
if (!flopay || !elements || isSubmitting || processingRef.current) return;
|
|
841
|
+
onButtonClick?.("card");
|
|
787
842
|
setProcessing(true);
|
|
788
843
|
setOverlayStatus("processing");
|
|
789
844
|
updateError(null);
|
|
@@ -801,7 +856,7 @@ function SplitCardFormInner({
|
|
|
801
856
|
return;
|
|
802
857
|
}
|
|
803
858
|
if (!sessionId || !email) {
|
|
804
|
-
throw new
|
|
859
|
+
throw new import_shared4.FloPayError("Missing sessionId or email", "validation_error");
|
|
805
860
|
}
|
|
806
861
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
807
862
|
method: "POST",
|
|
@@ -813,10 +868,10 @@ function SplitCardFormInner({
|
|
|
813
868
|
isPaypal: false
|
|
814
869
|
})
|
|
815
870
|
});
|
|
816
|
-
if (!intentResponse.ok) throw new
|
|
871
|
+
if (!intentResponse.ok) throw new import_shared4.FloPayError("Failed to create payment intent", "api_error");
|
|
817
872
|
const intentJson = await intentResponse.json();
|
|
818
873
|
const intentClientSecret = intentJson.data?.id;
|
|
819
|
-
if (!intentClientSecret) throw new
|
|
874
|
+
if (!intentClientSecret) throw new import_shared4.FloPayError("No client_secret in payment intent response", "api_error");
|
|
820
875
|
const confirmResult = await flopay.confirmCardPayment({
|
|
821
876
|
clientSecret: intentClientSecret,
|
|
822
877
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -851,15 +906,25 @@ function SplitCardFormInner({
|
|
|
851
906
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
|
|
852
907
|
}
|
|
853
908
|
const isButtons = layout === "buttons";
|
|
854
|
-
const
|
|
855
|
-
const cardBg = isButtons ? "white" : "#EDEDFF";
|
|
909
|
+
const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
|
|
910
|
+
const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
|
|
911
|
+
const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
|
|
912
|
+
const stripeElementStyle = isButtons && (bStyles.cardInputColor || bStyles.cardInputPlaceholderColor || bStyles.cardInputFontSize) ? {
|
|
913
|
+
style: {
|
|
914
|
+
base: {
|
|
915
|
+
...bStyles.cardInputColor ? { color: bStyles.cardInputColor } : {},
|
|
916
|
+
...bStyles.cardInputFontSize ? { fontSize: bStyles.cardInputFontSize } : {},
|
|
917
|
+
...bStyles.cardInputPlaceholderColor ? { "::placeholder": { color: bStyles.cardInputPlaceholderColor } } : {}
|
|
918
|
+
},
|
|
919
|
+
invalid: { color: "#ef4444" }
|
|
920
|
+
}
|
|
921
|
+
} : {};
|
|
856
922
|
const cardFormBlock = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
857
923
|
backgroundColor: cardBg,
|
|
858
924
|
borderRadius: "8px",
|
|
859
925
|
padding: isButtons ? "0" : "1rem",
|
|
860
|
-
...isButtons
|
|
861
|
-
|
|
862
|
-
} : {}
|
|
926
|
+
...isButtons ? bStyles.cardFormContainer : {},
|
|
927
|
+
...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
|
|
863
928
|
}, children: [
|
|
864
929
|
isButtons && showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
865
930
|
display: "flex",
|
|
@@ -870,7 +935,7 @@ function SplitCardFormInner({
|
|
|
870
935
|
"button",
|
|
871
936
|
{
|
|
872
937
|
type: "button",
|
|
873
|
-
onClick:
|
|
938
|
+
onClick: collapseToButtons,
|
|
874
939
|
style: {
|
|
875
940
|
display: "inline-flex",
|
|
876
941
|
alignItems: "center",
|
|
@@ -879,17 +944,12 @@ function SplitCardFormInner({
|
|
|
879
944
|
border: "none",
|
|
880
945
|
cursor: "pointer",
|
|
881
946
|
color: "#4b5563",
|
|
882
|
-
fontSize: "0.85rem",
|
|
947
|
+
fontSize: bStyles.backButtonFontSize ?? "0.85rem",
|
|
883
948
|
fontWeight: 500,
|
|
884
949
|
padding: 0,
|
|
885
950
|
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";
|
|
951
|
+
flexShrink: 0,
|
|
952
|
+
...bStyles.backButton
|
|
893
953
|
},
|
|
894
954
|
"aria-label": "Back to payment methods",
|
|
895
955
|
children: [
|
|
@@ -901,44 +961,53 @@ function SplitCardFormInner({
|
|
|
901
961
|
height: 28,
|
|
902
962
|
borderRadius: "50%",
|
|
903
963
|
backgroundColor: "#f3f4f6",
|
|
904
|
-
transition: "background-color 0.15s"
|
|
964
|
+
transition: "background-color 0.15s",
|
|
965
|
+
...bStyles.backButtonIcon
|
|
905
966
|
}, 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
967
|
"Go back"
|
|
907
968
|
]
|
|
908
969
|
}
|
|
909
970
|
),
|
|
910
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
972
|
+
flex: 1,
|
|
973
|
+
textAlign: "center",
|
|
974
|
+
fontWeight: 600,
|
|
975
|
+
fontSize: bStyles.titleFontSize ?? "1.05rem",
|
|
976
|
+
color: "#262833",
|
|
977
|
+
paddingRight: 80,
|
|
978
|
+
...bStyles.title
|
|
979
|
+
}, children: "Secure card checkout" })
|
|
911
980
|
] }),
|
|
912
981
|
!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
982
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
914
|
-
backgroundColor:
|
|
915
|
-
border: `1px solid ${
|
|
983
|
+
backgroundColor: cardInputBg,
|
|
984
|
+
border: `1px solid ${resolvedBorder}`,
|
|
916
985
|
borderTopLeftRadius: "8px",
|
|
917
986
|
borderTopRightRadius: "8px",
|
|
918
987
|
padding: "10px"
|
|
919
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true) }) }),
|
|
988
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
|
|
920
989
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex" }, children: [
|
|
921
990
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
922
991
|
flex: 1,
|
|
923
|
-
backgroundColor:
|
|
924
|
-
border: `1px solid ${
|
|
992
|
+
backgroundColor: cardInputBg,
|
|
993
|
+
border: `1px solid ${resolvedBorder}`,
|
|
925
994
|
borderTop: "none",
|
|
926
995
|
borderRight: "none",
|
|
927
996
|
borderBottomLeftRadius: "8px",
|
|
928
997
|
padding: "10px"
|
|
929
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, {}) }),
|
|
998
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
930
999
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
931
1000
|
flex: 1,
|
|
932
|
-
backgroundColor:
|
|
933
|
-
border: `1px solid ${
|
|
1001
|
+
backgroundColor: cardInputBg,
|
|
1002
|
+
border: `1px solid ${resolvedBorder}`,
|
|
934
1003
|
borderTop: "none",
|
|
935
1004
|
borderBottomRightRadius: "8px",
|
|
936
1005
|
padding: "10px"
|
|
937
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, {}) })
|
|
1006
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, { options: stripeElementStyle }) })
|
|
938
1007
|
] }),
|
|
939
1008
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
940
|
-
backgroundColor:
|
|
941
|
-
border: `1px solid ${
|
|
1009
|
+
backgroundColor: cardInputBg,
|
|
1010
|
+
border: `1px solid ${resolvedBorder}`,
|
|
942
1011
|
borderRadius: "8px",
|
|
943
1012
|
marginTop: "0.5rem",
|
|
944
1013
|
padding: "10px"
|
|
@@ -955,9 +1024,11 @@ function SplitCardFormInner({
|
|
|
955
1024
|
width: "100%",
|
|
956
1025
|
border: "none",
|
|
957
1026
|
outline: "none",
|
|
958
|
-
|
|
1027
|
+
background: "transparent",
|
|
1028
|
+
fontSize: bStyles.cardInputFontSize ?? "16px",
|
|
959
1029
|
fontFamily: "Poppins, sans-serif",
|
|
960
|
-
color: "#262833"
|
|
1030
|
+
color: bStyles.cardInputColor ?? "#262833",
|
|
1031
|
+
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
961
1032
|
}
|
|
962
1033
|
}
|
|
963
1034
|
) }),
|
|
@@ -972,7 +1043,8 @@ function SplitCardFormInner({
|
|
|
972
1043
|
fontWeight: 600,
|
|
973
1044
|
display: "flex",
|
|
974
1045
|
alignItems: "center",
|
|
975
|
-
gap: "0.5rem"
|
|
1046
|
+
gap: "0.5rem",
|
|
1047
|
+
...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
976
1048
|
}, children: [
|
|
977
1049
|
/* @__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
1050
|
displayError
|
|
@@ -991,10 +1063,11 @@ function SplitCardFormInner({
|
|
|
991
1063
|
color: "white",
|
|
992
1064
|
border: "none",
|
|
993
1065
|
borderRadius: "8px",
|
|
994
|
-
fontSize: "1rem",
|
|
1066
|
+
fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
|
|
995
1067
|
fontWeight: 600,
|
|
996
1068
|
cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
|
|
997
|
-
opacity: !formReady || isSubmitting ? 0.5 : 1
|
|
1069
|
+
opacity: !formReady || isSubmitting ? 0.5 : 1,
|
|
1070
|
+
...isButtons ? bStyles.submitButton : {}
|
|
998
1071
|
},
|
|
999
1072
|
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
1000
1073
|
}
|
|
@@ -1011,103 +1084,119 @@ function SplitCardFormInner({
|
|
|
1011
1084
|
}, children: "Secure Card Checkout" })
|
|
1012
1085
|
] });
|
|
1013
1086
|
if (layout === "buttons") {
|
|
1087
|
+
const isButtonsView = viewState === "buttons" || viewState === "expanding";
|
|
1088
|
+
const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
|
|
1089
|
+
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;
|
|
1090
|
+
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
1091
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1015
1093
|
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,
|
|
1094
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid" }, children: [
|
|
1095
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
1096
|
+
gridArea: "1 / 1",
|
|
1099
1097
|
display: "flex",
|
|
1100
|
-
|
|
1101
|
-
gap: "0.5rem"
|
|
1098
|
+
flexDirection: "column",
|
|
1099
|
+
gap: "0.5rem",
|
|
1100
|
+
// When card form is showing (and not transitioning), hide but keep mounted
|
|
1101
|
+
...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
|
|
1102
|
+
...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
|
|
1102
1103
|
}, children: [
|
|
1103
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1104
|
+
showPayPal && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1105
|
+
PayPalButtonInner,
|
|
1106
|
+
{
|
|
1107
|
+
sessionId,
|
|
1108
|
+
email,
|
|
1109
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
1110
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
1111
|
+
onErrorChange: updateError,
|
|
1112
|
+
isProcessing: isSubmitting,
|
|
1113
|
+
onButtonClick
|
|
1114
|
+
}
|
|
1115
|
+
) }) : 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,
|
|
1116
|
+
showWallets && stripeInstance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1117
|
+
WalletButtonInner,
|
|
1118
|
+
{
|
|
1119
|
+
sessionId,
|
|
1120
|
+
email,
|
|
1121
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
1122
|
+
showApplePay,
|
|
1123
|
+
showGooglePay,
|
|
1124
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
1125
|
+
onErrorChange: updateError,
|
|
1126
|
+
onButtonClick
|
|
1127
|
+
}
|
|
1128
|
+
) }) : 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,
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1130
|
+
"button",
|
|
1131
|
+
{
|
|
1132
|
+
type: "button",
|
|
1133
|
+
onClick: () => {
|
|
1134
|
+
onButtonClick?.("card");
|
|
1135
|
+
expandToCard();
|
|
1136
|
+
},
|
|
1137
|
+
style: {
|
|
1138
|
+
width: "100%",
|
|
1139
|
+
padding: "0.9rem 1rem",
|
|
1140
|
+
backgroundColor: "white",
|
|
1141
|
+
color: "#262833",
|
|
1142
|
+
border: "1px solid #d1d5db",
|
|
1143
|
+
borderRadius: "8px",
|
|
1144
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
1145
|
+
fontWeight: 600,
|
|
1146
|
+
cursor: "pointer",
|
|
1147
|
+
display: "flex",
|
|
1148
|
+
alignItems: "center",
|
|
1149
|
+
justifyContent: "center",
|
|
1150
|
+
gap: "0.625rem",
|
|
1151
|
+
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
1152
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
1153
|
+
position: "relative",
|
|
1154
|
+
...bStyles.cardButton
|
|
1155
|
+
},
|
|
1156
|
+
onMouseDown: (e) => {
|
|
1157
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
1158
|
+
},
|
|
1159
|
+
onMouseUp: (e) => {
|
|
1160
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
1161
|
+
},
|
|
1162
|
+
children: [
|
|
1163
|
+
/* @__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: [
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
|
|
1166
|
+
] }),
|
|
1167
|
+
"Credit / Debit Card",
|
|
1168
|
+
/* @__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" }) })
|
|
1169
|
+
]
|
|
1170
|
+
}
|
|
1171
|
+
),
|
|
1172
|
+
displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1173
|
+
margin: "0.25rem 0",
|
|
1174
|
+
padding: "0.625rem 0.875rem",
|
|
1175
|
+
background: "#FEF2F2",
|
|
1176
|
+
border: "1px solid #FECACA",
|
|
1177
|
+
borderRadius: "8px",
|
|
1178
|
+
color: "#991B1B",
|
|
1179
|
+
fontSize: "0.85rem",
|
|
1180
|
+
fontWeight: 600,
|
|
1181
|
+
display: "flex",
|
|
1182
|
+
alignItems: "center",
|
|
1183
|
+
gap: "0.5rem",
|
|
1184
|
+
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
1185
|
+
}, children: [
|
|
1186
|
+
/* @__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" }) }),
|
|
1187
|
+
displayError
|
|
1188
|
+
] })
|
|
1189
|
+
] }),
|
|
1190
|
+
isCardView && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1191
|
+
gridArea: "1 / 1",
|
|
1192
|
+
...cardAnim ? { animation: cardAnim } : {},
|
|
1193
|
+
...viewState === "collapsing" ? { pointerEvents: "none" } : {}
|
|
1194
|
+
}, children: cardFormBlock })
|
|
1195
|
+
] })
|
|
1108
1196
|
] });
|
|
1109
1197
|
}
|
|
1110
1198
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1111
1200
|
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
1112
1201
|
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
1202
|
WalletButtonInner,
|
|
@@ -1151,7 +1240,8 @@ function SplitCardFormInner({
|
|
|
1151
1240
|
// src/flopay-checkout.tsx
|
|
1152
1241
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1153
1242
|
function FloPayCheckout({
|
|
1154
|
-
sessionId,
|
|
1243
|
+
sessionId: sessionIdProp,
|
|
1244
|
+
createSession: createSessionParams,
|
|
1155
1245
|
billingApiUrl,
|
|
1156
1246
|
appearance,
|
|
1157
1247
|
locale,
|
|
@@ -1164,6 +1254,9 @@ function FloPayCheckout({
|
|
|
1164
1254
|
showApplePay = true,
|
|
1165
1255
|
showGooglePay = true,
|
|
1166
1256
|
layout,
|
|
1257
|
+
buttonsTheme,
|
|
1258
|
+
buttonsStyles,
|
|
1259
|
+
onButtonClick,
|
|
1167
1260
|
submitLabel,
|
|
1168
1261
|
className,
|
|
1169
1262
|
children,
|
|
@@ -1172,11 +1265,12 @@ function FloPayCheckout({
|
|
|
1172
1265
|
renderConfirmButton,
|
|
1173
1266
|
onSessionCompleted
|
|
1174
1267
|
}) {
|
|
1175
|
-
const resolvedBillingUrl = (0,
|
|
1268
|
+
const resolvedBillingUrl = (0, import_shared5.resolveBillingApiUrl)(billingApiUrl);
|
|
1176
1269
|
const [unified, setUnified] = (0, import_react6.useState)(null);
|
|
1177
1270
|
const [flopay, setFloPay] = (0, import_react6.useState)(null);
|
|
1178
1271
|
const flopayRef = (0, import_react6.useRef)(null);
|
|
1179
1272
|
const [session, setSession] = (0, import_react6.useState)(null);
|
|
1273
|
+
const [resolvedSessionId, setResolvedSessionId] = (0, import_react6.useState)(sessionIdProp ?? "");
|
|
1180
1274
|
const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
|
|
1181
1275
|
const [loadError, setLoadError] = (0, import_react6.useState)(null);
|
|
1182
1276
|
const [currentMode, setCurrentMode] = (0, import_react6.useState)("full");
|
|
@@ -1199,7 +1293,7 @@ function FloPayCheckout({
|
|
|
1199
1293
|
"x-user-id": sess.customer?.id ?? ""
|
|
1200
1294
|
},
|
|
1201
1295
|
body: JSON.stringify({
|
|
1202
|
-
sessionId,
|
|
1296
|
+
sessionId: resolvedSessionId,
|
|
1203
1297
|
tokenizedData: { id: void 0 },
|
|
1204
1298
|
accountData: {
|
|
1205
1299
|
userId: sess.customer?.id ?? "",
|
|
@@ -1228,12 +1322,12 @@ function FloPayCheckout({
|
|
|
1228
1322
|
// No client secret available
|
|
1229
1323
|
};
|
|
1230
1324
|
}
|
|
1231
|
-
throw new
|
|
1325
|
+
throw new import_shared5.FloPayError(
|
|
1232
1326
|
json?.message ?? "Payment failed. Please try again.",
|
|
1233
1327
|
"api_error"
|
|
1234
1328
|
);
|
|
1235
1329
|
},
|
|
1236
|
-
[resolvedBillingUrl,
|
|
1330
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
1237
1331
|
);
|
|
1238
1332
|
const handleRedirectResult = (0, import_react6.useCallback)(
|
|
1239
1333
|
async (redirectResult, sess, options) => {
|
|
@@ -1278,22 +1372,175 @@ function FloPayCheckout({
|
|
|
1278
1372
|
}
|
|
1279
1373
|
return false;
|
|
1280
1374
|
},
|
|
1281
|
-
[resolvedBillingUrl,
|
|
1375
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
1282
1376
|
);
|
|
1377
|
+
const inflightRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
1378
|
+
const initializedHashRef = (0, import_react6.useRef)(null);
|
|
1379
|
+
function hashCreateParams(params) {
|
|
1380
|
+
const key = JSON.stringify({
|
|
1381
|
+
c: params?.clientId,
|
|
1382
|
+
i: params?.items?.map((x) => `${x.providerItemId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
1383
|
+
s: params?.subscriptions?.map((x) => `${x.providerPlanId}:${x.totalAmount}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
1384
|
+
e: params?.account.email,
|
|
1385
|
+
m: params?.checkoutMode ?? "full"
|
|
1386
|
+
});
|
|
1387
|
+
let h = 0;
|
|
1388
|
+
for (let i = 0; i < key.length; i++) {
|
|
1389
|
+
h = (h << 5) - h + key.charCodeAt(i) | 0;
|
|
1390
|
+
}
|
|
1391
|
+
return `flopay_session_${Math.abs(h).toString(36)}`;
|
|
1392
|
+
}
|
|
1393
|
+
const createSessionHash = (0, import_react6.useMemo)(
|
|
1394
|
+
() => createSessionParams ? hashCreateParams(createSessionParams) : "",
|
|
1395
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1396
|
+
[
|
|
1397
|
+
createSessionParams?.clientId,
|
|
1398
|
+
createSessionParams?.account?.email,
|
|
1399
|
+
createSessionParams?.checkoutMode,
|
|
1400
|
+
JSON.stringify(createSessionParams?.items),
|
|
1401
|
+
JSON.stringify(createSessionParams?.subscriptions)
|
|
1402
|
+
]
|
|
1403
|
+
);
|
|
1404
|
+
const createSessionParamsRef = (0, import_react6.useRef)(createSessionParams);
|
|
1405
|
+
createSessionParamsRef.current = createSessionParams;
|
|
1283
1406
|
(0, import_react6.useEffect)(() => {
|
|
1284
1407
|
let cancelled = false;
|
|
1285
|
-
setIsLoading(true);
|
|
1286
1408
|
setLoadError(null);
|
|
1409
|
+
if (createSessionHash) {
|
|
1410
|
+
if (initializedHashRef.current === createSessionHash) return;
|
|
1411
|
+
const params = createSessionParamsRef.current;
|
|
1412
|
+
const totalAmount = [
|
|
1413
|
+
...(params.items ?? []).map((i) => i.overrideAmount ?? i.totalAmount ?? 0),
|
|
1414
|
+
...(params.subscriptions ?? []).map((s) => s.overrideAmount ?? s.totalAmount ?? 0)
|
|
1415
|
+
].reduce((sum, v) => sum + v, 0);
|
|
1416
|
+
const currency = params.items?.[0]?.currency ?? params.subscriptions?.[0]?.currency ?? "USD";
|
|
1417
|
+
const syntheticSession = {
|
|
1418
|
+
id: "",
|
|
1419
|
+
clientSecret: "",
|
|
1420
|
+
mode: "payment",
|
|
1421
|
+
amount: Math.round(totalAmount * 100),
|
|
1422
|
+
currency,
|
|
1423
|
+
status: "open",
|
|
1424
|
+
customer: {
|
|
1425
|
+
id: params.account.userId,
|
|
1426
|
+
email: params.account.email,
|
|
1427
|
+
firstName: params.account.firstName,
|
|
1428
|
+
lastName: params.account.lastName
|
|
1429
|
+
},
|
|
1430
|
+
successUrl: params.successUrl,
|
|
1431
|
+
cancelUrl: params.cancelUrl,
|
|
1432
|
+
checkoutMode: params.checkoutMode ?? "full",
|
|
1433
|
+
items: (params.items ?? []).map((i, idx) => ({
|
|
1434
|
+
uuid: `synthetic-item-${idx}`,
|
|
1435
|
+
checkoutSessionId: "",
|
|
1436
|
+
providerItemId: i.providerItemId,
|
|
1437
|
+
providerItemName: i.providerItemName ?? i.providerItemId,
|
|
1438
|
+
quantity: i.quantity ?? 1,
|
|
1439
|
+
totalAmount: i.totalAmount,
|
|
1440
|
+
overrideAmount: i.overrideAmount ?? null,
|
|
1441
|
+
currency: i.currency ?? currency
|
|
1442
|
+
})),
|
|
1443
|
+
subscriptions: (params.subscriptions ?? []).map((s, idx) => ({
|
|
1444
|
+
uuid: `synthetic-sub-${idx}`,
|
|
1445
|
+
checkoutSessionId: "",
|
|
1446
|
+
providerPlanId: s.providerPlanId,
|
|
1447
|
+
providerPlanName: s.providerPlanName ?? s.providerPlanId,
|
|
1448
|
+
quantity: s.quantity ?? 1,
|
|
1449
|
+
totalAmount: s.totalAmount,
|
|
1450
|
+
overrideAmount: s.overrideAmount ?? null,
|
|
1451
|
+
currency: s.currency ?? currency
|
|
1452
|
+
}))
|
|
1453
|
+
};
|
|
1454
|
+
setSession(syntheticSession);
|
|
1455
|
+
setCurrentMode(params.checkoutMode ?? checkoutModeProp ?? "full");
|
|
1456
|
+
setIsLoading(false);
|
|
1457
|
+
const cacheKey = createSessionHash;
|
|
1458
|
+
async function resolveSession() {
|
|
1459
|
+
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1460
|
+
let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
|
|
1461
|
+
let realResult = null;
|
|
1462
|
+
if (sid) {
|
|
1463
|
+
try {
|
|
1464
|
+
realResult = await api.getUnifiedCheckoutSession(sid);
|
|
1465
|
+
if (realResult.data.session?.status === "complete") {
|
|
1466
|
+
if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
|
|
1467
|
+
sid = null;
|
|
1468
|
+
realResult = null;
|
|
1469
|
+
}
|
|
1470
|
+
} catch {
|
|
1471
|
+
if (typeof window !== "undefined") window.sessionStorage.removeItem(cacheKey);
|
|
1472
|
+
sid = null;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
if (!sid) {
|
|
1476
|
+
realResult = await api.createAndFetchSession(createSessionParamsRef.current);
|
|
1477
|
+
sid = realResult.data.session?.id ?? "";
|
|
1478
|
+
if (sid && typeof window !== "undefined") {
|
|
1479
|
+
window.sessionStorage.setItem(cacheKey, sid);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return { sid: sid ?? "", result: realResult };
|
|
1483
|
+
}
|
|
1484
|
+
(async () => {
|
|
1485
|
+
try {
|
|
1486
|
+
let promise = inflightRef.current.get(cacheKey);
|
|
1487
|
+
if (!promise) {
|
|
1488
|
+
promise = resolveSession();
|
|
1489
|
+
inflightRef.current.set(cacheKey, promise);
|
|
1490
|
+
}
|
|
1491
|
+
let resolved;
|
|
1492
|
+
try {
|
|
1493
|
+
resolved = await promise;
|
|
1494
|
+
} finally {
|
|
1495
|
+
inflightRef.current.delete(cacheKey);
|
|
1496
|
+
}
|
|
1497
|
+
if (cancelled) return;
|
|
1498
|
+
const { sid, result: realResult } = resolved;
|
|
1499
|
+
if (realResult) {
|
|
1500
|
+
setUnified(realResult);
|
|
1501
|
+
if (realResult.data.session) setSession(realResult.data.session);
|
|
1502
|
+
}
|
|
1503
|
+
if (sid) {
|
|
1504
|
+
setResolvedSessionId(sid);
|
|
1505
|
+
}
|
|
1506
|
+
let pk;
|
|
1507
|
+
if (realResult?.provider === "stripe") {
|
|
1508
|
+
pk = realResult.data.stripe?.publishableKey;
|
|
1509
|
+
}
|
|
1510
|
+
if (!pk) pk = fallbackPublishableKey;
|
|
1511
|
+
if (!pk) {
|
|
1512
|
+
throw new import_shared5.FloPayError(
|
|
1513
|
+
"No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
|
|
1514
|
+
"validation_error"
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
const instance = await (0, import_js.loadFloPay)(pk, { billingApiUrl: resolvedBillingUrl, locale });
|
|
1518
|
+
if (!cancelled) {
|
|
1519
|
+
flopayRef.current = instance;
|
|
1520
|
+
setFloPay(instance);
|
|
1521
|
+
initializedHashRef.current = cacheKey;
|
|
1522
|
+
}
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
if (cancelled) return;
|
|
1525
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to create session", "api_error");
|
|
1526
|
+
setLoadError(floPayErr);
|
|
1527
|
+
}
|
|
1528
|
+
})();
|
|
1529
|
+
return () => {
|
|
1530
|
+
cancelled = true;
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
setIsLoading(true);
|
|
1287
1534
|
async function init() {
|
|
1288
1535
|
try {
|
|
1289
1536
|
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1290
|
-
const result = await api.getUnifiedCheckoutSession(
|
|
1537
|
+
const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
|
|
1291
1538
|
if (cancelled) return;
|
|
1292
1539
|
setUnified(result);
|
|
1293
1540
|
const sess = result.data.session ?? null;
|
|
1294
1541
|
setSession(sess);
|
|
1295
1542
|
if (!sess) {
|
|
1296
|
-
throw new
|
|
1543
|
+
throw new import_shared5.FloPayError("No session data returned", "api_error");
|
|
1297
1544
|
}
|
|
1298
1545
|
if (sess.status === "complete") {
|
|
1299
1546
|
setIsLoading(false);
|
|
@@ -1305,7 +1552,7 @@ function FloPayCheckout({
|
|
|
1305
1552
|
const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
|
|
1306
1553
|
if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
|
|
1307
1554
|
autoCheckoutAttempted.current = true;
|
|
1308
|
-
const stripeInitPromise = initStripe(result
|
|
1555
|
+
const stripeInitPromise = initStripe(result);
|
|
1309
1556
|
try {
|
|
1310
1557
|
const redirectResult = await processPaymentForMode(sess);
|
|
1311
1558
|
if (!redirectResult) {
|
|
@@ -1316,9 +1563,7 @@ function FloPayCheckout({
|
|
|
1316
1563
|
await stripeInitPromise;
|
|
1317
1564
|
const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
|
|
1318
1565
|
if (!cancelled) {
|
|
1319
|
-
if (!handled)
|
|
1320
|
-
setCurrentMode("full");
|
|
1321
|
-
}
|
|
1566
|
+
if (!handled) setCurrentMode("full");
|
|
1322
1567
|
setIsLoading(false);
|
|
1323
1568
|
}
|
|
1324
1569
|
return;
|
|
@@ -1330,26 +1575,23 @@ function FloPayCheckout({
|
|
|
1330
1575
|
return;
|
|
1331
1576
|
}
|
|
1332
1577
|
}
|
|
1333
|
-
await initStripe(result
|
|
1578
|
+
await initStripe(result);
|
|
1334
1579
|
if (!cancelled) setIsLoading(false);
|
|
1335
1580
|
} catch (err) {
|
|
1336
1581
|
if (cancelled) return;
|
|
1337
|
-
const floPayErr = err instanceof
|
|
1338
|
-
err instanceof Error ? err.message : "Failed to initialize checkout",
|
|
1339
|
-
"api_error"
|
|
1340
|
-
);
|
|
1582
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
1341
1583
|
setLoadError(floPayErr);
|
|
1342
1584
|
setIsLoading(false);
|
|
1343
1585
|
}
|
|
1344
1586
|
}
|
|
1345
|
-
async function initStripe(result
|
|
1587
|
+
async function initStripe(result) {
|
|
1346
1588
|
let publishableKey;
|
|
1347
1589
|
if (result.provider === "stripe") {
|
|
1348
1590
|
publishableKey = result.data.stripe?.publishableKey;
|
|
1349
1591
|
}
|
|
1350
1592
|
if (!publishableKey) publishableKey = fallbackPublishableKey;
|
|
1351
1593
|
if (!publishableKey) {
|
|
1352
|
-
throw new
|
|
1594
|
+
throw new import_shared5.FloPayError(
|
|
1353
1595
|
"No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
|
|
1354
1596
|
"validation_error"
|
|
1355
1597
|
);
|
|
@@ -1365,7 +1607,7 @@ function FloPayCheckout({
|
|
|
1365
1607
|
return () => {
|
|
1366
1608
|
cancelled = true;
|
|
1367
1609
|
};
|
|
1368
|
-
}, [
|
|
1610
|
+
}, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
|
|
1369
1611
|
const handleConfirmCheckout = (0, import_react6.useCallback)(async () => {
|
|
1370
1612
|
if (confirmProcessing || !session) return;
|
|
1371
1613
|
setConfirmProcessing(true);
|
|
@@ -1380,7 +1622,7 @@ function FloPayCheckout({
|
|
|
1380
1622
|
setCurrentMode("full");
|
|
1381
1623
|
}
|
|
1382
1624
|
} catch (err) {
|
|
1383
|
-
const floPayErr = err instanceof
|
|
1625
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
|
|
1384
1626
|
err instanceof Error ? err.message : "Payment failed",
|
|
1385
1627
|
"api_error"
|
|
1386
1628
|
);
|
|
@@ -1401,7 +1643,7 @@ function FloPayCheckout({
|
|
|
1401
1643
|
if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
|
|
1402
1644
|
opts.clientSecret = unified.data.stripe.clientSecret;
|
|
1403
1645
|
} else {
|
|
1404
|
-
const displayTotal = (0,
|
|
1646
|
+
const displayTotal = (0, import_shared5.buildCheckoutDisplayData)(session).total;
|
|
1405
1647
|
opts.amount = Math.round(displayTotal * 100) || session.amount;
|
|
1406
1648
|
opts.currency = session.currency?.toLowerCase();
|
|
1407
1649
|
}
|
|
@@ -1417,32 +1659,32 @@ function FloPayCheckout({
|
|
|
1417
1659
|
[session, isLoading, loadError, currentMode]
|
|
1418
1660
|
);
|
|
1419
1661
|
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
|
-
|
|
1662
|
+
if (loadingNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode });
|
|
1663
|
+
if (layout === "buttons") {
|
|
1664
|
+
const skeletonBar = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1665
|
+
height: h,
|
|
1666
|
+
borderRadius: 8,
|
|
1667
|
+
background: "#e5e7eb",
|
|
1668
|
+
animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
|
|
1669
|
+
} });
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1671
|
+
showPayPal && skeletonBar(44),
|
|
1672
|
+
(showApplePay || showGooglePay) && skeletonBar(44),
|
|
1673
|
+
skeletonBar(48),
|
|
1674
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
1675
|
+
] });
|
|
1676
|
+
}
|
|
1677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
|
|
1678
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1679
|
+
width: 24,
|
|
1680
|
+
height: 24,
|
|
1681
|
+
border: "2px solid #e5e7eb",
|
|
1682
|
+
borderTopColor: "#6b7280",
|
|
1683
|
+
borderRadius: "50%",
|
|
1684
|
+
animation: "spin 0.6s linear infinite"
|
|
1685
|
+
} }),
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
|
|
1687
|
+
] });
|
|
1446
1688
|
}
|
|
1447
1689
|
if (loadError) {
|
|
1448
1690
|
if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: errorNode(loadError) });
|
|
@@ -1459,6 +1701,19 @@ function FloPayCheckout({
|
|
|
1459
1701
|
}
|
|
1460
1702
|
);
|
|
1461
1703
|
}
|
|
1704
|
+
if ((!flopay || !providerOptions) && createSessionParams && layout === "buttons") {
|
|
1705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1706
|
+
InterimButtonsView,
|
|
1707
|
+
{
|
|
1708
|
+
onButtonClick,
|
|
1709
|
+
showPayPal,
|
|
1710
|
+
showApplePay,
|
|
1711
|
+
showGooglePay,
|
|
1712
|
+
buttonsTheme,
|
|
1713
|
+
buttonsStyles
|
|
1714
|
+
}
|
|
1715
|
+
) });
|
|
1716
|
+
}
|
|
1462
1717
|
if (!flopay || !providerOptions) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
|
|
1463
1718
|
if (currentMode === "confirm") {
|
|
1464
1719
|
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 +1774,7 @@ function FloPayCheckout({
|
|
|
1519
1774
|
children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1520
1775
|
SessionInjector,
|
|
1521
1776
|
{
|
|
1522
|
-
sessionId,
|
|
1777
|
+
sessionId: resolvedSessionId,
|
|
1523
1778
|
billingApiUrl: resolvedBillingUrl,
|
|
1524
1779
|
session,
|
|
1525
1780
|
children
|
|
@@ -1527,12 +1782,12 @@ function FloPayCheckout({
|
|
|
1527
1782
|
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1528
1783
|
SplitCardForm,
|
|
1529
1784
|
{
|
|
1530
|
-
sessionId,
|
|
1785
|
+
sessionId: resolvedSessionId,
|
|
1531
1786
|
email: session?.customer?.email,
|
|
1532
1787
|
userId: session?.customer?.id,
|
|
1533
1788
|
firstName: session?.customer?.firstName,
|
|
1534
1789
|
lastName: session?.customer?.lastName,
|
|
1535
|
-
totalAmount: session ? Math.round((0,
|
|
1790
|
+
totalAmount: session ? Math.round((0, import_shared5.buildCheckoutDisplayData)(session).total * 100) : 0,
|
|
1536
1791
|
currency: session?.currency?.toLowerCase() ?? "usd",
|
|
1537
1792
|
onComplete,
|
|
1538
1793
|
onError,
|
|
@@ -1540,6 +1795,9 @@ function FloPayCheckout({
|
|
|
1540
1795
|
showApplePay,
|
|
1541
1796
|
showGooglePay,
|
|
1542
1797
|
layout,
|
|
1798
|
+
buttonsTheme,
|
|
1799
|
+
buttonsStyles,
|
|
1800
|
+
onButtonClick,
|
|
1543
1801
|
submitLabel,
|
|
1544
1802
|
className
|
|
1545
1803
|
}
|
|
@@ -1570,9 +1828,165 @@ function SessionInjector({
|
|
|
1570
1828
|
return import_react6.default.cloneElement(child, injected);
|
|
1571
1829
|
}) });
|
|
1572
1830
|
}
|
|
1831
|
+
function InterimButtonsView({
|
|
1832
|
+
onButtonClick,
|
|
1833
|
+
showPayPal,
|
|
1834
|
+
showApplePay,
|
|
1835
|
+
showGooglePay,
|
|
1836
|
+
buttonsTheme,
|
|
1837
|
+
buttonsStyles: stylesOverride
|
|
1838
|
+
}) {
|
|
1839
|
+
const [showCardForm, setShowCardForm] = (0, import_react6.useState)(false);
|
|
1840
|
+
const bStyles = (0, import_react6.useMemo)(() => {
|
|
1841
|
+
const base = (0, import_shared5.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
1842
|
+
if (!stylesOverride) return base;
|
|
1843
|
+
return {
|
|
1844
|
+
...base,
|
|
1845
|
+
...stylesOverride,
|
|
1846
|
+
cardButton: { ...base.cardButton, ...stylesOverride.cardButton },
|
|
1847
|
+
cardFormContainer: { ...base.cardFormContainer, ...stylesOverride.cardFormContainer },
|
|
1848
|
+
submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
|
|
1849
|
+
title: { ...base.title, ...stylesOverride.title }
|
|
1850
|
+
};
|
|
1851
|
+
}, [buttonsTheme, stylesOverride]);
|
|
1852
|
+
const skeleton = (h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1853
|
+
height: h,
|
|
1854
|
+
borderRadius: 8,
|
|
1855
|
+
background: "#e5e7eb",
|
|
1856
|
+
animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
|
|
1857
|
+
} });
|
|
1858
|
+
if (showCardForm) {
|
|
1859
|
+
const inputBorder = bStyles.cardInputBorder ?? "#e5e7eb";
|
|
1860
|
+
const inputBg = bStyles.cardInputBackground ?? "white";
|
|
1861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
|
|
1862
|
+
backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
|
|
1863
|
+
borderRadius: "8px",
|
|
1864
|
+
animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
|
|
1865
|
+
overflow: "hidden",
|
|
1866
|
+
...bStyles.cardFormContainer
|
|
1867
|
+
}, children: [
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1870
|
+
"button",
|
|
1871
|
+
{
|
|
1872
|
+
type: "button",
|
|
1873
|
+
onClick: () => setShowCardForm(false),
|
|
1874
|
+
style: {
|
|
1875
|
+
display: "inline-flex",
|
|
1876
|
+
alignItems: "center",
|
|
1877
|
+
gap: "0.5rem",
|
|
1878
|
+
background: "none",
|
|
1879
|
+
border: "none",
|
|
1880
|
+
cursor: "pointer",
|
|
1881
|
+
color: "#4b5563",
|
|
1882
|
+
fontSize: "0.85rem",
|
|
1883
|
+
fontWeight: 500,
|
|
1884
|
+
padding: 0,
|
|
1885
|
+
flexShrink: 0,
|
|
1886
|
+
...bStyles.backButton
|
|
1887
|
+
},
|
|
1888
|
+
children: [
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
|
|
1890
|
+
display: "inline-flex",
|
|
1891
|
+
alignItems: "center",
|
|
1892
|
+
justifyContent: "center",
|
|
1893
|
+
width: 28,
|
|
1894
|
+
height: 28,
|
|
1895
|
+
borderRadius: "50%",
|
|
1896
|
+
backgroundColor: "#f3f4f6",
|
|
1897
|
+
...bStyles.backButtonIcon
|
|
1898
|
+
}, 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" }) }) }),
|
|
1899
|
+
"Go back"
|
|
1900
|
+
]
|
|
1901
|
+
}
|
|
1902
|
+
),
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1904
|
+
flex: 1,
|
|
1905
|
+
textAlign: "center",
|
|
1906
|
+
fontWeight: 600,
|
|
1907
|
+
fontSize: "1.05rem",
|
|
1908
|
+
color: "#262833",
|
|
1909
|
+
paddingRight: 80,
|
|
1910
|
+
...bStyles.title
|
|
1911
|
+
}, children: "Secure card checkout" })
|
|
1912
|
+
] }),
|
|
1913
|
+
/* @__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" } }) }),
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex" }, children: [
|
|
1915
|
+
/* @__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" } }) }),
|
|
1916
|
+
/* @__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" } }) })
|
|
1917
|
+
] }),
|
|
1918
|
+
/* @__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" } }) }),
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
1920
|
+
height: 50,
|
|
1921
|
+
borderRadius: 8,
|
|
1922
|
+
marginTop: 16,
|
|
1923
|
+
background: "#c8c8ff",
|
|
1924
|
+
animation: "flopay-interim-pulse 1.5s ease-in-out infinite",
|
|
1925
|
+
...bStyles.submitButton,
|
|
1926
|
+
opacity: 0.5
|
|
1927
|
+
} }),
|
|
1928
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
|
|
1929
|
+
@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
1930
|
+
@keyframes flopay-interim-expand {
|
|
1931
|
+
0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
|
|
1932
|
+
40% { opacity: 1; }
|
|
1933
|
+
100% { opacity: 1; max-height: 600px; transform: translateY(0); }
|
|
1934
|
+
}
|
|
1935
|
+
` })
|
|
1936
|
+
] });
|
|
1937
|
+
}
|
|
1938
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
1939
|
+
showPayPal && skeleton(44),
|
|
1940
|
+
(showApplePay || showGooglePay) && skeleton(44),
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1942
|
+
"button",
|
|
1943
|
+
{
|
|
1944
|
+
type: "button",
|
|
1945
|
+
onClick: () => {
|
|
1946
|
+
onButtonClick?.("card");
|
|
1947
|
+
setShowCardForm(true);
|
|
1948
|
+
},
|
|
1949
|
+
style: {
|
|
1950
|
+
width: "100%",
|
|
1951
|
+
padding: "0.9rem 1rem",
|
|
1952
|
+
backgroundColor: "white",
|
|
1953
|
+
color: "#262833",
|
|
1954
|
+
border: "1px solid #d1d5db",
|
|
1955
|
+
borderRadius: "8px",
|
|
1956
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
1957
|
+
fontWeight: 600,
|
|
1958
|
+
cursor: "pointer",
|
|
1959
|
+
display: "flex",
|
|
1960
|
+
alignItems: "center",
|
|
1961
|
+
justifyContent: "center",
|
|
1962
|
+
gap: "0.625rem",
|
|
1963
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
1964
|
+
transition: "transform 0.1s",
|
|
1965
|
+
position: "relative",
|
|
1966
|
+
...bStyles.cardButton
|
|
1967
|
+
},
|
|
1968
|
+
onMouseDown: (e) => {
|
|
1969
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
1970
|
+
},
|
|
1971
|
+
onMouseUp: (e) => {
|
|
1972
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
1973
|
+
},
|
|
1974
|
+
children: [
|
|
1975
|
+
/* @__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: [
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }),
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "2", x2: "22", y1: "10", y2: "10" })
|
|
1978
|
+
] }),
|
|
1979
|
+
"Credit / Debit Card",
|
|
1980
|
+
/* @__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" }) })
|
|
1981
|
+
]
|
|
1982
|
+
}
|
|
1983
|
+
),
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
1985
|
+
] });
|
|
1986
|
+
}
|
|
1573
1987
|
|
|
1574
1988
|
// src/checkout-form.tsx
|
|
1575
|
-
var
|
|
1989
|
+
var import_shared6 = require("@flopay/shared");
|
|
1576
1990
|
var import_react7 = require("react");
|
|
1577
1991
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1578
1992
|
var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
|
|
@@ -1780,7 +2194,7 @@ function CheckoutFormInner({
|
|
|
1780
2194
|
return;
|
|
1781
2195
|
}
|
|
1782
2196
|
if (!sessionId || !email) {
|
|
1783
|
-
throw new
|
|
2197
|
+
throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
|
|
1784
2198
|
}
|
|
1785
2199
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1786
2200
|
method: "POST",
|
|
@@ -1792,10 +2206,10 @@ function CheckoutFormInner({
|
|
|
1792
2206
|
isPaypal: false
|
|
1793
2207
|
})
|
|
1794
2208
|
});
|
|
1795
|
-
if (!intentResponse.ok) throw new
|
|
2209
|
+
if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
|
|
1796
2210
|
const intentJson = await intentResponse.json();
|
|
1797
2211
|
const intentClientSecret = intentJson.data?.id;
|
|
1798
|
-
if (!intentClientSecret) throw new
|
|
2212
|
+
if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
|
|
1799
2213
|
const confirmResult = await flopay.confirmCardPayment({
|
|
1800
2214
|
clientSecret: intentClientSecret,
|
|
1801
2215
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -1850,7 +2264,7 @@ function CheckoutFormInner({
|
|
|
1850
2264
|
}
|
|
1851
2265
|
|
|
1852
2266
|
// src/paypal-button.tsx
|
|
1853
|
-
var
|
|
2267
|
+
var import_shared7 = require("@flopay/shared");
|
|
1854
2268
|
var import_react8 = require("react");
|
|
1855
2269
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1856
2270
|
function PayPalButton({
|
|
@@ -1970,7 +2384,7 @@ function PayPalButton({
|
|
|
1970
2384
|
setSubmitting(true);
|
|
1971
2385
|
onErrorChange?.(null);
|
|
1972
2386
|
if (!sessionId || !email) {
|
|
1973
|
-
throw new
|
|
2387
|
+
throw new import_shared7.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
|
|
1974
2388
|
}
|
|
1975
2389
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1976
2390
|
method: "POST",
|
|
@@ -1982,10 +2396,10 @@ function PayPalButton({
|
|
|
1982
2396
|
isPaypal: "true"
|
|
1983
2397
|
})
|
|
1984
2398
|
});
|
|
1985
|
-
if (!intentResponse.ok) throw new
|
|
2399
|
+
if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
|
|
1986
2400
|
const intentJson = await intentResponse.json();
|
|
1987
2401
|
const intentClientSecret = intentJson.data?.id;
|
|
1988
|
-
if (!intentClientSecret) throw new
|
|
2402
|
+
if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in response", "api_error");
|
|
1989
2403
|
const result = await flopay.confirmPayment({
|
|
1990
2404
|
clientSecret: intentClientSecret,
|
|
1991
2405
|
returnUrl: window.location.href
|