@easypayment/medusa-paypal-ui 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/index.cjs +224 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +220 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -7
- package/src/client/http.ts +55 -11
- package/src/client/types.ts +10 -0
- package/src/components/PayPalAdvancedCard.tsx +114 -24
- package/src/components/PayPalSmartButtons.tsx +112 -7
- package/src/utils/next-errors.ts +5 -1
- package/src/utils/processing-overlay.ts +12 -0
package/dist/index.mjs
CHANGED
|
@@ -16,6 +16,18 @@ function isRetryableError(err) {
|
|
|
16
16
|
return err instanceof Error && !(err instanceof HttpError);
|
|
17
17
|
}
|
|
18
18
|
var wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
19
|
+
function extractJsonMessage(text) {
|
|
20
|
+
if (!text) return null;
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(text);
|
|
23
|
+
const message = parsed?.message;
|
|
24
|
+
if (typeof message === "string" && message.trim()) {
|
|
25
|
+
return message.trim().slice(0, 500);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
19
31
|
function toHeaderRecord(headers) {
|
|
20
32
|
if (!headers) {
|
|
21
33
|
return {};
|
|
@@ -40,39 +52,49 @@ function createHttpClient(opts) {
|
|
|
40
52
|
headers["x-publishable-api-key"] = opts.publishableApiKey;
|
|
41
53
|
}
|
|
42
54
|
const timeoutMs = 3e4;
|
|
43
|
-
let timeoutId;
|
|
44
55
|
const controller = new AbortController();
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
57
|
+
const callerSignal = init?.signal ?? null;
|
|
58
|
+
const onCallerAbort = () => controller.abort();
|
|
59
|
+
if (callerSignal) {
|
|
60
|
+
if (callerSignal.aborted) controller.abort();
|
|
61
|
+
else callerSignal.addEventListener("abort", onCallerAbort);
|
|
47
62
|
}
|
|
48
|
-
const effectiveSignal = init?.signal || controller.signal;
|
|
49
63
|
let res;
|
|
64
|
+
let text;
|
|
50
65
|
try {
|
|
51
|
-
res = await fetch(url, {
|
|
66
|
+
res = await fetch(url, {
|
|
67
|
+
...init,
|
|
68
|
+
headers,
|
|
69
|
+
credentials: "include",
|
|
70
|
+
signal: controller.signal
|
|
71
|
+
});
|
|
72
|
+
text = await res.text().catch(() => "");
|
|
52
73
|
} catch (err) {
|
|
53
|
-
if (err instanceof Error && err.name === "AbortError" && !
|
|
74
|
+
if (err instanceof Error && err.name === "AbortError" && !(callerSignal && callerSignal.aborted)) {
|
|
54
75
|
throw new Error(`[PayPal] Request to ${path} timed out after ${timeoutMs / 1e3}s`);
|
|
55
76
|
}
|
|
56
77
|
throw err;
|
|
57
78
|
} finally {
|
|
58
|
-
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
if (callerSignal) callerSignal.removeEventListener("abort", onCallerAbort);
|
|
59
81
|
}
|
|
60
|
-
const text = await res.text().catch(() => "");
|
|
61
82
|
if (!res.ok) {
|
|
83
|
+
const parsedMessage = extractJsonMessage(text);
|
|
62
84
|
if (res.status === 401) {
|
|
63
85
|
throw new HttpError(
|
|
64
|
-
"[PayPal] Unauthorized (401) \u2014 check that your publishable API key is correct and set in NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY",
|
|
86
|
+
parsedMessage || "[PayPal] Unauthorized (401) \u2014 check that your publishable API key is correct and set in NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY",
|
|
65
87
|
401
|
|
66
88
|
);
|
|
67
89
|
}
|
|
68
90
|
if (res.status === 403) {
|
|
69
91
|
throw new HttpError(
|
|
70
|
-
"[PayPal] Forbidden (403) \u2014 this request is not allowed. Check your CORS and API key settings.",
|
|
92
|
+
parsedMessage || "[PayPal] Forbidden (403) \u2014 this request is not allowed. Check your CORS and API key settings.",
|
|
71
93
|
403
|
|
72
94
|
);
|
|
73
95
|
}
|
|
74
96
|
throw new HttpError(
|
|
75
|
-
text.slice(0, 500).replace(/<[^>]*>/g, "") || `Request failed (${res.status})`,
|
|
97
|
+
parsedMessage || text.slice(0, 500).replace(/<[^>]*>/g, "") || `Request failed (${res.status})`,
|
|
76
98
|
res.status
|
|
77
99
|
);
|
|
78
100
|
}
|
|
@@ -268,14 +290,16 @@ function PayPalCurrencyNotice({ config }) {
|
|
|
268
290
|
}
|
|
269
291
|
|
|
270
292
|
// src/components/PayPalSmartButtons.tsx
|
|
271
|
-
import { useCallback, useMemo as useMemo3, useState as useState2 } from "react";
|
|
293
|
+
import { useCallback, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
|
|
272
294
|
import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
|
|
273
295
|
|
|
274
296
|
// src/utils/next-errors.ts
|
|
275
297
|
function isNextRouterError(e) {
|
|
276
298
|
if (typeof e !== "object" || e === null || !("digest" in e)) return false;
|
|
277
299
|
const digest = e.digest;
|
|
278
|
-
return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND"))
|
|
300
|
+
return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND") || // Next.js 15: notFound()/forbidden()/unauthorized() throw
|
|
301
|
+
// HTTPAccessFallbackError with digest "NEXT_HTTP_ERROR_FALLBACK;<status>".
|
|
302
|
+
digest.startsWith("NEXT_HTTP_ERROR_FALLBACK"));
|
|
279
303
|
}
|
|
280
304
|
|
|
281
305
|
// src/utils/processing-overlay.ts
|
|
@@ -339,8 +363,13 @@ function showProcessingOverlay() {
|
|
|
339
363
|
origReplace(...args);
|
|
340
364
|
onNav();
|
|
341
365
|
};
|
|
366
|
+
const onPageShow = (e) => {
|
|
367
|
+
if (e.persisted) hideProcessingOverlay();
|
|
368
|
+
};
|
|
369
|
+
window.addEventListener("pageshow", onPageShow);
|
|
342
370
|
cleanupNavListener = () => {
|
|
343
371
|
window.removeEventListener("popstate", onNav);
|
|
372
|
+
window.removeEventListener("pageshow", onPageShow);
|
|
344
373
|
history.pushState = origPush;
|
|
345
374
|
history.replaceState = origReplace;
|
|
346
375
|
};
|
|
@@ -373,11 +402,48 @@ function PayPalSmartButtons(props) {
|
|
|
373
402
|
const [error, setError] = useState2(null);
|
|
374
403
|
const [processing, setProcessing] = useState2(false);
|
|
375
404
|
const [buttonsReady, setButtonsReady] = useState2(false);
|
|
405
|
+
const [loadTimedOut, setLoadTimedOut] = useState2(false);
|
|
406
|
+
const capturedRef = useRef2(null);
|
|
407
|
+
const [completionPending, setCompletionPending] = useState2(false);
|
|
376
408
|
const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer();
|
|
377
409
|
const handleInit = useCallback(() => {
|
|
378
410
|
setButtonsReady(true);
|
|
379
411
|
}, []);
|
|
380
|
-
|
|
412
|
+
useEffect2(() => {
|
|
413
|
+
const onPageShow = (e) => {
|
|
414
|
+
if (e.persisted) setProcessing(false);
|
|
415
|
+
};
|
|
416
|
+
window.addEventListener("pageshow", onPageShow);
|
|
417
|
+
return () => window.removeEventListener("pageshow", onPageShow);
|
|
418
|
+
}, []);
|
|
419
|
+
useEffect2(() => {
|
|
420
|
+
if (!isResolved || buttonsReady) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const t = setTimeout(() => setLoadTimedOut(true), 1e4);
|
|
424
|
+
return () => clearTimeout(t);
|
|
425
|
+
}, [isResolved, buttonsReady]);
|
|
426
|
+
const finalizeCapturedPayment = useCallback(async () => {
|
|
427
|
+
const captured = capturedRef.current;
|
|
428
|
+
if (!captured) return;
|
|
429
|
+
setProcessing(true);
|
|
430
|
+
showProcessingOverlay();
|
|
431
|
+
setError(null);
|
|
432
|
+
try {
|
|
433
|
+
const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
|
|
434
|
+
await onPaid?.({ ...captured, ...completeResult });
|
|
435
|
+
setCompletionPending(false);
|
|
436
|
+
} catch (e) {
|
|
437
|
+
if (isNextRouterError(e)) return;
|
|
438
|
+
hideProcessingOverlay();
|
|
439
|
+
setProcessing(false);
|
|
440
|
+
setCompletionPending(true);
|
|
441
|
+
const msg = "Your payment was received, but finalizing your order failed. Please use the Retry button below \u2014 do not pay again.";
|
|
442
|
+
setError(msg);
|
|
443
|
+
onError?.(msg);
|
|
444
|
+
}
|
|
445
|
+
}, [baseUrl, cartId, publishableApiKey, onPaid, onError]);
|
|
446
|
+
const showSpinner = (isPending || isResolved && !buttonsReady) && !loadTimedOut;
|
|
381
447
|
if (!config.currency_supported) return null;
|
|
382
448
|
const containerWidth = BUTTON_WIDTH_MAP[config.button_width ?? "responsive"] ?? "100%";
|
|
383
449
|
return /* @__PURE__ */ jsxs2("div", { style: { width: containerWidth, position: "relative" }, children: [
|
|
@@ -516,6 +582,28 @@ function PayPalSmartButtons(props) {
|
|
|
516
582
|
]
|
|
517
583
|
}
|
|
518
584
|
),
|
|
585
|
+
loadTimedOut && !buttonsReady && !isRejected && /* @__PURE__ */ jsxs2(
|
|
586
|
+
"div",
|
|
587
|
+
{
|
|
588
|
+
role: "alert",
|
|
589
|
+
style: {
|
|
590
|
+
display: "flex",
|
|
591
|
+
alignItems: "flex-start",
|
|
592
|
+
gap: 8,
|
|
593
|
+
padding: "10px 14px",
|
|
594
|
+
background: "#fef2f2",
|
|
595
|
+
border: "1px solid #fecaca",
|
|
596
|
+
borderRadius: 8,
|
|
597
|
+
fontSize: 13,
|
|
598
|
+
color: "#b91c1c",
|
|
599
|
+
lineHeight: 1.5
|
|
600
|
+
},
|
|
601
|
+
children: [
|
|
602
|
+
/* @__PURE__ */ jsx3("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
|
|
603
|
+
/* @__PURE__ */ jsx3("span", { children: "PayPal is not available for this purchase. Please try a different payment method." })
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
),
|
|
519
607
|
isRejected && /* @__PURE__ */ jsxs2(
|
|
520
608
|
"div",
|
|
521
609
|
{
|
|
@@ -576,7 +664,7 @@ function PayPalSmartButtons(props) {
|
|
|
576
664
|
]
|
|
577
665
|
}
|
|
578
666
|
),
|
|
579
|
-
isResolved && /* @__PURE__ */ jsx3("div", { style: buttonsReady ? void 0 : { position: "absolute", left: -9999, opacity: 0, pointerEvents: "none" }, children: /* @__PURE__ */ jsx3(
|
|
667
|
+
isResolved && !completionPending && /* @__PURE__ */ jsx3("div", { style: buttonsReady ? void 0 : { position: "absolute", left: -9999, opacity: 0, pointerEvents: "none" }, children: /* @__PURE__ */ jsx3(
|
|
580
668
|
PayPalButtons,
|
|
581
669
|
{
|
|
582
670
|
forceReRender: [config.currency, config.intent, cartId],
|
|
@@ -590,6 +678,10 @@ function PayPalSmartButtons(props) {
|
|
|
590
678
|
},
|
|
591
679
|
createOrder: async () => {
|
|
592
680
|
if (processing) throw new Error("Payment already processing");
|
|
681
|
+
if (capturedRef.current) {
|
|
682
|
+
void finalizeCapturedPayment();
|
|
683
|
+
throw new Error("Your payment was already received \u2014 finalizing your order.");
|
|
684
|
+
}
|
|
593
685
|
setError(null);
|
|
594
686
|
setProcessing(true);
|
|
595
687
|
try {
|
|
@@ -608,8 +700,7 @@ function PayPalSmartButtons(props) {
|
|
|
608
700
|
const orderId = String(data?.orderID || "");
|
|
609
701
|
if (!orderId) throw new Error("PayPal order ID is missing from approval response");
|
|
610
702
|
const result = await api.captureOrder(cartId, orderId);
|
|
611
|
-
|
|
612
|
-
await onPaid?.({ ...result, ...completeResult });
|
|
703
|
+
capturedRef.current = result || {};
|
|
613
704
|
} catch (e) {
|
|
614
705
|
if (isNextRouterError(e)) return;
|
|
615
706
|
hideProcessingOverlay();
|
|
@@ -617,7 +708,9 @@ function PayPalSmartButtons(props) {
|
|
|
617
708
|
const msg = e instanceof Error ? e.message : "Payment capture failed";
|
|
618
709
|
setError(msg);
|
|
619
710
|
onError?.(msg);
|
|
711
|
+
return;
|
|
620
712
|
}
|
|
713
|
+
await finalizeCapturedPayment();
|
|
621
714
|
},
|
|
622
715
|
onCancel: () => {
|
|
623
716
|
hideProcessingOverlay();
|
|
@@ -632,6 +725,29 @@ function PayPalSmartButtons(props) {
|
|
|
632
725
|
}
|
|
633
726
|
}
|
|
634
727
|
) }),
|
|
728
|
+
completionPending && /* @__PURE__ */ jsx3(
|
|
729
|
+
"button",
|
|
730
|
+
{
|
|
731
|
+
type: "button",
|
|
732
|
+
onClick: () => void finalizeCapturedPayment(),
|
|
733
|
+
disabled: processing,
|
|
734
|
+
"aria-busy": processing,
|
|
735
|
+
style: {
|
|
736
|
+
width: "100%",
|
|
737
|
+
padding: "12px 16px",
|
|
738
|
+
marginTop: 4,
|
|
739
|
+
background: "#0070ba",
|
|
740
|
+
color: "#ffffff",
|
|
741
|
+
border: "none",
|
|
742
|
+
borderRadius: 8,
|
|
743
|
+
fontSize: 14,
|
|
744
|
+
fontWeight: 600,
|
|
745
|
+
cursor: processing ? "not-allowed" : "pointer",
|
|
746
|
+
opacity: processing ? 0.7 : 1
|
|
747
|
+
},
|
|
748
|
+
children: "Retry \u2014 finish placing my order"
|
|
749
|
+
}
|
|
750
|
+
),
|
|
635
751
|
error ? /* @__PURE__ */ jsxs2(
|
|
636
752
|
"div",
|
|
637
753
|
{
|
|
@@ -659,7 +775,7 @@ function PayPalSmartButtons(props) {
|
|
|
659
775
|
}
|
|
660
776
|
|
|
661
777
|
// src/components/PayPalAdvancedCard.tsx
|
|
662
|
-
import { useMemo as useMemo4, useRef as
|
|
778
|
+
import React3, { useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
|
|
663
779
|
import {
|
|
664
780
|
PayPalCardFieldsProvider,
|
|
665
781
|
PayPalNumberField,
|
|
@@ -718,7 +834,8 @@ var labelStyle = {
|
|
|
718
834
|
function SubmitButton({
|
|
719
835
|
disabled,
|
|
720
836
|
label,
|
|
721
|
-
onSubmit
|
|
837
|
+
onSubmit,
|
|
838
|
+
onSubmitError
|
|
722
839
|
}) {
|
|
723
840
|
const { cardFieldsForm } = usePayPalCardFields();
|
|
724
841
|
const isDisabled = disabled || !cardFieldsForm;
|
|
@@ -730,10 +847,10 @@ function SubmitButton({
|
|
|
730
847
|
"aria-busy": disabled,
|
|
731
848
|
onClick: () => {
|
|
732
849
|
onSubmit();
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
850
|
+
Promise.resolve(cardFieldsForm?.submit()).catch((e) => {
|
|
851
|
+
const msg = e instanceof Error && e.message ? e.message : "Card payment could not be submitted. Please check your card details.";
|
|
852
|
+
onSubmitError(msg);
|
|
853
|
+
});
|
|
737
854
|
},
|
|
738
855
|
style: {
|
|
739
856
|
width: "100%",
|
|
@@ -777,9 +894,47 @@ function PayPalAdvancedCard(props) {
|
|
|
777
894
|
);
|
|
778
895
|
const [error, setError] = useState3(null);
|
|
779
896
|
const [submitting, setSubmitting] = useState3(false);
|
|
780
|
-
const submittingRef =
|
|
781
|
-
const creatingOrderRef =
|
|
897
|
+
const submittingRef = useRef3(false);
|
|
898
|
+
const creatingOrderRef = useRef3(false);
|
|
899
|
+
const capturedRef = useRef3(null);
|
|
900
|
+
const [completionPending, setCompletionPending] = useState3(false);
|
|
782
901
|
const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer2();
|
|
902
|
+
const resetSubmitState = React3.useCallback(() => {
|
|
903
|
+
hideProcessingOverlay();
|
|
904
|
+
creatingOrderRef.current = false;
|
|
905
|
+
submittingRef.current = false;
|
|
906
|
+
setSubmitting(false);
|
|
907
|
+
}, []);
|
|
908
|
+
React3.useEffect(() => {
|
|
909
|
+
const onPageShow = (e) => {
|
|
910
|
+
if (e.persisted) {
|
|
911
|
+
creatingOrderRef.current = false;
|
|
912
|
+
submittingRef.current = false;
|
|
913
|
+
setSubmitting(false);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
window.addEventListener("pageshow", onPageShow);
|
|
917
|
+
return () => window.removeEventListener("pageshow", onPageShow);
|
|
918
|
+
}, []);
|
|
919
|
+
const finalizeCapturedPayment = React3.useCallback(async () => {
|
|
920
|
+
const captured = capturedRef.current;
|
|
921
|
+
if (!captured) return;
|
|
922
|
+
setSubmitting(true);
|
|
923
|
+
showProcessingOverlay();
|
|
924
|
+
setError(null);
|
|
925
|
+
try {
|
|
926
|
+
const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
|
|
927
|
+
await onPaid?.({ ...captured, ...completeResult });
|
|
928
|
+
setCompletionPending(false);
|
|
929
|
+
} catch (e) {
|
|
930
|
+
if (isNextRouterError(e)) return;
|
|
931
|
+
resetSubmitState();
|
|
932
|
+
setCompletionPending(true);
|
|
933
|
+
const msg = "Your payment was received, but finalizing your order failed. Please use the Retry button below \u2014 do not pay again.";
|
|
934
|
+
setError(msg);
|
|
935
|
+
onError?.(msg);
|
|
936
|
+
}
|
|
937
|
+
}, [baseUrl, cartId, publishableApiKey, onPaid, onError, resetSubmitState]);
|
|
783
938
|
if (!config.currency_supported) return null;
|
|
784
939
|
if (isRejected) {
|
|
785
940
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -968,6 +1123,10 @@ function PayPalAdvancedCard(props) {
|
|
|
968
1123
|
style: cardStyle,
|
|
969
1124
|
createOrder: async () => {
|
|
970
1125
|
if (creatingOrderRef.current) throw new Error("Payment already processing");
|
|
1126
|
+
if (capturedRef.current) {
|
|
1127
|
+
void finalizeCapturedPayment();
|
|
1128
|
+
throw new Error("Your payment was already received \u2014 finalizing your order.");
|
|
1129
|
+
}
|
|
971
1130
|
creatingOrderRef.current = true;
|
|
972
1131
|
try {
|
|
973
1132
|
setError(null);
|
|
@@ -983,19 +1142,18 @@ function PayPalAdvancedCard(props) {
|
|
|
983
1142
|
setError(null);
|
|
984
1143
|
showProcessingOverlay();
|
|
985
1144
|
const orderId = String(data?.orderID || "");
|
|
1145
|
+
if (!orderId) throw new Error("PayPal order ID is missing from approval response");
|
|
986
1146
|
const result = await api.captureOrder(cartId, orderId);
|
|
987
|
-
|
|
988
|
-
await onPaid?.({ ...result, ...completeResult });
|
|
1147
|
+
capturedRef.current = result || {};
|
|
989
1148
|
} catch (e) {
|
|
990
1149
|
if (isNextRouterError(e)) return;
|
|
991
|
-
|
|
992
|
-
creatingOrderRef.current = false;
|
|
993
|
-
submittingRef.current = false;
|
|
994
|
-
setSubmitting(false);
|
|
1150
|
+
resetSubmitState();
|
|
995
1151
|
const msg = e instanceof Error ? e.message : "Card payment failed";
|
|
996
1152
|
setError(msg);
|
|
997
1153
|
onError?.(msg);
|
|
1154
|
+
return;
|
|
998
1155
|
}
|
|
1156
|
+
await finalizeCapturedPayment();
|
|
999
1157
|
},
|
|
1000
1158
|
onCancel: () => {
|
|
1001
1159
|
hideProcessingOverlay();
|
|
@@ -1102,7 +1260,29 @@ function PayPalAdvancedCard(props) {
|
|
|
1102
1260
|
]
|
|
1103
1261
|
}
|
|
1104
1262
|
),
|
|
1105
|
-
/* @__PURE__ */ jsx4(
|
|
1263
|
+
completionPending ? /* @__PURE__ */ jsx4(
|
|
1264
|
+
"button",
|
|
1265
|
+
{
|
|
1266
|
+
type: "button",
|
|
1267
|
+
onClick: () => void finalizeCapturedPayment(),
|
|
1268
|
+
disabled: submitting,
|
|
1269
|
+
"aria-busy": submitting,
|
|
1270
|
+
style: {
|
|
1271
|
+
width: "100%",
|
|
1272
|
+
height: 48,
|
|
1273
|
+
padding: "0 20px",
|
|
1274
|
+
borderRadius: 8,
|
|
1275
|
+
border: "none",
|
|
1276
|
+
background: "#0070ba",
|
|
1277
|
+
color: "#ffffff",
|
|
1278
|
+
fontSize: 15,
|
|
1279
|
+
fontWeight: 600,
|
|
1280
|
+
cursor: submitting ? "not-allowed" : "pointer",
|
|
1281
|
+
opacity: submitting ? 0.7 : 1
|
|
1282
|
+
},
|
|
1283
|
+
children: "Retry \u2014 finish placing my order"
|
|
1284
|
+
}
|
|
1285
|
+
) : /* @__PURE__ */ jsx4(
|
|
1106
1286
|
SubmitButton,
|
|
1107
1287
|
{
|
|
1108
1288
|
disabled: submitting,
|
|
@@ -1113,6 +1293,11 @@ function PayPalAdvancedCard(props) {
|
|
|
1113
1293
|
setError(null);
|
|
1114
1294
|
setSubmitting(true);
|
|
1115
1295
|
showProcessingOverlay();
|
|
1296
|
+
},
|
|
1297
|
+
onSubmitError: (msg) => {
|
|
1298
|
+
resetSubmitState();
|
|
1299
|
+
setError(msg);
|
|
1300
|
+
onError?.(msg);
|
|
1116
1301
|
}
|
|
1117
1302
|
}
|
|
1118
1303
|
),
|
|
@@ -1454,7 +1639,7 @@ function PayPalPaymentSection({
|
|
|
1454
1639
|
}
|
|
1455
1640
|
|
|
1456
1641
|
// src/hooks/usePayPalPaymentMethods.ts
|
|
1457
|
-
import { useEffect as
|
|
1642
|
+
import { useEffect as useEffect3, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
|
|
1458
1643
|
var MAX_CACHE_ENTRIES2 = 50;
|
|
1459
1644
|
var _cache2 = /* @__PURE__ */ new Map();
|
|
1460
1645
|
var CACHE_TTL2 = 5 * 60 * 1e3;
|
|
@@ -1489,8 +1674,8 @@ function usePayPalPaymentMethods({
|
|
|
1489
1674
|
const hit = _cache2.get(key);
|
|
1490
1675
|
const seed = hit && Date.now() - hit.at < CACHE_TTL2 ? hit.result : null;
|
|
1491
1676
|
const [result, setResult] = useState4(seed ?? { ...DEFAULT_RESULT, loading: enabled });
|
|
1492
|
-
const fetchIdRef =
|
|
1493
|
-
|
|
1677
|
+
const fetchIdRef = useRef4(0);
|
|
1678
|
+
useEffect3(() => {
|
|
1494
1679
|
if (!enabled) {
|
|
1495
1680
|
setResult((prev) => ({ ...prev, loading: false }));
|
|
1496
1681
|
return;
|