@flopay/react 0.4.2 → 0.4.4
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 +64 -1
- package/dist/index.cjs +1889 -654
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.mjs +1777 -543
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -91,9 +91,9 @@ function FloPayProvider({
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// src/flopay-checkout.tsx
|
|
94
|
-
import
|
|
95
|
-
import {
|
|
96
|
-
import { SDK_VERSION, FloPayError as
|
|
94
|
+
import React6, { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
|
|
95
|
+
import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
|
|
96
|
+
import { SDK_VERSION, FloPayError as FloPayError4, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
|
|
97
97
|
|
|
98
98
|
// src/card-button-content.tsx
|
|
99
99
|
import "react";
|
|
@@ -255,6 +255,157 @@ function useBillingApiUrl() {
|
|
|
255
255
|
return ctx.billingApiUrl || resolveBillingApiUrl2();
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
// src/processing-overlay.tsx
|
|
259
|
+
import "react";
|
|
260
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
261
|
+
var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
|
|
262
|
+
var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
|
|
263
|
+
function ProcessingOverlay({
|
|
264
|
+
status,
|
|
265
|
+
errorMessage
|
|
266
|
+
}) {
|
|
267
|
+
return /* @__PURE__ */ jsx4(
|
|
268
|
+
"div",
|
|
269
|
+
{
|
|
270
|
+
"data-testid": "flopay-processing-overlay",
|
|
271
|
+
"data-status": status,
|
|
272
|
+
role: "dialog",
|
|
273
|
+
"aria-modal": "true",
|
|
274
|
+
style: {
|
|
275
|
+
position: "fixed",
|
|
276
|
+
inset: 0,
|
|
277
|
+
background: "rgba(0,0,0,0.35)",
|
|
278
|
+
display: "flex",
|
|
279
|
+
alignItems: "center",
|
|
280
|
+
justifyContent: "center",
|
|
281
|
+
zIndex: 1e3,
|
|
282
|
+
backdropFilter: "blur(2px)"
|
|
283
|
+
},
|
|
284
|
+
children: /* @__PURE__ */ jsxs2(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
style: {
|
|
288
|
+
background: "white",
|
|
289
|
+
borderRadius: 12,
|
|
290
|
+
padding: "2rem 2.5rem",
|
|
291
|
+
textAlign: "center",
|
|
292
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
293
|
+
minWidth: 240,
|
|
294
|
+
display: "flex",
|
|
295
|
+
flexDirection: "column",
|
|
296
|
+
alignItems: "center",
|
|
297
|
+
gap: 16
|
|
298
|
+
},
|
|
299
|
+
children: [
|
|
300
|
+
/* @__PURE__ */ jsxs2("div", { style: { width: 48, height: 48, position: "relative" }, children: [
|
|
301
|
+
status === "processing" && /* @__PURE__ */ jsxs2(
|
|
302
|
+
"svg",
|
|
303
|
+
{
|
|
304
|
+
width: "48",
|
|
305
|
+
height: "48",
|
|
306
|
+
viewBox: "0 0 24 24",
|
|
307
|
+
fill: "none",
|
|
308
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
309
|
+
style: { animation: "flopay-spin 0.8s linear infinite" },
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
312
|
+
/* @__PURE__ */ jsx4("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
status === "success" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
317
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
|
|
318
|
+
/* @__PURE__ */ jsx4(
|
|
319
|
+
"path",
|
|
320
|
+
{
|
|
321
|
+
d: "M7 12.5l3 3 7-7",
|
|
322
|
+
stroke: "white",
|
|
323
|
+
strokeWidth: "2.5",
|
|
324
|
+
strokeLinecap: "round",
|
|
325
|
+
strokeLinejoin: "round",
|
|
326
|
+
style: {
|
|
327
|
+
strokeDasharray: 20,
|
|
328
|
+
strokeDashoffset: 20,
|
|
329
|
+
animation: "flopay-draw 0.4s 0.15s ease forwards"
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
] }) }),
|
|
334
|
+
status === "error" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
335
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
|
|
336
|
+
/* @__PURE__ */ jsx4(
|
|
337
|
+
"path",
|
|
338
|
+
{
|
|
339
|
+
d: "M8 8l8 8M16 8l-8 8",
|
|
340
|
+
stroke: "white",
|
|
341
|
+
strokeWidth: "2.5",
|
|
342
|
+
strokeLinecap: "round",
|
|
343
|
+
style: {
|
|
344
|
+
strokeDasharray: 12,
|
|
345
|
+
strokeDashoffset: 12,
|
|
346
|
+
animation: "flopay-draw 0.3s 0.1s ease forwards"
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
)
|
|
350
|
+
] }) })
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ jsxs2(
|
|
353
|
+
"span",
|
|
354
|
+
{
|
|
355
|
+
style: {
|
|
356
|
+
fontSize: 14,
|
|
357
|
+
fontWeight: 600,
|
|
358
|
+
letterSpacing: "0.05em",
|
|
359
|
+
color: status === "success" ? "#16a34a" : status === "error" ? "#dc2626" : "#374151"
|
|
360
|
+
},
|
|
361
|
+
children: [
|
|
362
|
+
status === "processing" && "PROCESSING...",
|
|
363
|
+
status === "success" && "PAYMENT SUCCESSFUL",
|
|
364
|
+
status === "error" && "PAYMENT FAILED"
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
),
|
|
368
|
+
status === "success" && /* @__PURE__ */ jsx4(
|
|
369
|
+
"p",
|
|
370
|
+
{
|
|
371
|
+
style: {
|
|
372
|
+
fontSize: 13,
|
|
373
|
+
color: "#6b7280",
|
|
374
|
+
fontWeight: 400,
|
|
375
|
+
maxWidth: 260,
|
|
376
|
+
lineHeight: 1.4,
|
|
377
|
+
margin: 0
|
|
378
|
+
},
|
|
379
|
+
children: "You will be automatically redirected, do not close or navigate away from this window."
|
|
380
|
+
}
|
|
381
|
+
),
|
|
382
|
+
status === "error" && errorMessage && /* @__PURE__ */ jsx4(
|
|
383
|
+
"p",
|
|
384
|
+
{
|
|
385
|
+
style: {
|
|
386
|
+
fontSize: 13,
|
|
387
|
+
color: "#6b7280",
|
|
388
|
+
fontWeight: 400,
|
|
389
|
+
maxWidth: 260,
|
|
390
|
+
lineHeight: 1.4,
|
|
391
|
+
margin: 0
|
|
392
|
+
},
|
|
393
|
+
children: errorMessage
|
|
394
|
+
}
|
|
395
|
+
),
|
|
396
|
+
/* @__PURE__ */ jsx4("style", { children: `
|
|
397
|
+
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
398
|
+
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
399
|
+
@keyframes flopay-draw { to { stroke-dashoffset: 0; } }
|
|
400
|
+
@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
|
|
401
|
+
` })
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
258
409
|
// src/checkout-utils.ts
|
|
259
410
|
import { FloPayError } from "@flopay/shared";
|
|
260
411
|
function mergeInlineSessionPatches(base, patch) {
|
|
@@ -312,7 +463,7 @@ function buildSyntheticSession(params, checkoutModeOverride) {
|
|
|
312
463
|
},
|
|
313
464
|
successUrl: params.successUrl,
|
|
314
465
|
cancelUrl: params.cancelUrl,
|
|
315
|
-
checkoutMode: params.checkoutMode ??
|
|
466
|
+
checkoutMode: checkoutModeOverride ?? params.checkoutMode ?? "full",
|
|
316
467
|
items: (params.items ?? []).map((item, idx) => ({
|
|
317
468
|
uuid: `synthetic-item-${idx}`,
|
|
318
469
|
checkoutSessionId: "",
|
|
@@ -353,10 +504,22 @@ function buildDeclineEvent(method, input, overrides) {
|
|
|
353
504
|
...declineCode ? { declineCode } : {}
|
|
354
505
|
};
|
|
355
506
|
}
|
|
507
|
+
function resolveTokenizedPaymentMethodId(tokenizedBody) {
|
|
508
|
+
const candidates = [
|
|
509
|
+
tokenizedBody?.id,
|
|
510
|
+
tokenizedBody?.originalPaymentMethodId
|
|
511
|
+
];
|
|
512
|
+
for (const candidate of candidates) {
|
|
513
|
+
if (typeof candidate === "string" && candidate.startsWith("pm_")) {
|
|
514
|
+
return candidate;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
356
519
|
|
|
357
520
|
// src/split-card-form.tsx
|
|
358
521
|
import { FloPayError as FloPayError2 } from "@flopay/shared";
|
|
359
|
-
import { Fragment as Fragment2, jsx as
|
|
522
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
360
523
|
var WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
361
524
|
var FLOPAY_KEYFRAMES = `
|
|
362
525
|
@keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
@@ -398,7 +561,7 @@ function normalizeBeforeButtonClickError(method, err) {
|
|
|
398
561
|
);
|
|
399
562
|
}
|
|
400
563
|
function FloPayKeyframes() {
|
|
401
|
-
return /* @__PURE__ */
|
|
564
|
+
return /* @__PURE__ */ jsx5("style", { children: FLOPAY_KEYFRAMES });
|
|
402
565
|
}
|
|
403
566
|
function toCssSize(value) {
|
|
404
567
|
if (typeof value === "number") return `${value}px`;
|
|
@@ -419,8 +582,8 @@ function ExpressCheckoutReadySwap({
|
|
|
419
582
|
children
|
|
420
583
|
}) {
|
|
421
584
|
if (state === "unavailable") return null;
|
|
422
|
-
return /* @__PURE__ */
|
|
423
|
-
/* @__PURE__ */
|
|
585
|
+
return /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: 44 }, children: [
|
|
586
|
+
/* @__PURE__ */ jsx5(
|
|
424
587
|
"div",
|
|
425
588
|
{
|
|
426
589
|
"data-testid": placeholderTestId,
|
|
@@ -439,7 +602,7 @@ function ExpressCheckoutReadySwap({
|
|
|
439
602
|
}
|
|
440
603
|
}
|
|
441
604
|
),
|
|
442
|
-
/* @__PURE__ */
|
|
605
|
+
/* @__PURE__ */ jsx5(
|
|
443
606
|
"div",
|
|
444
607
|
{
|
|
445
608
|
style: {
|
|
@@ -454,109 +617,9 @@ function ExpressCheckoutReadySwap({
|
|
|
454
617
|
)
|
|
455
618
|
] });
|
|
456
619
|
}
|
|
457
|
-
function ProcessingOverlay({ status, errorMessage }) {
|
|
458
|
-
return /* @__PURE__ */ jsx4("div", { style: {
|
|
459
|
-
position: "fixed",
|
|
460
|
-
inset: 0,
|
|
461
|
-
background: "rgba(0,0,0,0.35)",
|
|
462
|
-
display: "flex",
|
|
463
|
-
alignItems: "center",
|
|
464
|
-
justifyContent: "center",
|
|
465
|
-
zIndex: 1e3,
|
|
466
|
-
backdropFilter: "blur(2px)"
|
|
467
|
-
}, children: /* @__PURE__ */ jsxs2("div", { style: {
|
|
468
|
-
background: "white",
|
|
469
|
-
borderRadius: 12,
|
|
470
|
-
padding: "2rem 2.5rem",
|
|
471
|
-
textAlign: "center",
|
|
472
|
-
boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
|
|
473
|
-
minWidth: 240,
|
|
474
|
-
display: "flex",
|
|
475
|
-
flexDirection: "column",
|
|
476
|
-
alignItems: "center",
|
|
477
|
-
gap: 16
|
|
478
|
-
}, children: [
|
|
479
|
-
/* @__PURE__ */ jsxs2("div", { style: { width: 48, height: 48, position: "relative" }, children: [
|
|
480
|
-
status === "processing" && /* @__PURE__ */ jsxs2(
|
|
481
|
-
"svg",
|
|
482
|
-
{
|
|
483
|
-
width: "48",
|
|
484
|
-
height: "48",
|
|
485
|
-
viewBox: "0 0 24 24",
|
|
486
|
-
fill: "none",
|
|
487
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
488
|
-
style: { animation: "flopay-spin 0.8s linear infinite" },
|
|
489
|
-
children: [
|
|
490
|
-
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
|
|
491
|
-
/* @__PURE__ */ jsx4("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
|
|
492
|
-
]
|
|
493
|
-
}
|
|
494
|
-
),
|
|
495
|
-
status === "success" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
496
|
-
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
|
|
497
|
-
/* @__PURE__ */ jsx4(
|
|
498
|
-
"path",
|
|
499
|
-
{
|
|
500
|
-
d: "M7 12.5l3 3 7-7",
|
|
501
|
-
stroke: "white",
|
|
502
|
-
strokeWidth: "2.5",
|
|
503
|
-
strokeLinecap: "round",
|
|
504
|
-
strokeLinejoin: "round",
|
|
505
|
-
style: { strokeDasharray: 20, strokeDashoffset: 20, animation: "flopay-draw 0.4s 0.15s ease forwards" }
|
|
506
|
-
}
|
|
507
|
-
)
|
|
508
|
-
] }) }),
|
|
509
|
-
status === "error" && /* @__PURE__ */ jsx4("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
510
|
-
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
|
|
511
|
-
/* @__PURE__ */ jsx4(
|
|
512
|
-
"path",
|
|
513
|
-
{
|
|
514
|
-
d: "M8 8l8 8M16 8l-8 8",
|
|
515
|
-
stroke: "white",
|
|
516
|
-
strokeWidth: "2.5",
|
|
517
|
-
strokeLinecap: "round",
|
|
518
|
-
style: { strokeDasharray: 12, strokeDashoffset: 12, animation: "flopay-draw 0.3s 0.1s ease forwards" }
|
|
519
|
-
}
|
|
520
|
-
)
|
|
521
|
-
] }) })
|
|
522
|
-
] }),
|
|
523
|
-
/* @__PURE__ */ jsxs2("span", { style: {
|
|
524
|
-
fontSize: 14,
|
|
525
|
-
fontWeight: 600,
|
|
526
|
-
letterSpacing: "0.05em",
|
|
527
|
-
color: status === "success" ? "#16a34a" : status === "error" ? "#dc2626" : "#374151"
|
|
528
|
-
}, children: [
|
|
529
|
-
status === "processing" && "PROCESSING...",
|
|
530
|
-
status === "success" && "PAYMENT SUCCESSFUL",
|
|
531
|
-
status === "error" && "PAYMENT FAILED"
|
|
532
|
-
] }),
|
|
533
|
-
status === "success" && /* @__PURE__ */ jsx4("p", { style: {
|
|
534
|
-
fontSize: 13,
|
|
535
|
-
color: "#6b7280",
|
|
536
|
-
fontWeight: 400,
|
|
537
|
-
maxWidth: 260,
|
|
538
|
-
lineHeight: 1.4,
|
|
539
|
-
margin: 0
|
|
540
|
-
}, children: "You will be automatically redirected, do not close or navigate away from this window." }),
|
|
541
|
-
status === "error" && errorMessage && /* @__PURE__ */ jsx4("p", { style: {
|
|
542
|
-
fontSize: 13,
|
|
543
|
-
color: "#6b7280",
|
|
544
|
-
fontWeight: 400,
|
|
545
|
-
maxWidth: 260,
|
|
546
|
-
lineHeight: 1.4,
|
|
547
|
-
margin: 0
|
|
548
|
-
}, children: errorMessage }),
|
|
549
|
-
/* @__PURE__ */ jsx4("style", { children: `
|
|
550
|
-
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
551
|
-
@keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
|
|
552
|
-
@keyframes flopay-draw { to { stroke-dashoffset: 0; } }
|
|
553
|
-
@keyframes flopay-shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-4px); } 40%,80% { transform: translateX(4px); } }
|
|
554
|
-
` })
|
|
555
|
-
] }) });
|
|
556
|
-
}
|
|
557
620
|
var SplitCardForm = forwardRef(
|
|
558
621
|
function SplitCardForm2(props, ref) {
|
|
559
|
-
return /* @__PURE__ */
|
|
622
|
+
return /* @__PURE__ */ jsx5(SplitCardFormInner, { ...props, innerRef: ref });
|
|
560
623
|
}
|
|
561
624
|
);
|
|
562
625
|
function PayPalButtonInner({
|
|
@@ -719,8 +782,8 @@ function PayPalButtonInner({
|
|
|
719
782
|
setSubmitting(false);
|
|
720
783
|
}
|
|
721
784
|
}, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]);
|
|
722
|
-
return /* @__PURE__ */
|
|
723
|
-
/* @__PURE__ */
|
|
785
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
786
|
+
/* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-paypal-placeholder", children: /* @__PURE__ */ jsx5(
|
|
724
787
|
ExpressCheckoutElement,
|
|
725
788
|
{
|
|
726
789
|
onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
|
|
@@ -742,7 +805,7 @@ function PayPalButtonInner({
|
|
|
742
805
|
}
|
|
743
806
|
}
|
|
744
807
|
) }),
|
|
745
|
-
submitting && /* @__PURE__ */
|
|
808
|
+
submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
|
|
746
809
|
] });
|
|
747
810
|
}
|
|
748
811
|
function WalletButtonInner({
|
|
@@ -845,8 +908,8 @@ function WalletButtonInner({
|
|
|
845
908
|
},
|
|
846
909
|
[stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, onButtonClick, runBeforeButtonClick]
|
|
847
910
|
);
|
|
848
|
-
return /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
911
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
912
|
+
/* @__PURE__ */ jsx5(ExpressCheckoutReadySwap, { state: loadState, placeholderTestId: "flopay-wallet-placeholder", children: /* @__PURE__ */ jsx5(
|
|
850
913
|
ExpressCheckoutElement,
|
|
851
914
|
{
|
|
852
915
|
onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["applePay", "googlePay"])),
|
|
@@ -884,7 +947,7 @@ function WalletButtonInner({
|
|
|
884
947
|
}
|
|
885
948
|
}
|
|
886
949
|
) }),
|
|
887
|
-
submitting && /* @__PURE__ */
|
|
950
|
+
submitting && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: "processing" })
|
|
888
951
|
] });
|
|
889
952
|
}
|
|
890
953
|
function SplitCardFormInner({
|
|
@@ -917,6 +980,7 @@ function SplitCardFormInner({
|
|
|
917
980
|
cardButtonContent,
|
|
918
981
|
cardBackButtonContent,
|
|
919
982
|
cardTitleContent,
|
|
983
|
+
showSecurityFooter = true,
|
|
920
984
|
onButtonClick,
|
|
921
985
|
onBeforeButtonClick,
|
|
922
986
|
enableAVS = false,
|
|
@@ -1118,10 +1182,11 @@ function SplitCardFormInner({
|
|
|
1118
1182
|
});
|
|
1119
1183
|
if (response.ok) {
|
|
1120
1184
|
setOverlayStatus("success");
|
|
1121
|
-
await new Promise((r) => setTimeout(r,
|
|
1185
|
+
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
|
|
1122
1186
|
onComplete?.({
|
|
1123
1187
|
status: "succeeded",
|
|
1124
|
-
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
|
|
1188
|
+
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
|
|
1189
|
+
paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
1125
1190
|
});
|
|
1126
1191
|
return;
|
|
1127
1192
|
}
|
|
@@ -1151,7 +1216,8 @@ function SplitCardFormInner({
|
|
|
1151
1216
|
await processPaymentInternal({
|
|
1152
1217
|
id: result.paymentIntentId,
|
|
1153
1218
|
type: "card",
|
|
1154
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
1219
|
+
threeDSecureActionResultTokenId: result.paymentIntentId,
|
|
1220
|
+
originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
1155
1221
|
});
|
|
1156
1222
|
}
|
|
1157
1223
|
} finally {
|
|
@@ -1205,11 +1271,11 @@ function SplitCardFormInner({
|
|
|
1205
1271
|
code: json?.code ?? json?.gatewayErrorCode,
|
|
1206
1272
|
declineCode: json?.declineCode ?? json?.gatewayDeclineReason
|
|
1207
1273
|
});
|
|
1208
|
-
await new Promise((r) => setTimeout(r,
|
|
1274
|
+
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
1209
1275
|
} catch (err) {
|
|
1210
1276
|
setOverlayStatus("error");
|
|
1211
1277
|
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
1212
|
-
await new Promise((r) => setTimeout(r,
|
|
1278
|
+
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
1213
1279
|
} finally {
|
|
1214
1280
|
setProcessing(false);
|
|
1215
1281
|
setOverlayStatus(null);
|
|
@@ -1333,19 +1399,20 @@ function SplitCardFormInner({
|
|
|
1333
1399
|
updateError(confirmResult.error.message);
|
|
1334
1400
|
onError?.(confirmResult.error);
|
|
1335
1401
|
emitDecline("card", confirmResult.error);
|
|
1336
|
-
await new Promise((r) => setTimeout(r,
|
|
1402
|
+
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
1337
1403
|
return;
|
|
1338
1404
|
}
|
|
1339
1405
|
handedOff = isSelfContained;
|
|
1340
1406
|
dispatchTokenizedBody({
|
|
1341
1407
|
id: pmResult.paymentMethodId,
|
|
1342
1408
|
type: "card",
|
|
1343
|
-
threeDSecureActionResultTokenId: confirmResult.paymentIntentId
|
|
1409
|
+
threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
|
|
1410
|
+
originalPaymentMethodId: pmResult.paymentMethodId
|
|
1344
1411
|
});
|
|
1345
1412
|
} catch (err) {
|
|
1346
1413
|
setOverlayStatus("error");
|
|
1347
1414
|
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
1348
|
-
await new Promise((r) => setTimeout(r,
|
|
1415
|
+
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
1349
1416
|
} finally {
|
|
1350
1417
|
if (!handedOff) {
|
|
1351
1418
|
setProcessing(false);
|
|
@@ -1357,7 +1424,7 @@ function SplitCardFormInner({
|
|
|
1357
1424
|
);
|
|
1358
1425
|
const isReady = flopay !== null && elements !== null;
|
|
1359
1426
|
if (!isReady) {
|
|
1360
|
-
return /* @__PURE__ */
|
|
1427
|
+
return /* @__PURE__ */ jsx5("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
|
|
1361
1428
|
}
|
|
1362
1429
|
const isButtons = layout === "buttons";
|
|
1363
1430
|
const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
|
|
@@ -1400,7 +1467,7 @@ function SplitCardFormInner({
|
|
|
1400
1467
|
invalid: { color: "#ef4444" }
|
|
1401
1468
|
}
|
|
1402
1469
|
};
|
|
1403
|
-
const cardFormBlock = /* @__PURE__ */
|
|
1470
|
+
const cardFormBlock = /* @__PURE__ */ jsxs3("div", { style: {
|
|
1404
1471
|
backgroundColor: cardBg,
|
|
1405
1472
|
borderRadius: "8px",
|
|
1406
1473
|
padding: isButtons ? "0" : "1rem",
|
|
@@ -1408,7 +1475,7 @@ function SplitCardFormInner({
|
|
|
1408
1475
|
...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {},
|
|
1409
1476
|
...sharedInputPlaceholderVars
|
|
1410
1477
|
}, children: [
|
|
1411
|
-
/* @__PURE__ */
|
|
1478
|
+
/* @__PURE__ */ jsx5("style", { children: `
|
|
1412
1479
|
.flopay-shared-input::placeholder {
|
|
1413
1480
|
color: var(--flopay-input-placeholder-color);
|
|
1414
1481
|
opacity: 1;
|
|
@@ -1417,12 +1484,12 @@ function SplitCardFormInner({
|
|
|
1417
1484
|
font-weight: var(--flopay-input-font-weight);
|
|
1418
1485
|
}
|
|
1419
1486
|
` }),
|
|
1420
|
-
isButtons && showCardForm && /* @__PURE__ */
|
|
1487
|
+
isButtons && showCardForm && /* @__PURE__ */ jsxs3("div", { style: {
|
|
1421
1488
|
display: "flex",
|
|
1422
1489
|
alignItems: "center",
|
|
1423
1490
|
padding: "0.75rem 0 0.625rem"
|
|
1424
1491
|
}, children: [
|
|
1425
|
-
/* @__PURE__ */
|
|
1492
|
+
/* @__PURE__ */ jsxs3(
|
|
1426
1493
|
"button",
|
|
1427
1494
|
{
|
|
1428
1495
|
type: "button",
|
|
@@ -1444,7 +1511,7 @@ function SplitCardFormInner({
|
|
|
1444
1511
|
},
|
|
1445
1512
|
"aria-label": "Back to payment methods",
|
|
1446
1513
|
children: [
|
|
1447
|
-
/* @__PURE__ */
|
|
1514
|
+
/* @__PURE__ */ jsx5("span", { style: {
|
|
1448
1515
|
display: "inline-flex",
|
|
1449
1516
|
alignItems: "center",
|
|
1450
1517
|
justifyContent: "center",
|
|
@@ -1454,12 +1521,12 @@ function SplitCardFormInner({
|
|
|
1454
1521
|
backgroundColor: "#f3f4f6",
|
|
1455
1522
|
transition: "background-color 0.15s",
|
|
1456
1523
|
...bStyles.backButtonIcon
|
|
1457
|
-
}, children: /* @__PURE__ */
|
|
1458
|
-
/* @__PURE__ */
|
|
1524
|
+
}, children: /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
1525
|
+
/* @__PURE__ */ jsx5(BackButtonContentSlot, { content: cardBackButtonContent })
|
|
1459
1526
|
]
|
|
1460
1527
|
}
|
|
1461
1528
|
),
|
|
1462
|
-
hideTitle ? /* @__PURE__ */
|
|
1529
|
+
hideTitle ? /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx5("div", { style: {
|
|
1463
1530
|
flex: 1,
|
|
1464
1531
|
textAlign: "center",
|
|
1465
1532
|
fontWeight: 600,
|
|
@@ -1467,18 +1534,18 @@ function SplitCardFormInner({
|
|
|
1467
1534
|
color: "#262833",
|
|
1468
1535
|
paddingRight: 80,
|
|
1469
1536
|
...bStyles.title
|
|
1470
|
-
}, children: /* @__PURE__ */
|
|
1537
|
+
}, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) })
|
|
1471
1538
|
] }),
|
|
1472
|
-
!isButtons && !hideTitle && /* @__PURE__ */
|
|
1473
|
-
/* @__PURE__ */
|
|
1539
|
+
!isButtons && !hideTitle && /* @__PURE__ */ jsx5("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx5(TitleContentSlot, { content: cardTitleContent }) }),
|
|
1540
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1474
1541
|
backgroundColor: cardInputBg,
|
|
1475
1542
|
border: `1px solid ${resolvedBorder}`,
|
|
1476
1543
|
borderTopLeftRadius: "8px",
|
|
1477
1544
|
borderTopRightRadius: "8px",
|
|
1478
1545
|
padding: "10px"
|
|
1479
|
-
}, children: /* @__PURE__ */
|
|
1480
|
-
/* @__PURE__ */
|
|
1481
|
-
/* @__PURE__ */
|
|
1546
|
+
}, children: /* @__PURE__ */ jsx5(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
|
|
1547
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
|
|
1548
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1482
1549
|
flex: 1,
|
|
1483
1550
|
backgroundColor: cardInputBg,
|
|
1484
1551
|
border: `1px solid ${resolvedBorder}`,
|
|
@@ -1486,23 +1553,23 @@ function SplitCardFormInner({
|
|
|
1486
1553
|
borderRight: "none",
|
|
1487
1554
|
borderBottomLeftRadius: "8px",
|
|
1488
1555
|
padding: "10px"
|
|
1489
|
-
}, children: /* @__PURE__ */
|
|
1490
|
-
/* @__PURE__ */
|
|
1556
|
+
}, children: /* @__PURE__ */ jsx5(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
1557
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1491
1558
|
flex: 1,
|
|
1492
1559
|
backgroundColor: cardInputBg,
|
|
1493
1560
|
border: `1px solid ${resolvedBorder}`,
|
|
1494
1561
|
borderTop: "none",
|
|
1495
1562
|
borderBottomRightRadius: "8px",
|
|
1496
1563
|
padding: "10px"
|
|
1497
|
-
}, children: /* @__PURE__ */
|
|
1564
|
+
}, children: /* @__PURE__ */ jsx5(CardCvcElement, { options: stripeElementStyle }) })
|
|
1498
1565
|
] }),
|
|
1499
|
-
/* @__PURE__ */
|
|
1566
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1500
1567
|
backgroundColor: cardInputBg,
|
|
1501
1568
|
border: `1px solid ${resolvedBorder}`,
|
|
1502
1569
|
borderRadius: "8px",
|
|
1503
1570
|
marginTop: "0.5rem",
|
|
1504
1571
|
padding: "10px"
|
|
1505
|
-
}, children: /* @__PURE__ */
|
|
1572
|
+
}, children: /* @__PURE__ */ jsx5(
|
|
1506
1573
|
"input",
|
|
1507
1574
|
{
|
|
1508
1575
|
className: "flopay-shared-input",
|
|
@@ -1522,20 +1589,20 @@ function SplitCardFormInner({
|
|
|
1522
1589
|
}
|
|
1523
1590
|
}
|
|
1524
1591
|
) }),
|
|
1525
|
-
enableAVS && /* @__PURE__ */
|
|
1592
|
+
enableAVS && /* @__PURE__ */ jsxs3("div", { style: {
|
|
1526
1593
|
display: "flex",
|
|
1527
1594
|
flexDirection: avsLayoutProp === "column" ? "column" : "row",
|
|
1528
1595
|
gap: avsLayoutProp === "column" ? "0.5rem" : "0",
|
|
1529
1596
|
marginTop: "0.5rem"
|
|
1530
1597
|
}, children: [
|
|
1531
|
-
/* @__PURE__ */
|
|
1598
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1532
1599
|
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
1533
1600
|
backgroundColor: cardInputBg,
|
|
1534
1601
|
border: `1px solid ${resolvedBorder}`,
|
|
1535
1602
|
padding: "10px",
|
|
1536
1603
|
...avsLayoutProp === "row" ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
|
|
1537
1604
|
...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
|
|
1538
|
-
}, children: /* @__PURE__ */
|
|
1605
|
+
}, children: /* @__PURE__ */ jsx5(
|
|
1539
1606
|
"select",
|
|
1540
1607
|
{
|
|
1541
1608
|
value: selectedCountry,
|
|
@@ -1556,21 +1623,21 @@ function SplitCardFormInner({
|
|
|
1556
1623
|
cursor: "pointer",
|
|
1557
1624
|
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
1558
1625
|
},
|
|
1559
|
-
children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */
|
|
1626
|
+
children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs3("option", { value: c.code, children: [
|
|
1560
1627
|
c.flag,
|
|
1561
1628
|
" ",
|
|
1562
1629
|
c.name
|
|
1563
1630
|
] }, c.code))
|
|
1564
1631
|
}
|
|
1565
1632
|
) }),
|
|
1566
|
-
/* @__PURE__ */
|
|
1633
|
+
/* @__PURE__ */ jsx5("div", { style: {
|
|
1567
1634
|
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
1568
1635
|
backgroundColor: cardInputBg,
|
|
1569
1636
|
border: `1px solid ${resolvedBorder}`,
|
|
1570
1637
|
padding: "10px",
|
|
1571
1638
|
...avsLayoutProp === "row" ? { borderRadius: "0", borderTopRightRadius: "8px", borderBottomRightRadius: "8px" } : { borderRadius: "8px" },
|
|
1572
1639
|
...isButtons && bStyles.zipInput ? bStyles.zipInput : {}
|
|
1573
|
-
}, children: /* @__PURE__ */
|
|
1640
|
+
}, children: /* @__PURE__ */ jsx5(
|
|
1574
1641
|
"input",
|
|
1575
1642
|
{
|
|
1576
1643
|
className: "flopay-shared-input",
|
|
@@ -1596,7 +1663,7 @@ function SplitCardFormInner({
|
|
|
1596
1663
|
}
|
|
1597
1664
|
) })
|
|
1598
1665
|
] }),
|
|
1599
|
-
displayError && /* @__PURE__ */
|
|
1666
|
+
displayError && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1600
1667
|
margin: "0.75rem 0",
|
|
1601
1668
|
padding: "0.625rem 0.875rem",
|
|
1602
1669
|
background: "#FEF2F2",
|
|
@@ -1610,10 +1677,10 @@ function SplitCardFormInner({
|
|
|
1610
1677
|
gap: "0.5rem",
|
|
1611
1678
|
...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
1612
1679
|
}, children: [
|
|
1613
|
-
/* @__PURE__ */
|
|
1680
|
+
/* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
1614
1681
|
displayError
|
|
1615
1682
|
] }),
|
|
1616
|
-
children ?? /* @__PURE__ */
|
|
1683
|
+
children ?? /* @__PURE__ */ jsx5(
|
|
1617
1684
|
"button",
|
|
1618
1685
|
{
|
|
1619
1686
|
type: "submit",
|
|
@@ -1636,7 +1703,7 @@ function SplitCardFormInner({
|
|
|
1636
1703
|
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
1637
1704
|
}
|
|
1638
1705
|
),
|
|
1639
|
-
!isButtons && /* @__PURE__ */
|
|
1706
|
+
!isButtons && showSecurityFooter && /* @__PURE__ */ jsx5("div", { style: {
|
|
1640
1707
|
backgroundColor: "#EFF9F0",
|
|
1641
1708
|
borderRadius: "8px",
|
|
1642
1709
|
padding: "0.75rem",
|
|
@@ -1653,11 +1720,11 @@ function SplitCardFormInner({
|
|
|
1653
1720
|
const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
|
|
1654
1721
|
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;
|
|
1655
1722
|
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;
|
|
1656
|
-
return /* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
overlayStatus && /* @__PURE__ */
|
|
1659
|
-
/* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
1723
|
+
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1724
|
+
/* @__PURE__ */ jsx5(FloPayKeyframes, {}),
|
|
1725
|
+
overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
1726
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "grid" }, children: [
|
|
1727
|
+
/* @__PURE__ */ jsxs3("div", { style: {
|
|
1661
1728
|
gridArea: "1 / 1",
|
|
1662
1729
|
display: "flex",
|
|
1663
1730
|
flexDirection: "column",
|
|
@@ -1666,7 +1733,7 @@ function SplitCardFormInner({
|
|
|
1666
1733
|
...!isButtonsView && !buttonsAnim ? { visibility: "hidden", position: "absolute", pointerEvents: "none", width: "100%" } : {},
|
|
1667
1734
|
...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
|
|
1668
1735
|
}, children: [
|
|
1669
|
-
showPayPal && paypalStripeInstance && /* @__PURE__ */
|
|
1736
|
+
showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
|
|
1670
1737
|
PayPalButtonInner,
|
|
1671
1738
|
{
|
|
1672
1739
|
sessionId,
|
|
@@ -1680,7 +1747,7 @@ function SplitCardFormInner({
|
|
|
1680
1747
|
runBeforeButtonClick
|
|
1681
1748
|
}
|
|
1682
1749
|
) }),
|
|
1683
|
-
showWallets && stripeInstance ? /* @__PURE__ */
|
|
1750
|
+
showWallets && stripeInstance ? /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
|
|
1684
1751
|
WalletButtonInner,
|
|
1685
1752
|
{
|
|
1686
1753
|
sessionId,
|
|
@@ -1694,8 +1761,8 @@ function SplitCardFormInner({
|
|
|
1694
1761
|
onDecline,
|
|
1695
1762
|
runBeforeButtonClick
|
|
1696
1763
|
}
|
|
1697
|
-
) }) : showWallets ? /* @__PURE__ */
|
|
1698
|
-
/* @__PURE__ */
|
|
1764
|
+
) }) : showWallets ? /* @__PURE__ */ jsx5("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
|
|
1765
|
+
/* @__PURE__ */ jsx5(
|
|
1699
1766
|
"button",
|
|
1700
1767
|
{
|
|
1701
1768
|
type: "button",
|
|
@@ -1733,10 +1800,10 @@ function SplitCardFormInner({
|
|
|
1733
1800
|
onMouseUp: (e) => {
|
|
1734
1801
|
e.currentTarget.style.transform = "scale(1)";
|
|
1735
1802
|
},
|
|
1736
|
-
children: /* @__PURE__ */
|
|
1803
|
+
children: /* @__PURE__ */ jsx5(CardButtonContentSlot, { content: cardButtonContent })
|
|
1737
1804
|
}
|
|
1738
1805
|
),
|
|
1739
|
-
displayError && viewState === "buttons" && /* @__PURE__ */
|
|
1806
|
+
displayError && viewState === "buttons" && /* @__PURE__ */ jsxs3("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
1740
1807
|
margin: "0.25rem 0",
|
|
1741
1808
|
padding: "0.625rem 0.875rem",
|
|
1742
1809
|
background: "#FEF2F2",
|
|
@@ -1750,11 +1817,11 @@ function SplitCardFormInner({
|
|
|
1750
1817
|
gap: "0.5rem",
|
|
1751
1818
|
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
1752
1819
|
}, children: [
|
|
1753
|
-
/* @__PURE__ */
|
|
1820
|
+
/* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
1754
1821
|
displayError
|
|
1755
1822
|
] })
|
|
1756
1823
|
] }),
|
|
1757
|
-
isCardView && /* @__PURE__ */
|
|
1824
|
+
isCardView && /* @__PURE__ */ jsx5("div", { style: {
|
|
1758
1825
|
gridArea: "1 / 1",
|
|
1759
1826
|
...cardAnim ? { animation: cardAnim } : {},
|
|
1760
1827
|
...viewState === "collapsing" ? { pointerEvents: "none" } : {}
|
|
@@ -1762,10 +1829,10 @@ function SplitCardFormInner({
|
|
|
1762
1829
|
] })
|
|
1763
1830
|
] });
|
|
1764
1831
|
}
|
|
1765
|
-
return /* @__PURE__ */
|
|
1766
|
-
/* @__PURE__ */
|
|
1767
|
-
overlayStatus && /* @__PURE__ */
|
|
1768
|
-
showWallets && stripeInstance && /* @__PURE__ */
|
|
1832
|
+
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
1833
|
+
/* @__PURE__ */ jsx5(FloPayKeyframes, {}),
|
|
1834
|
+
overlayStatus && /* @__PURE__ */ jsx5(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
1835
|
+
showWallets && stripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx5(
|
|
1769
1836
|
WalletButtonInner,
|
|
1770
1837
|
{
|
|
1771
1838
|
sessionId,
|
|
@@ -1778,7 +1845,7 @@ function SplitCardFormInner({
|
|
|
1778
1845
|
onDecline
|
|
1779
1846
|
}
|
|
1780
1847
|
) }),
|
|
1781
|
-
showPayPal && paypalStripeInstance && /* @__PURE__ */
|
|
1848
|
+
showPayPal && paypalStripeInstance && /* @__PURE__ */ jsx5(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx5(
|
|
1782
1849
|
PayPalButtonInner,
|
|
1783
1850
|
{
|
|
1784
1851
|
sessionId,
|
|
@@ -1790,7 +1857,7 @@ function SplitCardFormInner({
|
|
|
1790
1857
|
onDecline
|
|
1791
1858
|
}
|
|
1792
1859
|
) }),
|
|
1793
|
-
(showWallets && stripeInstance || showPayPal && paypalStripeInstance) && /* @__PURE__ */
|
|
1860
|
+
(showWallets && stripeInstance || showPayPal && paypalStripeInstance) && /* @__PURE__ */ jsxs3("div", { style: {
|
|
1794
1861
|
display: "flex",
|
|
1795
1862
|
alignItems: "center",
|
|
1796
1863
|
gap: "0.75rem",
|
|
@@ -1798,17 +1865,503 @@ function SplitCardFormInner({
|
|
|
1798
1865
|
color: "#999",
|
|
1799
1866
|
fontSize: "0.85rem"
|
|
1800
1867
|
}, children: [
|
|
1801
|
-
/* @__PURE__ */
|
|
1802
|
-
/* @__PURE__ */
|
|
1803
|
-
/* @__PURE__ */
|
|
1868
|
+
/* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
|
|
1869
|
+
/* @__PURE__ */ jsx5("span", { children: "or pay with card" }),
|
|
1870
|
+
/* @__PURE__ */ jsx5("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
|
|
1804
1871
|
] }),
|
|
1805
1872
|
cardFormBlock
|
|
1806
1873
|
] });
|
|
1807
1874
|
}
|
|
1808
1875
|
|
|
1876
|
+
// src/saved-payment-flow.ts
|
|
1877
|
+
import { loadFloPay, PaymentAPI } from "@flopay/js";
|
|
1878
|
+
import { FloPayError as FloPayError3 } from "@flopay/shared";
|
|
1879
|
+
var DEFAULT_SAVED_PAYMENT_DECLINE_METHOD = "card";
|
|
1880
|
+
function resolveSavedPaymentReturnUrl(session) {
|
|
1881
|
+
if (typeof window !== "undefined" && window.location.href) {
|
|
1882
|
+
return window.location.href;
|
|
1883
|
+
}
|
|
1884
|
+
return session.successUrl || session.cancelUrl || void 0;
|
|
1885
|
+
}
|
|
1886
|
+
function isStripePaymentIntentClientSecret(value) {
|
|
1887
|
+
return typeof value === "string" && value.startsWith("pi_") && value.includes("_secret_");
|
|
1888
|
+
}
|
|
1889
|
+
async function retryOnceOnFetchFailure(action) {
|
|
1890
|
+
try {
|
|
1891
|
+
return await action();
|
|
1892
|
+
} catch (err) {
|
|
1893
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1894
|
+
if (!/failed to fetch|fetch failed/i.test(message)) {
|
|
1895
|
+
throw err;
|
|
1896
|
+
}
|
|
1897
|
+
return action();
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
function getRedirectTokenFromProcessResponse(json, options) {
|
|
1901
|
+
const directCandidates = [
|
|
1902
|
+
json?.threeDSecureToken,
|
|
1903
|
+
json?.clientSecret,
|
|
1904
|
+
json?.stripeClientSecret
|
|
1905
|
+
];
|
|
1906
|
+
for (const candidate of directCandidates) {
|
|
1907
|
+
if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
|
|
1908
|
+
return candidate;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
const nested = json?.data;
|
|
1912
|
+
if (nested && typeof nested === "object") {
|
|
1913
|
+
const nestedRecord = nested;
|
|
1914
|
+
const nestedCandidates = [
|
|
1915
|
+
nestedRecord.threeDSecureToken,
|
|
1916
|
+
nestedRecord.clientSecret,
|
|
1917
|
+
nestedRecord.stripeClientSecret,
|
|
1918
|
+
nestedRecord.id
|
|
1919
|
+
];
|
|
1920
|
+
for (const candidate of nestedCandidates) {
|
|
1921
|
+
if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
|
|
1922
|
+
return candidate;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
const nestedGatewayData = nestedRecord.gatewayData;
|
|
1926
|
+
if (nestedGatewayData && typeof nestedGatewayData === "object") {
|
|
1927
|
+
const nestedGatewayDataRecord = nestedGatewayData;
|
|
1928
|
+
const gatewayCandidates = [
|
|
1929
|
+
nestedGatewayDataRecord.stripeClientSecret,
|
|
1930
|
+
nestedGatewayDataRecord.clientSecret
|
|
1931
|
+
];
|
|
1932
|
+
for (const candidate of gatewayCandidates) {
|
|
1933
|
+
if (typeof candidate === "string" && candidate.length > 0 && (!options?.requirePaymentIntentClientSecret || isStripePaymentIntentClientSecret(candidate))) {
|
|
1934
|
+
return candidate;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
return void 0;
|
|
1940
|
+
}
|
|
1941
|
+
async function recover3DSRedirectResult({
|
|
1942
|
+
billingApiUrl,
|
|
1943
|
+
sessionId,
|
|
1944
|
+
responseJson
|
|
1945
|
+
}) {
|
|
1946
|
+
const directToken = getRedirectTokenFromProcessResponse(responseJson, {
|
|
1947
|
+
requirePaymentIntentClientSecret: true
|
|
1948
|
+
});
|
|
1949
|
+
if (typeof directToken === "string" && directToken.length > 0) {
|
|
1950
|
+
return {
|
|
1951
|
+
type: "3ds_required",
|
|
1952
|
+
threeDSecureToken: directToken
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
if (!sessionId) {
|
|
1956
|
+
return null;
|
|
1957
|
+
}
|
|
1958
|
+
try {
|
|
1959
|
+
const api = new PaymentAPI(billingApiUrl);
|
|
1960
|
+
const unified = await api.getUnifiedCheckoutSession(sessionId);
|
|
1961
|
+
const refreshedToken = unified.provider === "stripe" ? unified.data.stripe?.clientSecret : void 0;
|
|
1962
|
+
if (isStripePaymentIntentClientSecret(refreshedToken)) {
|
|
1963
|
+
return {
|
|
1964
|
+
type: "3ds_required",
|
|
1965
|
+
threeDSecureToken: refreshedToken
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
} catch {
|
|
1969
|
+
}
|
|
1970
|
+
return null;
|
|
1971
|
+
}
|
|
1972
|
+
async function processSavedPaymentForMode({
|
|
1973
|
+
billingApiUrl,
|
|
1974
|
+
sessionId,
|
|
1975
|
+
session,
|
|
1976
|
+
tokenizedData,
|
|
1977
|
+
returnUrl
|
|
1978
|
+
}) {
|
|
1979
|
+
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
1980
|
+
const resolvedSessionId = sessionId ?? session.id;
|
|
1981
|
+
const customerId = session.customer?.id ?? session.accountData?.userId ?? "";
|
|
1982
|
+
const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
|
|
1983
|
+
const firstName = session.customer?.firstName ?? session.accountData?.firstName ?? "";
|
|
1984
|
+
const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
|
|
1985
|
+
const country = session.customer?.country ?? session.accountData?.country ?? void 0;
|
|
1986
|
+
const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
|
|
1987
|
+
const response = await retryOnceOnFetchFailure(() => fetch(`${baseUrl}/v1/checkouts/sessions/process`, {
|
|
1988
|
+
method: "POST",
|
|
1989
|
+
headers: {
|
|
1990
|
+
"Content-Type": "application/json",
|
|
1991
|
+
"x-user-id": customerId
|
|
1992
|
+
},
|
|
1993
|
+
body: JSON.stringify({
|
|
1994
|
+
sessionId: resolvedSessionId,
|
|
1995
|
+
tokenizedData,
|
|
1996
|
+
accountData: {
|
|
1997
|
+
userId: customerId,
|
|
1998
|
+
email: customerEmail,
|
|
1999
|
+
firstName,
|
|
2000
|
+
lastName,
|
|
2001
|
+
country,
|
|
2002
|
+
zip
|
|
2003
|
+
},
|
|
2004
|
+
returnUrl: returnUrl ?? resolveSavedPaymentReturnUrl(session)
|
|
2005
|
+
})
|
|
2006
|
+
}));
|
|
2007
|
+
if (response.ok) {
|
|
2008
|
+
return {
|
|
2009
|
+
type: "success",
|
|
2010
|
+
result: {
|
|
2011
|
+
status: "succeeded",
|
|
2012
|
+
paymentIntentId: tokenizedData?.threeDSecureActionResultTokenId,
|
|
2013
|
+
paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedData)
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
const json = await response.json().catch(() => null);
|
|
2018
|
+
if (json?.type === "paypal_redirect_required" || json?.type === "3ds_required") {
|
|
2019
|
+
const redirectToken = getRedirectTokenFromProcessResponse(json);
|
|
2020
|
+
if (!redirectToken) {
|
|
2021
|
+
throw new FloPayError3(
|
|
2022
|
+
"Authentication is required but no redirect token was provided.",
|
|
2023
|
+
"api_error",
|
|
2024
|
+
{ code: "authentication_required" }
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
return {
|
|
2028
|
+
type: json.type,
|
|
2029
|
+
threeDSecureToken: redirectToken,
|
|
2030
|
+
paymentMethodId: json.paymentMethodId
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
if (json?.gatewayErrorCode === "authentication_required") {
|
|
2034
|
+
const recoveredRedirect = await recover3DSRedirectResult({
|
|
2035
|
+
billingApiUrl: baseUrl,
|
|
2036
|
+
sessionId: resolvedSessionId,
|
|
2037
|
+
responseJson: json
|
|
2038
|
+
});
|
|
2039
|
+
if (recoveredRedirect) {
|
|
2040
|
+
return recoveredRedirect;
|
|
2041
|
+
}
|
|
2042
|
+
throw Object.assign(
|
|
2043
|
+
new FloPayError3(
|
|
2044
|
+
"Your card requires authentication. Please enter your payment details below.",
|
|
2045
|
+
"api_error",
|
|
2046
|
+
{ code: "authentication_required" }
|
|
2047
|
+
),
|
|
2048
|
+
{ checkoutMethod: "card" }
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
throw new FloPayError3(
|
|
2052
|
+
json?.message ?? "Payment failed. Please try again.",
|
|
2053
|
+
"api_error",
|
|
2054
|
+
{
|
|
2055
|
+
code: json?.code ?? json?.gatewayErrorCode,
|
|
2056
|
+
declineCode: json?.declineCode ?? json?.gatewayDeclineReason
|
|
2057
|
+
}
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
async function processSavedPaymentWithIntent({
|
|
2061
|
+
billingApiUrl,
|
|
2062
|
+
sessionId,
|
|
2063
|
+
session,
|
|
2064
|
+
paymentMethodId,
|
|
2065
|
+
flopay,
|
|
2066
|
+
returnUrl
|
|
2067
|
+
}) {
|
|
2068
|
+
const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
|
|
2069
|
+
if (!customerEmail) {
|
|
2070
|
+
throw Object.assign(
|
|
2071
|
+
new FloPayError3("Customer email is required to create a payment intent.", "validation_error", {
|
|
2072
|
+
param: "email"
|
|
2073
|
+
}),
|
|
2074
|
+
{ checkoutMethod: "card" }
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
const api = new PaymentAPI(billingApiUrl);
|
|
2078
|
+
const intentResponse = await retryOnceOnFetchFailure(() => api.createPaymentIntent(
|
|
2079
|
+
sessionId,
|
|
2080
|
+
customerEmail,
|
|
2081
|
+
paymentMethodId
|
|
2082
|
+
));
|
|
2083
|
+
const intentJson = await intentResponse.json().catch(() => null);
|
|
2084
|
+
if (!intentResponse.ok) {
|
|
2085
|
+
throw Object.assign(
|
|
2086
|
+
new FloPayError3(
|
|
2087
|
+
intentJson?.message ?? "Failed to create payment intent.",
|
|
2088
|
+
"api_error",
|
|
2089
|
+
{ code: intentJson?.code ?? intentJson?.gatewayErrorCode }
|
|
2090
|
+
),
|
|
2091
|
+
{ checkoutMethod: "card" }
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
const intentClientSecret = getRedirectTokenFromProcessResponse(intentJson, {
|
|
2095
|
+
requirePaymentIntentClientSecret: true
|
|
2096
|
+
});
|
|
2097
|
+
if (!intentClientSecret) {
|
|
2098
|
+
throw Object.assign(
|
|
2099
|
+
new FloPayError3("No client secret in payment intent response.", "api_error"),
|
|
2100
|
+
{ checkoutMethod: "card" }
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
const confirmResult = await flopay.confirmCardPayment({
|
|
2104
|
+
clientSecret: intentClientSecret,
|
|
2105
|
+
paymentMethodId
|
|
2106
|
+
});
|
|
2107
|
+
if (confirmResult.error) {
|
|
2108
|
+
throw Object.assign(
|
|
2109
|
+
confirmResult.error,
|
|
2110
|
+
{ checkoutMethod: "card" }
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
if (!confirmResult.paymentIntentId || confirmResult.status !== "succeeded" && confirmResult.status !== "processing" && confirmResult.status !== "requires_capture") {
|
|
2114
|
+
throw Object.assign(
|
|
2115
|
+
new FloPayError3(
|
|
2116
|
+
`Payment intent in unexpected status: ${confirmResult.status}`,
|
|
2117
|
+
"api_error",
|
|
2118
|
+
{
|
|
2119
|
+
code: confirmResult.status === "requires_action" ? "authentication_required" : void 0
|
|
2120
|
+
}
|
|
2121
|
+
),
|
|
2122
|
+
{ checkoutMethod: "card" }
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2125
|
+
const followUp = await processSavedPaymentForMode({
|
|
2126
|
+
billingApiUrl,
|
|
2127
|
+
sessionId,
|
|
2128
|
+
session,
|
|
2129
|
+
tokenizedData: {
|
|
2130
|
+
id: paymentMethodId,
|
|
2131
|
+
type: "card",
|
|
2132
|
+
threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
|
|
2133
|
+
originalPaymentMethodId: paymentMethodId
|
|
2134
|
+
},
|
|
2135
|
+
returnUrl
|
|
2136
|
+
});
|
|
2137
|
+
const finalResult = followUp.type === "success" ? followUp.result : await handleSavedPaymentRedirectResult(followUp, {
|
|
2138
|
+
flopay,
|
|
2139
|
+
paypalFlopay: null,
|
|
2140
|
+
attempt3DS: true,
|
|
2141
|
+
billingApiUrl,
|
|
2142
|
+
sessionId,
|
|
2143
|
+
session,
|
|
2144
|
+
returnUrl
|
|
2145
|
+
});
|
|
2146
|
+
return {
|
|
2147
|
+
...finalResult,
|
|
2148
|
+
paymentIntentId: finalResult.paymentIntentId ?? confirmResult.paymentIntentId,
|
|
2149
|
+
paymentMethodId: finalResult.paymentMethodId ?? paymentMethodId
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
2153
|
+
flopay,
|
|
2154
|
+
paypalFlopay,
|
|
2155
|
+
attempt3DS,
|
|
2156
|
+
billingApiUrl,
|
|
2157
|
+
sessionId,
|
|
2158
|
+
session,
|
|
2159
|
+
returnUrl
|
|
2160
|
+
}) {
|
|
2161
|
+
const stripe = flopay?.getRawProvider();
|
|
2162
|
+
if (!stripe) {
|
|
2163
|
+
throw new FloPayError3("Payment provider is not available.", "api_error");
|
|
2164
|
+
}
|
|
2165
|
+
if (redirectResult.type === "3ds_required") {
|
|
2166
|
+
if (!attempt3DS || !redirectResult.threeDSecureToken) {
|
|
2167
|
+
throw Object.assign(
|
|
2168
|
+
new FloPayError3(
|
|
2169
|
+
"Your card requires authentication. Please enter your payment details below.",
|
|
2170
|
+
"api_error",
|
|
2171
|
+
{ code: "authentication_required" }
|
|
2172
|
+
),
|
|
2173
|
+
{ checkoutMethod: "card" }
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
let paymentIntent = null;
|
|
2177
|
+
let savedPaymentMethodId = redirectResult.paymentMethodId;
|
|
2178
|
+
if (typeof stripe.retrievePaymentIntent === "function") {
|
|
2179
|
+
const { paymentIntent: existingPaymentIntent, error: retrieveError } = await stripe.retrievePaymentIntent(
|
|
2180
|
+
redirectResult.threeDSecureToken
|
|
2181
|
+
);
|
|
2182
|
+
if (retrieveError) {
|
|
2183
|
+
throw Object.assign(
|
|
2184
|
+
new FloPayError3(
|
|
2185
|
+
retrieveError.message ?? "Failed to retrieve 3DS payment status.",
|
|
2186
|
+
"api_error",
|
|
2187
|
+
{ code: retrieveError.code }
|
|
2188
|
+
),
|
|
2189
|
+
{ checkoutMethod: "card" }
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
2192
|
+
if (!savedPaymentMethodId && existingPaymentIntent?.payment_method) {
|
|
2193
|
+
if (typeof existingPaymentIntent.payment_method === "string") {
|
|
2194
|
+
savedPaymentMethodId = existingPaymentIntent.payment_method;
|
|
2195
|
+
} else if ("id" in existingPaymentIntent.payment_method) {
|
|
2196
|
+
savedPaymentMethodId = existingPaymentIntent.payment_method.id;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
if (savedPaymentMethodId && typeof stripe.confirmCardPayment === "function") {
|
|
2201
|
+
const { error: confirmError, paymentIntent: confirmedPaymentIntent } = await stripe.confirmCardPayment(
|
|
2202
|
+
redirectResult.threeDSecureToken,
|
|
2203
|
+
{
|
|
2204
|
+
payment_method: savedPaymentMethodId,
|
|
2205
|
+
return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
|
|
2206
|
+
}
|
|
2207
|
+
);
|
|
2208
|
+
if (confirmError) {
|
|
2209
|
+
throw Object.assign(
|
|
2210
|
+
new FloPayError3(
|
|
2211
|
+
confirmError.message ?? "3DS authentication failed.",
|
|
2212
|
+
"api_error",
|
|
2213
|
+
{ code: confirmError.code }
|
|
2214
|
+
),
|
|
2215
|
+
{ checkoutMethod: "card" }
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
paymentIntent = confirmedPaymentIntent ?? null;
|
|
2219
|
+
} else {
|
|
2220
|
+
const { error: nextActionError, paymentIntent: nextActionPaymentIntent } = await stripe.handleNextAction({
|
|
2221
|
+
clientSecret: redirectResult.threeDSecureToken
|
|
2222
|
+
});
|
|
2223
|
+
if (nextActionError) {
|
|
2224
|
+
throw Object.assign(
|
|
2225
|
+
new FloPayError3(
|
|
2226
|
+
nextActionError.message ?? "3DS authentication failed.",
|
|
2227
|
+
"api_error",
|
|
2228
|
+
{ code: nextActionError.code }
|
|
2229
|
+
),
|
|
2230
|
+
{ checkoutMethod: "card" }
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
paymentIntent = nextActionPaymentIntent ?? null;
|
|
2234
|
+
}
|
|
2235
|
+
if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
|
|
2236
|
+
const followUp = await processSavedPaymentForMode({
|
|
2237
|
+
billingApiUrl,
|
|
2238
|
+
sessionId,
|
|
2239
|
+
session,
|
|
2240
|
+
tokenizedData: {
|
|
2241
|
+
id: paymentIntent.id,
|
|
2242
|
+
type: "card",
|
|
2243
|
+
threeDSecureActionResultTokenId: paymentIntent.id,
|
|
2244
|
+
originalPaymentMethodId: savedPaymentMethodId
|
|
2245
|
+
},
|
|
2246
|
+
returnUrl
|
|
2247
|
+
});
|
|
2248
|
+
if (followUp.type === "success") {
|
|
2249
|
+
return {
|
|
2250
|
+
...followUp.result,
|
|
2251
|
+
paymentIntentId: followUp.result.paymentIntentId ?? paymentIntent.id,
|
|
2252
|
+
paymentMethodId: followUp.result.paymentMethodId ?? savedPaymentMethodId
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
return handleSavedPaymentRedirectResult(followUp, {
|
|
2256
|
+
flopay,
|
|
2257
|
+
paypalFlopay,
|
|
2258
|
+
attempt3DS,
|
|
2259
|
+
billingApiUrl,
|
|
2260
|
+
sessionId,
|
|
2261
|
+
session,
|
|
2262
|
+
returnUrl
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
throw Object.assign(
|
|
2266
|
+
new FloPayError3("3DS authentication did not complete successfully.", "api_error"),
|
|
2267
|
+
{ checkoutMethod: "card" }
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
if (redirectResult.type === "paypal_redirect_required") {
|
|
2271
|
+
const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
|
|
2272
|
+
if (!paypalStripe) {
|
|
2273
|
+
throw Object.assign(
|
|
2274
|
+
new FloPayError3("PayPal is not available.", "api_error"),
|
|
2275
|
+
{ checkoutMethod: "paypal" }
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
const confirmParams = {
|
|
2279
|
+
return_url: returnUrl ?? resolveSavedPaymentReturnUrl(session) ?? window.location.href
|
|
2280
|
+
};
|
|
2281
|
+
if (redirectResult.paymentMethodId) {
|
|
2282
|
+
confirmParams["payment_method"] = redirectResult.paymentMethodId;
|
|
2283
|
+
}
|
|
2284
|
+
const { error } = await paypalStripe.confirmPayment({
|
|
2285
|
+
clientSecret: redirectResult.threeDSecureToken,
|
|
2286
|
+
confirmParams,
|
|
2287
|
+
redirect: "if_required"
|
|
2288
|
+
});
|
|
2289
|
+
if (error) {
|
|
2290
|
+
throw Object.assign(
|
|
2291
|
+
new FloPayError3(
|
|
2292
|
+
error.message ?? "PayPal authorization failed.",
|
|
2293
|
+
"api_error",
|
|
2294
|
+
{ code: error.code }
|
|
2295
|
+
),
|
|
2296
|
+
{ checkoutMethod: "paypal" }
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
return { status: "succeeded" };
|
|
2300
|
+
}
|
|
2301
|
+
throw new FloPayError3("Unsupported payment redirect state.", "api_error");
|
|
2302
|
+
}
|
|
2303
|
+
function normalizeSavedPaymentError(err) {
|
|
2304
|
+
if (err instanceof FloPayError3) {
|
|
2305
|
+
return err;
|
|
2306
|
+
}
|
|
2307
|
+
return new FloPayError3(
|
|
2308
|
+
err instanceof Error ? err.message : "Payment failed. Please try again.",
|
|
2309
|
+
"api_error"
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2312
|
+
function resolveSavedPaymentPublishableKeys(unified, fallbackPublishableKey) {
|
|
2313
|
+
let publishableKey;
|
|
2314
|
+
let paypalPublishableKey;
|
|
2315
|
+
if (unified.provider === "stripe") {
|
|
2316
|
+
publishableKey = unified.data.stripe?.publishableKey;
|
|
2317
|
+
paypalPublishableKey = unified.data.stripe?.paypalPublishableKey ?? void 0;
|
|
2318
|
+
}
|
|
2319
|
+
if (!publishableKey) {
|
|
2320
|
+
publishableKey = fallbackPublishableKey;
|
|
2321
|
+
}
|
|
2322
|
+
if (!publishableKey) {
|
|
2323
|
+
throw new FloPayError3(
|
|
2324
|
+
"No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
|
|
2325
|
+
"validation_error"
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
return {
|
|
2329
|
+
publishableKey,
|
|
2330
|
+
paypalPublishableKey
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
async function loadSavedPaymentProviders({
|
|
2334
|
+
publishableKey,
|
|
2335
|
+
paypalPublishableKey,
|
|
2336
|
+
billingApiUrl,
|
|
2337
|
+
locale
|
|
2338
|
+
}) {
|
|
2339
|
+
const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
|
|
2340
|
+
const [instance, paypalInstanceOrError] = await Promise.all([
|
|
2341
|
+
loadFloPay(publishableKey, {
|
|
2342
|
+
billingApiUrl,
|
|
2343
|
+
locale
|
|
2344
|
+
}),
|
|
2345
|
+
needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
|
|
2346
|
+
billingApiUrl,
|
|
2347
|
+
locale
|
|
2348
|
+
}).catch((err) => {
|
|
2349
|
+
console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
|
|
2350
|
+
return null;
|
|
2351
|
+
}) : Promise.resolve(null)
|
|
2352
|
+
]);
|
|
2353
|
+
return {
|
|
2354
|
+
flopay: instance,
|
|
2355
|
+
paypalFlopay: paypalPublishableKey ? needsSeparatePaypal ? paypalInstanceOrError : instance : null
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
|
|
1809
2359
|
// src/flopay-checkout.tsx
|
|
1810
|
-
import { Fragment as Fragment3, jsx as
|
|
2360
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1811
2361
|
var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
|
|
2362
|
+
function sleep(ms) {
|
|
2363
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2364
|
+
}
|
|
1812
2365
|
function hasInlineSessionPatchData(patch) {
|
|
1813
2366
|
if (!patch) return false;
|
|
1814
2367
|
return Boolean(
|
|
@@ -1839,6 +2392,7 @@ function FloPayCheckout({
|
|
|
1839
2392
|
cardButtonContent,
|
|
1840
2393
|
cardBackButtonContent,
|
|
1841
2394
|
cardTitleContent,
|
|
2395
|
+
showSecurityFooter = true,
|
|
1842
2396
|
onButtonClick,
|
|
1843
2397
|
onBeforeButtonClick,
|
|
1844
2398
|
enableAVS,
|
|
@@ -1861,11 +2415,15 @@ function FloPayCheckout({
|
|
|
1861
2415
|
const paypalFlopayRef = useRef3(null);
|
|
1862
2416
|
const [session, setSession] = useState3(null);
|
|
1863
2417
|
const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
|
|
2418
|
+
const activeSessionId = sessionIdProp ?? resolvedSessionId;
|
|
2419
|
+
const initSessionDependency = createSessionParams ? "" : activeSessionId;
|
|
1864
2420
|
const [isLoading, setIsLoading] = useState3(true);
|
|
1865
2421
|
const [loadError, setLoadError] = useState3(null);
|
|
1866
2422
|
const [currentMode, setCurrentMode] = useState3("full");
|
|
1867
2423
|
const [confirmProcessing, setConfirmProcessing] = useState3(false);
|
|
1868
2424
|
const [modeError, setModeError] = useState3(null);
|
|
2425
|
+
const [modeOverlayStatus, setModeOverlayStatus] = useState3(null);
|
|
2426
|
+
const [modeOverlayError, setModeOverlayError] = useState3(null);
|
|
1869
2427
|
const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
|
|
1870
2428
|
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState3("");
|
|
1871
2429
|
const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
|
|
@@ -1894,10 +2452,18 @@ function FloPayCheckout({
|
|
|
1894
2452
|
() => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
|
|
1895
2453
|
[createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
|
|
1896
2454
|
);
|
|
1897
|
-
const
|
|
2455
|
+
const effectiveCreateSessionBase = useMemo3(
|
|
1898
2456
|
() => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
|
|
1899
2457
|
[createSessionParams, activeCreateSessionPatch]
|
|
1900
2458
|
);
|
|
2459
|
+
const effectiveCreateSessionMode = checkoutModeProp ?? effectiveCreateSessionBase?.checkoutMode ?? "full";
|
|
2460
|
+
const effectiveCreateSession = useMemo3(
|
|
2461
|
+
() => effectiveCreateSessionBase ? {
|
|
2462
|
+
...effectiveCreateSessionBase,
|
|
2463
|
+
checkoutMode: effectiveCreateSessionMode
|
|
2464
|
+
} : void 0,
|
|
2465
|
+
[effectiveCreateSessionBase, effectiveCreateSessionMode]
|
|
2466
|
+
);
|
|
1901
2467
|
useEffect4(() => {
|
|
1902
2468
|
setCreateSessionPatch(void 0);
|
|
1903
2469
|
setCreateSessionPatchBaseHash(baseCreateSessionHash);
|
|
@@ -1908,111 +2474,59 @@ function FloPayCheckout({
|
|
|
1908
2474
|
},
|
|
1909
2475
|
[]
|
|
1910
2476
|
);
|
|
1911
|
-
const
|
|
1912
|
-
async (sess) => {
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
tokenizedData: { id: void 0 },
|
|
1923
|
-
accountData: {
|
|
1924
|
-
userId: sess.customer?.id ?? "",
|
|
1925
|
-
email: sess.customer?.email ?? "",
|
|
1926
|
-
firstName: sess.customer?.firstName ?? "",
|
|
1927
|
-
lastName: sess.customer?.lastName ?? ""
|
|
1928
|
-
}
|
|
1929
|
-
})
|
|
1930
|
-
});
|
|
1931
|
-
if (response.ok) {
|
|
1932
|
-
onCompleteRef.current?.({ status: "succeeded" });
|
|
1933
|
-
return null;
|
|
1934
|
-
}
|
|
1935
|
-
const json = await response.json().catch(() => null);
|
|
1936
|
-
if ((json?.type === "paypal_redirect_required" || json?.type === "3ds_required") && json?.threeDSecureToken) {
|
|
1937
|
-
return {
|
|
1938
|
-
type: json.type,
|
|
1939
|
-
threeDSecureToken: json.threeDSecureToken,
|
|
1940
|
-
paymentMethodId: json.paymentMethodId
|
|
1941
|
-
};
|
|
1942
|
-
}
|
|
1943
|
-
if (json?.gatewayErrorCode === "authentication_required") {
|
|
1944
|
-
return {
|
|
1945
|
-
type: "3ds_required",
|
|
1946
|
-
threeDSecureToken: ""
|
|
1947
|
-
// No client secret available
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
throw new FloPayError3(
|
|
1951
|
-
json?.message ?? "Payment failed. Please try again.",
|
|
1952
|
-
"api_error",
|
|
1953
|
-
{
|
|
1954
|
-
code: json?.code ?? json?.gatewayErrorCode,
|
|
1955
|
-
declineCode: json?.declineCode ?? json?.gatewayDeclineReason
|
|
1956
|
-
}
|
|
1957
|
-
);
|
|
1958
|
-
},
|
|
1959
|
-
[resolvedBillingUrl, resolvedSessionId]
|
|
1960
|
-
);
|
|
1961
|
-
const handleRedirectResult = useCallback2(
|
|
1962
|
-
async (redirectResult, sess, options) => {
|
|
1963
|
-
const stripe = flopayRef.current?.getRawProvider();
|
|
1964
|
-
if (!stripe) return false;
|
|
1965
|
-
if (redirectResult.type === "3ds_required") {
|
|
1966
|
-
if (!options?.attempt3DS || !redirectResult.threeDSecureToken) {
|
|
1967
|
-
setModeError("Your card requires authentication. Please enter your payment details below.");
|
|
1968
|
-
return false;
|
|
1969
|
-
}
|
|
1970
|
-
const { error: nextActionError, paymentIntent } = await stripe.handleNextAction({
|
|
1971
|
-
clientSecret: redirectResult.threeDSecureToken
|
|
2477
|
+
const runSavedPaymentFlow = useCallback2(
|
|
2478
|
+
async (sess, options) => {
|
|
2479
|
+
setModeError(null);
|
|
2480
|
+
setModeOverlayError(null);
|
|
2481
|
+
setModeOverlayStatus("processing");
|
|
2482
|
+
const activeSessionId2 = options?.sessionId ?? sess.id;
|
|
2483
|
+
try {
|
|
2484
|
+
const result = await processSavedPaymentForMode({
|
|
2485
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2486
|
+
sessionId: activeSessionId2,
|
|
2487
|
+
session: sess
|
|
1972
2488
|
});
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2489
|
+
const paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
|
|
2490
|
+
flopay: flopayRef.current,
|
|
2491
|
+
paypalFlopay: paypalFlopayRef.current,
|
|
2492
|
+
attempt3DS: options?.attempt3DS,
|
|
2493
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2494
|
+
sessionId: activeSessionId2,
|
|
2495
|
+
session: sess
|
|
2496
|
+
});
|
|
2497
|
+
setModeOverlayStatus("success");
|
|
2498
|
+
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
2499
|
+
onCompleteRef.current?.(paymentResult);
|
|
2500
|
+
return true;
|
|
2501
|
+
} catch (err) {
|
|
2502
|
+
const floPayErr = normalizeSavedPaymentError(err);
|
|
2503
|
+
const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
|
|
2504
|
+
setModeError(floPayErr.message);
|
|
2505
|
+
setModeOverlayError(floPayErr.message);
|
|
2506
|
+
if (options?.fallbackToFull) {
|
|
2507
|
+
setCurrentMode("full");
|
|
1983
2508
|
}
|
|
2509
|
+
onErrorRef.current?.(floPayErr);
|
|
2510
|
+
emitDecline(method, floPayErr, {
|
|
2511
|
+
code: floPayErr.code,
|
|
2512
|
+
declineCode: floPayErr.declineCode
|
|
2513
|
+
});
|
|
2514
|
+
setModeOverlayStatus("error");
|
|
2515
|
+
await Promise.allSettled([
|
|
2516
|
+
sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
|
|
2517
|
+
options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
|
|
2518
|
+
]);
|
|
1984
2519
|
return false;
|
|
2520
|
+
} finally {
|
|
2521
|
+
setModeOverlayStatus(null);
|
|
2522
|
+
setModeOverlayError(null);
|
|
1985
2523
|
}
|
|
1986
|
-
if (redirectResult.type === "paypal_redirect_required") {
|
|
1987
|
-
const paypalStripe = paypalFlopayRef.current?.getRawProvider();
|
|
1988
|
-
if (!paypalStripe) {
|
|
1989
|
-
setModeError("PayPal is not available.");
|
|
1990
|
-
return false;
|
|
1991
|
-
}
|
|
1992
|
-
const confirmParams = {
|
|
1993
|
-
return_url: window.location.href
|
|
1994
|
-
};
|
|
1995
|
-
if (redirectResult.paymentMethodId) {
|
|
1996
|
-
confirmParams["payment_method"] = redirectResult.paymentMethodId;
|
|
1997
|
-
}
|
|
1998
|
-
const { error } = await paypalStripe.confirmPayment({
|
|
1999
|
-
clientSecret: redirectResult.threeDSecureToken,
|
|
2000
|
-
confirmParams,
|
|
2001
|
-
redirect: "if_required"
|
|
2002
|
-
});
|
|
2003
|
-
if (error) {
|
|
2004
|
-
setModeError(error.message ?? "PayPal authorization failed.");
|
|
2005
|
-
emitDecline("paypal", error.message ?? "PayPal authorization failed.", {
|
|
2006
|
-
code: error.code
|
|
2007
|
-
});
|
|
2008
|
-
return false;
|
|
2009
|
-
}
|
|
2010
|
-
onCompleteRef.current?.({ status: "succeeded" });
|
|
2011
|
-
return true;
|
|
2012
|
-
}
|
|
2013
|
-
return false;
|
|
2014
2524
|
},
|
|
2015
|
-
[
|
|
2525
|
+
[
|
|
2526
|
+
emitDecline,
|
|
2527
|
+
normalizeSavedPaymentError,
|
|
2528
|
+
resolvedBillingUrl
|
|
2529
|
+
]
|
|
2016
2530
|
);
|
|
2017
2531
|
const inflightRef = useRef3(/* @__PURE__ */ new Map());
|
|
2018
2532
|
const initializedHashRef = useRef3(null);
|
|
@@ -2044,8 +2558,14 @@ function FloPayCheckout({
|
|
|
2044
2558
|
useEffect4(() => {
|
|
2045
2559
|
setResolvedSessionId(sessionIdProp ?? "");
|
|
2046
2560
|
}, [sessionIdProp]);
|
|
2561
|
+
useEffect4(() => {
|
|
2562
|
+
autoCheckoutAttempted.current = false;
|
|
2563
|
+
setModeError(null);
|
|
2564
|
+
setModeOverlayError(null);
|
|
2565
|
+
setModeOverlayStatus(null);
|
|
2566
|
+
}, [sessionIdProp, createSessionHash]);
|
|
2047
2567
|
async function resolveInlineSession(params, cacheKey) {
|
|
2048
|
-
const api = new
|
|
2568
|
+
const api = new PaymentAPI2(resolvedBillingUrl);
|
|
2049
2569
|
let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
|
|
2050
2570
|
let realResult = null;
|
|
2051
2571
|
if (sid) {
|
|
@@ -2074,7 +2594,7 @@ function FloPayCheckout({
|
|
|
2074
2594
|
async (patch) => {
|
|
2075
2595
|
const baseParams = createSessionParamsRef.current;
|
|
2076
2596
|
if (!baseParams) {
|
|
2077
|
-
throw new
|
|
2597
|
+
throw new FloPayError4("createSession is required to bootstrap checkout.", "validation_error");
|
|
2078
2598
|
}
|
|
2079
2599
|
const mergedParams = mergeInlineSessionPatch(baseParams, patch);
|
|
2080
2600
|
const cacheKey = hashCreateParams(mergedParams);
|
|
@@ -2103,43 +2623,23 @@ function FloPayCheckout({
|
|
|
2103
2623
|
if (sid) {
|
|
2104
2624
|
setResolvedSessionId(sid);
|
|
2105
2625
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
}
|
|
2119
|
-
const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
|
|
2120
|
-
const [instance, paypalInstanceOrError] = await Promise.all([
|
|
2121
|
-
loadFloPay(publishableKey, {
|
|
2122
|
-
billingApiUrl: resolvedBillingUrl,
|
|
2123
|
-
locale
|
|
2124
|
-
}),
|
|
2125
|
-
needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
|
|
2126
|
-
billingApiUrl: resolvedBillingUrl,
|
|
2127
|
-
locale
|
|
2128
|
-
}).catch((err) => {
|
|
2129
|
-
console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
|
|
2130
|
-
return null;
|
|
2131
|
-
}) : Promise.resolve(null)
|
|
2132
|
-
]);
|
|
2626
|
+
const {
|
|
2627
|
+
publishableKey,
|
|
2628
|
+
paypalPublishableKey
|
|
2629
|
+
} = resolveSavedPaymentPublishableKeys(realResult, fallbackPublishableKey);
|
|
2630
|
+
const {
|
|
2631
|
+
flopay: instance,
|
|
2632
|
+
paypalFlopay: paypalInstance
|
|
2633
|
+
} = await loadSavedPaymentProviders({
|
|
2634
|
+
publishableKey,
|
|
2635
|
+
paypalPublishableKey,
|
|
2636
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2637
|
+
locale
|
|
2638
|
+
});
|
|
2133
2639
|
flopayRef.current = instance;
|
|
2134
2640
|
setFloPay(instance);
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
paypalFlopayRef.current = paypalInstance;
|
|
2138
|
-
setPaypalFloPay(paypalInstance);
|
|
2139
|
-
} else {
|
|
2140
|
-
paypalFlopayRef.current = null;
|
|
2141
|
-
setPaypalFloPay(null);
|
|
2142
|
-
}
|
|
2641
|
+
paypalFlopayRef.current = paypalInstance;
|
|
2642
|
+
setPaypalFloPay(paypalInstance);
|
|
2143
2643
|
setIsLoading(false);
|
|
2144
2644
|
} catch (err) {
|
|
2145
2645
|
if (initializedHashRef.current === cacheKey) {
|
|
@@ -2178,17 +2678,57 @@ function FloPayCheckout({
|
|
|
2178
2678
|
let cancelled = false;
|
|
2179
2679
|
setLoadError(null);
|
|
2180
2680
|
if (createSessionHash) {
|
|
2181
|
-
if (initializedHashRef.current === createSessionHash) return;
|
|
2182
2681
|
const params = createSessionParamsRef.current;
|
|
2183
2682
|
setSession(buildSyntheticSession(params, checkoutModeProp));
|
|
2184
|
-
setCurrentMode(
|
|
2683
|
+
setCurrentMode(effectiveCreateSessionMode);
|
|
2185
2684
|
setIsLoading(false);
|
|
2685
|
+
if (initializedHashRef.current === createSessionHash) {
|
|
2686
|
+
return () => {
|
|
2687
|
+
cancelled = true;
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2186
2690
|
(async () => {
|
|
2691
|
+
const shouldAutoProcessInlineSession = effectiveCreateSessionMode === "auto" && !autoCheckoutAttempted.current;
|
|
2692
|
+
if (shouldAutoProcessInlineSession) {
|
|
2693
|
+
setModeError(null);
|
|
2694
|
+
setModeOverlayError(null);
|
|
2695
|
+
setModeOverlayStatus("processing");
|
|
2696
|
+
}
|
|
2187
2697
|
try {
|
|
2188
|
-
await bootstrapInlineSession();
|
|
2698
|
+
const resolved = await bootstrapInlineSession();
|
|
2699
|
+
const resolvedSession = resolved.result.data.session ?? null;
|
|
2700
|
+
if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
|
|
2701
|
+
autoCheckoutAttempted.current = true;
|
|
2702
|
+
await runSavedPaymentFlow(resolvedSession, {
|
|
2703
|
+
attempt3DS: true,
|
|
2704
|
+
fallbackToFull: true,
|
|
2705
|
+
sessionId: resolved.sid || resolvedSession.id
|
|
2706
|
+
});
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
if (!cancelled && shouldAutoProcessInlineSession) {
|
|
2710
|
+
setModeOverlayStatus(null);
|
|
2711
|
+
setModeOverlayError(null);
|
|
2712
|
+
}
|
|
2189
2713
|
} catch (err) {
|
|
2190
2714
|
if (cancelled) return;
|
|
2191
|
-
const
|
|
2715
|
+
const errorCode = err instanceof FloPayError4 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
2716
|
+
if (shouldAutoProcessInlineSession && errorCode === "session_auto_completed") {
|
|
2717
|
+
autoCheckoutAttempted.current = true;
|
|
2718
|
+
setModeOverlayStatus("success");
|
|
2719
|
+
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
2720
|
+
if (!cancelled) {
|
|
2721
|
+
onCompleteRef.current?.({ status: "succeeded" });
|
|
2722
|
+
setModeOverlayStatus(null);
|
|
2723
|
+
setModeOverlayError(null);
|
|
2724
|
+
}
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
if (shouldAutoProcessInlineSession) {
|
|
2728
|
+
setModeOverlayStatus(null);
|
|
2729
|
+
setModeOverlayError(null);
|
|
2730
|
+
}
|
|
2731
|
+
const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to create session", "api_error");
|
|
2192
2732
|
setLoadError(floPayErr);
|
|
2193
2733
|
}
|
|
2194
2734
|
})();
|
|
@@ -2199,14 +2739,14 @@ function FloPayCheckout({
|
|
|
2199
2739
|
setIsLoading(true);
|
|
2200
2740
|
async function init() {
|
|
2201
2741
|
try {
|
|
2202
|
-
const api = new
|
|
2203
|
-
const result = await api.getUnifiedCheckoutSession(
|
|
2742
|
+
const api = new PaymentAPI2(resolvedBillingUrl);
|
|
2743
|
+
const result = await api.getUnifiedCheckoutSession(activeSessionId);
|
|
2204
2744
|
if (cancelled) return;
|
|
2205
2745
|
setUnified(result);
|
|
2206
2746
|
const sess = result.data.session ?? null;
|
|
2207
2747
|
setSession(sess);
|
|
2208
2748
|
if (!sess) {
|
|
2209
|
-
throw new
|
|
2749
|
+
throw new FloPayError4("No session data returned", "api_error");
|
|
2210
2750
|
}
|
|
2211
2751
|
if (sess.status === "complete") {
|
|
2212
2752
|
setIsLoading(false);
|
|
@@ -2220,22 +2760,20 @@ function FloPayCheckout({
|
|
|
2220
2760
|
autoCheckoutAttempted.current = true;
|
|
2221
2761
|
const stripeInitPromise = initStripe(result);
|
|
2222
2762
|
try {
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2763
|
+
await runSavedPaymentFlow(sess, {
|
|
2764
|
+
attempt3DS: true,
|
|
2765
|
+
fallbackToFull: true,
|
|
2766
|
+
ensureProvidersReady: () => stripeInitPromise,
|
|
2767
|
+
sessionId: activeSessionId || sess.id
|
|
2768
|
+
});
|
|
2228
2769
|
if (cancelled) return;
|
|
2229
2770
|
await stripeInitPromise;
|
|
2230
|
-
const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
|
|
2231
2771
|
if (!cancelled) {
|
|
2232
|
-
if (!handled) setCurrentMode("full");
|
|
2233
2772
|
setIsLoading(false);
|
|
2234
2773
|
}
|
|
2235
2774
|
return;
|
|
2236
2775
|
} catch {
|
|
2237
2776
|
if (cancelled) return;
|
|
2238
|
-
setCurrentMode("full");
|
|
2239
2777
|
await stripeInitPromise;
|
|
2240
2778
|
if (!cancelled) setIsLoading(false);
|
|
2241
2779
|
return;
|
|
@@ -2245,81 +2783,55 @@ function FloPayCheckout({
|
|
|
2245
2783
|
if (!cancelled) setIsLoading(false);
|
|
2246
2784
|
} catch (err) {
|
|
2247
2785
|
if (cancelled) return;
|
|
2248
|
-
const floPayErr = err instanceof
|
|
2786
|
+
const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
2249
2787
|
setLoadError(floPayErr);
|
|
2250
2788
|
setIsLoading(false);
|
|
2251
2789
|
}
|
|
2252
2790
|
}
|
|
2253
2791
|
async function initStripe(result) {
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
}
|
|
2267
|
-
const needsSeparatePaypal = Boolean(paypalPublishableKey) && paypalPublishableKey !== publishableKey;
|
|
2268
|
-
const [instance, paypalInstanceOrError] = await Promise.all([
|
|
2269
|
-
loadFloPay(publishableKey, {
|
|
2270
|
-
billingApiUrl: resolvedBillingUrl,
|
|
2271
|
-
locale
|
|
2272
|
-
}),
|
|
2273
|
-
needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
|
|
2274
|
-
billingApiUrl: resolvedBillingUrl,
|
|
2275
|
-
locale
|
|
2276
|
-
}).catch((err) => {
|
|
2277
|
-
console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
|
|
2278
|
-
return null;
|
|
2279
|
-
}) : Promise.resolve(null)
|
|
2280
|
-
]);
|
|
2792
|
+
const {
|
|
2793
|
+
publishableKey,
|
|
2794
|
+
paypalPublishableKey
|
|
2795
|
+
} = resolveSavedPaymentPublishableKeys(result, fallbackPublishableKey);
|
|
2796
|
+
const {
|
|
2797
|
+
flopay: instance,
|
|
2798
|
+
paypalFlopay: paypalInstance
|
|
2799
|
+
} = await loadSavedPaymentProviders({
|
|
2800
|
+
publishableKey,
|
|
2801
|
+
paypalPublishableKey,
|
|
2802
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2803
|
+
locale
|
|
2804
|
+
});
|
|
2281
2805
|
flopayRef.current = instance;
|
|
2282
2806
|
setFloPay(instance);
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
paypalFlopayRef.current = paypalInstance;
|
|
2286
|
-
setPaypalFloPay(paypalInstance);
|
|
2287
|
-
} else {
|
|
2288
|
-
paypalFlopayRef.current = null;
|
|
2289
|
-
setPaypalFloPay(null);
|
|
2290
|
-
}
|
|
2807
|
+
paypalFlopayRef.current = paypalInstance;
|
|
2808
|
+
setPaypalFloPay(paypalInstance);
|
|
2291
2809
|
}
|
|
2292
2810
|
init();
|
|
2293
2811
|
return () => {
|
|
2294
2812
|
cancelled = true;
|
|
2295
2813
|
};
|
|
2296
|
-
}, [
|
|
2814
|
+
}, [
|
|
2815
|
+
bootstrapInlineSession,
|
|
2816
|
+
checkoutModeProp,
|
|
2817
|
+
createSessionHash,
|
|
2818
|
+
effectiveCreateSessionMode,
|
|
2819
|
+
initSessionDependency,
|
|
2820
|
+
runSavedPaymentFlow
|
|
2821
|
+
]);
|
|
2297
2822
|
const handleConfirmCheckout = useCallback2(async () => {
|
|
2298
2823
|
if (confirmProcessing || !session) return;
|
|
2299
2824
|
setConfirmProcessing(true);
|
|
2300
2825
|
setModeError(null);
|
|
2301
2826
|
try {
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
}
|
|
2306
|
-
const handled = await handleRedirectResult(redirectResult, session);
|
|
2307
|
-
if (!handled) {
|
|
2308
|
-
setCurrentMode("full");
|
|
2309
|
-
}
|
|
2310
|
-
} catch (err) {
|
|
2311
|
-
const floPayErr = err instanceof FloPayError3 ? err : new FloPayError3(
|
|
2312
|
-
err instanceof Error ? err.message : "Payment failed",
|
|
2313
|
-
"api_error"
|
|
2314
|
-
);
|
|
2315
|
-
setModeError(floPayErr.message);
|
|
2316
|
-
onError?.(floPayErr);
|
|
2317
|
-
emitDecline("card", floPayErr);
|
|
2318
|
-
setCurrentMode("full");
|
|
2827
|
+
await runSavedPaymentFlow(session, {
|
|
2828
|
+
fallbackToFull: true,
|
|
2829
|
+
sessionId: activeSessionId || session.id
|
|
2830
|
+
});
|
|
2319
2831
|
} finally {
|
|
2320
2832
|
setConfirmProcessing(false);
|
|
2321
2833
|
}
|
|
2322
|
-
}, [
|
|
2834
|
+
}, [activeSessionId, confirmProcessing, runSavedPaymentFlow, session]);
|
|
2323
2835
|
const providerOptions = useMemo3(() => {
|
|
2324
2836
|
if (!unified || !session) return void 0;
|
|
2325
2837
|
const opts = {
|
|
@@ -2337,7 +2849,7 @@ function FloPayCheckout({
|
|
|
2337
2849
|
return opts;
|
|
2338
2850
|
}, [unified, session, appearance, resolvedBillingUrl]);
|
|
2339
2851
|
const shouldHandleInlineSessionPatch = Boolean(
|
|
2340
|
-
createSessionParams && !children && layout === "buttons" && onBeforeButtonClick &&
|
|
2852
|
+
createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && effectiveCreateSessionMode === "full"
|
|
2341
2853
|
);
|
|
2342
2854
|
const checkoutValue = useMemo3(
|
|
2343
2855
|
() => ({
|
|
@@ -2361,37 +2873,55 @@ function FloPayCheckout({
|
|
|
2361
2873
|
]
|
|
2362
2874
|
);
|
|
2363
2875
|
const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
|
|
2876
|
+
const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx6(
|
|
2877
|
+
ProcessingOverlay,
|
|
2878
|
+
{
|
|
2879
|
+
status: modeOverlayStatus,
|
|
2880
|
+
errorMessage: modeOverlayError
|
|
2881
|
+
}
|
|
2882
|
+
) : null;
|
|
2364
2883
|
if (isLoading) {
|
|
2365
|
-
if (loadingNode)
|
|
2884
|
+
if (loadingNode) {
|
|
2885
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2886
|
+
loadingNode,
|
|
2887
|
+
modeOverlay
|
|
2888
|
+
] });
|
|
2889
|
+
}
|
|
2366
2890
|
if (layout === "buttons") {
|
|
2367
|
-
const skeletonBar = (h) => /* @__PURE__ */
|
|
2891
|
+
const skeletonBar = (h) => /* @__PURE__ */ jsx6("div", { style: {
|
|
2368
2892
|
height: h,
|
|
2369
2893
|
borderRadius: 8,
|
|
2370
2894
|
background: "#e5e7eb",
|
|
2371
2895
|
animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
|
|
2372
2896
|
} });
|
|
2373
|
-
return /* @__PURE__ */
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2897
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2898
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2899
|
+
showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2900
|
+
(showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2901
|
+
skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2902
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
2903
|
+
] }),
|
|
2904
|
+
modeOverlay
|
|
2378
2905
|
] });
|
|
2379
2906
|
}
|
|
2380
|
-
return /* @__PURE__ */
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2907
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2908
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
|
|
2909
|
+
/* @__PURE__ */ jsx6("div", { style: {
|
|
2910
|
+
width: 24,
|
|
2911
|
+
height: 24,
|
|
2912
|
+
border: "2px solid #e5e7eb",
|
|
2913
|
+
borderTopColor: "#6b7280",
|
|
2914
|
+
borderRadius: "50%",
|
|
2915
|
+
animation: "spin 0.6s linear infinite"
|
|
2916
|
+
} }),
|
|
2917
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
|
|
2918
|
+
] }),
|
|
2919
|
+
modeOverlay
|
|
2390
2920
|
] });
|
|
2391
2921
|
}
|
|
2392
2922
|
if (loadError) {
|
|
2393
|
-
if (errorNode) return /* @__PURE__ */
|
|
2394
|
-
return /* @__PURE__ */
|
|
2923
|
+
if (errorNode) return /* @__PURE__ */ jsx6(Fragment3, { children: errorNode(loadError) });
|
|
2924
|
+
return /* @__PURE__ */ jsx6(
|
|
2395
2925
|
"div",
|
|
2396
2926
|
{
|
|
2397
2927
|
style: {
|
|
@@ -2405,90 +2935,100 @@ function FloPayCheckout({
|
|
|
2405
2935
|
);
|
|
2406
2936
|
}
|
|
2407
2937
|
if (shouldShowInterimButtons) {
|
|
2408
|
-
return /* @__PURE__ */
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2938
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2939
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
|
|
2940
|
+
InterimButtonsView,
|
|
2941
|
+
{
|
|
2942
|
+
onButtonClick,
|
|
2943
|
+
showPayPal,
|
|
2944
|
+
showApplePay,
|
|
2945
|
+
showGooglePay,
|
|
2946
|
+
buttonsTheme,
|
|
2947
|
+
buttonsStyles,
|
|
2948
|
+
cardButtonContent,
|
|
2949
|
+
cardBackButtonContent,
|
|
2950
|
+
cardTitleContent
|
|
2951
|
+
}
|
|
2952
|
+
) }),
|
|
2953
|
+
modeOverlay
|
|
2954
|
+
] });
|
|
2955
|
+
}
|
|
2956
|
+
if (!flopay || !providerOptions) {
|
|
2957
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: modeOverlay });
|
|
2422
2958
|
}
|
|
2423
|
-
if (!flopay || !providerOptions) return /* @__PURE__ */ jsx5(Fragment3, {});
|
|
2424
2959
|
if (currentMode === "confirm") {
|
|
2425
|
-
return /* @__PURE__ */
|
|
2426
|
-
|
|
2960
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2961
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs4("div", { className, children: [
|
|
2962
|
+
modeError && /* @__PURE__ */ jsx6(
|
|
2963
|
+
"div",
|
|
2964
|
+
{
|
|
2965
|
+
style: {
|
|
2966
|
+
color: "#dc2626",
|
|
2967
|
+
fontSize: "0.875rem",
|
|
2968
|
+
marginBottom: "0.75rem",
|
|
2969
|
+
textAlign: "center"
|
|
2970
|
+
},
|
|
2971
|
+
children: modeError
|
|
2972
|
+
}
|
|
2973
|
+
),
|
|
2974
|
+
renderConfirmButton ? renderConfirmButton({
|
|
2975
|
+
onConfirm: handleConfirmCheckout,
|
|
2976
|
+
isProcessing: confirmProcessing
|
|
2977
|
+
}) : /* @__PURE__ */ jsx6(
|
|
2978
|
+
"button",
|
|
2979
|
+
{
|
|
2980
|
+
type: "button",
|
|
2981
|
+
onClick: handleConfirmCheckout,
|
|
2982
|
+
disabled: confirmProcessing,
|
|
2983
|
+
style: {
|
|
2984
|
+
width: "100%",
|
|
2985
|
+
padding: "0.875rem",
|
|
2986
|
+
backgroundColor: "#4A49FF",
|
|
2987
|
+
color: "white",
|
|
2988
|
+
border: "none",
|
|
2989
|
+
borderRadius: "8px",
|
|
2990
|
+
fontSize: "1rem",
|
|
2991
|
+
fontWeight: 600,
|
|
2992
|
+
cursor: confirmProcessing ? "not-allowed" : "pointer",
|
|
2993
|
+
opacity: confirmProcessing ? 0.6 : 1
|
|
2994
|
+
},
|
|
2995
|
+
children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
|
|
2996
|
+
}
|
|
2997
|
+
)
|
|
2998
|
+
] }) }) }),
|
|
2999
|
+
modeOverlay
|
|
3000
|
+
] });
|
|
3001
|
+
}
|
|
3002
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3003
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3004
|
+
modeError && /* @__PURE__ */ jsx6(
|
|
2427
3005
|
"div",
|
|
2428
3006
|
{
|
|
2429
3007
|
style: {
|
|
2430
|
-
|
|
2431
|
-
fontSize: "0.875rem",
|
|
3008
|
+
padding: "0.75rem 1rem",
|
|
2432
3009
|
marginBottom: "0.75rem",
|
|
2433
|
-
|
|
3010
|
+
backgroundColor: "#FEF3C7",
|
|
3011
|
+
border: "1px solid #F59E0B",
|
|
3012
|
+
borderRadius: "8px",
|
|
3013
|
+
color: "#92400E",
|
|
3014
|
+
fontSize: "0.875rem"
|
|
2434
3015
|
},
|
|
2435
3016
|
children: modeError
|
|
2436
3017
|
}
|
|
2437
3018
|
),
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
isProcessing: confirmProcessing
|
|
2441
|
-
}) : /* @__PURE__ */ jsx5(
|
|
2442
|
-
"button",
|
|
3019
|
+
/* @__PURE__ */ jsx6(
|
|
3020
|
+
SessionInjector,
|
|
2443
3021
|
{
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
width: "100%",
|
|
2449
|
-
padding: "0.875rem",
|
|
2450
|
-
backgroundColor: "#4A49FF",
|
|
2451
|
-
color: "white",
|
|
2452
|
-
border: "none",
|
|
2453
|
-
borderRadius: "8px",
|
|
2454
|
-
fontSize: "1rem",
|
|
2455
|
-
fontWeight: 600,
|
|
2456
|
-
cursor: confirmProcessing ? "not-allowed" : "pointer",
|
|
2457
|
-
opacity: confirmProcessing ? 0.6 : 1
|
|
2458
|
-
},
|
|
2459
|
-
children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
|
|
3022
|
+
sessionId: activeSessionId,
|
|
3023
|
+
billingApiUrl: resolvedBillingUrl,
|
|
3024
|
+
session,
|
|
3025
|
+
children
|
|
2460
3026
|
}
|
|
2461
3027
|
)
|
|
2462
|
-
] })
|
|
2463
|
-
}
|
|
2464
|
-
return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs3(FloPayProvider, { flopay, options: providerOptions, children: [
|
|
2465
|
-
modeError && /* @__PURE__ */ jsx5(
|
|
2466
|
-
"div",
|
|
2467
|
-
{
|
|
2468
|
-
style: {
|
|
2469
|
-
padding: "0.75rem 1rem",
|
|
2470
|
-
marginBottom: "0.75rem",
|
|
2471
|
-
backgroundColor: "#FEF3C7",
|
|
2472
|
-
border: "1px solid #F59E0B",
|
|
2473
|
-
borderRadius: "8px",
|
|
2474
|
-
color: "#92400E",
|
|
2475
|
-
fontSize: "0.875rem"
|
|
2476
|
-
},
|
|
2477
|
-
children: modeError
|
|
2478
|
-
}
|
|
2479
|
-
),
|
|
2480
|
-
children ? /* @__PURE__ */ jsx5(
|
|
2481
|
-
SessionInjector,
|
|
2482
|
-
{
|
|
2483
|
-
sessionId: resolvedSessionId,
|
|
2484
|
-
billingApiUrl: resolvedBillingUrl,
|
|
2485
|
-
session,
|
|
2486
|
-
children
|
|
2487
|
-
}
|
|
2488
|
-
) : /* @__PURE__ */ jsx5(
|
|
3028
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
2489
3029
|
SplitCardForm,
|
|
2490
3030
|
{
|
|
2491
|
-
sessionId:
|
|
3031
|
+
sessionId: activeSessionId,
|
|
2492
3032
|
email: session?.customer?.email,
|
|
2493
3033
|
userId: session?.customer?.id,
|
|
2494
3034
|
firstName: session?.customer?.firstName,
|
|
@@ -2498,6 +3038,8 @@ function FloPayCheckout({
|
|
|
2498
3038
|
onComplete,
|
|
2499
3039
|
onError,
|
|
2500
3040
|
onDecline,
|
|
3041
|
+
error: modeError,
|
|
3042
|
+
onErrorChange: setModeError,
|
|
2501
3043
|
onFullNameChange,
|
|
2502
3044
|
showPayPal,
|
|
2503
3045
|
showApplePay,
|
|
@@ -2508,6 +3050,7 @@ function FloPayCheckout({
|
|
|
2508
3050
|
cardButtonContent,
|
|
2509
3051
|
cardBackButtonContent,
|
|
2510
3052
|
cardTitleContent,
|
|
3053
|
+
showSecurityFooter,
|
|
2511
3054
|
onButtonClick,
|
|
2512
3055
|
onBeforeButtonClick,
|
|
2513
3056
|
enableAVS,
|
|
@@ -2518,8 +3061,9 @@ function FloPayCheckout({
|
|
|
2518
3061
|
submitLabel,
|
|
2519
3062
|
className
|
|
2520
3063
|
}
|
|
2521
|
-
)
|
|
2522
|
-
|
|
3064
|
+
) }) }),
|
|
3065
|
+
modeOverlay
|
|
3066
|
+
] });
|
|
2523
3067
|
}
|
|
2524
3068
|
function SessionInjector({
|
|
2525
3069
|
sessionId,
|
|
@@ -2527,8 +3071,8 @@ function SessionInjector({
|
|
|
2527
3071
|
session,
|
|
2528
3072
|
children
|
|
2529
3073
|
}) {
|
|
2530
|
-
return /* @__PURE__ */
|
|
2531
|
-
if (!
|
|
3074
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: React6.Children.map(children, (child) => {
|
|
3075
|
+
if (!React6.isValidElement(child)) return child;
|
|
2532
3076
|
const existing = child.props;
|
|
2533
3077
|
const injected = {};
|
|
2534
3078
|
if (!existing.sessionId) injected.sessionId = sessionId;
|
|
@@ -2542,7 +3086,7 @@ function SessionInjector({
|
|
|
2542
3086
|
injected.lastName = session.customer.lastName;
|
|
2543
3087
|
}
|
|
2544
3088
|
if (Object.keys(injected).length === 0) return child;
|
|
2545
|
-
return
|
|
3089
|
+
return React6.cloneElement(child, injected);
|
|
2546
3090
|
}) });
|
|
2547
3091
|
}
|
|
2548
3092
|
function InterimButtonsView({
|
|
@@ -2581,7 +3125,7 @@ function InterimButtonsView({
|
|
|
2581
3125
|
title: { ...base.title, ...stylesOverride.title }
|
|
2582
3126
|
};
|
|
2583
3127
|
}, [buttonsTheme, stylesOverride]);
|
|
2584
|
-
const skeleton = (h) => /* @__PURE__ */
|
|
3128
|
+
const skeleton = (h) => /* @__PURE__ */ jsx6("div", { style: {
|
|
2585
3129
|
height: h,
|
|
2586
3130
|
borderRadius: 8,
|
|
2587
3131
|
background: "#e5e7eb",
|
|
@@ -2593,15 +3137,15 @@ function InterimButtonsView({
|
|
|
2593
3137
|
const inputBg = bStyles.cardInputBackground ?? "white";
|
|
2594
3138
|
const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
|
|
2595
3139
|
const hideTitle = isEmptySlotContent(cardTitleContent);
|
|
2596
|
-
return /* @__PURE__ */
|
|
3140
|
+
return /* @__PURE__ */ jsxs4("div", { style: {
|
|
2597
3141
|
backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
|
|
2598
3142
|
borderRadius: "8px",
|
|
2599
3143
|
animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
|
|
2600
3144
|
overflow: "hidden",
|
|
2601
3145
|
...bStyles.cardFormContainer
|
|
2602
3146
|
}, children: [
|
|
2603
|
-
/* @__PURE__ */
|
|
2604
|
-
/* @__PURE__ */
|
|
3147
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
|
|
3148
|
+
/* @__PURE__ */ jsxs4(
|
|
2605
3149
|
"button",
|
|
2606
3150
|
{
|
|
2607
3151
|
type: "button",
|
|
@@ -2628,7 +3172,7 @@ function InterimButtonsView({
|
|
|
2628
3172
|
...bStyles.backButton
|
|
2629
3173
|
},
|
|
2630
3174
|
children: [
|
|
2631
|
-
/* @__PURE__ */
|
|
3175
|
+
/* @__PURE__ */ jsx6("span", { style: {
|
|
2632
3176
|
display: "inline-flex",
|
|
2633
3177
|
alignItems: "center",
|
|
2634
3178
|
justifyContent: "center",
|
|
@@ -2637,12 +3181,12 @@ function InterimButtonsView({
|
|
|
2637
3181
|
borderRadius: "50%",
|
|
2638
3182
|
backgroundColor: "#f3f4f6",
|
|
2639
3183
|
...bStyles.backButtonIcon
|
|
2640
|
-
}, children: /* @__PURE__ */
|
|
2641
|
-
/* @__PURE__ */
|
|
3184
|
+
}, children: /* @__PURE__ */ jsx6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
3185
|
+
/* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
|
|
2642
3186
|
]
|
|
2643
3187
|
}
|
|
2644
3188
|
),
|
|
2645
|
-
hideTitle ? /* @__PURE__ */
|
|
3189
|
+
hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
|
|
2646
3190
|
flex: 1,
|
|
2647
3191
|
textAlign: "center",
|
|
2648
3192
|
fontWeight: 600,
|
|
@@ -2650,15 +3194,15 @@ function InterimButtonsView({
|
|
|
2650
3194
|
color: "#262833",
|
|
2651
3195
|
paddingRight: 80,
|
|
2652
3196
|
...bStyles.title
|
|
2653
|
-
}, children: /* @__PURE__ */
|
|
3197
|
+
}, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
|
|
2654
3198
|
] }),
|
|
2655
|
-
/* @__PURE__ */
|
|
2656
|
-
/* @__PURE__ */
|
|
2657
|
-
/* @__PURE__ */
|
|
2658
|
-
/* @__PURE__ */
|
|
3199
|
+
/* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
3200
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
|
|
3201
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderRight: "none", borderBottomLeftRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
3202
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1, backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTop: "none", borderBottomRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
|
|
2659
3203
|
] }),
|
|
2660
|
-
/* @__PURE__ */
|
|
2661
|
-
/* @__PURE__ */
|
|
3204
|
+
/* @__PURE__ */ jsx6("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx6("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
3205
|
+
/* @__PURE__ */ jsx6("div", { style: {
|
|
2662
3206
|
height: 50,
|
|
2663
3207
|
borderRadius: 8,
|
|
2664
3208
|
marginTop: 16,
|
|
@@ -2667,7 +3211,7 @@ function InterimButtonsView({
|
|
|
2667
3211
|
...bStyles.submitButton,
|
|
2668
3212
|
opacity: 0.5
|
|
2669
3213
|
} }),
|
|
2670
|
-
/* @__PURE__ */
|
|
3214
|
+
/* @__PURE__ */ jsx6("style", { children: `
|
|
2671
3215
|
@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
2672
3216
|
@keyframes flopay-interim-expand {
|
|
2673
3217
|
0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
|
|
@@ -2677,10 +3221,10 @@ function InterimButtonsView({
|
|
|
2677
3221
|
` })
|
|
2678
3222
|
] });
|
|
2679
3223
|
}
|
|
2680
|
-
return /* @__PURE__ */
|
|
3224
|
+
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2681
3225
|
showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2682
3226
|
(showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2683
|
-
/* @__PURE__ */
|
|
3227
|
+
/* @__PURE__ */ jsx6(
|
|
2684
3228
|
"button",
|
|
2685
3229
|
{
|
|
2686
3230
|
type: "button",
|
|
@@ -2720,10 +3264,10 @@ function InterimButtonsView({
|
|
|
2720
3264
|
onMouseUp: (e) => {
|
|
2721
3265
|
e.currentTarget.style.transform = "scale(1)";
|
|
2722
3266
|
},
|
|
2723
|
-
children: /* @__PURE__ */
|
|
3267
|
+
children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
|
|
2724
3268
|
}
|
|
2725
3269
|
),
|
|
2726
|
-
errorMessage && /* @__PURE__ */
|
|
3270
|
+
errorMessage && /* @__PURE__ */ jsxs4("div", { style: {
|
|
2727
3271
|
margin: "0.25rem 0",
|
|
2728
3272
|
padding: "0.625rem 0.875rem",
|
|
2729
3273
|
background: "#FEF2F2",
|
|
@@ -2737,21 +3281,21 @@ function InterimButtonsView({
|
|
|
2737
3281
|
gap: "0.5rem",
|
|
2738
3282
|
...bStyles.errorBanner
|
|
2739
3283
|
}, children: [
|
|
2740
|
-
/* @__PURE__ */
|
|
3284
|
+
/* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
2741
3285
|
errorMessage
|
|
2742
3286
|
] }),
|
|
2743
|
-
/* @__PURE__ */
|
|
3287
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
2744
3288
|
] });
|
|
2745
3289
|
}
|
|
2746
3290
|
|
|
2747
3291
|
// src/checkout-form.tsx
|
|
2748
|
-
import { FloPayError as
|
|
3292
|
+
import { FloPayError as FloPayError5 } from "@flopay/shared";
|
|
2749
3293
|
import { forwardRef as forwardRef2, useCallback as useCallback3, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useState as useState4 } from "react";
|
|
2750
|
-
import { Fragment as Fragment4, jsx as
|
|
3294
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2751
3295
|
var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
|
|
2752
3296
|
var CheckoutForm = forwardRef2(
|
|
2753
3297
|
function CheckoutForm2(props, ref) {
|
|
2754
|
-
return /* @__PURE__ */
|
|
3298
|
+
return /* @__PURE__ */ jsx7(CheckoutFormInner, { ...props, innerRef: ref });
|
|
2755
3299
|
}
|
|
2756
3300
|
);
|
|
2757
3301
|
function CheckoutFormInner({
|
|
@@ -2777,6 +3321,7 @@ function CheckoutFormInner({
|
|
|
2777
3321
|
innerRef
|
|
2778
3322
|
}) {
|
|
2779
3323
|
const flopay = useFloPay();
|
|
3324
|
+
const paypalFlopay = usePayPalFloPay();
|
|
2780
3325
|
const elements = useElements();
|
|
2781
3326
|
const contextBillingUrl = useBillingApiUrl();
|
|
2782
3327
|
const [processing, setProcessing] = useState4(false);
|
|
@@ -2825,7 +3370,8 @@ function CheckoutFormInner({
|
|
|
2825
3370
|
if (response.ok) {
|
|
2826
3371
|
onComplete?.({
|
|
2827
3372
|
status: "succeeded",
|
|
2828
|
-
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
|
|
3373
|
+
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
|
|
3374
|
+
paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
2829
3375
|
});
|
|
2830
3376
|
return;
|
|
2831
3377
|
}
|
|
@@ -2852,7 +3398,8 @@ function CheckoutFormInner({
|
|
|
2852
3398
|
await processPaymentInternal({
|
|
2853
3399
|
id: result.paymentIntentId,
|
|
2854
3400
|
type: "card",
|
|
2855
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
3401
|
+
threeDSecureActionResultTokenId: result.paymentIntentId,
|
|
3402
|
+
originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
2856
3403
|
});
|
|
2857
3404
|
}
|
|
2858
3405
|
} finally {
|
|
@@ -2861,20 +3408,35 @@ function CheckoutFormInner({
|
|
|
2861
3408
|
return;
|
|
2862
3409
|
}
|
|
2863
3410
|
if (json?.type === "paypal_redirect_required") {
|
|
2864
|
-
const secret = json["clientSecret"];
|
|
3411
|
+
const secret = json["threeDSecureToken"] ?? json["clientSecret"];
|
|
2865
3412
|
const pmId = json["paymentMethodId"];
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
3413
|
+
const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
|
|
3414
|
+
if (!paypalStripe || !secret) {
|
|
3415
|
+
const message = "PayPal is not available.";
|
|
3416
|
+
updateError(message);
|
|
3417
|
+
onDecline?.(buildDeclineEvent("paypal", message));
|
|
3418
|
+
return;
|
|
3419
|
+
}
|
|
3420
|
+
localStorage.setItem(WALLET_RESUME_KEY2, JSON.stringify({
|
|
3421
|
+
sessionId,
|
|
3422
|
+
paymentIntentId: "",
|
|
3423
|
+
tokenType: "card",
|
|
3424
|
+
tokenId: pmId ?? "",
|
|
3425
|
+
status: "pending_redirect"
|
|
3426
|
+
}));
|
|
3427
|
+
const confirmParams = {
|
|
3428
|
+
return_url: window.location.href
|
|
3429
|
+
};
|
|
3430
|
+
if (pmId) confirmParams["payment_method"] = pmId;
|
|
3431
|
+
const { error: confirmError } = await paypalStripe.confirmPayment({
|
|
3432
|
+
clientSecret: secret,
|
|
3433
|
+
confirmParams,
|
|
3434
|
+
redirect: "if_required"
|
|
3435
|
+
});
|
|
3436
|
+
if (confirmError) {
|
|
3437
|
+
const message = confirmError.message ?? "PayPal payment failed.";
|
|
3438
|
+
updateError(message);
|
|
3439
|
+
onDecline?.(buildDeclineEvent("paypal", message, { code: confirmError.code }));
|
|
2878
3440
|
}
|
|
2879
3441
|
return;
|
|
2880
3442
|
}
|
|
@@ -2890,7 +3452,7 @@ function CheckoutFormInner({
|
|
|
2890
3452
|
setProcessing(false);
|
|
2891
3453
|
}
|
|
2892
3454
|
},
|
|
2893
|
-
[baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, onComplete, onError, updateError, emitDecline]
|
|
3455
|
+
[baseUrl, sessionId, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
|
|
2894
3456
|
);
|
|
2895
3457
|
const dispatchTokenizedBody = useCallback3(
|
|
2896
3458
|
(tokenizedBody) => {
|
|
@@ -2966,7 +3528,7 @@ function CheckoutFormInner({
|
|
|
2966
3528
|
return;
|
|
2967
3529
|
}
|
|
2968
3530
|
if (!sessionId || !email) {
|
|
2969
|
-
throw new
|
|
3531
|
+
throw new FloPayError5("Missing sessionId or email", "validation_error");
|
|
2970
3532
|
}
|
|
2971
3533
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
2972
3534
|
method: "POST",
|
|
@@ -2978,10 +3540,10 @@ function CheckoutFormInner({
|
|
|
2978
3540
|
isPaypal: false
|
|
2979
3541
|
})
|
|
2980
3542
|
});
|
|
2981
|
-
if (!intentResponse.ok) throw new
|
|
3543
|
+
if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
|
|
2982
3544
|
const intentJson = await intentResponse.json();
|
|
2983
3545
|
const intentClientSecret = intentJson.data?.id;
|
|
2984
|
-
if (!intentClientSecret) throw new
|
|
3546
|
+
if (!intentClientSecret) throw new FloPayError5("No client_secret in payment intent response", "api_error");
|
|
2985
3547
|
const confirmResult = await flopay.confirmCardPayment({
|
|
2986
3548
|
clientSecret: intentClientSecret,
|
|
2987
3549
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -2995,7 +3557,8 @@ function CheckoutFormInner({
|
|
|
2995
3557
|
dispatchTokenizedBody({
|
|
2996
3558
|
id: pmResult.paymentMethodId,
|
|
2997
3559
|
type: "card",
|
|
2998
|
-
threeDSecureActionResultTokenId: confirmResult.paymentIntentId
|
|
3560
|
+
threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
|
|
3561
|
+
originalPaymentMethodId: pmResult.paymentMethodId
|
|
2999
3562
|
});
|
|
3000
3563
|
} catch (err) {
|
|
3001
3564
|
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
@@ -3009,8 +3572,8 @@ function CheckoutFormInner({
|
|
|
3009
3572
|
[flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
|
|
3010
3573
|
);
|
|
3011
3574
|
const isReady = flopay !== null && elements !== null;
|
|
3012
|
-
return /* @__PURE__ */
|
|
3013
|
-
(is3DSActive || isSubmitting) && /* @__PURE__ */
|
|
3575
|
+
return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
3576
|
+
(is3DSActive || isSubmitting) && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-overlay", style: {
|
|
3014
3577
|
position: "absolute",
|
|
3015
3578
|
inset: 0,
|
|
3016
3579
|
background: "rgba(255,255,255,0.7)",
|
|
@@ -3019,12 +3582,12 @@ function CheckoutFormInner({
|
|
|
3019
3582
|
justifyContent: "center",
|
|
3020
3583
|
zIndex: 10
|
|
3021
3584
|
}, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
|
|
3022
|
-
!isReady && /* @__PURE__ */
|
|
3023
|
-
isReady && /* @__PURE__ */
|
|
3024
|
-
/* @__PURE__ */
|
|
3025
|
-
showAddress && /* @__PURE__ */
|
|
3026
|
-
displayError && /* @__PURE__ */
|
|
3027
|
-
children ?? /* @__PURE__ */
|
|
3585
|
+
!isReady && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
|
|
3586
|
+
isReady && /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
3587
|
+
/* @__PURE__ */ jsx7(PaymentElement, { options: { layout } }),
|
|
3588
|
+
showAddress && /* @__PURE__ */ jsx7(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
|
|
3589
|
+
displayError && /* @__PURE__ */ jsx7("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
|
|
3590
|
+
children ?? /* @__PURE__ */ jsx7(
|
|
3028
3591
|
"button",
|
|
3029
3592
|
{
|
|
3030
3593
|
type: "submit",
|
|
@@ -3038,9 +3601,9 @@ function CheckoutFormInner({
|
|
|
3038
3601
|
}
|
|
3039
3602
|
|
|
3040
3603
|
// src/paypal-button.tsx
|
|
3041
|
-
import { FloPayError as
|
|
3604
|
+
import { FloPayError as FloPayError6 } from "@flopay/shared";
|
|
3042
3605
|
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
|
|
3043
|
-
import { Fragment as Fragment5, jsx as
|
|
3606
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3044
3607
|
function PayPalButton({
|
|
3045
3608
|
sessionId,
|
|
3046
3609
|
billingApiUrl,
|
|
@@ -3158,7 +3721,7 @@ function PayPalButton({
|
|
|
3158
3721
|
setSubmitting(true);
|
|
3159
3722
|
onErrorChange?.(null);
|
|
3160
3723
|
if (!sessionId || !email) {
|
|
3161
|
-
throw new
|
|
3724
|
+
throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
|
|
3162
3725
|
}
|
|
3163
3726
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
3164
3727
|
method: "POST",
|
|
@@ -3170,10 +3733,10 @@ function PayPalButton({
|
|
|
3170
3733
|
isPaypal: "true"
|
|
3171
3734
|
})
|
|
3172
3735
|
});
|
|
3173
|
-
if (!intentResponse.ok) throw new
|
|
3736
|
+
if (!intentResponse.ok) throw new FloPayError6("Failed to create payment intent", "api_error");
|
|
3174
3737
|
const intentJson = await intentResponse.json();
|
|
3175
3738
|
const intentClientSecret = intentJson.data?.id;
|
|
3176
|
-
if (!intentClientSecret) throw new
|
|
3739
|
+
if (!intentClientSecret) throw new FloPayError6("No client_secret in response", "api_error");
|
|
3177
3740
|
const result = await flopay.confirmPayment({
|
|
3178
3741
|
clientSecret: intentClientSecret,
|
|
3179
3742
|
returnUrl: window.location.href
|
|
@@ -3195,11 +3758,11 @@ function PayPalButton({
|
|
|
3195
3758
|
}
|
|
3196
3759
|
}, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
|
|
3197
3760
|
if (!flopay || !elements) {
|
|
3198
|
-
return /* @__PURE__ */
|
|
3761
|
+
return /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
|
|
3199
3762
|
}
|
|
3200
|
-
return /* @__PURE__ */
|
|
3201
|
-
!ready && /* @__PURE__ */
|
|
3202
|
-
/* @__PURE__ */
|
|
3763
|
+
return /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
3764
|
+
!ready && /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
|
|
3765
|
+
/* @__PURE__ */ jsx8("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx8(
|
|
3203
3766
|
"button",
|
|
3204
3767
|
{
|
|
3205
3768
|
type: "button",
|
|
@@ -3221,7 +3784,7 @@ function PayPalButton({
|
|
|
3221
3784
|
children: submitting ? "Processing..." : "PayPal"
|
|
3222
3785
|
}
|
|
3223
3786
|
) }),
|
|
3224
|
-
(submitting || isProcessing) && /* @__PURE__ */
|
|
3787
|
+
(submitting || isProcessing) && /* @__PURE__ */ jsx8("div", { style: {
|
|
3225
3788
|
position: "fixed",
|
|
3226
3789
|
inset: 0,
|
|
3227
3790
|
background: "rgba(0,0,0,0.4)",
|
|
@@ -3229,7 +3792,7 @@ function PayPalButton({
|
|
|
3229
3792
|
alignItems: "center",
|
|
3230
3793
|
justifyContent: "center",
|
|
3231
3794
|
zIndex: 1e3
|
|
3232
|
-
}, children: /* @__PURE__ */
|
|
3795
|
+
}, children: /* @__PURE__ */ jsx8("div", { style: {
|
|
3233
3796
|
background: "white",
|
|
3234
3797
|
borderRadius: 8,
|
|
3235
3798
|
padding: "1.5rem",
|
|
@@ -3239,6 +3802,676 @@ function PayPalButton({
|
|
|
3239
3802
|
}, children: "Processing PayPal payment..." }) })
|
|
3240
3803
|
] });
|
|
3241
3804
|
}
|
|
3805
|
+
|
|
3806
|
+
// src/automatic-payment-button.tsx
|
|
3807
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
|
|
3808
|
+
import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
|
|
3809
|
+
import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
|
|
3810
|
+
import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3811
|
+
var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
|
|
3812
|
+
var PAYPAL_RESUME_STORAGE_KEY = "flopay_automatic_payment_button_resume";
|
|
3813
|
+
function sleep2(ms) {
|
|
3814
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3815
|
+
}
|
|
3816
|
+
function coerceError(err, fallbackMessage) {
|
|
3817
|
+
if (err instanceof FloPayError7) {
|
|
3818
|
+
return err;
|
|
3819
|
+
}
|
|
3820
|
+
return new FloPayError7(
|
|
3821
|
+
err instanceof Error ? err.message : fallbackMessage,
|
|
3822
|
+
"api_error"
|
|
3823
|
+
);
|
|
3824
|
+
}
|
|
3825
|
+
function canUseStorage() {
|
|
3826
|
+
return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
|
|
3827
|
+
}
|
|
3828
|
+
function shouldShowFallbackCheckout(error, sessionId) {
|
|
3829
|
+
if (!sessionId) return false;
|
|
3830
|
+
if (error.type === "validation_error") return false;
|
|
3831
|
+
if (error.checkoutMethod && error.checkoutMethod !== "card") return false;
|
|
3832
|
+
return true;
|
|
3833
|
+
}
|
|
3834
|
+
function readPayPalResumeState() {
|
|
3835
|
+
if (!canUseStorage()) return null;
|
|
3836
|
+
try {
|
|
3837
|
+
const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
|
|
3838
|
+
if (!raw) return null;
|
|
3839
|
+
return JSON.parse(raw);
|
|
3840
|
+
} catch {
|
|
3841
|
+
return null;
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
function persistPayPalResumeState(state) {
|
|
3845
|
+
if (!canUseStorage()) return;
|
|
3846
|
+
try {
|
|
3847
|
+
window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
|
|
3848
|
+
} catch (error) {
|
|
3849
|
+
console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
function clearPayPalResumeState() {
|
|
3853
|
+
if (!canUseStorage()) return;
|
|
3854
|
+
try {
|
|
3855
|
+
window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
|
|
3856
|
+
} catch (error) {
|
|
3857
|
+
console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
function clearPayPalRedirectParams() {
|
|
3861
|
+
if (typeof window === "undefined") return;
|
|
3862
|
+
const url = new URL(window.location.href);
|
|
3863
|
+
const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
|
|
3864
|
+
let changed = false;
|
|
3865
|
+
for (const key of keys) {
|
|
3866
|
+
if (url.searchParams.has(key)) {
|
|
3867
|
+
url.searchParams.delete(key);
|
|
3868
|
+
changed = true;
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
if (changed) {
|
|
3872
|
+
window.history.replaceState({}, "", url.toString());
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
function resolveCreateSessionDraft(props) {
|
|
3876
|
+
if (props.createSession) {
|
|
3877
|
+
return {
|
|
3878
|
+
...props.createSession,
|
|
3879
|
+
checkoutMode: "auto"
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
if (!props.clientId || !props.account || !props.successUrl || !props.cancelUrl) {
|
|
3883
|
+
return null;
|
|
3884
|
+
}
|
|
3885
|
+
return {
|
|
3886
|
+
clientId: props.clientId,
|
|
3887
|
+
items: props.items,
|
|
3888
|
+
subscriptions: props.subscriptions,
|
|
3889
|
+
account: props.account,
|
|
3890
|
+
successUrl: props.successUrl,
|
|
3891
|
+
cancelUrl: props.cancelUrl,
|
|
3892
|
+
couponCodes: props.couponCodes,
|
|
3893
|
+
tagsData: props.tagsData,
|
|
3894
|
+
utmMetadata: props.utmMetadata,
|
|
3895
|
+
checkoutMode: "auto"
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3898
|
+
function FloPayAutomaticPaymentButton({
|
|
3899
|
+
sessionId,
|
|
3900
|
+
createSession,
|
|
3901
|
+
paymentMethodId,
|
|
3902
|
+
clientId,
|
|
3903
|
+
items,
|
|
3904
|
+
subscriptions,
|
|
3905
|
+
account,
|
|
3906
|
+
successUrl,
|
|
3907
|
+
cancelUrl,
|
|
3908
|
+
couponCodes,
|
|
3909
|
+
tagsData,
|
|
3910
|
+
utmMetadata,
|
|
3911
|
+
billingApiUrl,
|
|
3912
|
+
locale,
|
|
3913
|
+
fallbackPublishableKey,
|
|
3914
|
+
buttonsTheme,
|
|
3915
|
+
buttonsStyles: stylesOverride,
|
|
3916
|
+
onSuccess,
|
|
3917
|
+
onError,
|
|
3918
|
+
onDecline,
|
|
3919
|
+
children,
|
|
3920
|
+
disabled = false,
|
|
3921
|
+
type = "button",
|
|
3922
|
+
style,
|
|
3923
|
+
...buttonProps
|
|
3924
|
+
}) {
|
|
3925
|
+
const resolvedBillingUrl = useMemo4(
|
|
3926
|
+
() => resolveBillingApiUrl4(billingApiUrl),
|
|
3927
|
+
[billingApiUrl]
|
|
3928
|
+
);
|
|
3929
|
+
const createSessionDraft = useMemo4(
|
|
3930
|
+
() => resolveCreateSessionDraft({
|
|
3931
|
+
createSession,
|
|
3932
|
+
clientId,
|
|
3933
|
+
items,
|
|
3934
|
+
subscriptions,
|
|
3935
|
+
account,
|
|
3936
|
+
successUrl,
|
|
3937
|
+
cancelUrl,
|
|
3938
|
+
couponCodes,
|
|
3939
|
+
tagsData,
|
|
3940
|
+
utmMetadata
|
|
3941
|
+
}),
|
|
3942
|
+
[
|
|
3943
|
+
account,
|
|
3944
|
+
cancelUrl,
|
|
3945
|
+
clientId,
|
|
3946
|
+
couponCodes,
|
|
3947
|
+
createSession,
|
|
3948
|
+
items,
|
|
3949
|
+
subscriptions,
|
|
3950
|
+
successUrl,
|
|
3951
|
+
tagsData,
|
|
3952
|
+
utmMetadata
|
|
3953
|
+
]
|
|
3954
|
+
);
|
|
3955
|
+
const [isProcessing, setIsProcessing] = useState6(false);
|
|
3956
|
+
const [overlayStatus, setOverlayStatus] = useState6(null);
|
|
3957
|
+
const [overlayError, setOverlayError] = useState6(null);
|
|
3958
|
+
const [fallbackSession, setFallbackSession] = useState6(null);
|
|
3959
|
+
const automaticPaymentToken = useMemo4(
|
|
3960
|
+
() => paymentMethodId ? {
|
|
3961
|
+
id: paymentMethodId,
|
|
3962
|
+
type: "card"
|
|
3963
|
+
} : void 0,
|
|
3964
|
+
[paymentMethodId]
|
|
3965
|
+
);
|
|
3966
|
+
const isMountedRef = useRef5(true);
|
|
3967
|
+
const resumeAttemptedRef = useRef5(false);
|
|
3968
|
+
const fallbackSessionRef = useRef5(fallbackSession);
|
|
3969
|
+
const onSuccessRef = useRef5(onSuccess);
|
|
3970
|
+
const onErrorRef = useRef5(onError);
|
|
3971
|
+
const onDeclineRef = useRef5(onDecline);
|
|
3972
|
+
useEffect7(() => {
|
|
3973
|
+
fallbackSessionRef.current = fallbackSession;
|
|
3974
|
+
}, [fallbackSession]);
|
|
3975
|
+
useEffect7(() => {
|
|
3976
|
+
onSuccessRef.current = onSuccess;
|
|
3977
|
+
}, [onSuccess]);
|
|
3978
|
+
useEffect7(() => {
|
|
3979
|
+
onErrorRef.current = onError;
|
|
3980
|
+
}, [onError]);
|
|
3981
|
+
useEffect7(() => {
|
|
3982
|
+
onDeclineRef.current = onDecline;
|
|
3983
|
+
}, [onDecline]);
|
|
3984
|
+
useEffect7(() => {
|
|
3985
|
+
isMountedRef.current = true;
|
|
3986
|
+
return () => {
|
|
3987
|
+
isMountedRef.current = false;
|
|
3988
|
+
};
|
|
3989
|
+
}, []);
|
|
3990
|
+
useEffect7(() => {
|
|
3991
|
+
if (!fallbackSession || typeof window === "undefined") {
|
|
3992
|
+
return;
|
|
3993
|
+
}
|
|
3994
|
+
const handleKeyDown = (event) => {
|
|
3995
|
+
if (event.key === "Escape") {
|
|
3996
|
+
setFallbackSession(null);
|
|
3997
|
+
}
|
|
3998
|
+
};
|
|
3999
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
4000
|
+
return () => {
|
|
4001
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
4002
|
+
};
|
|
4003
|
+
}, [fallbackSession]);
|
|
4004
|
+
const emitDecline = useCallback5((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
|
|
4005
|
+
onDeclineRef.current?.(buildDeclineEvent(method, error, {
|
|
4006
|
+
code: error.code,
|
|
4007
|
+
declineCode: error.declineCode
|
|
4008
|
+
}));
|
|
4009
|
+
}, []);
|
|
4010
|
+
const showSuccess = useCallback5(async (event) => {
|
|
4011
|
+
if (!isMountedRef.current) return;
|
|
4012
|
+
setOverlayError(null);
|
|
4013
|
+
setOverlayStatus("success");
|
|
4014
|
+
await sleep2(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
4015
|
+
if (!isMountedRef.current) return;
|
|
4016
|
+
onSuccessRef.current?.(event);
|
|
4017
|
+
}, []);
|
|
4018
|
+
const showError = useCallback5(async (error, options) => {
|
|
4019
|
+
if (!isMountedRef.current) return;
|
|
4020
|
+
onErrorRef.current?.(error);
|
|
4021
|
+
if (options?.emitDecline) {
|
|
4022
|
+
emitDecline(error, options.method);
|
|
4023
|
+
}
|
|
4024
|
+
setOverlayError(error.message);
|
|
4025
|
+
setOverlayStatus("error");
|
|
4026
|
+
await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
|
|
4027
|
+
}, [emitDecline]);
|
|
4028
|
+
const processResolvedSession = useCallback5(async (apiResult, resolvedSessionId) => {
|
|
4029
|
+
const session = apiResult.data.session ?? null;
|
|
4030
|
+
if (!session) {
|
|
4031
|
+
throw new FloPayError7("No session data returned", "api_error");
|
|
4032
|
+
}
|
|
4033
|
+
if (session.status === "complete") {
|
|
4034
|
+
await showSuccess({
|
|
4035
|
+
result: { status: "succeeded" },
|
|
4036
|
+
session,
|
|
4037
|
+
sessionId: session.id || resolvedSessionId,
|
|
4038
|
+
autoCompleted: false
|
|
4039
|
+
});
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
try {
|
|
4043
|
+
const result = await processSavedPaymentForMode({
|
|
4044
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4045
|
+
sessionId: resolvedSessionId ?? session.id,
|
|
4046
|
+
session,
|
|
4047
|
+
tokenizedData: automaticPaymentToken
|
|
4048
|
+
});
|
|
4049
|
+
let paymentResult = result.type === "success" ? result.result : null;
|
|
4050
|
+
if (result.type !== "success") {
|
|
4051
|
+
const {
|
|
4052
|
+
publishableKey,
|
|
4053
|
+
paypalPublishableKey
|
|
4054
|
+
} = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
|
|
4055
|
+
if (result.type === "paypal_redirect_required") {
|
|
4056
|
+
persistPayPalResumeState({
|
|
4057
|
+
sessionId: session.id || resolvedSessionId,
|
|
4058
|
+
publishableKey,
|
|
4059
|
+
paypalPublishableKey
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
const {
|
|
4063
|
+
flopay,
|
|
4064
|
+
paypalFlopay
|
|
4065
|
+
} = await loadSavedPaymentProviders({
|
|
4066
|
+
publishableKey,
|
|
4067
|
+
paypalPublishableKey,
|
|
4068
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4069
|
+
locale
|
|
4070
|
+
});
|
|
4071
|
+
paymentResult = await handleSavedPaymentRedirectResult(result, {
|
|
4072
|
+
flopay,
|
|
4073
|
+
paypalFlopay,
|
|
4074
|
+
attempt3DS: true,
|
|
4075
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4076
|
+
sessionId: session.id || resolvedSessionId,
|
|
4077
|
+
session
|
|
4078
|
+
});
|
|
4079
|
+
if (result.type === "paypal_redirect_required") {
|
|
4080
|
+
clearPayPalResumeState();
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
await showSuccess({
|
|
4084
|
+
result: paymentResult ? {
|
|
4085
|
+
...paymentResult,
|
|
4086
|
+
paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
|
|
4087
|
+
} : {
|
|
4088
|
+
status: "succeeded",
|
|
4089
|
+
paymentMethodId: paymentMethodId ?? void 0
|
|
4090
|
+
},
|
|
4091
|
+
session,
|
|
4092
|
+
sessionId: session.id || resolvedSessionId,
|
|
4093
|
+
autoCompleted: false
|
|
4094
|
+
});
|
|
4095
|
+
} catch (err) {
|
|
4096
|
+
let floPayErr = normalizeSavedPaymentError(err);
|
|
4097
|
+
const fallbackSessionId = session.id || resolvedSessionId;
|
|
4098
|
+
if (floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
|
|
4099
|
+
try {
|
|
4100
|
+
const {
|
|
4101
|
+
publishableKey,
|
|
4102
|
+
paypalPublishableKey
|
|
4103
|
+
} = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
|
|
4104
|
+
const {
|
|
4105
|
+
flopay
|
|
4106
|
+
} = await loadSavedPaymentProviders({
|
|
4107
|
+
publishableKey,
|
|
4108
|
+
paypalPublishableKey,
|
|
4109
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4110
|
+
locale
|
|
4111
|
+
});
|
|
4112
|
+
const paymentResult = await processSavedPaymentWithIntent({
|
|
4113
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4114
|
+
sessionId: fallbackSessionId,
|
|
4115
|
+
session,
|
|
4116
|
+
paymentMethodId: automaticPaymentToken.id,
|
|
4117
|
+
flopay
|
|
4118
|
+
});
|
|
4119
|
+
await showSuccess({
|
|
4120
|
+
result: {
|
|
4121
|
+
...paymentResult,
|
|
4122
|
+
paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
|
|
4123
|
+
},
|
|
4124
|
+
session,
|
|
4125
|
+
sessionId: fallbackSessionId,
|
|
4126
|
+
autoCompleted: false
|
|
4127
|
+
});
|
|
4128
|
+
return;
|
|
4129
|
+
} catch (intentRecoveryErr) {
|
|
4130
|
+
floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
await showError(floPayErr, {
|
|
4134
|
+
emitDecline: true,
|
|
4135
|
+
method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
|
|
4136
|
+
});
|
|
4137
|
+
if (shouldShowFallbackCheckout(floPayErr, fallbackSessionId) && fallbackSessionId && isMountedRef.current) {
|
|
4138
|
+
setFallbackSession({
|
|
4139
|
+
sessionId: fallbackSessionId,
|
|
4140
|
+
errorMessage: floPayErr.message
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
}, [
|
|
4145
|
+
fallbackPublishableKey,
|
|
4146
|
+
locale,
|
|
4147
|
+
automaticPaymentToken,
|
|
4148
|
+
paymentMethodId,
|
|
4149
|
+
resolvedBillingUrl,
|
|
4150
|
+
showError,
|
|
4151
|
+
showSuccess
|
|
4152
|
+
]);
|
|
4153
|
+
const handleButtonClick = useCallback5(async (event) => {
|
|
4154
|
+
buttonProps.onClick?.(event);
|
|
4155
|
+
if (event.defaultPrevented || disabled || isProcessing) {
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
setFallbackSession(null);
|
|
4159
|
+
if (sessionId && createSessionDraft) {
|
|
4160
|
+
const error = new FloPayError7(
|
|
4161
|
+
"Provide either sessionId or create-session props, not both.",
|
|
4162
|
+
"validation_error"
|
|
4163
|
+
);
|
|
4164
|
+
await showError(error);
|
|
4165
|
+
if (isMountedRef.current) {
|
|
4166
|
+
setOverlayStatus(null);
|
|
4167
|
+
setOverlayError(null);
|
|
4168
|
+
}
|
|
4169
|
+
return;
|
|
4170
|
+
}
|
|
4171
|
+
if (!sessionId && !createSessionDraft) {
|
|
4172
|
+
const error = new FloPayError7(
|
|
4173
|
+
"Provide a sessionId or the props required to create an automatic payment session.",
|
|
4174
|
+
"validation_error"
|
|
4175
|
+
);
|
|
4176
|
+
await showError(error);
|
|
4177
|
+
if (isMountedRef.current) {
|
|
4178
|
+
setOverlayStatus(null);
|
|
4179
|
+
setOverlayError(null);
|
|
4180
|
+
}
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
setIsProcessing(true);
|
|
4184
|
+
setOverlayError(null);
|
|
4185
|
+
setOverlayStatus("processing");
|
|
4186
|
+
try {
|
|
4187
|
+
const api = new PaymentAPI3(resolvedBillingUrl);
|
|
4188
|
+
if (sessionId) {
|
|
4189
|
+
const result = await api.getUnifiedCheckoutSession(sessionId);
|
|
4190
|
+
await processResolvedSession(result, sessionId);
|
|
4191
|
+
return;
|
|
4192
|
+
}
|
|
4193
|
+
try {
|
|
4194
|
+
const result = await api.createAndFetchSession(createSessionDraft);
|
|
4195
|
+
await processResolvedSession(result, result.data.session?.id ?? null);
|
|
4196
|
+
} catch (err) {
|
|
4197
|
+
if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
|
|
4198
|
+
await showSuccess({
|
|
4199
|
+
result: { status: "succeeded" },
|
|
4200
|
+
session: null,
|
|
4201
|
+
sessionId: null,
|
|
4202
|
+
autoCompleted: true
|
|
4203
|
+
});
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
4206
|
+
throw err;
|
|
4207
|
+
}
|
|
4208
|
+
} catch (err) {
|
|
4209
|
+
await showError(
|
|
4210
|
+
coerceError(err, "Automatic payment failed. Please try again.")
|
|
4211
|
+
);
|
|
4212
|
+
} finally {
|
|
4213
|
+
if (isMountedRef.current) {
|
|
4214
|
+
setOverlayStatus(null);
|
|
4215
|
+
setOverlayError(null);
|
|
4216
|
+
setIsProcessing(false);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
}, [
|
|
4220
|
+
buttonProps,
|
|
4221
|
+
createSessionDraft,
|
|
4222
|
+
disabled,
|
|
4223
|
+
isProcessing,
|
|
4224
|
+
processResolvedSession,
|
|
4225
|
+
resolvedBillingUrl,
|
|
4226
|
+
sessionId,
|
|
4227
|
+
showError,
|
|
4228
|
+
showSuccess
|
|
4229
|
+
]);
|
|
4230
|
+
const handleFallbackComplete = useCallback5((result) => {
|
|
4231
|
+
const activeFallback = fallbackSessionRef.current;
|
|
4232
|
+
setFallbackSession(null);
|
|
4233
|
+
onSuccessRef.current?.({
|
|
4234
|
+
result,
|
|
4235
|
+
session: null,
|
|
4236
|
+
sessionId: activeFallback?.sessionId ?? null,
|
|
4237
|
+
autoCompleted: false
|
|
4238
|
+
});
|
|
4239
|
+
}, []);
|
|
4240
|
+
const handleFallbackError = useCallback5((error) => {
|
|
4241
|
+
onErrorRef.current?.(error);
|
|
4242
|
+
}, []);
|
|
4243
|
+
const handleFallbackDecline = useCallback5((decline) => {
|
|
4244
|
+
onDeclineRef.current?.(decline);
|
|
4245
|
+
}, []);
|
|
4246
|
+
useEffect7(() => {
|
|
4247
|
+
if (typeof window === "undefined" || resumeAttemptedRef.current) {
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
4250
|
+
const params = new URLSearchParams(window.location.search);
|
|
4251
|
+
const clientSecret = params.get("payment_intent_client_secret");
|
|
4252
|
+
if (!clientSecret) {
|
|
4253
|
+
return;
|
|
4254
|
+
}
|
|
4255
|
+
const resumeState = readPayPalResumeState();
|
|
4256
|
+
if (!resumeState) {
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
resumeAttemptedRef.current = true;
|
|
4260
|
+
void (async () => {
|
|
4261
|
+
setIsProcessing(true);
|
|
4262
|
+
setOverlayError(null);
|
|
4263
|
+
setOverlayStatus("processing");
|
|
4264
|
+
try {
|
|
4265
|
+
if (params.get("redirect_status") === "failed") {
|
|
4266
|
+
throw Object.assign(
|
|
4267
|
+
new FloPayError7("PayPal payment was declined. Please try again.", "api_error"),
|
|
4268
|
+
{ checkoutMethod: "paypal" }
|
|
4269
|
+
);
|
|
4270
|
+
}
|
|
4271
|
+
const {
|
|
4272
|
+
flopay,
|
|
4273
|
+
paypalFlopay
|
|
4274
|
+
} = await loadSavedPaymentProviders({
|
|
4275
|
+
publishableKey: resumeState.publishableKey,
|
|
4276
|
+
paypalPublishableKey: resumeState.paypalPublishableKey,
|
|
4277
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4278
|
+
locale
|
|
4279
|
+
});
|
|
4280
|
+
const paypalStripe = (paypalFlopay ?? flopay).getRawProvider();
|
|
4281
|
+
if (!paypalStripe) {
|
|
4282
|
+
throw Object.assign(
|
|
4283
|
+
new FloPayError7("PayPal is not available.", "api_error"),
|
|
4284
|
+
{ checkoutMethod: "paypal" }
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
|
|
4288
|
+
if (error) {
|
|
4289
|
+
throw Object.assign(
|
|
4290
|
+
new FloPayError7(
|
|
4291
|
+
error.message ?? "Failed to retrieve PayPal payment status.",
|
|
4292
|
+
"api_error",
|
|
4293
|
+
{ code: error.code }
|
|
4294
|
+
),
|
|
4295
|
+
{ checkoutMethod: "paypal" }
|
|
4296
|
+
);
|
|
4297
|
+
}
|
|
4298
|
+
if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
|
|
4299
|
+
await showSuccess({
|
|
4300
|
+
result: { status: "succeeded", paymentIntentId: paymentIntent.id },
|
|
4301
|
+
session: null,
|
|
4302
|
+
sessionId: resumeState.sessionId,
|
|
4303
|
+
autoCompleted: false
|
|
4304
|
+
});
|
|
4305
|
+
} else {
|
|
4306
|
+
throw Object.assign(
|
|
4307
|
+
new FloPayError7("PayPal payment was not completed. Please try again.", "api_error"),
|
|
4308
|
+
{ checkoutMethod: "paypal" }
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4311
|
+
} catch (err) {
|
|
4312
|
+
const floPayErr = normalizeSavedPaymentError(err);
|
|
4313
|
+
await showError(floPayErr, {
|
|
4314
|
+
emitDecline: true,
|
|
4315
|
+
method: floPayErr.checkoutMethod ?? "paypal"
|
|
4316
|
+
});
|
|
4317
|
+
} finally {
|
|
4318
|
+
clearPayPalResumeState();
|
|
4319
|
+
clearPayPalRedirectParams();
|
|
4320
|
+
if (isMountedRef.current) {
|
|
4321
|
+
setOverlayStatus(null);
|
|
4322
|
+
setOverlayError(null);
|
|
4323
|
+
setIsProcessing(false);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
})();
|
|
4327
|
+
}, [locale, resolvedBillingUrl, showError, showSuccess]);
|
|
4328
|
+
const bStyles = useMemo4(() => {
|
|
4329
|
+
const base = resolveButtonsLayoutTheme3(buttonsTheme);
|
|
4330
|
+
if (!stylesOverride) return base;
|
|
4331
|
+
return {
|
|
4332
|
+
...base,
|
|
4333
|
+
...stylesOverride,
|
|
4334
|
+
cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
|
|
4335
|
+
};
|
|
4336
|
+
}, [buttonsTheme, stylesOverride]);
|
|
4337
|
+
const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
|
|
4338
|
+
return /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
4339
|
+
/* @__PURE__ */ jsx9(
|
|
4340
|
+
"button",
|
|
4341
|
+
{
|
|
4342
|
+
...buttonProps,
|
|
4343
|
+
type,
|
|
4344
|
+
onClick: handleButtonClick,
|
|
4345
|
+
disabled: disabled || isProcessing,
|
|
4346
|
+
"aria-busy": isProcessing,
|
|
4347
|
+
style: {
|
|
4348
|
+
width: "100%",
|
|
4349
|
+
...cardButtonSizing,
|
|
4350
|
+
backgroundColor: "white",
|
|
4351
|
+
color: "#262833",
|
|
4352
|
+
border: "1px solid #d1d5db",
|
|
4353
|
+
borderRadius: "8px",
|
|
4354
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
4355
|
+
fontWeight: 600,
|
|
4356
|
+
cursor: disabled || isProcessing ? "not-allowed" : "pointer",
|
|
4357
|
+
display: "flex",
|
|
4358
|
+
alignItems: "center",
|
|
4359
|
+
justifyContent: "center",
|
|
4360
|
+
gap: "0.625rem",
|
|
4361
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
4362
|
+
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
4363
|
+
position: "relative",
|
|
4364
|
+
opacity: disabled || isProcessing ? 0.6 : 1,
|
|
4365
|
+
...bStyles.cardButton,
|
|
4366
|
+
...style
|
|
4367
|
+
},
|
|
4368
|
+
onMouseDown: (e) => {
|
|
4369
|
+
buttonProps.onMouseDown?.(e);
|
|
4370
|
+
if (!e.defaultPrevented) {
|
|
4371
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
4372
|
+
}
|
|
4373
|
+
},
|
|
4374
|
+
onMouseUp: (e) => {
|
|
4375
|
+
buttonProps.onMouseUp?.(e);
|
|
4376
|
+
if (!e.defaultPrevented) {
|
|
4377
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
4378
|
+
}
|
|
4379
|
+
},
|
|
4380
|
+
children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: children })
|
|
4381
|
+
}
|
|
4382
|
+
),
|
|
4383
|
+
overlayStatus && /* @__PURE__ */ jsx9(
|
|
4384
|
+
ProcessingOverlay,
|
|
4385
|
+
{
|
|
4386
|
+
status: overlayStatus,
|
|
4387
|
+
errorMessage: overlayError
|
|
4388
|
+
}
|
|
4389
|
+
),
|
|
4390
|
+
fallbackSession && /* @__PURE__ */ jsx9(
|
|
4391
|
+
"div",
|
|
4392
|
+
{
|
|
4393
|
+
"data-testid": "flopay-automatic-payment-fallback",
|
|
4394
|
+
role: "dialog",
|
|
4395
|
+
"aria-modal": "true",
|
|
4396
|
+
onClick: (event) => {
|
|
4397
|
+
if (event.target === event.currentTarget) {
|
|
4398
|
+
setFallbackSession(null);
|
|
4399
|
+
}
|
|
4400
|
+
},
|
|
4401
|
+
style: {
|
|
4402
|
+
position: "fixed",
|
|
4403
|
+
inset: 0,
|
|
4404
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
4405
|
+
display: "flex",
|
|
4406
|
+
alignItems: "center",
|
|
4407
|
+
justifyContent: "center",
|
|
4408
|
+
padding: "1.5rem",
|
|
4409
|
+
zIndex: 1100
|
|
4410
|
+
},
|
|
4411
|
+
children: /* @__PURE__ */ jsxs7(
|
|
4412
|
+
"div",
|
|
4413
|
+
{
|
|
4414
|
+
style: {
|
|
4415
|
+
width: "100%",
|
|
4416
|
+
maxWidth: 520,
|
|
4417
|
+
maxHeight: "90vh",
|
|
4418
|
+
overflowY: "auto",
|
|
4419
|
+
background: "white",
|
|
4420
|
+
borderRadius: 20,
|
|
4421
|
+
padding: "1.5rem",
|
|
4422
|
+
boxShadow: "0 24px 80px rgba(15, 23, 42, 0.28)",
|
|
4423
|
+
display: "flex",
|
|
4424
|
+
flexDirection: "column",
|
|
4425
|
+
gap: "1rem"
|
|
4426
|
+
},
|
|
4427
|
+
children: [
|
|
4428
|
+
/* @__PURE__ */ jsx9(
|
|
4429
|
+
"div",
|
|
4430
|
+
{
|
|
4431
|
+
style: {
|
|
4432
|
+
display: "flex",
|
|
4433
|
+
gap: "1rem",
|
|
4434
|
+
alignItems: "flex-start"
|
|
4435
|
+
},
|
|
4436
|
+
children: /* @__PURE__ */ jsx9(
|
|
4437
|
+
"div",
|
|
4438
|
+
{
|
|
4439
|
+
style: {
|
|
4440
|
+
flex: 1,
|
|
4441
|
+
background: "#fef2f2",
|
|
4442
|
+
color: "#b91c1c",
|
|
4443
|
+
border: "1px solid #fecaca",
|
|
4444
|
+
borderRadius: "12px",
|
|
4445
|
+
padding: "0.75rem 1rem",
|
|
4446
|
+
fontSize: "0.9rem",
|
|
4447
|
+
lineHeight: 1.5
|
|
4448
|
+
},
|
|
4449
|
+
children: fallbackSession.errorMessage
|
|
4450
|
+
}
|
|
4451
|
+
)
|
|
4452
|
+
}
|
|
4453
|
+
),
|
|
4454
|
+
/* @__PURE__ */ jsx9(
|
|
4455
|
+
FloPayCheckout,
|
|
4456
|
+
{
|
|
4457
|
+
sessionId: fallbackSession.sessionId,
|
|
4458
|
+
checkoutMode: "full",
|
|
4459
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4460
|
+
fallbackPublishableKey,
|
|
4461
|
+
cardTitleContent: null,
|
|
4462
|
+
showSecurityFooter: false,
|
|
4463
|
+
onComplete: handleFallbackComplete,
|
|
4464
|
+
onError: handleFallbackError,
|
|
4465
|
+
onDecline: handleFallbackDecline
|
|
4466
|
+
}
|
|
4467
|
+
)
|
|
4468
|
+
]
|
|
4469
|
+
}
|
|
4470
|
+
)
|
|
4471
|
+
}
|
|
4472
|
+
)
|
|
4473
|
+
] });
|
|
4474
|
+
}
|
|
3242
4475
|
export {
|
|
3243
4476
|
AddressElement,
|
|
3244
4477
|
CardCvcElement,
|
|
@@ -3246,6 +4479,7 @@ export {
|
|
|
3246
4479
|
CardExpiryElement,
|
|
3247
4480
|
CardNumberElement,
|
|
3248
4481
|
CheckoutForm,
|
|
4482
|
+
FloPayAutomaticPaymentButton,
|
|
3249
4483
|
FloPayCheckout,
|
|
3250
4484
|
FloPayProvider,
|
|
3251
4485
|
PayPalButton,
|