@flopay/react 0.1.5 → 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 +762 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -4
- package/dist/index.d.ts +48 -4
- package/dist/index.mjs +743 -142
- 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,10 +205,33 @@ 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";
|
|
210
|
-
|
|
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
|
+
}
|
|
234
|
+
function ProcessingOverlay({ status, errorMessage }) {
|
|
211
235
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
212
236
|
position: "fixed",
|
|
213
237
|
inset: 0,
|
|
@@ -283,6 +307,14 @@ function ProcessingOverlay({ status }) {
|
|
|
283
307
|
status === "success" && "PAYMENT SUCCESSFUL",
|
|
284
308
|
status === "error" && "PAYMENT FAILED"
|
|
285
309
|
] }),
|
|
310
|
+
status === "error" && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: {
|
|
311
|
+
fontSize: 13,
|
|
312
|
+
color: "#6b7280",
|
|
313
|
+
fontWeight: 400,
|
|
314
|
+
maxWidth: 260,
|
|
315
|
+
lineHeight: 1.4,
|
|
316
|
+
margin: 0
|
|
317
|
+
}, children: errorMessage }),
|
|
286
318
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
|
|
287
319
|
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
288
320
|
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
@@ -302,7 +334,8 @@ function PayPalButtonInner({
|
|
|
302
334
|
billingApiUrl,
|
|
303
335
|
onTokenizedBody,
|
|
304
336
|
onErrorChange,
|
|
305
|
-
isProcessing = false
|
|
337
|
+
isProcessing = false,
|
|
338
|
+
onButtonClick
|
|
306
339
|
}) {
|
|
307
340
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
308
341
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -355,6 +388,7 @@ function PayPalButtonInner({
|
|
|
355
388
|
}, [stripe, onTokenizedBody, onErrorChange]);
|
|
356
389
|
const handlePayPalConfirm = (0, import_react5.useCallback)(async (_event) => {
|
|
357
390
|
if (!stripe || !elements) return;
|
|
391
|
+
onButtonClick?.("paypal");
|
|
358
392
|
try {
|
|
359
393
|
setSubmitting(true);
|
|
360
394
|
onErrorChange?.(null);
|
|
@@ -409,7 +443,7 @@ function PayPalButtonInner({
|
|
|
409
443
|
}
|
|
410
444
|
}, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]);
|
|
411
445
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
412
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
413
447
|
import_react_stripe_js.ExpressCheckoutElement,
|
|
414
448
|
{
|
|
415
449
|
onReady: () => setReady(true),
|
|
@@ -437,7 +471,8 @@ function WalletButtonInner({
|
|
|
437
471
|
showApplePay = true,
|
|
438
472
|
showGooglePay = true,
|
|
439
473
|
onTokenizedBody,
|
|
440
|
-
onErrorChange
|
|
474
|
+
onErrorChange,
|
|
475
|
+
onButtonClick
|
|
441
476
|
}) {
|
|
442
477
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
443
478
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -447,6 +482,8 @@ function WalletButtonInner({
|
|
|
447
482
|
const handleWalletConfirm = (0, import_react5.useCallback)(
|
|
448
483
|
async (_event) => {
|
|
449
484
|
if (!stripe || !elements) return;
|
|
485
|
+
const walletType = _event.expressPaymentType;
|
|
486
|
+
onButtonClick?.(walletType === "apple_pay" ? "apple_pay" : "google_pay");
|
|
450
487
|
try {
|
|
451
488
|
setSubmitting(true);
|
|
452
489
|
onErrorChange?.(null);
|
|
@@ -499,7 +536,7 @@ function WalletButtonInner({
|
|
|
499
536
|
[stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange]
|
|
500
537
|
);
|
|
501
538
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
502
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
503
540
|
import_react_stripe_js.ExpressCheckoutElement,
|
|
504
541
|
{
|
|
505
542
|
onReady: () => setReady(true),
|
|
@@ -509,10 +546,12 @@ function WalletButtonInner({
|
|
|
509
546
|
options: {
|
|
510
547
|
buttonType: { applePay: "plain", googlePay: "plain" },
|
|
511
548
|
paymentMethods: {
|
|
512
|
-
applePay: showApplePay ? "
|
|
513
|
-
googlePay: showGooglePay ? "
|
|
549
|
+
applePay: showApplePay ? "always" : "never",
|
|
550
|
+
googlePay: showGooglePay ? "always" : "never",
|
|
514
551
|
paypal: "never",
|
|
515
|
-
link: "never"
|
|
552
|
+
link: "never",
|
|
553
|
+
amazonPay: "never",
|
|
554
|
+
klarna: "never"
|
|
516
555
|
},
|
|
517
556
|
layout: { overflow: "never" }
|
|
518
557
|
}
|
|
@@ -543,6 +582,10 @@ function SplitCardFormInner({
|
|
|
543
582
|
showPayPal = true,
|
|
544
583
|
showApplePay = true,
|
|
545
584
|
showGooglePay = true,
|
|
585
|
+
layout = "default",
|
|
586
|
+
buttonsTheme,
|
|
587
|
+
buttonsStyles: buttonsStylesOverride,
|
|
588
|
+
onButtonClick,
|
|
546
589
|
totalAmount = 0,
|
|
547
590
|
currency = "usd",
|
|
548
591
|
innerRef
|
|
@@ -553,12 +596,37 @@ function SplitCardFormInner({
|
|
|
553
596
|
const [processing, setProcessing] = (0, import_react5.useState)(false);
|
|
554
597
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
555
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
|
+
}, []);
|
|
556
610
|
const [fullName, setFullName] = (0, import_react5.useState)("");
|
|
557
611
|
const [formReady, setFormReady] = (0, import_react5.useState)(false);
|
|
558
612
|
const [overlayStatus, setOverlayStatus] = (0, import_react5.useState)(null);
|
|
559
613
|
const processingRef = (0, import_react5.useRef)(false);
|
|
560
614
|
const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
|
|
561
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]);
|
|
562
630
|
const isSubmitting = externalProcessing ?? processing;
|
|
563
631
|
const isSelfContained = !onTokenizedBody;
|
|
564
632
|
const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
|
|
@@ -770,6 +838,7 @@ function SplitCardFormInner({
|
|
|
770
838
|
async (e) => {
|
|
771
839
|
e.preventDefault();
|
|
772
840
|
if (!flopay || !elements || isSubmitting || processingRef.current) return;
|
|
841
|
+
onButtonClick?.("card");
|
|
773
842
|
setProcessing(true);
|
|
774
843
|
setOverlayStatus("processing");
|
|
775
844
|
updateError(null);
|
|
@@ -787,7 +856,7 @@ function SplitCardFormInner({
|
|
|
787
856
|
return;
|
|
788
857
|
}
|
|
789
858
|
if (!sessionId || !email) {
|
|
790
|
-
throw new
|
|
859
|
+
throw new import_shared4.FloPayError("Missing sessionId or email", "validation_error");
|
|
791
860
|
}
|
|
792
861
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
793
862
|
method: "POST",
|
|
@@ -799,10 +868,10 @@ function SplitCardFormInner({
|
|
|
799
868
|
isPaypal: false
|
|
800
869
|
})
|
|
801
870
|
});
|
|
802
|
-
if (!intentResponse.ok) throw new
|
|
871
|
+
if (!intentResponse.ok) throw new import_shared4.FloPayError("Failed to create payment intent", "api_error");
|
|
803
872
|
const intentJson = await intentResponse.json();
|
|
804
873
|
const intentClientSecret = intentJson.data?.id;
|
|
805
|
-
if (!intentClientSecret) throw new
|
|
874
|
+
if (!intentClientSecret) throw new import_shared4.FloPayError("No client_secret in payment intent response", "api_error");
|
|
806
875
|
const confirmResult = await flopay.confirmCardPayment({
|
|
807
876
|
clientSecret: intentClientSecret,
|
|
808
877
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -836,8 +905,299 @@ function SplitCardFormInner({
|
|
|
836
905
|
if (!isReady) {
|
|
837
906
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
|
|
838
907
|
}
|
|
908
|
+
const isButtons = layout === "buttons";
|
|
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
|
+
} : {};
|
|
922
|
+
const cardFormBlock = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
923
|
+
backgroundColor: cardBg,
|
|
924
|
+
borderRadius: "8px",
|
|
925
|
+
padding: isButtons ? "0" : "1rem",
|
|
926
|
+
...isButtons ? bStyles.cardFormContainer : {},
|
|
927
|
+
...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {}
|
|
928
|
+
}, children: [
|
|
929
|
+
isButtons && showCardForm && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
930
|
+
display: "flex",
|
|
931
|
+
alignItems: "center",
|
|
932
|
+
padding: "0.75rem 0 0.625rem"
|
|
933
|
+
}, children: [
|
|
934
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
935
|
+
"button",
|
|
936
|
+
{
|
|
937
|
+
type: "button",
|
|
938
|
+
onClick: collapseToButtons,
|
|
939
|
+
style: {
|
|
940
|
+
display: "inline-flex",
|
|
941
|
+
alignItems: "center",
|
|
942
|
+
gap: "0.5rem",
|
|
943
|
+
background: "none",
|
|
944
|
+
border: "none",
|
|
945
|
+
cursor: "pointer",
|
|
946
|
+
color: "#4b5563",
|
|
947
|
+
fontSize: bStyles.backButtonFontSize ?? "0.85rem",
|
|
948
|
+
fontWeight: 500,
|
|
949
|
+
padding: 0,
|
|
950
|
+
transition: "color 0.15s",
|
|
951
|
+
flexShrink: 0,
|
|
952
|
+
...bStyles.backButton
|
|
953
|
+
},
|
|
954
|
+
"aria-label": "Back to payment methods",
|
|
955
|
+
children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: {
|
|
957
|
+
display: "inline-flex",
|
|
958
|
+
alignItems: "center",
|
|
959
|
+
justifyContent: "center",
|
|
960
|
+
width: 28,
|
|
961
|
+
height: 28,
|
|
962
|
+
borderRadius: "50%",
|
|
963
|
+
backgroundColor: "#f3f4f6",
|
|
964
|
+
transition: "background-color 0.15s",
|
|
965
|
+
...bStyles.backButtonIcon
|
|
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" }) }) }),
|
|
967
|
+
"Go back"
|
|
968
|
+
]
|
|
969
|
+
}
|
|
970
|
+
),
|
|
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" })
|
|
980
|
+
] }),
|
|
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" }),
|
|
982
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
983
|
+
backgroundColor: cardInputBg,
|
|
984
|
+
border: `1px solid ${resolvedBorder}`,
|
|
985
|
+
borderTopLeftRadius: "8px",
|
|
986
|
+
borderTopRightRadius: "8px",
|
|
987
|
+
padding: "10px"
|
|
988
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
|
|
989
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex" }, children: [
|
|
990
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
991
|
+
flex: 1,
|
|
992
|
+
backgroundColor: cardInputBg,
|
|
993
|
+
border: `1px solid ${resolvedBorder}`,
|
|
994
|
+
borderTop: "none",
|
|
995
|
+
borderRight: "none",
|
|
996
|
+
borderBottomLeftRadius: "8px",
|
|
997
|
+
padding: "10px"
|
|
998
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
999
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1000
|
+
flex: 1,
|
|
1001
|
+
backgroundColor: cardInputBg,
|
|
1002
|
+
border: `1px solid ${resolvedBorder}`,
|
|
1003
|
+
borderTop: "none",
|
|
1004
|
+
borderBottomRightRadius: "8px",
|
|
1005
|
+
padding: "10px"
|
|
1006
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, { options: stripeElementStyle }) })
|
|
1007
|
+
] }),
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1009
|
+
backgroundColor: cardInputBg,
|
|
1010
|
+
border: `1px solid ${resolvedBorder}`,
|
|
1011
|
+
borderRadius: "8px",
|
|
1012
|
+
marginTop: "0.5rem",
|
|
1013
|
+
padding: "10px"
|
|
1014
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1015
|
+
"input",
|
|
1016
|
+
{
|
|
1017
|
+
placeholder: "Full Name on Card",
|
|
1018
|
+
autoComplete: "cc-name",
|
|
1019
|
+
value: fullName,
|
|
1020
|
+
onChange: (e) => handleNameChange(e.target.value),
|
|
1021
|
+
disabled: isSubmitting,
|
|
1022
|
+
required: true,
|
|
1023
|
+
style: {
|
|
1024
|
+
width: "100%",
|
|
1025
|
+
border: "none",
|
|
1026
|
+
outline: "none",
|
|
1027
|
+
background: "transparent",
|
|
1028
|
+
fontSize: bStyles.cardInputFontSize ?? "16px",
|
|
1029
|
+
fontFamily: "Poppins, sans-serif",
|
|
1030
|
+
color: bStyles.cardInputColor ?? "#262833",
|
|
1031
|
+
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
) }),
|
|
1035
|
+
displayError && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1036
|
+
margin: "0.75rem 0",
|
|
1037
|
+
padding: "0.625rem 0.875rem",
|
|
1038
|
+
background: "#FEF2F2",
|
|
1039
|
+
border: "1px solid #FECACA",
|
|
1040
|
+
borderRadius: "8px",
|
|
1041
|
+
color: "#991B1B",
|
|
1042
|
+
fontSize: "0.85rem",
|
|
1043
|
+
fontWeight: 600,
|
|
1044
|
+
display: "flex",
|
|
1045
|
+
alignItems: "center",
|
|
1046
|
+
gap: "0.5rem",
|
|
1047
|
+
...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
1048
|
+
}, children: [
|
|
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" }) }),
|
|
1050
|
+
displayError
|
|
1051
|
+
] }),
|
|
1052
|
+
children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1053
|
+
"button",
|
|
1054
|
+
{
|
|
1055
|
+
type: "submit",
|
|
1056
|
+
disabled: !formReady || isSubmitting,
|
|
1057
|
+
"data-testid": "flopay-submit",
|
|
1058
|
+
style: {
|
|
1059
|
+
width: "100%",
|
|
1060
|
+
padding: "0.875rem",
|
|
1061
|
+
marginTop: "1rem",
|
|
1062
|
+
backgroundColor: "#4A49FF",
|
|
1063
|
+
color: "white",
|
|
1064
|
+
border: "none",
|
|
1065
|
+
borderRadius: "8px",
|
|
1066
|
+
fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
|
|
1067
|
+
fontWeight: 600,
|
|
1068
|
+
cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
|
|
1069
|
+
opacity: !formReady || isSubmitting ? 0.5 : 1,
|
|
1070
|
+
...isButtons ? bStyles.submitButton : {}
|
|
1071
|
+
},
|
|
1072
|
+
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
1073
|
+
}
|
|
1074
|
+
),
|
|
1075
|
+
!isButtons && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
1076
|
+
backgroundColor: "#EFF9F0",
|
|
1077
|
+
borderRadius: "8px",
|
|
1078
|
+
padding: "0.75rem",
|
|
1079
|
+
marginTop: "0.75rem",
|
|
1080
|
+
textAlign: "center",
|
|
1081
|
+
fontSize: "0.85rem",
|
|
1082
|
+
fontWeight: 600,
|
|
1083
|
+
color: "#7DAD3A"
|
|
1084
|
+
}, children: "Secure Card Checkout" })
|
|
1085
|
+
] });
|
|
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;
|
|
1091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1093
|
+
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
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",
|
|
1097
|
+
display: "flex",
|
|
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" } : {}
|
|
1103
|
+
}, children: [
|
|
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
|
+
] })
|
|
1196
|
+
] });
|
|
1197
|
+
}
|
|
839
1198
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
840
|
-
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FloPayKeyframes, {}),
|
|
1200
|
+
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
841
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)(
|
|
842
1202
|
WalletButtonInner,
|
|
843
1203
|
{
|
|
@@ -873,100 +1233,15 @@ function SplitCardFormInner({
|
|
|
873
1233
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "or pay with card" }),
|
|
874
1234
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
|
|
875
1235
|
] }),
|
|
876
|
-
|
|
877
|
-
/* @__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" }),
|
|
878
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
879
|
-
backgroundColor: "white",
|
|
880
|
-
border: "1px solid #A4A4FF",
|
|
881
|
-
borderTopLeftRadius: "8px",
|
|
882
|
-
borderTopRightRadius: "8px",
|
|
883
|
-
padding: "10px"
|
|
884
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardNumberElement, { onReady: () => setFormReady(true) }) }),
|
|
885
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex" }, children: [
|
|
886
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
887
|
-
flex: 1,
|
|
888
|
-
backgroundColor: "white",
|
|
889
|
-
border: "1px solid #A4A4FF",
|
|
890
|
-
borderTop: "none",
|
|
891
|
-
borderRight: "none",
|
|
892
|
-
borderBottomLeftRadius: "8px",
|
|
893
|
-
padding: "10px"
|
|
894
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardExpiryElement, {}) }),
|
|
895
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
896
|
-
flex: 1,
|
|
897
|
-
backgroundColor: "white",
|
|
898
|
-
border: "1px solid #A4A4FF",
|
|
899
|
-
borderTop: "none",
|
|
900
|
-
borderBottomRightRadius: "8px",
|
|
901
|
-
padding: "10px"
|
|
902
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CardCvcElement, {}) })
|
|
903
|
-
] }),
|
|
904
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
905
|
-
backgroundColor: "white",
|
|
906
|
-
border: "1px solid #A4A4FF",
|
|
907
|
-
borderRadius: "8px",
|
|
908
|
-
marginTop: "0.5rem",
|
|
909
|
-
padding: "10px"
|
|
910
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
911
|
-
"input",
|
|
912
|
-
{
|
|
913
|
-
placeholder: "Full Name on Card",
|
|
914
|
-
autoComplete: "cc-name",
|
|
915
|
-
value: fullName,
|
|
916
|
-
onChange: (e) => handleNameChange(e.target.value),
|
|
917
|
-
disabled: isSubmitting,
|
|
918
|
-
required: true,
|
|
919
|
-
style: {
|
|
920
|
-
width: "100%",
|
|
921
|
-
border: "none",
|
|
922
|
-
outline: "none",
|
|
923
|
-
fontSize: "16px",
|
|
924
|
-
fontFamily: "Poppins, sans-serif",
|
|
925
|
-
color: "#262833"
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
) }),
|
|
929
|
-
displayError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0", fontSize: "0.9rem" }, children: displayError }),
|
|
930
|
-
children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
931
|
-
"button",
|
|
932
|
-
{
|
|
933
|
-
type: "submit",
|
|
934
|
-
disabled: !formReady || isSubmitting,
|
|
935
|
-
"data-testid": "flopay-submit",
|
|
936
|
-
style: {
|
|
937
|
-
width: "100%",
|
|
938
|
-
padding: "0.875rem",
|
|
939
|
-
marginTop: "1rem",
|
|
940
|
-
backgroundColor: "#4A49FF",
|
|
941
|
-
color: "white",
|
|
942
|
-
border: "none",
|
|
943
|
-
borderRadius: "8px",
|
|
944
|
-
fontSize: "1rem",
|
|
945
|
-
fontWeight: 600,
|
|
946
|
-
cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
|
|
947
|
-
opacity: !formReady || isSubmitting ? 0.5 : 1
|
|
948
|
-
},
|
|
949
|
-
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
950
|
-
}
|
|
951
|
-
),
|
|
952
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
953
|
-
backgroundColor: "#EFF9F0",
|
|
954
|
-
borderRadius: "8px",
|
|
955
|
-
padding: "0.75rem",
|
|
956
|
-
marginTop: "0.75rem",
|
|
957
|
-
textAlign: "center",
|
|
958
|
-
fontSize: "0.85rem",
|
|
959
|
-
fontWeight: 600,
|
|
960
|
-
color: "#7DAD3A"
|
|
961
|
-
}, children: "Secure Card Checkout" })
|
|
962
|
-
] })
|
|
1236
|
+
cardFormBlock
|
|
963
1237
|
] });
|
|
964
1238
|
}
|
|
965
1239
|
|
|
966
1240
|
// src/flopay-checkout.tsx
|
|
967
1241
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
968
1242
|
function FloPayCheckout({
|
|
969
|
-
sessionId,
|
|
1243
|
+
sessionId: sessionIdProp,
|
|
1244
|
+
createSession: createSessionParams,
|
|
970
1245
|
billingApiUrl,
|
|
971
1246
|
appearance,
|
|
972
1247
|
locale,
|
|
@@ -978,6 +1253,10 @@ function FloPayCheckout({
|
|
|
978
1253
|
showPayPal = true,
|
|
979
1254
|
showApplePay = true,
|
|
980
1255
|
showGooglePay = true,
|
|
1256
|
+
layout,
|
|
1257
|
+
buttonsTheme,
|
|
1258
|
+
buttonsStyles,
|
|
1259
|
+
onButtonClick,
|
|
981
1260
|
submitLabel,
|
|
982
1261
|
className,
|
|
983
1262
|
children,
|
|
@@ -986,11 +1265,12 @@ function FloPayCheckout({
|
|
|
986
1265
|
renderConfirmButton,
|
|
987
1266
|
onSessionCompleted
|
|
988
1267
|
}) {
|
|
989
|
-
const resolvedBillingUrl = (0,
|
|
1268
|
+
const resolvedBillingUrl = (0, import_shared5.resolveBillingApiUrl)(billingApiUrl);
|
|
990
1269
|
const [unified, setUnified] = (0, import_react6.useState)(null);
|
|
991
1270
|
const [flopay, setFloPay] = (0, import_react6.useState)(null);
|
|
992
1271
|
const flopayRef = (0, import_react6.useRef)(null);
|
|
993
1272
|
const [session, setSession] = (0, import_react6.useState)(null);
|
|
1273
|
+
const [resolvedSessionId, setResolvedSessionId] = (0, import_react6.useState)(sessionIdProp ?? "");
|
|
994
1274
|
const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
|
|
995
1275
|
const [loadError, setLoadError] = (0, import_react6.useState)(null);
|
|
996
1276
|
const [currentMode, setCurrentMode] = (0, import_react6.useState)("full");
|
|
@@ -1013,7 +1293,7 @@ function FloPayCheckout({
|
|
|
1013
1293
|
"x-user-id": sess.customer?.id ?? ""
|
|
1014
1294
|
},
|
|
1015
1295
|
body: JSON.stringify({
|
|
1016
|
-
sessionId,
|
|
1296
|
+
sessionId: resolvedSessionId,
|
|
1017
1297
|
tokenizedData: { id: void 0 },
|
|
1018
1298
|
accountData: {
|
|
1019
1299
|
userId: sess.customer?.id ?? "",
|
|
@@ -1042,12 +1322,12 @@ function FloPayCheckout({
|
|
|
1042
1322
|
// No client secret available
|
|
1043
1323
|
};
|
|
1044
1324
|
}
|
|
1045
|
-
throw new
|
|
1325
|
+
throw new import_shared5.FloPayError(
|
|
1046
1326
|
json?.message ?? "Payment failed. Please try again.",
|
|
1047
1327
|
"api_error"
|
|
1048
1328
|
);
|
|
1049
1329
|
},
|
|
1050
|
-
[resolvedBillingUrl,
|
|
1330
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
1051
1331
|
);
|
|
1052
1332
|
const handleRedirectResult = (0, import_react6.useCallback)(
|
|
1053
1333
|
async (redirectResult, sess, options) => {
|
|
@@ -1092,22 +1372,175 @@ function FloPayCheckout({
|
|
|
1092
1372
|
}
|
|
1093
1373
|
return false;
|
|
1094
1374
|
},
|
|
1095
|
-
[resolvedBillingUrl,
|
|
1375
|
+
[resolvedBillingUrl, resolvedSessionId]
|
|
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
|
+
]
|
|
1096
1403
|
);
|
|
1404
|
+
const createSessionParamsRef = (0, import_react6.useRef)(createSessionParams);
|
|
1405
|
+
createSessionParamsRef.current = createSessionParams;
|
|
1097
1406
|
(0, import_react6.useEffect)(() => {
|
|
1098
1407
|
let cancelled = false;
|
|
1099
|
-
setIsLoading(true);
|
|
1100
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);
|
|
1101
1534
|
async function init() {
|
|
1102
1535
|
try {
|
|
1103
1536
|
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1104
|
-
const result = await api.getUnifiedCheckoutSession(
|
|
1537
|
+
const result = await api.getUnifiedCheckoutSession(resolvedSessionId);
|
|
1105
1538
|
if (cancelled) return;
|
|
1106
1539
|
setUnified(result);
|
|
1107
1540
|
const sess = result.data.session ?? null;
|
|
1108
1541
|
setSession(sess);
|
|
1109
1542
|
if (!sess) {
|
|
1110
|
-
throw new
|
|
1543
|
+
throw new import_shared5.FloPayError("No session data returned", "api_error");
|
|
1111
1544
|
}
|
|
1112
1545
|
if (sess.status === "complete") {
|
|
1113
1546
|
setIsLoading(false);
|
|
@@ -1119,7 +1552,7 @@ function FloPayCheckout({
|
|
|
1119
1552
|
const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
|
|
1120
1553
|
if (effectiveMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams) {
|
|
1121
1554
|
autoCheckoutAttempted.current = true;
|
|
1122
|
-
const stripeInitPromise = initStripe(result
|
|
1555
|
+
const stripeInitPromise = initStripe(result);
|
|
1123
1556
|
try {
|
|
1124
1557
|
const redirectResult = await processPaymentForMode(sess);
|
|
1125
1558
|
if (!redirectResult) {
|
|
@@ -1130,9 +1563,7 @@ function FloPayCheckout({
|
|
|
1130
1563
|
await stripeInitPromise;
|
|
1131
1564
|
const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
|
|
1132
1565
|
if (!cancelled) {
|
|
1133
|
-
if (!handled)
|
|
1134
|
-
setCurrentMode("full");
|
|
1135
|
-
}
|
|
1566
|
+
if (!handled) setCurrentMode("full");
|
|
1136
1567
|
setIsLoading(false);
|
|
1137
1568
|
}
|
|
1138
1569
|
return;
|
|
@@ -1144,26 +1575,23 @@ function FloPayCheckout({
|
|
|
1144
1575
|
return;
|
|
1145
1576
|
}
|
|
1146
1577
|
}
|
|
1147
|
-
await initStripe(result
|
|
1578
|
+
await initStripe(result);
|
|
1148
1579
|
if (!cancelled) setIsLoading(false);
|
|
1149
1580
|
} catch (err) {
|
|
1150
1581
|
if (cancelled) return;
|
|
1151
|
-
const floPayErr = err instanceof
|
|
1152
|
-
err instanceof Error ? err.message : "Failed to initialize checkout",
|
|
1153
|
-
"api_error"
|
|
1154
|
-
);
|
|
1582
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
1155
1583
|
setLoadError(floPayErr);
|
|
1156
1584
|
setIsLoading(false);
|
|
1157
1585
|
}
|
|
1158
1586
|
}
|
|
1159
|
-
async function initStripe(result
|
|
1587
|
+
async function initStripe(result) {
|
|
1160
1588
|
let publishableKey;
|
|
1161
1589
|
if (result.provider === "stripe") {
|
|
1162
1590
|
publishableKey = result.data.stripe?.publishableKey;
|
|
1163
1591
|
}
|
|
1164
1592
|
if (!publishableKey) publishableKey = fallbackPublishableKey;
|
|
1165
1593
|
if (!publishableKey) {
|
|
1166
|
-
throw new
|
|
1594
|
+
throw new import_shared5.FloPayError(
|
|
1167
1595
|
"No publishable key found in session response. Provide a fallbackPublishableKey prop or ensure the session includes gatewayData.publishableKey.",
|
|
1168
1596
|
"validation_error"
|
|
1169
1597
|
);
|
|
@@ -1179,7 +1607,7 @@ function FloPayCheckout({
|
|
|
1179
1607
|
return () => {
|
|
1180
1608
|
cancelled = true;
|
|
1181
1609
|
};
|
|
1182
|
-
}, [
|
|
1610
|
+
}, [resolvedSessionId, createSessionHash, resolvedBillingUrl, fallbackPublishableKey, locale, checkoutModeProp]);
|
|
1183
1611
|
const handleConfirmCheckout = (0, import_react6.useCallback)(async () => {
|
|
1184
1612
|
if (confirmProcessing || !session) return;
|
|
1185
1613
|
setConfirmProcessing(true);
|
|
@@ -1194,7 +1622,7 @@ function FloPayCheckout({
|
|
|
1194
1622
|
setCurrentMode("full");
|
|
1195
1623
|
}
|
|
1196
1624
|
} catch (err) {
|
|
1197
|
-
const floPayErr = err instanceof
|
|
1625
|
+
const floPayErr = err instanceof import_shared5.FloPayError ? err : new import_shared5.FloPayError(
|
|
1198
1626
|
err instanceof Error ? err.message : "Payment failed",
|
|
1199
1627
|
"api_error"
|
|
1200
1628
|
);
|
|
@@ -1215,7 +1643,7 @@ function FloPayCheckout({
|
|
|
1215
1643
|
if (unified.provider === "stripe" && unified.data.stripe?.clientSecret) {
|
|
1216
1644
|
opts.clientSecret = unified.data.stripe.clientSecret;
|
|
1217
1645
|
} else {
|
|
1218
|
-
const displayTotal = (0,
|
|
1646
|
+
const displayTotal = (0, import_shared5.buildCheckoutDisplayData)(session).total;
|
|
1219
1647
|
opts.amount = Math.round(displayTotal * 100) || session.amount;
|
|
1220
1648
|
opts.currency = session.currency?.toLowerCase();
|
|
1221
1649
|
}
|
|
@@ -1231,32 +1659,32 @@ function FloPayCheckout({
|
|
|
1231
1659
|
[session, isLoading, loadError, currentMode]
|
|
1232
1660
|
);
|
|
1233
1661
|
if (isLoading) {
|
|
1234
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: loadingNode
|
|
1235
|
-
|
|
1236
|
-
{
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
}
|
|
1259
|
-
|
|
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
|
+
] });
|
|
1260
1688
|
}
|
|
1261
1689
|
if (loadError) {
|
|
1262
1690
|
if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: errorNode(loadError) });
|
|
@@ -1273,6 +1701,19 @@ function FloPayCheckout({
|
|
|
1273
1701
|
}
|
|
1274
1702
|
);
|
|
1275
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
|
+
}
|
|
1276
1717
|
if (!flopay || !providerOptions) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
|
|
1277
1718
|
if (currentMode === "confirm") {
|
|
1278
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: [
|
|
@@ -1333,7 +1774,7 @@ function FloPayCheckout({
|
|
|
1333
1774
|
children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1334
1775
|
SessionInjector,
|
|
1335
1776
|
{
|
|
1336
|
-
sessionId,
|
|
1777
|
+
sessionId: resolvedSessionId,
|
|
1337
1778
|
billingApiUrl: resolvedBillingUrl,
|
|
1338
1779
|
session,
|
|
1339
1780
|
children
|
|
@@ -1341,18 +1782,22 @@ function FloPayCheckout({
|
|
|
1341
1782
|
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1342
1783
|
SplitCardForm,
|
|
1343
1784
|
{
|
|
1344
|
-
sessionId,
|
|
1785
|
+
sessionId: resolvedSessionId,
|
|
1345
1786
|
email: session?.customer?.email,
|
|
1346
1787
|
userId: session?.customer?.id,
|
|
1347
1788
|
firstName: session?.customer?.firstName,
|
|
1348
1789
|
lastName: session?.customer?.lastName,
|
|
1349
|
-
totalAmount: session ? Math.round((0,
|
|
1790
|
+
totalAmount: session ? Math.round((0, import_shared5.buildCheckoutDisplayData)(session).total * 100) : 0,
|
|
1350
1791
|
currency: session?.currency?.toLowerCase() ?? "usd",
|
|
1351
1792
|
onComplete,
|
|
1352
1793
|
onError,
|
|
1353
1794
|
showPayPal,
|
|
1354
1795
|
showApplePay,
|
|
1355
1796
|
showGooglePay,
|
|
1797
|
+
layout,
|
|
1798
|
+
buttonsTheme,
|
|
1799
|
+
buttonsStyles,
|
|
1800
|
+
onButtonClick,
|
|
1356
1801
|
submitLabel,
|
|
1357
1802
|
className
|
|
1358
1803
|
}
|
|
@@ -1383,9 +1828,165 @@ function SessionInjector({
|
|
|
1383
1828
|
return import_react6.default.cloneElement(child, injected);
|
|
1384
1829
|
}) });
|
|
1385
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
|
+
}
|
|
1386
1987
|
|
|
1387
1988
|
// src/checkout-form.tsx
|
|
1388
|
-
var
|
|
1989
|
+
var import_shared6 = require("@flopay/shared");
|
|
1389
1990
|
var import_react7 = require("react");
|
|
1390
1991
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1391
1992
|
var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
|
|
@@ -1593,7 +2194,7 @@ function CheckoutFormInner({
|
|
|
1593
2194
|
return;
|
|
1594
2195
|
}
|
|
1595
2196
|
if (!sessionId || !email) {
|
|
1596
|
-
throw new
|
|
2197
|
+
throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
|
|
1597
2198
|
}
|
|
1598
2199
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1599
2200
|
method: "POST",
|
|
@@ -1605,10 +2206,10 @@ function CheckoutFormInner({
|
|
|
1605
2206
|
isPaypal: false
|
|
1606
2207
|
})
|
|
1607
2208
|
});
|
|
1608
|
-
if (!intentResponse.ok) throw new
|
|
2209
|
+
if (!intentResponse.ok) throw new import_shared6.FloPayError("Failed to create payment intent", "api_error");
|
|
1609
2210
|
const intentJson = await intentResponse.json();
|
|
1610
2211
|
const intentClientSecret = intentJson.data?.id;
|
|
1611
|
-
if (!intentClientSecret) throw new
|
|
2212
|
+
if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
|
|
1612
2213
|
const confirmResult = await flopay.confirmCardPayment({
|
|
1613
2214
|
clientSecret: intentClientSecret,
|
|
1614
2215
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -1663,7 +2264,7 @@ function CheckoutFormInner({
|
|
|
1663
2264
|
}
|
|
1664
2265
|
|
|
1665
2266
|
// src/paypal-button.tsx
|
|
1666
|
-
var
|
|
2267
|
+
var import_shared7 = require("@flopay/shared");
|
|
1667
2268
|
var import_react8 = require("react");
|
|
1668
2269
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1669
2270
|
function PayPalButton({
|
|
@@ -1783,7 +2384,7 @@ function PayPalButton({
|
|
|
1783
2384
|
setSubmitting(true);
|
|
1784
2385
|
onErrorChange?.(null);
|
|
1785
2386
|
if (!sessionId || !email) {
|
|
1786
|
-
throw new
|
|
2387
|
+
throw new import_shared7.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
|
|
1787
2388
|
}
|
|
1788
2389
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1789
2390
|
method: "POST",
|
|
@@ -1795,10 +2396,10 @@ function PayPalButton({
|
|
|
1795
2396
|
isPaypal: "true"
|
|
1796
2397
|
})
|
|
1797
2398
|
});
|
|
1798
|
-
if (!intentResponse.ok) throw new
|
|
2399
|
+
if (!intentResponse.ok) throw new import_shared7.FloPayError("Failed to create payment intent", "api_error");
|
|
1799
2400
|
const intentJson = await intentResponse.json();
|
|
1800
2401
|
const intentClientSecret = intentJson.data?.id;
|
|
1801
|
-
if (!intentClientSecret) throw new
|
|
2402
|
+
if (!intentClientSecret) throw new import_shared7.FloPayError("No client_secret in response", "api_error");
|
|
1802
2403
|
const result = await flopay.confirmPayment({
|
|
1803
2404
|
clientSecret: intentClientSecret,
|
|
1804
2405
|
returnUrl: window.location.href
|