@flopay/react 0.4.3 → 0.4.5
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 +1836 -640
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.mjs +1725 -530
- 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,12 +2392,14 @@ function FloPayCheckout({
|
|
|
1839
2392
|
cardButtonContent,
|
|
1840
2393
|
cardBackButtonContent,
|
|
1841
2394
|
cardTitleContent,
|
|
2395
|
+
showSecurityFooter = true,
|
|
1842
2396
|
onButtonClick,
|
|
1843
2397
|
onBeforeButtonClick,
|
|
1844
2398
|
enableAVS,
|
|
1845
2399
|
avsLayout,
|
|
1846
2400
|
submitLabel,
|
|
1847
2401
|
className,
|
|
2402
|
+
initialErrorMessage = null,
|
|
1848
2403
|
children,
|
|
1849
2404
|
checkoutMode: checkoutModeProp,
|
|
1850
2405
|
confirmLabel,
|
|
@@ -1861,11 +2416,15 @@ function FloPayCheckout({
|
|
|
1861
2416
|
const paypalFlopayRef = useRef3(null);
|
|
1862
2417
|
const [session, setSession] = useState3(null);
|
|
1863
2418
|
const [resolvedSessionId, setResolvedSessionId] = useState3(sessionIdProp ?? "");
|
|
2419
|
+
const activeSessionId = sessionIdProp ?? resolvedSessionId;
|
|
2420
|
+
const initSessionDependency = createSessionParams ? "" : activeSessionId;
|
|
1864
2421
|
const [isLoading, setIsLoading] = useState3(true);
|
|
1865
2422
|
const [loadError, setLoadError] = useState3(null);
|
|
1866
2423
|
const [currentMode, setCurrentMode] = useState3("full");
|
|
1867
2424
|
const [confirmProcessing, setConfirmProcessing] = useState3(false);
|
|
1868
|
-
const [modeError, setModeError] = useState3(
|
|
2425
|
+
const [modeError, setModeError] = useState3(initialErrorMessage);
|
|
2426
|
+
const [modeOverlayStatus, setModeOverlayStatus] = useState3(null);
|
|
2427
|
+
const [modeOverlayError, setModeOverlayError] = useState3(null);
|
|
1869
2428
|
const [createSessionPatch, setCreateSessionPatch] = useState3(void 0);
|
|
1870
2429
|
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState3("");
|
|
1871
2430
|
const [cardBootstrapPending, setCardBootstrapPending] = useState3(false);
|
|
@@ -1894,125 +2453,84 @@ function FloPayCheckout({
|
|
|
1894
2453
|
() => createSessionPatchBaseHash === baseCreateSessionHash ? createSessionPatch : void 0,
|
|
1895
2454
|
[createSessionPatch, createSessionPatchBaseHash, baseCreateSessionHash]
|
|
1896
2455
|
);
|
|
1897
|
-
const
|
|
2456
|
+
const effectiveCreateSessionBase = useMemo3(
|
|
1898
2457
|
() => createSessionParams ? mergeInlineSessionPatch(createSessionParams, activeCreateSessionPatch) : void 0,
|
|
1899
2458
|
[createSessionParams, activeCreateSessionPatch]
|
|
1900
2459
|
);
|
|
2460
|
+
const effectiveCreateSessionMode = checkoutModeProp ?? effectiveCreateSessionBase?.checkoutMode ?? "full";
|
|
2461
|
+
const effectiveCreateSession = useMemo3(
|
|
2462
|
+
() => effectiveCreateSessionBase ? {
|
|
2463
|
+
...effectiveCreateSessionBase,
|
|
2464
|
+
checkoutMode: effectiveCreateSessionMode
|
|
2465
|
+
} : void 0,
|
|
2466
|
+
[effectiveCreateSessionBase, effectiveCreateSessionMode]
|
|
2467
|
+
);
|
|
1901
2468
|
useEffect4(() => {
|
|
1902
2469
|
setCreateSessionPatch(void 0);
|
|
1903
2470
|
setCreateSessionPatchBaseHash(baseCreateSessionHash);
|
|
1904
2471
|
}, [baseCreateSessionHash]);
|
|
2472
|
+
useEffect4(() => {
|
|
2473
|
+
setModeError(initialErrorMessage);
|
|
2474
|
+
}, [initialErrorMessage]);
|
|
1905
2475
|
const emitDecline = useCallback2(
|
|
1906
2476
|
(method, input, overrides) => {
|
|
1907
2477
|
onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
|
|
1908
2478
|
},
|
|
1909
2479
|
[]
|
|
1910
2480
|
);
|
|
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
|
|
2481
|
+
const runSavedPaymentFlow = useCallback2(
|
|
2482
|
+
async (sess, options) => {
|
|
2483
|
+
setModeError(null);
|
|
2484
|
+
setModeOverlayError(null);
|
|
2485
|
+
setModeOverlayStatus("processing");
|
|
2486
|
+
const activeSessionId2 = options?.sessionId ?? sess.id;
|
|
2487
|
+
try {
|
|
2488
|
+
const result = await processSavedPaymentForMode({
|
|
2489
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2490
|
+
sessionId: activeSessionId2,
|
|
2491
|
+
session: sess
|
|
1972
2492
|
});
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2493
|
+
const paymentResult = result.type === "success" ? result.result : await handleSavedPaymentRedirectResult(result, {
|
|
2494
|
+
flopay: flopayRef.current,
|
|
2495
|
+
paypalFlopay: paypalFlopayRef.current,
|
|
2496
|
+
attempt3DS: options?.attempt3DS,
|
|
2497
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2498
|
+
sessionId: activeSessionId2,
|
|
2499
|
+
session: sess
|
|
2500
|
+
});
|
|
2501
|
+
setModeOverlayStatus("success");
|
|
2502
|
+
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
2503
|
+
onCompleteRef.current?.(paymentResult);
|
|
2504
|
+
return true;
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
const floPayErr = normalizeSavedPaymentError(err);
|
|
2507
|
+
const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
|
|
2508
|
+
setModeError(floPayErr.message);
|
|
2509
|
+
setModeOverlayError(floPayErr.message);
|
|
2510
|
+
if (options?.fallbackToFull) {
|
|
2511
|
+
setCurrentMode("full");
|
|
1983
2512
|
}
|
|
2513
|
+
onErrorRef.current?.(floPayErr);
|
|
2514
|
+
emitDecline(method, floPayErr, {
|
|
2515
|
+
code: floPayErr.code,
|
|
2516
|
+
declineCode: floPayErr.declineCode
|
|
2517
|
+
});
|
|
2518
|
+
setModeOverlayStatus("error");
|
|
2519
|
+
await Promise.allSettled([
|
|
2520
|
+
sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
|
|
2521
|
+
options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
|
|
2522
|
+
]);
|
|
1984
2523
|
return false;
|
|
2524
|
+
} finally {
|
|
2525
|
+
setModeOverlayStatus(null);
|
|
2526
|
+
setModeOverlayError(null);
|
|
1985
2527
|
}
|
|
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
2528
|
},
|
|
2015
|
-
[
|
|
2529
|
+
[
|
|
2530
|
+
emitDecline,
|
|
2531
|
+
normalizeSavedPaymentError,
|
|
2532
|
+
resolvedBillingUrl
|
|
2533
|
+
]
|
|
2016
2534
|
);
|
|
2017
2535
|
const inflightRef = useRef3(/* @__PURE__ */ new Map());
|
|
2018
2536
|
const initializedHashRef = useRef3(null);
|
|
@@ -2044,8 +2562,14 @@ function FloPayCheckout({
|
|
|
2044
2562
|
useEffect4(() => {
|
|
2045
2563
|
setResolvedSessionId(sessionIdProp ?? "");
|
|
2046
2564
|
}, [sessionIdProp]);
|
|
2565
|
+
useEffect4(() => {
|
|
2566
|
+
autoCheckoutAttempted.current = false;
|
|
2567
|
+
setModeError(initialErrorMessage);
|
|
2568
|
+
setModeOverlayError(null);
|
|
2569
|
+
setModeOverlayStatus(null);
|
|
2570
|
+
}, [createSessionHash, initialErrorMessage, sessionIdProp]);
|
|
2047
2571
|
async function resolveInlineSession(params, cacheKey) {
|
|
2048
|
-
const api = new
|
|
2572
|
+
const api = new PaymentAPI2(resolvedBillingUrl);
|
|
2049
2573
|
let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
|
|
2050
2574
|
let realResult = null;
|
|
2051
2575
|
if (sid) {
|
|
@@ -2074,7 +2598,7 @@ function FloPayCheckout({
|
|
|
2074
2598
|
async (patch) => {
|
|
2075
2599
|
const baseParams = createSessionParamsRef.current;
|
|
2076
2600
|
if (!baseParams) {
|
|
2077
|
-
throw new
|
|
2601
|
+
throw new FloPayError4("createSession is required to bootstrap checkout.", "validation_error");
|
|
2078
2602
|
}
|
|
2079
2603
|
const mergedParams = mergeInlineSessionPatch(baseParams, patch);
|
|
2080
2604
|
const cacheKey = hashCreateParams(mergedParams);
|
|
@@ -2103,43 +2627,23 @@ function FloPayCheckout({
|
|
|
2103
2627
|
if (sid) {
|
|
2104
2628
|
setResolvedSessionId(sid);
|
|
2105
2629
|
}
|
|
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
|
-
]);
|
|
2630
|
+
const {
|
|
2631
|
+
publishableKey,
|
|
2632
|
+
paypalPublishableKey
|
|
2633
|
+
} = resolveSavedPaymentPublishableKeys(realResult, fallbackPublishableKey);
|
|
2634
|
+
const {
|
|
2635
|
+
flopay: instance,
|
|
2636
|
+
paypalFlopay: paypalInstance
|
|
2637
|
+
} = await loadSavedPaymentProviders({
|
|
2638
|
+
publishableKey,
|
|
2639
|
+
paypalPublishableKey,
|
|
2640
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2641
|
+
locale
|
|
2642
|
+
});
|
|
2133
2643
|
flopayRef.current = instance;
|
|
2134
2644
|
setFloPay(instance);
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
paypalFlopayRef.current = paypalInstance;
|
|
2138
|
-
setPaypalFloPay(paypalInstance);
|
|
2139
|
-
} else {
|
|
2140
|
-
paypalFlopayRef.current = null;
|
|
2141
|
-
setPaypalFloPay(null);
|
|
2142
|
-
}
|
|
2645
|
+
paypalFlopayRef.current = paypalInstance;
|
|
2646
|
+
setPaypalFloPay(paypalInstance);
|
|
2143
2647
|
setIsLoading(false);
|
|
2144
2648
|
} catch (err) {
|
|
2145
2649
|
if (initializedHashRef.current === cacheKey) {
|
|
@@ -2178,17 +2682,57 @@ function FloPayCheckout({
|
|
|
2178
2682
|
let cancelled = false;
|
|
2179
2683
|
setLoadError(null);
|
|
2180
2684
|
if (createSessionHash) {
|
|
2181
|
-
if (initializedHashRef.current === createSessionHash) return;
|
|
2182
2685
|
const params = createSessionParamsRef.current;
|
|
2183
2686
|
setSession(buildSyntheticSession(params, checkoutModeProp));
|
|
2184
|
-
setCurrentMode(
|
|
2687
|
+
setCurrentMode(effectiveCreateSessionMode);
|
|
2185
2688
|
setIsLoading(false);
|
|
2689
|
+
if (initializedHashRef.current === createSessionHash) {
|
|
2690
|
+
return () => {
|
|
2691
|
+
cancelled = true;
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2186
2694
|
(async () => {
|
|
2695
|
+
const shouldAutoProcessInlineSession = effectiveCreateSessionMode === "auto" && !autoCheckoutAttempted.current;
|
|
2696
|
+
if (shouldAutoProcessInlineSession) {
|
|
2697
|
+
setModeError(null);
|
|
2698
|
+
setModeOverlayError(null);
|
|
2699
|
+
setModeOverlayStatus("processing");
|
|
2700
|
+
}
|
|
2187
2701
|
try {
|
|
2188
|
-
await bootstrapInlineSession();
|
|
2702
|
+
const resolved = await bootstrapInlineSession();
|
|
2703
|
+
const resolvedSession = resolved.result.data.session ?? null;
|
|
2704
|
+
if (!cancelled && shouldAutoProcessInlineSession && resolvedSession) {
|
|
2705
|
+
autoCheckoutAttempted.current = true;
|
|
2706
|
+
await runSavedPaymentFlow(resolvedSession, {
|
|
2707
|
+
attempt3DS: true,
|
|
2708
|
+
fallbackToFull: true,
|
|
2709
|
+
sessionId: resolved.sid || resolvedSession.id
|
|
2710
|
+
});
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
if (!cancelled && shouldAutoProcessInlineSession) {
|
|
2714
|
+
setModeOverlayStatus(null);
|
|
2715
|
+
setModeOverlayError(null);
|
|
2716
|
+
}
|
|
2189
2717
|
} catch (err) {
|
|
2190
2718
|
if (cancelled) return;
|
|
2191
|
-
const
|
|
2719
|
+
const errorCode = err instanceof FloPayError4 ? err.code : typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
2720
|
+
if (shouldAutoProcessInlineSession && errorCode === "session_auto_completed") {
|
|
2721
|
+
autoCheckoutAttempted.current = true;
|
|
2722
|
+
setModeOverlayStatus("success");
|
|
2723
|
+
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
2724
|
+
if (!cancelled) {
|
|
2725
|
+
onCompleteRef.current?.({ status: "succeeded" });
|
|
2726
|
+
setModeOverlayStatus(null);
|
|
2727
|
+
setModeOverlayError(null);
|
|
2728
|
+
}
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
if (shouldAutoProcessInlineSession) {
|
|
2732
|
+
setModeOverlayStatus(null);
|
|
2733
|
+
setModeOverlayError(null);
|
|
2734
|
+
}
|
|
2735
|
+
const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to create session", "api_error");
|
|
2192
2736
|
setLoadError(floPayErr);
|
|
2193
2737
|
}
|
|
2194
2738
|
})();
|
|
@@ -2199,14 +2743,14 @@ function FloPayCheckout({
|
|
|
2199
2743
|
setIsLoading(true);
|
|
2200
2744
|
async function init() {
|
|
2201
2745
|
try {
|
|
2202
|
-
const api = new
|
|
2203
|
-
const result = await api.getUnifiedCheckoutSession(
|
|
2746
|
+
const api = new PaymentAPI2(resolvedBillingUrl);
|
|
2747
|
+
const result = await api.getUnifiedCheckoutSession(activeSessionId);
|
|
2204
2748
|
if (cancelled) return;
|
|
2205
2749
|
setUnified(result);
|
|
2206
2750
|
const sess = result.data.session ?? null;
|
|
2207
2751
|
setSession(sess);
|
|
2208
2752
|
if (!sess) {
|
|
2209
|
-
throw new
|
|
2753
|
+
throw new FloPayError4("No session data returned", "api_error");
|
|
2210
2754
|
}
|
|
2211
2755
|
if (sess.status === "complete") {
|
|
2212
2756
|
setIsLoading(false);
|
|
@@ -2220,22 +2764,20 @@ function FloPayCheckout({
|
|
|
2220
2764
|
autoCheckoutAttempted.current = true;
|
|
2221
2765
|
const stripeInitPromise = initStripe(result);
|
|
2222
2766
|
try {
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2767
|
+
await runSavedPaymentFlow(sess, {
|
|
2768
|
+
attempt3DS: true,
|
|
2769
|
+
fallbackToFull: true,
|
|
2770
|
+
ensureProvidersReady: () => stripeInitPromise,
|
|
2771
|
+
sessionId: activeSessionId || sess.id
|
|
2772
|
+
});
|
|
2228
2773
|
if (cancelled) return;
|
|
2229
2774
|
await stripeInitPromise;
|
|
2230
|
-
const handled = await handleRedirectResult(redirectResult, sess, { attempt3DS: true });
|
|
2231
2775
|
if (!cancelled) {
|
|
2232
|
-
if (!handled) setCurrentMode("full");
|
|
2233
2776
|
setIsLoading(false);
|
|
2234
2777
|
}
|
|
2235
2778
|
return;
|
|
2236
2779
|
} catch {
|
|
2237
2780
|
if (cancelled) return;
|
|
2238
|
-
setCurrentMode("full");
|
|
2239
2781
|
await stripeInitPromise;
|
|
2240
2782
|
if (!cancelled) setIsLoading(false);
|
|
2241
2783
|
return;
|
|
@@ -2245,81 +2787,55 @@ function FloPayCheckout({
|
|
|
2245
2787
|
if (!cancelled) setIsLoading(false);
|
|
2246
2788
|
} catch (err) {
|
|
2247
2789
|
if (cancelled) return;
|
|
2248
|
-
const floPayErr = err instanceof
|
|
2790
|
+
const floPayErr = err instanceof FloPayError4 ? err : new FloPayError4(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
2249
2791
|
setLoadError(floPayErr);
|
|
2250
2792
|
setIsLoading(false);
|
|
2251
2793
|
}
|
|
2252
2794
|
}
|
|
2253
2795
|
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
|
-
]);
|
|
2796
|
+
const {
|
|
2797
|
+
publishableKey,
|
|
2798
|
+
paypalPublishableKey
|
|
2799
|
+
} = resolveSavedPaymentPublishableKeys(result, fallbackPublishableKey);
|
|
2800
|
+
const {
|
|
2801
|
+
flopay: instance,
|
|
2802
|
+
paypalFlopay: paypalInstance
|
|
2803
|
+
} = await loadSavedPaymentProviders({
|
|
2804
|
+
publishableKey,
|
|
2805
|
+
paypalPublishableKey,
|
|
2806
|
+
billingApiUrl: resolvedBillingUrl,
|
|
2807
|
+
locale
|
|
2808
|
+
});
|
|
2281
2809
|
flopayRef.current = instance;
|
|
2282
2810
|
setFloPay(instance);
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
paypalFlopayRef.current = paypalInstance;
|
|
2286
|
-
setPaypalFloPay(paypalInstance);
|
|
2287
|
-
} else {
|
|
2288
|
-
paypalFlopayRef.current = null;
|
|
2289
|
-
setPaypalFloPay(null);
|
|
2290
|
-
}
|
|
2811
|
+
paypalFlopayRef.current = paypalInstance;
|
|
2812
|
+
setPaypalFloPay(paypalInstance);
|
|
2291
2813
|
}
|
|
2292
2814
|
init();
|
|
2293
2815
|
return () => {
|
|
2294
2816
|
cancelled = true;
|
|
2295
2817
|
};
|
|
2296
|
-
}, [
|
|
2818
|
+
}, [
|
|
2819
|
+
bootstrapInlineSession,
|
|
2820
|
+
checkoutModeProp,
|
|
2821
|
+
createSessionHash,
|
|
2822
|
+
effectiveCreateSessionMode,
|
|
2823
|
+
initSessionDependency,
|
|
2824
|
+
runSavedPaymentFlow
|
|
2825
|
+
]);
|
|
2297
2826
|
const handleConfirmCheckout = useCallback2(async () => {
|
|
2298
2827
|
if (confirmProcessing || !session) return;
|
|
2299
2828
|
setConfirmProcessing(true);
|
|
2300
2829
|
setModeError(null);
|
|
2301
2830
|
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");
|
|
2831
|
+
await runSavedPaymentFlow(session, {
|
|
2832
|
+
fallbackToFull: true,
|
|
2833
|
+
sessionId: activeSessionId || session.id
|
|
2834
|
+
});
|
|
2319
2835
|
} finally {
|
|
2320
2836
|
setConfirmProcessing(false);
|
|
2321
2837
|
}
|
|
2322
|
-
}, [
|
|
2838
|
+
}, [activeSessionId, confirmProcessing, runSavedPaymentFlow, session]);
|
|
2323
2839
|
const providerOptions = useMemo3(() => {
|
|
2324
2840
|
if (!unified || !session) return void 0;
|
|
2325
2841
|
const opts = {
|
|
@@ -2337,7 +2853,7 @@ function FloPayCheckout({
|
|
|
2337
2853
|
return opts;
|
|
2338
2854
|
}, [unified, session, appearance, resolvedBillingUrl]);
|
|
2339
2855
|
const shouldHandleInlineSessionPatch = Boolean(
|
|
2340
|
-
createSessionParams && !children && layout === "buttons" && onBeforeButtonClick &&
|
|
2856
|
+
createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && effectiveCreateSessionMode === "full"
|
|
2341
2857
|
);
|
|
2342
2858
|
const checkoutValue = useMemo3(
|
|
2343
2859
|
() => ({
|
|
@@ -2361,37 +2877,55 @@ function FloPayCheckout({
|
|
|
2361
2877
|
]
|
|
2362
2878
|
);
|
|
2363
2879
|
const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
|
|
2880
|
+
const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx6(
|
|
2881
|
+
ProcessingOverlay,
|
|
2882
|
+
{
|
|
2883
|
+
status: modeOverlayStatus,
|
|
2884
|
+
errorMessage: modeOverlayError
|
|
2885
|
+
}
|
|
2886
|
+
) : null;
|
|
2364
2887
|
if (isLoading) {
|
|
2365
|
-
if (loadingNode)
|
|
2888
|
+
if (loadingNode) {
|
|
2889
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2890
|
+
loadingNode,
|
|
2891
|
+
modeOverlay
|
|
2892
|
+
] });
|
|
2893
|
+
}
|
|
2366
2894
|
if (layout === "buttons") {
|
|
2367
|
-
const skeletonBar = (h) => /* @__PURE__ */
|
|
2895
|
+
const skeletonBar = (h) => /* @__PURE__ */ jsx6("div", { style: {
|
|
2368
2896
|
height: h,
|
|
2369
2897
|
borderRadius: 8,
|
|
2370
2898
|
background: "#e5e7eb",
|
|
2371
2899
|
animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
|
|
2372
2900
|
} });
|
|
2373
|
-
return /* @__PURE__ */
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2901
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2902
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2903
|
+
showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2904
|
+
(showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2905
|
+
skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2906
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
2907
|
+
] }),
|
|
2908
|
+
modeOverlay
|
|
2378
2909
|
] });
|
|
2379
2910
|
}
|
|
2380
|
-
return /* @__PURE__ */
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2911
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2912
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
|
|
2913
|
+
/* @__PURE__ */ jsx6("div", { style: {
|
|
2914
|
+
width: 24,
|
|
2915
|
+
height: 24,
|
|
2916
|
+
border: "2px solid #e5e7eb",
|
|
2917
|
+
borderTopColor: "#6b7280",
|
|
2918
|
+
borderRadius: "50%",
|
|
2919
|
+
animation: "spin 0.6s linear infinite"
|
|
2920
|
+
} }),
|
|
2921
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
|
|
2922
|
+
] }),
|
|
2923
|
+
modeOverlay
|
|
2390
2924
|
] });
|
|
2391
2925
|
}
|
|
2392
2926
|
if (loadError) {
|
|
2393
|
-
if (errorNode) return /* @__PURE__ */
|
|
2394
|
-
return /* @__PURE__ */
|
|
2927
|
+
if (errorNode) return /* @__PURE__ */ jsx6(Fragment3, { children: errorNode(loadError) });
|
|
2928
|
+
return /* @__PURE__ */ jsx6(
|
|
2395
2929
|
"div",
|
|
2396
2930
|
{
|
|
2397
2931
|
style: {
|
|
@@ -2405,90 +2939,100 @@ function FloPayCheckout({
|
|
|
2405
2939
|
);
|
|
2406
2940
|
}
|
|
2407
2941
|
if (shouldShowInterimButtons) {
|
|
2408
|
-
return /* @__PURE__ */
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2942
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2943
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(
|
|
2944
|
+
InterimButtonsView,
|
|
2945
|
+
{
|
|
2946
|
+
onButtonClick,
|
|
2947
|
+
showPayPal,
|
|
2948
|
+
showApplePay,
|
|
2949
|
+
showGooglePay,
|
|
2950
|
+
buttonsTheme,
|
|
2951
|
+
buttonsStyles,
|
|
2952
|
+
cardButtonContent,
|
|
2953
|
+
cardBackButtonContent,
|
|
2954
|
+
cardTitleContent
|
|
2955
|
+
}
|
|
2956
|
+
) }),
|
|
2957
|
+
modeOverlay
|
|
2958
|
+
] });
|
|
2959
|
+
}
|
|
2960
|
+
if (!flopay || !providerOptions) {
|
|
2961
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: modeOverlay });
|
|
2422
2962
|
}
|
|
2423
|
-
if (!flopay || !providerOptions) return /* @__PURE__ */ jsx5(Fragment3, {});
|
|
2424
2963
|
if (currentMode === "confirm") {
|
|
2425
|
-
return /* @__PURE__ */
|
|
2426
|
-
|
|
2964
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2965
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs4("div", { className, children: [
|
|
2966
|
+
modeError && /* @__PURE__ */ jsx6(
|
|
2967
|
+
"div",
|
|
2968
|
+
{
|
|
2969
|
+
style: {
|
|
2970
|
+
color: "#dc2626",
|
|
2971
|
+
fontSize: "0.875rem",
|
|
2972
|
+
marginBottom: "0.75rem",
|
|
2973
|
+
textAlign: "center"
|
|
2974
|
+
},
|
|
2975
|
+
children: modeError
|
|
2976
|
+
}
|
|
2977
|
+
),
|
|
2978
|
+
renderConfirmButton ? renderConfirmButton({
|
|
2979
|
+
onConfirm: handleConfirmCheckout,
|
|
2980
|
+
isProcessing: confirmProcessing
|
|
2981
|
+
}) : /* @__PURE__ */ jsx6(
|
|
2982
|
+
"button",
|
|
2983
|
+
{
|
|
2984
|
+
type: "button",
|
|
2985
|
+
onClick: handleConfirmCheckout,
|
|
2986
|
+
disabled: confirmProcessing,
|
|
2987
|
+
style: {
|
|
2988
|
+
width: "100%",
|
|
2989
|
+
padding: "0.875rem",
|
|
2990
|
+
backgroundColor: "#4A49FF",
|
|
2991
|
+
color: "white",
|
|
2992
|
+
border: "none",
|
|
2993
|
+
borderRadius: "8px",
|
|
2994
|
+
fontSize: "1rem",
|
|
2995
|
+
fontWeight: 600,
|
|
2996
|
+
cursor: confirmProcessing ? "not-allowed" : "pointer",
|
|
2997
|
+
opacity: confirmProcessing ? 0.6 : 1
|
|
2998
|
+
},
|
|
2999
|
+
children: confirmProcessing ? "Processing..." : confirmLabel ?? "Confirm Purchase"
|
|
3000
|
+
}
|
|
3001
|
+
)
|
|
3002
|
+
] }) }) }),
|
|
3003
|
+
modeOverlay
|
|
3004
|
+
] });
|
|
3005
|
+
}
|
|
3006
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3007
|
+
/* @__PURE__ */ jsx6(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx6(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3008
|
+
modeError && /* @__PURE__ */ jsx6(
|
|
2427
3009
|
"div",
|
|
2428
3010
|
{
|
|
2429
3011
|
style: {
|
|
2430
|
-
|
|
2431
|
-
fontSize: "0.875rem",
|
|
3012
|
+
padding: "0.75rem 1rem",
|
|
2432
3013
|
marginBottom: "0.75rem",
|
|
2433
|
-
|
|
3014
|
+
backgroundColor: "#FEF3C7",
|
|
3015
|
+
border: "1px solid #F59E0B",
|
|
3016
|
+
borderRadius: "8px",
|
|
3017
|
+
color: "#92400E",
|
|
3018
|
+
fontSize: "0.875rem"
|
|
2434
3019
|
},
|
|
2435
3020
|
children: modeError
|
|
2436
3021
|
}
|
|
2437
3022
|
),
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
isProcessing: confirmProcessing
|
|
2441
|
-
}) : /* @__PURE__ */ jsx5(
|
|
2442
|
-
"button",
|
|
3023
|
+
/* @__PURE__ */ jsx6(
|
|
3024
|
+
SessionInjector,
|
|
2443
3025
|
{
|
|
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"
|
|
3026
|
+
sessionId: activeSessionId,
|
|
3027
|
+
billingApiUrl: resolvedBillingUrl,
|
|
3028
|
+
session,
|
|
3029
|
+
children
|
|
2460
3030
|
}
|
|
2461
3031
|
)
|
|
2462
|
-
] })
|
|
2463
|
-
}
|
|
2464
|
-
return /* @__PURE__ */ jsx5(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs3(FloPayProvider, { flopay, paypalFlopay, 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(
|
|
3032
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
2489
3033
|
SplitCardForm,
|
|
2490
3034
|
{
|
|
2491
|
-
sessionId:
|
|
3035
|
+
sessionId: activeSessionId,
|
|
2492
3036
|
email: session?.customer?.email,
|
|
2493
3037
|
userId: session?.customer?.id,
|
|
2494
3038
|
firstName: session?.customer?.firstName,
|
|
@@ -2498,6 +3042,8 @@ function FloPayCheckout({
|
|
|
2498
3042
|
onComplete,
|
|
2499
3043
|
onError,
|
|
2500
3044
|
onDecline,
|
|
3045
|
+
error: modeError,
|
|
3046
|
+
onErrorChange: setModeError,
|
|
2501
3047
|
onFullNameChange,
|
|
2502
3048
|
showPayPal,
|
|
2503
3049
|
showApplePay,
|
|
@@ -2508,6 +3054,7 @@ function FloPayCheckout({
|
|
|
2508
3054
|
cardButtonContent,
|
|
2509
3055
|
cardBackButtonContent,
|
|
2510
3056
|
cardTitleContent,
|
|
3057
|
+
showSecurityFooter,
|
|
2511
3058
|
onButtonClick,
|
|
2512
3059
|
onBeforeButtonClick,
|
|
2513
3060
|
enableAVS,
|
|
@@ -2518,8 +3065,9 @@ function FloPayCheckout({
|
|
|
2518
3065
|
submitLabel,
|
|
2519
3066
|
className
|
|
2520
3067
|
}
|
|
2521
|
-
)
|
|
2522
|
-
|
|
3068
|
+
) }) }),
|
|
3069
|
+
modeOverlay
|
|
3070
|
+
] });
|
|
2523
3071
|
}
|
|
2524
3072
|
function SessionInjector({
|
|
2525
3073
|
sessionId,
|
|
@@ -2527,8 +3075,8 @@ function SessionInjector({
|
|
|
2527
3075
|
session,
|
|
2528
3076
|
children
|
|
2529
3077
|
}) {
|
|
2530
|
-
return /* @__PURE__ */
|
|
2531
|
-
if (!
|
|
3078
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: React6.Children.map(children, (child) => {
|
|
3079
|
+
if (!React6.isValidElement(child)) return child;
|
|
2532
3080
|
const existing = child.props;
|
|
2533
3081
|
const injected = {};
|
|
2534
3082
|
if (!existing.sessionId) injected.sessionId = sessionId;
|
|
@@ -2542,7 +3090,7 @@ function SessionInjector({
|
|
|
2542
3090
|
injected.lastName = session.customer.lastName;
|
|
2543
3091
|
}
|
|
2544
3092
|
if (Object.keys(injected).length === 0) return child;
|
|
2545
|
-
return
|
|
3093
|
+
return React6.cloneElement(child, injected);
|
|
2546
3094
|
}) });
|
|
2547
3095
|
}
|
|
2548
3096
|
function InterimButtonsView({
|
|
@@ -2581,7 +3129,7 @@ function InterimButtonsView({
|
|
|
2581
3129
|
title: { ...base.title, ...stylesOverride.title }
|
|
2582
3130
|
};
|
|
2583
3131
|
}, [buttonsTheme, stylesOverride]);
|
|
2584
|
-
const skeleton = (h) => /* @__PURE__ */
|
|
3132
|
+
const skeleton = (h) => /* @__PURE__ */ jsx6("div", { style: {
|
|
2585
3133
|
height: h,
|
|
2586
3134
|
borderRadius: 8,
|
|
2587
3135
|
background: "#e5e7eb",
|
|
@@ -2593,15 +3141,15 @@ function InterimButtonsView({
|
|
|
2593
3141
|
const inputBg = bStyles.cardInputBackground ?? "white";
|
|
2594
3142
|
const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
|
|
2595
3143
|
const hideTitle = isEmptySlotContent(cardTitleContent);
|
|
2596
|
-
return /* @__PURE__ */
|
|
3144
|
+
return /* @__PURE__ */ jsxs4("div", { style: {
|
|
2597
3145
|
backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
|
|
2598
3146
|
borderRadius: "8px",
|
|
2599
3147
|
animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
|
|
2600
3148
|
overflow: "hidden",
|
|
2601
3149
|
...bStyles.cardFormContainer
|
|
2602
3150
|
}, children: [
|
|
2603
|
-
/* @__PURE__ */
|
|
2604
|
-
/* @__PURE__ */
|
|
3151
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
|
|
3152
|
+
/* @__PURE__ */ jsxs4(
|
|
2605
3153
|
"button",
|
|
2606
3154
|
{
|
|
2607
3155
|
type: "button",
|
|
@@ -2628,7 +3176,7 @@ function InterimButtonsView({
|
|
|
2628
3176
|
...bStyles.backButton
|
|
2629
3177
|
},
|
|
2630
3178
|
children: [
|
|
2631
|
-
/* @__PURE__ */
|
|
3179
|
+
/* @__PURE__ */ jsx6("span", { style: {
|
|
2632
3180
|
display: "inline-flex",
|
|
2633
3181
|
alignItems: "center",
|
|
2634
3182
|
justifyContent: "center",
|
|
@@ -2637,12 +3185,12 @@ function InterimButtonsView({
|
|
|
2637
3185
|
borderRadius: "50%",
|
|
2638
3186
|
backgroundColor: "#f3f4f6",
|
|
2639
3187
|
...bStyles.backButtonIcon
|
|
2640
|
-
}, children: /* @__PURE__ */
|
|
2641
|
-
/* @__PURE__ */
|
|
3188
|
+
}, 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" }) }) }),
|
|
3189
|
+
/* @__PURE__ */ jsx6(BackButtonContentSlot, { content: cardBackButtonContent })
|
|
2642
3190
|
]
|
|
2643
3191
|
}
|
|
2644
3192
|
),
|
|
2645
|
-
hideTitle ? /* @__PURE__ */
|
|
3193
|
+
hideTitle ? /* @__PURE__ */ jsx6("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx6("div", { style: {
|
|
2646
3194
|
flex: 1,
|
|
2647
3195
|
textAlign: "center",
|
|
2648
3196
|
fontWeight: 600,
|
|
@@ -2650,15 +3198,15 @@ function InterimButtonsView({
|
|
|
2650
3198
|
color: "#262833",
|
|
2651
3199
|
paddingRight: 80,
|
|
2652
3200
|
...bStyles.title
|
|
2653
|
-
}, children: /* @__PURE__ */
|
|
3201
|
+
}, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
|
|
2654
3202
|
] }),
|
|
2655
|
-
/* @__PURE__ */
|
|
2656
|
-
/* @__PURE__ */
|
|
2657
|
-
/* @__PURE__ */
|
|
2658
|
-
/* @__PURE__ */
|
|
3203
|
+
/* @__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" } }) }),
|
|
3204
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
|
|
3205
|
+
/* @__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" } }) }),
|
|
3206
|
+
/* @__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
3207
|
] }),
|
|
2660
|
-
/* @__PURE__ */
|
|
2661
|
-
/* @__PURE__ */
|
|
3208
|
+
/* @__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" } }) }),
|
|
3209
|
+
/* @__PURE__ */ jsx6("div", { style: {
|
|
2662
3210
|
height: 50,
|
|
2663
3211
|
borderRadius: 8,
|
|
2664
3212
|
marginTop: 16,
|
|
@@ -2667,7 +3215,7 @@ function InterimButtonsView({
|
|
|
2667
3215
|
...bStyles.submitButton,
|
|
2668
3216
|
opacity: 0.5
|
|
2669
3217
|
} }),
|
|
2670
|
-
/* @__PURE__ */
|
|
3218
|
+
/* @__PURE__ */ jsx6("style", { children: `
|
|
2671
3219
|
@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
2672
3220
|
@keyframes flopay-interim-expand {
|
|
2673
3221
|
0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
|
|
@@ -2677,10 +3225,10 @@ function InterimButtonsView({
|
|
|
2677
3225
|
` })
|
|
2678
3226
|
] });
|
|
2679
3227
|
}
|
|
2680
|
-
return /* @__PURE__ */
|
|
3228
|
+
return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2681
3229
|
showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2682
3230
|
(showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
2683
|
-
/* @__PURE__ */
|
|
3231
|
+
/* @__PURE__ */ jsx6(
|
|
2684
3232
|
"button",
|
|
2685
3233
|
{
|
|
2686
3234
|
type: "button",
|
|
@@ -2720,10 +3268,10 @@ function InterimButtonsView({
|
|
|
2720
3268
|
onMouseUp: (e) => {
|
|
2721
3269
|
e.currentTarget.style.transform = "scale(1)";
|
|
2722
3270
|
},
|
|
2723
|
-
children: /* @__PURE__ */
|
|
3271
|
+
children: /* @__PURE__ */ jsx6(CardButtonContentSlot, { content: cardButtonContent })
|
|
2724
3272
|
}
|
|
2725
3273
|
),
|
|
2726
|
-
errorMessage && /* @__PURE__ */
|
|
3274
|
+
errorMessage && /* @__PURE__ */ jsxs4("div", { style: {
|
|
2727
3275
|
margin: "0.25rem 0",
|
|
2728
3276
|
padding: "0.625rem 0.875rem",
|
|
2729
3277
|
background: "#FEF2F2",
|
|
@@ -2737,21 +3285,21 @@ function InterimButtonsView({
|
|
|
2737
3285
|
gap: "0.5rem",
|
|
2738
3286
|
...bStyles.errorBanner
|
|
2739
3287
|
}, children: [
|
|
2740
|
-
/* @__PURE__ */
|
|
3288
|
+
/* @__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
3289
|
errorMessage
|
|
2742
3290
|
] }),
|
|
2743
|
-
/* @__PURE__ */
|
|
3291
|
+
/* @__PURE__ */ jsx6("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
2744
3292
|
] });
|
|
2745
3293
|
}
|
|
2746
3294
|
|
|
2747
3295
|
// src/checkout-form.tsx
|
|
2748
|
-
import { FloPayError as
|
|
3296
|
+
import { FloPayError as FloPayError5 } from "@flopay/shared";
|
|
2749
3297
|
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
|
|
3298
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2751
3299
|
var WALLET_RESUME_KEY2 = "flopay_wallet_resume";
|
|
2752
3300
|
var CheckoutForm = forwardRef2(
|
|
2753
3301
|
function CheckoutForm2(props, ref) {
|
|
2754
|
-
return /* @__PURE__ */
|
|
3302
|
+
return /* @__PURE__ */ jsx7(CheckoutFormInner, { ...props, innerRef: ref });
|
|
2755
3303
|
}
|
|
2756
3304
|
);
|
|
2757
3305
|
function CheckoutFormInner({
|
|
@@ -2826,7 +3374,8 @@ function CheckoutFormInner({
|
|
|
2826
3374
|
if (response.ok) {
|
|
2827
3375
|
onComplete?.({
|
|
2828
3376
|
status: "succeeded",
|
|
2829
|
-
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId
|
|
3377
|
+
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
|
|
3378
|
+
paymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
2830
3379
|
});
|
|
2831
3380
|
return;
|
|
2832
3381
|
}
|
|
@@ -2853,7 +3402,8 @@ function CheckoutFormInner({
|
|
|
2853
3402
|
await processPaymentInternal({
|
|
2854
3403
|
id: result.paymentIntentId,
|
|
2855
3404
|
type: "card",
|
|
2856
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
3405
|
+
threeDSecureActionResultTokenId: result.paymentIntentId,
|
|
3406
|
+
originalPaymentMethodId: resolveTokenizedPaymentMethodId(tokenizedBody)
|
|
2857
3407
|
});
|
|
2858
3408
|
}
|
|
2859
3409
|
} finally {
|
|
@@ -2982,7 +3532,7 @@ function CheckoutFormInner({
|
|
|
2982
3532
|
return;
|
|
2983
3533
|
}
|
|
2984
3534
|
if (!sessionId || !email) {
|
|
2985
|
-
throw new
|
|
3535
|
+
throw new FloPayError5("Missing sessionId or email", "validation_error");
|
|
2986
3536
|
}
|
|
2987
3537
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
2988
3538
|
method: "POST",
|
|
@@ -2994,10 +3544,10 @@ function CheckoutFormInner({
|
|
|
2994
3544
|
isPaypal: false
|
|
2995
3545
|
})
|
|
2996
3546
|
});
|
|
2997
|
-
if (!intentResponse.ok) throw new
|
|
3547
|
+
if (!intentResponse.ok) throw new FloPayError5("Failed to create payment intent", "api_error");
|
|
2998
3548
|
const intentJson = await intentResponse.json();
|
|
2999
3549
|
const intentClientSecret = intentJson.data?.id;
|
|
3000
|
-
if (!intentClientSecret) throw new
|
|
3550
|
+
if (!intentClientSecret) throw new FloPayError5("No client_secret in payment intent response", "api_error");
|
|
3001
3551
|
const confirmResult = await flopay.confirmCardPayment({
|
|
3002
3552
|
clientSecret: intentClientSecret,
|
|
3003
3553
|
paymentMethodId: pmResult.paymentMethodId
|
|
@@ -3011,7 +3561,8 @@ function CheckoutFormInner({
|
|
|
3011
3561
|
dispatchTokenizedBody({
|
|
3012
3562
|
id: pmResult.paymentMethodId,
|
|
3013
3563
|
type: "card",
|
|
3014
|
-
threeDSecureActionResultTokenId: confirmResult.paymentIntentId
|
|
3564
|
+
threeDSecureActionResultTokenId: confirmResult.paymentIntentId,
|
|
3565
|
+
originalPaymentMethodId: pmResult.paymentMethodId
|
|
3015
3566
|
});
|
|
3016
3567
|
} catch (err) {
|
|
3017
3568
|
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
@@ -3025,8 +3576,8 @@ function CheckoutFormInner({
|
|
|
3025
3576
|
[flopay, elements, isSubmitting, sessionId, email, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
|
|
3026
3577
|
);
|
|
3027
3578
|
const isReady = flopay !== null && elements !== null;
|
|
3028
|
-
return /* @__PURE__ */
|
|
3029
|
-
(is3DSActive || isSubmitting) && /* @__PURE__ */
|
|
3579
|
+
return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
3580
|
+
(is3DSActive || isSubmitting) && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-overlay", style: {
|
|
3030
3581
|
position: "absolute",
|
|
3031
3582
|
inset: 0,
|
|
3032
3583
|
background: "rgba(255,255,255,0.7)",
|
|
@@ -3035,12 +3586,12 @@ function CheckoutFormInner({
|
|
|
3035
3586
|
justifyContent: "center",
|
|
3036
3587
|
zIndex: 10
|
|
3037
3588
|
}, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
|
|
3038
|
-
!isReady && /* @__PURE__ */
|
|
3039
|
-
isReady && /* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
3041
|
-
showAddress && /* @__PURE__ */
|
|
3042
|
-
displayError && /* @__PURE__ */
|
|
3043
|
-
children ?? /* @__PURE__ */
|
|
3589
|
+
!isReady && /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
|
|
3590
|
+
isReady && /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
3591
|
+
/* @__PURE__ */ jsx7(PaymentElement, { options: { layout } }),
|
|
3592
|
+
showAddress && /* @__PURE__ */ jsx7(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
|
|
3593
|
+
displayError && /* @__PURE__ */ jsx7("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
|
|
3594
|
+
children ?? /* @__PURE__ */ jsx7(
|
|
3044
3595
|
"button",
|
|
3045
3596
|
{
|
|
3046
3597
|
type: "submit",
|
|
@@ -3054,9 +3605,9 @@ function CheckoutFormInner({
|
|
|
3054
3605
|
}
|
|
3055
3606
|
|
|
3056
3607
|
// src/paypal-button.tsx
|
|
3057
|
-
import { FloPayError as
|
|
3608
|
+
import { FloPayError as FloPayError6 } from "@flopay/shared";
|
|
3058
3609
|
import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef4, useState as useState5 } from "react";
|
|
3059
|
-
import { Fragment as Fragment5, jsx as
|
|
3610
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3060
3611
|
function PayPalButton({
|
|
3061
3612
|
sessionId,
|
|
3062
3613
|
billingApiUrl,
|
|
@@ -3174,7 +3725,7 @@ function PayPalButton({
|
|
|
3174
3725
|
setSubmitting(true);
|
|
3175
3726
|
onErrorChange?.(null);
|
|
3176
3727
|
if (!sessionId || !email) {
|
|
3177
|
-
throw new
|
|
3728
|
+
throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
|
|
3178
3729
|
}
|
|
3179
3730
|
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
3180
3731
|
method: "POST",
|
|
@@ -3186,10 +3737,10 @@ function PayPalButton({
|
|
|
3186
3737
|
isPaypal: "true"
|
|
3187
3738
|
})
|
|
3188
3739
|
});
|
|
3189
|
-
if (!intentResponse.ok) throw new
|
|
3740
|
+
if (!intentResponse.ok) throw new FloPayError6("Failed to create payment intent", "api_error");
|
|
3190
3741
|
const intentJson = await intentResponse.json();
|
|
3191
3742
|
const intentClientSecret = intentJson.data?.id;
|
|
3192
|
-
if (!intentClientSecret) throw new
|
|
3743
|
+
if (!intentClientSecret) throw new FloPayError6("No client_secret in response", "api_error");
|
|
3193
3744
|
const result = await flopay.confirmPayment({
|
|
3194
3745
|
clientSecret: intentClientSecret,
|
|
3195
3746
|
returnUrl: window.location.href
|
|
@@ -3211,11 +3762,11 @@ function PayPalButton({
|
|
|
3211
3762
|
}
|
|
3212
3763
|
}, [flopay, elements, sessionId, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
|
|
3213
3764
|
if (!flopay || !elements) {
|
|
3214
|
-
return /* @__PURE__ */
|
|
3765
|
+
return /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
|
|
3215
3766
|
}
|
|
3216
|
-
return /* @__PURE__ */
|
|
3217
|
-
!ready && /* @__PURE__ */
|
|
3218
|
-
/* @__PURE__ */
|
|
3767
|
+
return /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
3768
|
+
!ready && /* @__PURE__ */ jsx8("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
|
|
3769
|
+
/* @__PURE__ */ jsx8("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx8(
|
|
3219
3770
|
"button",
|
|
3220
3771
|
{
|
|
3221
3772
|
type: "button",
|
|
@@ -3237,7 +3788,7 @@ function PayPalButton({
|
|
|
3237
3788
|
children: submitting ? "Processing..." : "PayPal"
|
|
3238
3789
|
}
|
|
3239
3790
|
) }),
|
|
3240
|
-
(submitting || isProcessing) && /* @__PURE__ */
|
|
3791
|
+
(submitting || isProcessing) && /* @__PURE__ */ jsx8("div", { style: {
|
|
3241
3792
|
position: "fixed",
|
|
3242
3793
|
inset: 0,
|
|
3243
3794
|
background: "rgba(0,0,0,0.4)",
|
|
@@ -3245,7 +3796,7 @@ function PayPalButton({
|
|
|
3245
3796
|
alignItems: "center",
|
|
3246
3797
|
justifyContent: "center",
|
|
3247
3798
|
zIndex: 1e3
|
|
3248
|
-
}, children: /* @__PURE__ */
|
|
3799
|
+
}, children: /* @__PURE__ */ jsx8("div", { style: {
|
|
3249
3800
|
background: "white",
|
|
3250
3801
|
borderRadius: 8,
|
|
3251
3802
|
padding: "1.5rem",
|
|
@@ -3255,6 +3806,649 @@ function PayPalButton({
|
|
|
3255
3806
|
}, children: "Processing PayPal payment..." }) })
|
|
3256
3807
|
] });
|
|
3257
3808
|
}
|
|
3809
|
+
|
|
3810
|
+
// src/automatic-payment-button.tsx
|
|
3811
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
|
|
3812
|
+
import { PaymentAPI as PaymentAPI3 } from "@flopay/js";
|
|
3813
|
+
import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
|
|
3814
|
+
import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3815
|
+
var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
|
|
3816
|
+
var PAYPAL_RESUME_STORAGE_KEY = "flopay_automatic_payment_button_resume";
|
|
3817
|
+
function sleep2(ms) {
|
|
3818
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3819
|
+
}
|
|
3820
|
+
function coerceError(err, fallbackMessage) {
|
|
3821
|
+
if (err instanceof FloPayError7) {
|
|
3822
|
+
return err;
|
|
3823
|
+
}
|
|
3824
|
+
return new FloPayError7(
|
|
3825
|
+
err instanceof Error ? err.message : fallbackMessage,
|
|
3826
|
+
"api_error"
|
|
3827
|
+
);
|
|
3828
|
+
}
|
|
3829
|
+
function canUseStorage() {
|
|
3830
|
+
return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
|
|
3831
|
+
}
|
|
3832
|
+
function shouldShowFallbackCheckout(error, sessionId) {
|
|
3833
|
+
if (!sessionId) return false;
|
|
3834
|
+
if (error.type === "validation_error") return false;
|
|
3835
|
+
if (error.checkoutMethod && error.checkoutMethod !== "card") return false;
|
|
3836
|
+
return true;
|
|
3837
|
+
}
|
|
3838
|
+
function readPayPalResumeState() {
|
|
3839
|
+
if (!canUseStorage()) return null;
|
|
3840
|
+
try {
|
|
3841
|
+
const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY);
|
|
3842
|
+
if (!raw) return null;
|
|
3843
|
+
return JSON.parse(raw);
|
|
3844
|
+
} catch {
|
|
3845
|
+
return null;
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
function persistPayPalResumeState(state) {
|
|
3849
|
+
if (!canUseStorage()) return;
|
|
3850
|
+
try {
|
|
3851
|
+
window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY, JSON.stringify(state));
|
|
3852
|
+
} catch (error) {
|
|
3853
|
+
console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
function clearPayPalResumeState() {
|
|
3857
|
+
if (!canUseStorage()) return;
|
|
3858
|
+
try {
|
|
3859
|
+
window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY);
|
|
3860
|
+
} catch (error) {
|
|
3861
|
+
console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
function clearPayPalRedirectParams() {
|
|
3865
|
+
if (typeof window === "undefined") return;
|
|
3866
|
+
const url = new URL(window.location.href);
|
|
3867
|
+
const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
|
|
3868
|
+
let changed = false;
|
|
3869
|
+
for (const key of keys) {
|
|
3870
|
+
if (url.searchParams.has(key)) {
|
|
3871
|
+
url.searchParams.delete(key);
|
|
3872
|
+
changed = true;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
if (changed) {
|
|
3876
|
+
window.history.replaceState({}, "", url.toString());
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
function resolveCreateSessionDraft(props) {
|
|
3880
|
+
if (props.createSession) {
|
|
3881
|
+
return {
|
|
3882
|
+
...props.createSession,
|
|
3883
|
+
checkoutMode: "auto"
|
|
3884
|
+
};
|
|
3885
|
+
}
|
|
3886
|
+
if (!props.clientId || !props.account || !props.successUrl || !props.cancelUrl) {
|
|
3887
|
+
return null;
|
|
3888
|
+
}
|
|
3889
|
+
return {
|
|
3890
|
+
clientId: props.clientId,
|
|
3891
|
+
items: props.items,
|
|
3892
|
+
subscriptions: props.subscriptions,
|
|
3893
|
+
account: props.account,
|
|
3894
|
+
successUrl: props.successUrl,
|
|
3895
|
+
cancelUrl: props.cancelUrl,
|
|
3896
|
+
couponCodes: props.couponCodes,
|
|
3897
|
+
tagsData: props.tagsData,
|
|
3898
|
+
utmMetadata: props.utmMetadata,
|
|
3899
|
+
checkoutMode: "auto"
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
function FloPayAutomaticPaymentButton({
|
|
3903
|
+
sessionId,
|
|
3904
|
+
createSession,
|
|
3905
|
+
paymentMethodId,
|
|
3906
|
+
clientId,
|
|
3907
|
+
items,
|
|
3908
|
+
subscriptions,
|
|
3909
|
+
account,
|
|
3910
|
+
successUrl,
|
|
3911
|
+
cancelUrl,
|
|
3912
|
+
couponCodes,
|
|
3913
|
+
tagsData,
|
|
3914
|
+
utmMetadata,
|
|
3915
|
+
billingApiUrl,
|
|
3916
|
+
locale,
|
|
3917
|
+
fallbackPublishableKey,
|
|
3918
|
+
buttonsTheme,
|
|
3919
|
+
buttonsStyles: stylesOverride,
|
|
3920
|
+
onSuccess,
|
|
3921
|
+
onError,
|
|
3922
|
+
onDecline,
|
|
3923
|
+
children,
|
|
3924
|
+
disabled = false,
|
|
3925
|
+
type = "button",
|
|
3926
|
+
style,
|
|
3927
|
+
...buttonProps
|
|
3928
|
+
}) {
|
|
3929
|
+
const resolvedBillingUrl = useMemo4(
|
|
3930
|
+
() => resolveBillingApiUrl4(billingApiUrl),
|
|
3931
|
+
[billingApiUrl]
|
|
3932
|
+
);
|
|
3933
|
+
const createSessionDraft = useMemo4(
|
|
3934
|
+
() => resolveCreateSessionDraft({
|
|
3935
|
+
createSession,
|
|
3936
|
+
clientId,
|
|
3937
|
+
items,
|
|
3938
|
+
subscriptions,
|
|
3939
|
+
account,
|
|
3940
|
+
successUrl,
|
|
3941
|
+
cancelUrl,
|
|
3942
|
+
couponCodes,
|
|
3943
|
+
tagsData,
|
|
3944
|
+
utmMetadata
|
|
3945
|
+
}),
|
|
3946
|
+
[
|
|
3947
|
+
account,
|
|
3948
|
+
cancelUrl,
|
|
3949
|
+
clientId,
|
|
3950
|
+
couponCodes,
|
|
3951
|
+
createSession,
|
|
3952
|
+
items,
|
|
3953
|
+
subscriptions,
|
|
3954
|
+
successUrl,
|
|
3955
|
+
tagsData,
|
|
3956
|
+
utmMetadata
|
|
3957
|
+
]
|
|
3958
|
+
);
|
|
3959
|
+
const [isProcessing, setIsProcessing] = useState6(false);
|
|
3960
|
+
const [overlayStatus, setOverlayStatus] = useState6(null);
|
|
3961
|
+
const [overlayError, setOverlayError] = useState6(null);
|
|
3962
|
+
const [fallbackSession, setFallbackSession] = useState6(null);
|
|
3963
|
+
const automaticPaymentToken = useMemo4(
|
|
3964
|
+
() => paymentMethodId ? {
|
|
3965
|
+
id: paymentMethodId,
|
|
3966
|
+
type: "card"
|
|
3967
|
+
} : void 0,
|
|
3968
|
+
[paymentMethodId]
|
|
3969
|
+
);
|
|
3970
|
+
const isMountedRef = useRef5(true);
|
|
3971
|
+
const resumeAttemptedRef = useRef5(false);
|
|
3972
|
+
const fallbackSessionRef = useRef5(fallbackSession);
|
|
3973
|
+
const onSuccessRef = useRef5(onSuccess);
|
|
3974
|
+
const onErrorRef = useRef5(onError);
|
|
3975
|
+
const onDeclineRef = useRef5(onDecline);
|
|
3976
|
+
useEffect7(() => {
|
|
3977
|
+
fallbackSessionRef.current = fallbackSession;
|
|
3978
|
+
}, [fallbackSession]);
|
|
3979
|
+
useEffect7(() => {
|
|
3980
|
+
onSuccessRef.current = onSuccess;
|
|
3981
|
+
}, [onSuccess]);
|
|
3982
|
+
useEffect7(() => {
|
|
3983
|
+
onErrorRef.current = onError;
|
|
3984
|
+
}, [onError]);
|
|
3985
|
+
useEffect7(() => {
|
|
3986
|
+
onDeclineRef.current = onDecline;
|
|
3987
|
+
}, [onDecline]);
|
|
3988
|
+
useEffect7(() => {
|
|
3989
|
+
isMountedRef.current = true;
|
|
3990
|
+
return () => {
|
|
3991
|
+
isMountedRef.current = false;
|
|
3992
|
+
};
|
|
3993
|
+
}, []);
|
|
3994
|
+
useEffect7(() => {
|
|
3995
|
+
if (!fallbackSession || typeof window === "undefined") {
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
3998
|
+
const handleKeyDown = (event) => {
|
|
3999
|
+
if (event.key === "Escape") {
|
|
4000
|
+
setFallbackSession(null);
|
|
4001
|
+
}
|
|
4002
|
+
};
|
|
4003
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
4004
|
+
return () => {
|
|
4005
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
4006
|
+
};
|
|
4007
|
+
}, [fallbackSession]);
|
|
4008
|
+
const emitDecline = useCallback5((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
|
|
4009
|
+
onDeclineRef.current?.(buildDeclineEvent(method, error, {
|
|
4010
|
+
code: error.code,
|
|
4011
|
+
declineCode: error.declineCode
|
|
4012
|
+
}));
|
|
4013
|
+
}, []);
|
|
4014
|
+
const showSuccess = useCallback5(async (event) => {
|
|
4015
|
+
if (!isMountedRef.current) return;
|
|
4016
|
+
setOverlayError(null);
|
|
4017
|
+
setOverlayStatus("success");
|
|
4018
|
+
await sleep2(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
4019
|
+
if (!isMountedRef.current) return;
|
|
4020
|
+
onSuccessRef.current?.(event);
|
|
4021
|
+
}, []);
|
|
4022
|
+
const showError = useCallback5(async (error, options) => {
|
|
4023
|
+
if (!isMountedRef.current) return;
|
|
4024
|
+
onErrorRef.current?.(error);
|
|
4025
|
+
if (options?.emitDecline) {
|
|
4026
|
+
emitDecline(error, options.method);
|
|
4027
|
+
}
|
|
4028
|
+
setOverlayError(error.message);
|
|
4029
|
+
setOverlayStatus("error");
|
|
4030
|
+
await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
|
|
4031
|
+
}, [emitDecline]);
|
|
4032
|
+
const processResolvedSession = useCallback5(async (apiResult, resolvedSessionId) => {
|
|
4033
|
+
const session = apiResult.data.session ?? null;
|
|
4034
|
+
if (!session) {
|
|
4035
|
+
throw new FloPayError7("No session data returned", "api_error");
|
|
4036
|
+
}
|
|
4037
|
+
if (session.status === "complete") {
|
|
4038
|
+
await showSuccess({
|
|
4039
|
+
result: { status: "succeeded" },
|
|
4040
|
+
session,
|
|
4041
|
+
sessionId: session.id || resolvedSessionId,
|
|
4042
|
+
autoCompleted: false
|
|
4043
|
+
});
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
try {
|
|
4047
|
+
const result = await processSavedPaymentForMode({
|
|
4048
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4049
|
+
sessionId: resolvedSessionId ?? session.id,
|
|
4050
|
+
session,
|
|
4051
|
+
tokenizedData: automaticPaymentToken
|
|
4052
|
+
});
|
|
4053
|
+
let paymentResult = result.type === "success" ? result.result : null;
|
|
4054
|
+
if (result.type !== "success") {
|
|
4055
|
+
const {
|
|
4056
|
+
publishableKey,
|
|
4057
|
+
paypalPublishableKey
|
|
4058
|
+
} = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
|
|
4059
|
+
if (result.type === "paypal_redirect_required") {
|
|
4060
|
+
persistPayPalResumeState({
|
|
4061
|
+
sessionId: session.id || resolvedSessionId,
|
|
4062
|
+
publishableKey,
|
|
4063
|
+
paypalPublishableKey
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4066
|
+
const {
|
|
4067
|
+
flopay,
|
|
4068
|
+
paypalFlopay
|
|
4069
|
+
} = await loadSavedPaymentProviders({
|
|
4070
|
+
publishableKey,
|
|
4071
|
+
paypalPublishableKey,
|
|
4072
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4073
|
+
locale
|
|
4074
|
+
});
|
|
4075
|
+
paymentResult = await handleSavedPaymentRedirectResult(result, {
|
|
4076
|
+
flopay,
|
|
4077
|
+
paypalFlopay,
|
|
4078
|
+
attempt3DS: true,
|
|
4079
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4080
|
+
sessionId: session.id || resolvedSessionId,
|
|
4081
|
+
session
|
|
4082
|
+
});
|
|
4083
|
+
if (result.type === "paypal_redirect_required") {
|
|
4084
|
+
clearPayPalResumeState();
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
await showSuccess({
|
|
4088
|
+
result: paymentResult ? {
|
|
4089
|
+
...paymentResult,
|
|
4090
|
+
paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
|
|
4091
|
+
} : {
|
|
4092
|
+
status: "succeeded",
|
|
4093
|
+
paymentMethodId: paymentMethodId ?? void 0
|
|
4094
|
+
},
|
|
4095
|
+
session,
|
|
4096
|
+
sessionId: session.id || resolvedSessionId,
|
|
4097
|
+
autoCompleted: false
|
|
4098
|
+
});
|
|
4099
|
+
} catch (err) {
|
|
4100
|
+
let floPayErr = normalizeSavedPaymentError(err);
|
|
4101
|
+
const fallbackSessionId = session.id || resolvedSessionId;
|
|
4102
|
+
if (floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
|
|
4103
|
+
try {
|
|
4104
|
+
const {
|
|
4105
|
+
publishableKey,
|
|
4106
|
+
paypalPublishableKey
|
|
4107
|
+
} = resolveSavedPaymentPublishableKeys(apiResult, fallbackPublishableKey);
|
|
4108
|
+
const {
|
|
4109
|
+
flopay
|
|
4110
|
+
} = await loadSavedPaymentProviders({
|
|
4111
|
+
publishableKey,
|
|
4112
|
+
paypalPublishableKey,
|
|
4113
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4114
|
+
locale
|
|
4115
|
+
});
|
|
4116
|
+
const paymentResult = await processSavedPaymentWithIntent({
|
|
4117
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4118
|
+
sessionId: fallbackSessionId,
|
|
4119
|
+
session,
|
|
4120
|
+
paymentMethodId: automaticPaymentToken.id,
|
|
4121
|
+
flopay
|
|
4122
|
+
});
|
|
4123
|
+
await showSuccess({
|
|
4124
|
+
result: {
|
|
4125
|
+
...paymentResult,
|
|
4126
|
+
paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId
|
|
4127
|
+
},
|
|
4128
|
+
session,
|
|
4129
|
+
sessionId: fallbackSessionId,
|
|
4130
|
+
autoCompleted: false
|
|
4131
|
+
});
|
|
4132
|
+
return;
|
|
4133
|
+
} catch (intentRecoveryErr) {
|
|
4134
|
+
floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
await showError(floPayErr, {
|
|
4138
|
+
emitDecline: true,
|
|
4139
|
+
method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
|
|
4140
|
+
});
|
|
4141
|
+
if (shouldShowFallbackCheckout(floPayErr, fallbackSessionId) && fallbackSessionId && isMountedRef.current) {
|
|
4142
|
+
setFallbackSession({
|
|
4143
|
+
sessionId: fallbackSessionId,
|
|
4144
|
+
errorMessage: floPayErr.message
|
|
4145
|
+
});
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
}, [
|
|
4149
|
+
fallbackPublishableKey,
|
|
4150
|
+
locale,
|
|
4151
|
+
automaticPaymentToken,
|
|
4152
|
+
paymentMethodId,
|
|
4153
|
+
resolvedBillingUrl,
|
|
4154
|
+
showError,
|
|
4155
|
+
showSuccess
|
|
4156
|
+
]);
|
|
4157
|
+
const handleButtonClick = useCallback5(async (event) => {
|
|
4158
|
+
buttonProps.onClick?.(event);
|
|
4159
|
+
if (event.defaultPrevented || disabled || isProcessing) {
|
|
4160
|
+
return;
|
|
4161
|
+
}
|
|
4162
|
+
setFallbackSession(null);
|
|
4163
|
+
if (sessionId && createSessionDraft) {
|
|
4164
|
+
const error = new FloPayError7(
|
|
4165
|
+
"Provide either sessionId or create-session props, not both.",
|
|
4166
|
+
"validation_error"
|
|
4167
|
+
);
|
|
4168
|
+
await showError(error);
|
|
4169
|
+
if (isMountedRef.current) {
|
|
4170
|
+
setOverlayStatus(null);
|
|
4171
|
+
setOverlayError(null);
|
|
4172
|
+
}
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
4175
|
+
if (!sessionId && !createSessionDraft) {
|
|
4176
|
+
const error = new FloPayError7(
|
|
4177
|
+
"Provide a sessionId or the props required to create an automatic payment session.",
|
|
4178
|
+
"validation_error"
|
|
4179
|
+
);
|
|
4180
|
+
await showError(error);
|
|
4181
|
+
if (isMountedRef.current) {
|
|
4182
|
+
setOverlayStatus(null);
|
|
4183
|
+
setOverlayError(null);
|
|
4184
|
+
}
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
setIsProcessing(true);
|
|
4188
|
+
setOverlayError(null);
|
|
4189
|
+
setOverlayStatus("processing");
|
|
4190
|
+
try {
|
|
4191
|
+
const api = new PaymentAPI3(resolvedBillingUrl);
|
|
4192
|
+
if (sessionId) {
|
|
4193
|
+
const result = await api.getUnifiedCheckoutSession(sessionId);
|
|
4194
|
+
await processResolvedSession(result, sessionId);
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
try {
|
|
4198
|
+
const result = await api.createAndFetchSession(createSessionDraft);
|
|
4199
|
+
await processResolvedSession(result, result.data.session?.id ?? null);
|
|
4200
|
+
} catch (err) {
|
|
4201
|
+
if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
|
|
4202
|
+
await showSuccess({
|
|
4203
|
+
result: { status: "succeeded" },
|
|
4204
|
+
session: null,
|
|
4205
|
+
sessionId: null,
|
|
4206
|
+
autoCompleted: true
|
|
4207
|
+
});
|
|
4208
|
+
return;
|
|
4209
|
+
}
|
|
4210
|
+
throw err;
|
|
4211
|
+
}
|
|
4212
|
+
} catch (err) {
|
|
4213
|
+
await showError(
|
|
4214
|
+
coerceError(err, "Automatic payment failed. Please try again.")
|
|
4215
|
+
);
|
|
4216
|
+
} finally {
|
|
4217
|
+
if (isMountedRef.current) {
|
|
4218
|
+
setOverlayStatus(null);
|
|
4219
|
+
setOverlayError(null);
|
|
4220
|
+
setIsProcessing(false);
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
}, [
|
|
4224
|
+
buttonProps,
|
|
4225
|
+
createSessionDraft,
|
|
4226
|
+
disabled,
|
|
4227
|
+
isProcessing,
|
|
4228
|
+
processResolvedSession,
|
|
4229
|
+
resolvedBillingUrl,
|
|
4230
|
+
sessionId,
|
|
4231
|
+
showError,
|
|
4232
|
+
showSuccess
|
|
4233
|
+
]);
|
|
4234
|
+
const handleFallbackComplete = useCallback5((result) => {
|
|
4235
|
+
const activeFallback = fallbackSessionRef.current;
|
|
4236
|
+
setFallbackSession(null);
|
|
4237
|
+
onSuccessRef.current?.({
|
|
4238
|
+
result,
|
|
4239
|
+
session: null,
|
|
4240
|
+
sessionId: activeFallback?.sessionId ?? null,
|
|
4241
|
+
autoCompleted: false
|
|
4242
|
+
});
|
|
4243
|
+
}, []);
|
|
4244
|
+
const handleFallbackError = useCallback5((error) => {
|
|
4245
|
+
onErrorRef.current?.(error);
|
|
4246
|
+
}, []);
|
|
4247
|
+
const handleFallbackDecline = useCallback5((decline) => {
|
|
4248
|
+
onDeclineRef.current?.(decline);
|
|
4249
|
+
}, []);
|
|
4250
|
+
useEffect7(() => {
|
|
4251
|
+
if (typeof window === "undefined" || resumeAttemptedRef.current) {
|
|
4252
|
+
return;
|
|
4253
|
+
}
|
|
4254
|
+
const params = new URLSearchParams(window.location.search);
|
|
4255
|
+
const clientSecret = params.get("payment_intent_client_secret");
|
|
4256
|
+
if (!clientSecret) {
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
const resumeState = readPayPalResumeState();
|
|
4260
|
+
if (!resumeState) {
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
resumeAttemptedRef.current = true;
|
|
4264
|
+
void (async () => {
|
|
4265
|
+
setIsProcessing(true);
|
|
4266
|
+
setOverlayError(null);
|
|
4267
|
+
setOverlayStatus("processing");
|
|
4268
|
+
try {
|
|
4269
|
+
if (params.get("redirect_status") === "failed") {
|
|
4270
|
+
throw Object.assign(
|
|
4271
|
+
new FloPayError7("PayPal payment was declined. Please try again.", "api_error"),
|
|
4272
|
+
{ checkoutMethod: "paypal" }
|
|
4273
|
+
);
|
|
4274
|
+
}
|
|
4275
|
+
const {
|
|
4276
|
+
flopay,
|
|
4277
|
+
paypalFlopay
|
|
4278
|
+
} = await loadSavedPaymentProviders({
|
|
4279
|
+
publishableKey: resumeState.publishableKey,
|
|
4280
|
+
paypalPublishableKey: resumeState.paypalPublishableKey,
|
|
4281
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4282
|
+
locale
|
|
4283
|
+
});
|
|
4284
|
+
const paypalStripe = (paypalFlopay ?? flopay).getRawProvider();
|
|
4285
|
+
if (!paypalStripe) {
|
|
4286
|
+
throw Object.assign(
|
|
4287
|
+
new FloPayError7("PayPal is not available.", "api_error"),
|
|
4288
|
+
{ checkoutMethod: "paypal" }
|
|
4289
|
+
);
|
|
4290
|
+
}
|
|
4291
|
+
const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
|
|
4292
|
+
if (error) {
|
|
4293
|
+
throw Object.assign(
|
|
4294
|
+
new FloPayError7(
|
|
4295
|
+
error.message ?? "Failed to retrieve PayPal payment status.",
|
|
4296
|
+
"api_error",
|
|
4297
|
+
{ code: error.code }
|
|
4298
|
+
),
|
|
4299
|
+
{ checkoutMethod: "paypal" }
|
|
4300
|
+
);
|
|
4301
|
+
}
|
|
4302
|
+
if (paymentIntent && (paymentIntent.status === "requires_capture" || paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) {
|
|
4303
|
+
await showSuccess({
|
|
4304
|
+
result: { status: "succeeded", paymentIntentId: paymentIntent.id },
|
|
4305
|
+
session: null,
|
|
4306
|
+
sessionId: resumeState.sessionId,
|
|
4307
|
+
autoCompleted: false
|
|
4308
|
+
});
|
|
4309
|
+
} else {
|
|
4310
|
+
throw Object.assign(
|
|
4311
|
+
new FloPayError7("PayPal payment was not completed. Please try again.", "api_error"),
|
|
4312
|
+
{ checkoutMethod: "paypal" }
|
|
4313
|
+
);
|
|
4314
|
+
}
|
|
4315
|
+
} catch (err) {
|
|
4316
|
+
const floPayErr = normalizeSavedPaymentError(err);
|
|
4317
|
+
await showError(floPayErr, {
|
|
4318
|
+
emitDecline: true,
|
|
4319
|
+
method: floPayErr.checkoutMethod ?? "paypal"
|
|
4320
|
+
});
|
|
4321
|
+
} finally {
|
|
4322
|
+
clearPayPalResumeState();
|
|
4323
|
+
clearPayPalRedirectParams();
|
|
4324
|
+
if (isMountedRef.current) {
|
|
4325
|
+
setOverlayStatus(null);
|
|
4326
|
+
setOverlayError(null);
|
|
4327
|
+
setIsProcessing(false);
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
})();
|
|
4331
|
+
}, [locale, resolvedBillingUrl, showError, showSuccess]);
|
|
4332
|
+
const bStyles = useMemo4(() => {
|
|
4333
|
+
const base = resolveButtonsLayoutTheme3(buttonsTheme);
|
|
4334
|
+
if (!stylesOverride) return base;
|
|
4335
|
+
return {
|
|
4336
|
+
...base,
|
|
4337
|
+
...stylesOverride,
|
|
4338
|
+
cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
|
|
4339
|
+
};
|
|
4340
|
+
}, [buttonsTheme, stylesOverride]);
|
|
4341
|
+
const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
|
|
4342
|
+
return /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
4343
|
+
/* @__PURE__ */ jsx9(
|
|
4344
|
+
"button",
|
|
4345
|
+
{
|
|
4346
|
+
...buttonProps,
|
|
4347
|
+
type,
|
|
4348
|
+
onClick: handleButtonClick,
|
|
4349
|
+
disabled: disabled || isProcessing,
|
|
4350
|
+
"aria-busy": isProcessing,
|
|
4351
|
+
style: {
|
|
4352
|
+
width: "100%",
|
|
4353
|
+
...cardButtonSizing,
|
|
4354
|
+
backgroundColor: "white",
|
|
4355
|
+
color: "#262833",
|
|
4356
|
+
border: "1px solid #d1d5db",
|
|
4357
|
+
borderRadius: "8px",
|
|
4358
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
4359
|
+
fontWeight: 600,
|
|
4360
|
+
cursor: disabled || isProcessing ? "not-allowed" : "pointer",
|
|
4361
|
+
display: "flex",
|
|
4362
|
+
alignItems: "center",
|
|
4363
|
+
justifyContent: "center",
|
|
4364
|
+
gap: "0.625rem",
|
|
4365
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
4366
|
+
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
4367
|
+
position: "relative",
|
|
4368
|
+
opacity: disabled || isProcessing ? 0.6 : 1,
|
|
4369
|
+
...bStyles.cardButton,
|
|
4370
|
+
...style
|
|
4371
|
+
},
|
|
4372
|
+
onMouseDown: (e) => {
|
|
4373
|
+
buttonProps.onMouseDown?.(e);
|
|
4374
|
+
if (!e.defaultPrevented) {
|
|
4375
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
4376
|
+
}
|
|
4377
|
+
},
|
|
4378
|
+
onMouseUp: (e) => {
|
|
4379
|
+
buttonProps.onMouseUp?.(e);
|
|
4380
|
+
if (!e.defaultPrevented) {
|
|
4381
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
4382
|
+
}
|
|
4383
|
+
},
|
|
4384
|
+
children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: children })
|
|
4385
|
+
}
|
|
4386
|
+
),
|
|
4387
|
+
overlayStatus && /* @__PURE__ */ jsx9(
|
|
4388
|
+
ProcessingOverlay,
|
|
4389
|
+
{
|
|
4390
|
+
status: overlayStatus,
|
|
4391
|
+
errorMessage: overlayError
|
|
4392
|
+
}
|
|
4393
|
+
),
|
|
4394
|
+
fallbackSession && /* @__PURE__ */ jsx9(
|
|
4395
|
+
"div",
|
|
4396
|
+
{
|
|
4397
|
+
"data-testid": "flopay-automatic-payment-fallback",
|
|
4398
|
+
role: "dialog",
|
|
4399
|
+
"aria-modal": "true",
|
|
4400
|
+
onClick: (event) => {
|
|
4401
|
+
if (event.target === event.currentTarget) {
|
|
4402
|
+
setFallbackSession(null);
|
|
4403
|
+
}
|
|
4404
|
+
},
|
|
4405
|
+
style: {
|
|
4406
|
+
position: "fixed",
|
|
4407
|
+
inset: 0,
|
|
4408
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
4409
|
+
display: "flex",
|
|
4410
|
+
alignItems: "center",
|
|
4411
|
+
justifyContent: "center",
|
|
4412
|
+
padding: "1.5rem",
|
|
4413
|
+
zIndex: 1100
|
|
4414
|
+
},
|
|
4415
|
+
children: /* @__PURE__ */ jsx9(
|
|
4416
|
+
"div",
|
|
4417
|
+
{
|
|
4418
|
+
style: {
|
|
4419
|
+
width: "100%",
|
|
4420
|
+
maxWidth: 520,
|
|
4421
|
+
maxHeight: "90vh",
|
|
4422
|
+
overflowY: "auto",
|
|
4423
|
+
background: "white",
|
|
4424
|
+
borderRadius: 20,
|
|
4425
|
+
padding: "1.5rem",
|
|
4426
|
+
boxShadow: "0 24px 80px rgba(15, 23, 42, 0.28)",
|
|
4427
|
+
display: "flex",
|
|
4428
|
+
flexDirection: "column",
|
|
4429
|
+
gap: "1rem"
|
|
4430
|
+
},
|
|
4431
|
+
children: /* @__PURE__ */ jsx9(
|
|
4432
|
+
FloPayCheckout,
|
|
4433
|
+
{
|
|
4434
|
+
sessionId: fallbackSession.sessionId,
|
|
4435
|
+
checkoutMode: "full",
|
|
4436
|
+
billingApiUrl: resolvedBillingUrl,
|
|
4437
|
+
fallbackPublishableKey,
|
|
4438
|
+
initialErrorMessage: fallbackSession.errorMessage,
|
|
4439
|
+
cardTitleContent: null,
|
|
4440
|
+
showSecurityFooter: false,
|
|
4441
|
+
onComplete: handleFallbackComplete,
|
|
4442
|
+
onError: handleFallbackError,
|
|
4443
|
+
onDecline: handleFallbackDecline
|
|
4444
|
+
}
|
|
4445
|
+
)
|
|
4446
|
+
}
|
|
4447
|
+
)
|
|
4448
|
+
}
|
|
4449
|
+
)
|
|
4450
|
+
] });
|
|
4451
|
+
}
|
|
3258
4452
|
export {
|
|
3259
4453
|
AddressElement,
|
|
3260
4454
|
CardCvcElement,
|
|
@@ -3262,6 +4456,7 @@ export {
|
|
|
3262
4456
|
CardExpiryElement,
|
|
3263
4457
|
CardNumberElement,
|
|
3264
4458
|
CheckoutForm,
|
|
4459
|
+
FloPayAutomaticPaymentButton,
|
|
3265
4460
|
FloPayCheckout,
|
|
3266
4461
|
FloPayProvider,
|
|
3267
4462
|
PayPalButton,
|