@easypayment/medusa-paypal-ui 1.0.50 → 1.0.52
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 +0 -60
- package/dist/index.cjs +457 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -17
- package/dist/index.d.ts +11 -17
- package/dist/index.mjs +466 -155
- package/dist/index.mjs.map +1 -1
- package/dist/order.cjs +20 -7
- package/dist/order.cjs.map +1 -1
- package/dist/order.mjs +20 -7
- package/dist/order.mjs.map +1 -1
- package/package.json +6 -1
- package/src/adapters/MedusaNextPayPalAdapter.tsx +6 -14
- package/src/client/http.ts +15 -44
- package/src/client/paypal.ts +7 -37
- package/src/components/PayPalAdvancedCard.tsx +163 -42
- package/src/components/PayPalCurrencyNotice.tsx +1 -1
- package/src/components/PayPalPaymentSection.tsx +8 -14
- package/src/components/PayPalProvider.tsx +1 -1
- package/src/components/PayPalSmartButtons.tsx +192 -54
- package/src/hooks/usePayPalConfig.ts +13 -4
- package/src/hooks/usePayPalPaymentMethods.ts +14 -4
- package/src/index.ts +1 -0
- package/src/order.ts +20 -7
- package/src/utils/next-errors.ts +17 -0
- package/src/utils/processing-overlay.ts +55 -0
package/dist/index.mjs
CHANGED
|
@@ -13,7 +13,6 @@ function toHeaderRecord(headers) {
|
|
|
13
13
|
}
|
|
14
14
|
return { ...headers };
|
|
15
15
|
}
|
|
16
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
17
16
|
function createHttpClient(opts) {
|
|
18
17
|
const base = opts.baseUrl.replace(/\/+$/, "");
|
|
19
18
|
async function request(path, init) {
|
|
@@ -25,33 +24,25 @@ function createHttpClient(opts) {
|
|
|
25
24
|
if (opts.publishableApiKey) {
|
|
26
25
|
headers["x-publishable-api-key"] = opts.publishableApiKey;
|
|
27
26
|
}
|
|
27
|
+
const timeoutMs = 3e4;
|
|
28
|
+
let timeoutId;
|
|
28
29
|
const controller = new AbortController();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
timedOut = true;
|
|
32
|
-
controller.abort();
|
|
33
|
-
}, DEFAULT_TIMEOUT_MS);
|
|
34
|
-
const callerSignal = init?.signal;
|
|
35
|
-
if (callerSignal) {
|
|
36
|
-
if (callerSignal.aborted) {
|
|
37
|
-
controller.abort();
|
|
38
|
-
} else {
|
|
39
|
-
callerSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
40
|
-
}
|
|
30
|
+
if (!init?.signal) {
|
|
31
|
+
timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
41
32
|
}
|
|
33
|
+
const effectiveSignal = init?.signal || controller.signal;
|
|
42
34
|
let res;
|
|
43
|
-
let text;
|
|
44
35
|
try {
|
|
45
|
-
res = await fetch(url, { ...init, headers, credentials: "include", signal:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error("[PayPal] Request timed out. Please check your connection and try again.");
|
|
36
|
+
res = await fetch(url, { ...init, headers, credentials: "include", signal: effectiveSignal });
|
|
37
|
+
} catch (err) {
|
|
38
|
+
if (err instanceof Error && err.name === "AbortError" && !init?.signal) {
|
|
39
|
+
throw new Error(`[PayPal] Request to ${path} timed out after ${timeoutMs / 1e3}s`);
|
|
50
40
|
}
|
|
51
|
-
throw
|
|
41
|
+
throw err;
|
|
52
42
|
} finally {
|
|
53
|
-
clearTimeout(
|
|
43
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
54
44
|
}
|
|
45
|
+
const text = await res.text().catch(() => "");
|
|
55
46
|
if (!res.ok) {
|
|
56
47
|
if (res.status === 401) {
|
|
57
48
|
throw new Error(
|
|
@@ -63,21 +54,9 @@ function createHttpClient(opts) {
|
|
|
63
54
|
"[PayPal] Forbidden (403) \u2014 this request is not allowed. Check your CORS and API key settings."
|
|
64
55
|
);
|
|
65
56
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} catch {
|
|
70
|
-
parsed = null;
|
|
71
|
-
}
|
|
72
|
-
const backendMessage = typeof parsed?.message === "string" ? parsed.message : "";
|
|
73
|
-
const requestId = typeof parsed?.request_id === "string" ? parsed.request_id : "";
|
|
74
|
-
if (res.status >= 500) {
|
|
75
|
-
console.error(`[PayPal] ${path} failed (${res.status})`, text.slice(0, 1e3));
|
|
76
|
-
throw new Error(
|
|
77
|
-
`Payment service error${requestId ? ` (ref ${requestId})` : ""}. Please try again or contact support.`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
throw new Error(backendMessage || `Request failed (${res.status})`);
|
|
57
|
+
throw new Error(
|
|
58
|
+
text.slice(0, 500).replace(/<[^>]*>/g, "") || `Request failed (${res.status})`
|
|
59
|
+
);
|
|
81
60
|
}
|
|
82
61
|
if (!text) {
|
|
83
62
|
throw new Error(`[PayPal] Empty response body from ${path} (${res.status})`);
|
|
@@ -98,34 +77,13 @@ function createHttpClient(opts) {
|
|
|
98
77
|
}
|
|
99
78
|
|
|
100
79
|
// src/client/paypal.ts
|
|
101
|
-
function isNextRedirectError(e) {
|
|
102
|
-
if (!e || typeof e !== "object") {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
const digest = e.digest;
|
|
106
|
-
return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest === "NEXT_NOT_FOUND");
|
|
107
|
-
}
|
|
108
80
|
async function markPaymentComplete(baseUrl, cartId, publishableApiKey) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
body: JSON.stringify({ cart_id: cartId }),
|
|
117
|
-
credentials: "include"
|
|
118
|
-
});
|
|
119
|
-
const data = await resp.json().catch(() => ({}));
|
|
120
|
-
if (!resp.ok) {
|
|
121
|
-
console.warn("[PayPal] paypal-complete returned", resp.status, data);
|
|
122
|
-
return { ok: false, ...data || {} };
|
|
123
|
-
}
|
|
124
|
-
return { ok: true, ...data || {} };
|
|
125
|
-
} catch (e) {
|
|
126
|
-
console.warn("[PayPal] paypal-complete call failed:", e);
|
|
127
|
-
return { ok: false };
|
|
128
|
-
}
|
|
81
|
+
const http = createHttpClient({ baseUrl, publishableApiKey });
|
|
82
|
+
return http.request(`/store/paypal-complete`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
body: JSON.stringify({ cart_id: cartId })
|
|
86
|
+
});
|
|
129
87
|
}
|
|
130
88
|
function createPayPalStoreApi(opts) {
|
|
131
89
|
const http = createHttpClient(opts);
|
|
@@ -156,8 +114,16 @@ function createPayPalStoreApi(opts) {
|
|
|
156
114
|
|
|
157
115
|
// src/hooks/usePayPalConfig.ts
|
|
158
116
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
117
|
+
var MAX_CACHE_ENTRIES = 50;
|
|
159
118
|
var _cache = /* @__PURE__ */ new Map();
|
|
160
119
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
120
|
+
function cacheSet(key, value) {
|
|
121
|
+
if (_cache.size >= MAX_CACHE_ENTRIES) {
|
|
122
|
+
const oldest = _cache.keys().next().value;
|
|
123
|
+
if (oldest !== void 0) _cache.delete(oldest);
|
|
124
|
+
}
|
|
125
|
+
_cache.set(key, value);
|
|
126
|
+
}
|
|
161
127
|
function cacheKey(baseUrl, cartId) {
|
|
162
128
|
return `${baseUrl}::${cartId ?? ""}`;
|
|
163
129
|
}
|
|
@@ -201,12 +167,12 @@ function usePayPalConfig({
|
|
|
201
167
|
try {
|
|
202
168
|
const cfg = await api.getConfig(cartId, controller.signal);
|
|
203
169
|
if (!mounted || id !== fetchIdRef.current) return;
|
|
204
|
-
|
|
170
|
+
cacheSet(k, { config: cfg, at: Date.now() });
|
|
205
171
|
setConfig(cfg);
|
|
206
172
|
} catch (e) {
|
|
207
|
-
if (e
|
|
173
|
+
if (e instanceof Error && e.name === "AbortError") return;
|
|
208
174
|
if (!mounted || id !== fetchIdRef.current) return;
|
|
209
|
-
setError(e
|
|
175
|
+
setError(e instanceof Error ? e.message : "Failed to load PayPal config");
|
|
210
176
|
} finally {
|
|
211
177
|
if (mounted && id === fetchIdRef.current) setLoading(false);
|
|
212
178
|
}
|
|
@@ -236,7 +202,7 @@ function PayPalProvider(props) {
|
|
|
236
202
|
"disable-funding": disableFunding,
|
|
237
203
|
"data-partner-attribution-id": BN_CODE
|
|
238
204
|
};
|
|
239
|
-
}, [config, intent, disableFunding]);
|
|
205
|
+
}, [config.client_id, config.currency, config.client_token, intent, disableFunding]);
|
|
240
206
|
return /* @__PURE__ */ jsx(
|
|
241
207
|
PayPalScriptProvider,
|
|
242
208
|
{
|
|
@@ -251,17 +217,80 @@ function PayPalProvider(props) {
|
|
|
251
217
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
252
218
|
function PayPalCurrencyNotice({ config }) {
|
|
253
219
|
if (config.currency_supported) return null;
|
|
254
|
-
return /* @__PURE__ */ jsxs("div", { style: { padding: 12, border: "1px solid #ddd", borderRadius: 10 }, children: [
|
|
220
|
+
return /* @__PURE__ */ jsxs("div", { role: "alert", style: { padding: 12, border: "1px solid #ddd", borderRadius: 10 }, children: [
|
|
255
221
|
/* @__PURE__ */ jsx2("div", { style: { fontWeight: 600, marginBottom: 6 }, children: "PayPal currency issue" }),
|
|
256
222
|
/* @__PURE__ */ jsx2("ul", { style: { margin: 0, paddingLeft: 18 }, children: (config.currency_errors || []).map((e) => /* @__PURE__ */ jsx2("li", { children: e }, e)) })
|
|
257
223
|
] });
|
|
258
224
|
}
|
|
259
225
|
|
|
260
226
|
// src/components/PayPalSmartButtons.tsx
|
|
261
|
-
import { useMemo as useMemo3, useState as useState2 } from "react";
|
|
262
|
-
import { PayPalButtons } from "@paypal/react-paypal-js";
|
|
227
|
+
import { useCallback, useMemo as useMemo3, useState as useState2 } from "react";
|
|
228
|
+
import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
|
|
229
|
+
|
|
230
|
+
// src/utils/next-errors.ts
|
|
231
|
+
function isNextRouterError(e) {
|
|
232
|
+
if (typeof e !== "object" || e === null || !("digest" in e)) return false;
|
|
233
|
+
const digest = e.digest;
|
|
234
|
+
return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND"));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/utils/processing-overlay.ts
|
|
238
|
+
var OVERLAY_ID = "__pp_processing_overlay";
|
|
239
|
+
function showProcessingOverlay() {
|
|
240
|
+
if (typeof document === "undefined") return;
|
|
241
|
+
if (document.getElementById(OVERLAY_ID)) return;
|
|
242
|
+
const overlay = document.createElement("div");
|
|
243
|
+
overlay.id = OVERLAY_ID;
|
|
244
|
+
overlay.setAttribute("role", "status");
|
|
245
|
+
overlay.setAttribute("aria-label", "Processing payment");
|
|
246
|
+
overlay.innerHTML = `
|
|
247
|
+
<style>
|
|
248
|
+
@keyframes _pp_spin { to { transform: rotate(360deg) } }
|
|
249
|
+
@keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
|
|
250
|
+
</style>
|
|
251
|
+
<div style="position:relative;width:56px;height:56px">
|
|
252
|
+
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid #e5e7eb"></div>
|
|
253
|
+
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid transparent;border-top-color:#0070ba;animation:_pp_spin .8s linear infinite"></div>
|
|
254
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="#0070ba" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"
|
|
255
|
+
style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:24px;height:24px">
|
|
256
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
257
|
+
</svg>
|
|
258
|
+
</div>
|
|
259
|
+
<div style="text-align:center">
|
|
260
|
+
<div style="font-size:18px;font-weight:600;color:#111827;letter-spacing:-0.01em">Processing your payment</div>
|
|
261
|
+
<div style="font-size:14px;color:#6b7280;margin-top:6px;line-height:1.5">
|
|
262
|
+
This may take a few moments. Please do not close<br>or refresh this page.
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
<div style="width:200px;height:3px;background:#e5e7eb;border-radius:3px;overflow:hidden;margin-top:4px">
|
|
266
|
+
<div style="width:40%;height:100%;background:linear-gradient(90deg,#0070ba,#003087);border-radius:3px;animation:_pp_progress 1.5s ease-in-out infinite"></div>
|
|
267
|
+
</div>
|
|
268
|
+
`;
|
|
269
|
+
Object.assign(overlay.style, {
|
|
270
|
+
position: "fixed",
|
|
271
|
+
inset: "0",
|
|
272
|
+
zIndex: "99999",
|
|
273
|
+
background: "rgba(255,255,255,0.96)",
|
|
274
|
+
display: "flex",
|
|
275
|
+
flexDirection: "column",
|
|
276
|
+
alignItems: "center",
|
|
277
|
+
justifyContent: "center",
|
|
278
|
+
gap: "20px",
|
|
279
|
+
fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif"
|
|
280
|
+
});
|
|
281
|
+
document.body.appendChild(overlay);
|
|
282
|
+
}
|
|
283
|
+
function hideProcessingOverlay() {
|
|
284
|
+
if (typeof document === "undefined") return;
|
|
285
|
+
document.getElementById(OVERLAY_ID)?.remove();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/components/PayPalSmartButtons.tsx
|
|
263
289
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
264
|
-
var SPIN_STYLE =
|
|
290
|
+
var SPIN_STYLE = `
|
|
291
|
+
@keyframes _pp_spin { to { transform: rotate(360deg) } }
|
|
292
|
+
@keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
|
|
293
|
+
`;
|
|
265
294
|
var BUTTON_WIDTH_MAP = {
|
|
266
295
|
small: "300px",
|
|
267
296
|
medium: "400px",
|
|
@@ -276,6 +305,12 @@ function PayPalSmartButtons(props) {
|
|
|
276
305
|
);
|
|
277
306
|
const [error, setError] = useState2(null);
|
|
278
307
|
const [processing, setProcessing] = useState2(false);
|
|
308
|
+
const [buttonsReady, setButtonsReady] = useState2(false);
|
|
309
|
+
const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer();
|
|
310
|
+
const handleInit = useCallback(() => {
|
|
311
|
+
setButtonsReady(true);
|
|
312
|
+
}, []);
|
|
313
|
+
const showSpinner = isPending || isResolved && !buttonsReady;
|
|
279
314
|
if (!config.currency_supported) return null;
|
|
280
315
|
const containerWidth = BUTTON_WIDTH_MAP[config.button_width ?? "responsive"] ?? "100%";
|
|
281
316
|
return /* @__PURE__ */ jsxs2("div", { style: { width: containerWidth, position: "relative" }, children: [
|
|
@@ -283,41 +318,160 @@ function PayPalSmartButtons(props) {
|
|
|
283
318
|
processing && /* @__PURE__ */ jsxs2(
|
|
284
319
|
"div",
|
|
285
320
|
{
|
|
321
|
+
role: "status",
|
|
322
|
+
"aria-label": "Processing payment",
|
|
286
323
|
style: {
|
|
287
|
-
position: "
|
|
324
|
+
position: "fixed",
|
|
288
325
|
inset: 0,
|
|
289
|
-
zIndex:
|
|
290
|
-
background: "rgba(255,255,255,0.
|
|
291
|
-
borderRadius: 8,
|
|
326
|
+
zIndex: 9999,
|
|
327
|
+
background: "rgba(255,255,255,0.96)",
|
|
292
328
|
display: "flex",
|
|
293
329
|
flexDirection: "column",
|
|
294
330
|
alignItems: "center",
|
|
295
331
|
justifyContent: "center",
|
|
332
|
+
gap: 20
|
|
333
|
+
},
|
|
334
|
+
children: [
|
|
335
|
+
/* @__PURE__ */ jsxs2("div", { style: { position: "relative", width: 56, height: 56 }, children: [
|
|
336
|
+
/* @__PURE__ */ jsx3(
|
|
337
|
+
"div",
|
|
338
|
+
{
|
|
339
|
+
style: {
|
|
340
|
+
position: "absolute",
|
|
341
|
+
inset: 0,
|
|
342
|
+
borderRadius: "50%",
|
|
343
|
+
border: "3px solid #e5e7eb"
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
/* @__PURE__ */ jsx3(
|
|
348
|
+
"div",
|
|
349
|
+
{
|
|
350
|
+
style: {
|
|
351
|
+
position: "absolute",
|
|
352
|
+
inset: 0,
|
|
353
|
+
borderRadius: "50%",
|
|
354
|
+
border: "3px solid transparent",
|
|
355
|
+
borderTopColor: "#0070ba",
|
|
356
|
+
animation: "_pp_spin .8s linear infinite"
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
),
|
|
360
|
+
/* @__PURE__ */ jsx3(
|
|
361
|
+
"svg",
|
|
362
|
+
{
|
|
363
|
+
viewBox: "0 0 24 24",
|
|
364
|
+
fill: "none",
|
|
365
|
+
stroke: "#0070ba",
|
|
366
|
+
strokeWidth: "1.8",
|
|
367
|
+
strokeLinecap: "round",
|
|
368
|
+
strokeLinejoin: "round",
|
|
369
|
+
style: {
|
|
370
|
+
position: "absolute",
|
|
371
|
+
top: "50%",
|
|
372
|
+
left: "50%",
|
|
373
|
+
transform: "translate(-50%, -50%)",
|
|
374
|
+
width: 24,
|
|
375
|
+
height: 24
|
|
376
|
+
},
|
|
377
|
+
children: /* @__PURE__ */ jsx3("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
] }),
|
|
381
|
+
/* @__PURE__ */ jsxs2("div", { style: { textAlign: "center" }, children: [
|
|
382
|
+
/* @__PURE__ */ jsx3("div", { style: { fontSize: 18, fontWeight: 600, color: "#111827", letterSpacing: "-0.01em" }, children: "Processing your payment" }),
|
|
383
|
+
/* @__PURE__ */ jsxs2("div", { style: { fontSize: 14, color: "#6b7280", marginTop: 6, lineHeight: 1.5 }, children: [
|
|
384
|
+
"This may take a few moments. Please do not close",
|
|
385
|
+
/* @__PURE__ */ jsx3("br", {}),
|
|
386
|
+
"or refresh this page."
|
|
387
|
+
] })
|
|
388
|
+
] }),
|
|
389
|
+
/* @__PURE__ */ jsx3(
|
|
390
|
+
"div",
|
|
391
|
+
{
|
|
392
|
+
style: {
|
|
393
|
+
width: 200,
|
|
394
|
+
height: 3,
|
|
395
|
+
background: "#e5e7eb",
|
|
396
|
+
borderRadius: 3,
|
|
397
|
+
overflow: "hidden",
|
|
398
|
+
marginTop: 4
|
|
399
|
+
},
|
|
400
|
+
children: /* @__PURE__ */ jsx3(
|
|
401
|
+
"div",
|
|
402
|
+
{
|
|
403
|
+
style: {
|
|
404
|
+
width: "40%",
|
|
405
|
+
height: "100%",
|
|
406
|
+
background: "linear-gradient(90deg, #0070ba, #003087)",
|
|
407
|
+
borderRadius: 3,
|
|
408
|
+
animation: "_pp_progress 1.5s ease-in-out infinite"
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
)
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
),
|
|
417
|
+
showSpinner && /* @__PURE__ */ jsxs2(
|
|
418
|
+
"div",
|
|
419
|
+
{
|
|
420
|
+
role: "status",
|
|
421
|
+
"aria-label": "Loading PayPal",
|
|
422
|
+
style: {
|
|
423
|
+
display: "flex",
|
|
424
|
+
alignItems: "center",
|
|
425
|
+
justifyContent: "center",
|
|
296
426
|
gap: 10,
|
|
297
|
-
|
|
427
|
+
padding: "18px 16px",
|
|
428
|
+
background: "#f9fafb",
|
|
429
|
+
border: "1px solid #e5e7eb",
|
|
430
|
+
borderRadius: 8,
|
|
431
|
+
minHeight: 55
|
|
298
432
|
},
|
|
299
433
|
children: [
|
|
300
434
|
/* @__PURE__ */ jsx3(
|
|
301
435
|
"div",
|
|
302
436
|
{
|
|
303
437
|
style: {
|
|
304
|
-
width:
|
|
305
|
-
height:
|
|
438
|
+
width: 22,
|
|
439
|
+
height: 22,
|
|
306
440
|
borderRadius: "50%",
|
|
307
441
|
border: "2.5px solid #e5e7eb",
|
|
308
442
|
borderTopColor: "#0070ba",
|
|
309
|
-
animation: "_pp_spin .7s linear infinite"
|
|
443
|
+
animation: "_pp_spin .7s linear infinite",
|
|
444
|
+
flexShrink: 0
|
|
310
445
|
}
|
|
311
446
|
}
|
|
312
447
|
),
|
|
313
|
-
/* @__PURE__ */
|
|
314
|
-
/* @__PURE__ */ jsx3("div", { style: { fontSize: 13, color: "#374151", fontWeight: 500 }, children: "Processing your payment\u2026" }),
|
|
315
|
-
/* @__PURE__ */ jsx3("div", { style: { fontSize: 12, color: "#6b7280", marginTop: 4 }, children: "Please do not close or refresh this page" })
|
|
316
|
-
] })
|
|
448
|
+
/* @__PURE__ */ jsx3("div", { style: { fontSize: 13, fontWeight: 500, color: "#6b7280" }, children: "Loading PayPal\u2026" })
|
|
317
449
|
]
|
|
318
450
|
}
|
|
319
451
|
),
|
|
320
|
-
|
|
452
|
+
isRejected && /* @__PURE__ */ jsxs2(
|
|
453
|
+
"div",
|
|
454
|
+
{
|
|
455
|
+
role: "alert",
|
|
456
|
+
style: {
|
|
457
|
+
display: "flex",
|
|
458
|
+
alignItems: "flex-start",
|
|
459
|
+
gap: 8,
|
|
460
|
+
padding: "10px 14px",
|
|
461
|
+
background: "#fef2f2",
|
|
462
|
+
border: "1px solid #fecaca",
|
|
463
|
+
borderRadius: 8,
|
|
464
|
+
fontSize: 13,
|
|
465
|
+
color: "#b91c1c",
|
|
466
|
+
lineHeight: 1.5
|
|
467
|
+
},
|
|
468
|
+
children: [
|
|
469
|
+
/* @__PURE__ */ jsx3("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
|
|
470
|
+
/* @__PURE__ */ jsx3("span", { children: "PayPal failed to load. Please refresh the page or try a different payment method." })
|
|
471
|
+
]
|
|
472
|
+
}
|
|
473
|
+
),
|
|
474
|
+
config.environment === "sandbox" && buttonsReady && /* @__PURE__ */ jsxs2(
|
|
321
475
|
"div",
|
|
322
476
|
{
|
|
323
477
|
style: {
|
|
@@ -355,9 +509,11 @@ function PayPalSmartButtons(props) {
|
|
|
355
509
|
]
|
|
356
510
|
}
|
|
357
511
|
),
|
|
358
|
-
/* @__PURE__ */ jsx3(
|
|
512
|
+
isResolved && /* @__PURE__ */ jsx3("div", { style: buttonsReady ? void 0 : { position: "absolute", left: -9999, opacity: 0, pointerEvents: "none" }, children: /* @__PURE__ */ jsx3(
|
|
359
513
|
PayPalButtons,
|
|
360
514
|
{
|
|
515
|
+
forceReRender: [config.currency, config.intent, cartId],
|
|
516
|
+
onInit: handleInit,
|
|
361
517
|
style: {
|
|
362
518
|
layout: "vertical",
|
|
363
519
|
color: config.button_color,
|
|
@@ -366,40 +522,53 @@ function PayPalSmartButtons(props) {
|
|
|
366
522
|
height: config.button_height
|
|
367
523
|
},
|
|
368
524
|
createOrder: async () => {
|
|
525
|
+
if (processing) throw new Error("Payment already processing");
|
|
369
526
|
setError(null);
|
|
370
|
-
|
|
371
|
-
|
|
527
|
+
setProcessing(true);
|
|
528
|
+
try {
|
|
529
|
+
const r = await api.createOrder(cartId);
|
|
530
|
+
return r.id;
|
|
531
|
+
} catch (e) {
|
|
532
|
+
setProcessing(false);
|
|
533
|
+
throw e;
|
|
534
|
+
}
|
|
372
535
|
},
|
|
373
536
|
onApprove: async (data) => {
|
|
374
537
|
try {
|
|
375
538
|
setProcessing(true);
|
|
539
|
+
showProcessingOverlay();
|
|
376
540
|
setError(null);
|
|
377
541
|
const orderId = String(data?.orderID || "");
|
|
542
|
+
if (!orderId) throw new Error("PayPal order ID is missing from approval response");
|
|
378
543
|
const result = await api.captureOrder(cartId, orderId);
|
|
379
544
|
const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
|
|
380
|
-
onPaid?.({ ...result, ...completeResult });
|
|
545
|
+
await onPaid?.({ ...result, ...completeResult });
|
|
381
546
|
} catch (e) {
|
|
547
|
+
if (isNextRouterError(e)) throw e;
|
|
548
|
+
hideProcessingOverlay();
|
|
549
|
+
setProcessing(false);
|
|
382
550
|
const msg = e instanceof Error ? e.message : "Payment capture failed";
|
|
383
551
|
setError(msg);
|
|
384
552
|
onError?.(msg);
|
|
385
|
-
} finally {
|
|
386
|
-
setProcessing(false);
|
|
387
553
|
}
|
|
388
554
|
},
|
|
389
555
|
onCancel: () => {
|
|
556
|
+
hideProcessingOverlay();
|
|
390
557
|
setProcessing(false);
|
|
391
558
|
},
|
|
392
559
|
onError: (err) => {
|
|
560
|
+
hideProcessingOverlay();
|
|
393
561
|
setProcessing(false);
|
|
394
562
|
const msg = err instanceof Error ? err.message : err?.message || "PayPal error";
|
|
395
563
|
setError(msg);
|
|
396
564
|
onError?.(msg);
|
|
397
565
|
}
|
|
398
566
|
}
|
|
399
|
-
),
|
|
567
|
+
) }),
|
|
400
568
|
error ? /* @__PURE__ */ jsxs2(
|
|
401
569
|
"div",
|
|
402
570
|
{
|
|
571
|
+
role: "alert",
|
|
403
572
|
style: {
|
|
404
573
|
marginTop: 10,
|
|
405
574
|
display: "flex",
|
|
@@ -423,16 +592,20 @@ function PayPalSmartButtons(props) {
|
|
|
423
592
|
}
|
|
424
593
|
|
|
425
594
|
// src/components/PayPalAdvancedCard.tsx
|
|
426
|
-
import { useMemo as useMemo4, useState as useState3 } from "react";
|
|
595
|
+
import { useMemo as useMemo4, useRef as useRef2, useState as useState3 } from "react";
|
|
427
596
|
import {
|
|
428
597
|
PayPalCardFieldsProvider,
|
|
429
598
|
PayPalNumberField,
|
|
430
599
|
PayPalExpiryField,
|
|
431
600
|
PayPalCVVField,
|
|
432
|
-
usePayPalCardFields
|
|
601
|
+
usePayPalCardFields,
|
|
602
|
+
usePayPalScriptReducer as usePayPalScriptReducer2
|
|
433
603
|
} from "@paypal/react-paypal-js";
|
|
434
604
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
435
|
-
var SPIN_STYLE2 =
|
|
605
|
+
var SPIN_STYLE2 = `
|
|
606
|
+
@keyframes _pp_spin { to { transform: rotate(360deg) } }
|
|
607
|
+
@keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
|
|
608
|
+
`;
|
|
436
609
|
var cardStyle = {
|
|
437
610
|
input: {
|
|
438
611
|
"font-size": "15px",
|
|
@@ -478,8 +651,7 @@ var labelStyle = {
|
|
|
478
651
|
function SubmitButton({
|
|
479
652
|
disabled,
|
|
480
653
|
label,
|
|
481
|
-
onSubmit
|
|
482
|
-
onSubmitError
|
|
654
|
+
onSubmit
|
|
483
655
|
}) {
|
|
484
656
|
const { cardFieldsForm } = usePayPalCardFields();
|
|
485
657
|
const isDisabled = disabled || !cardFieldsForm;
|
|
@@ -488,15 +660,12 @@ function SubmitButton({
|
|
|
488
660
|
{
|
|
489
661
|
type: "button",
|
|
490
662
|
disabled: isDisabled,
|
|
663
|
+
"aria-busy": disabled,
|
|
491
664
|
onClick: () => {
|
|
492
665
|
onSubmit();
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
onSubmitError(
|
|
497
|
-
e instanceof Error ? e.message : "Card payment failed. Please try again."
|
|
498
|
-
);
|
|
499
|
-
});
|
|
666
|
+
try {
|
|
667
|
+
cardFieldsForm?.submit();
|
|
668
|
+
} catch {
|
|
500
669
|
}
|
|
501
670
|
},
|
|
502
671
|
style: {
|
|
@@ -541,7 +710,33 @@ function PayPalAdvancedCard(props) {
|
|
|
541
710
|
);
|
|
542
711
|
const [error, setError] = useState3(null);
|
|
543
712
|
const [submitting, setSubmitting] = useState3(false);
|
|
713
|
+
const submittingRef = useRef2(false);
|
|
714
|
+
const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer2();
|
|
544
715
|
if (!config.currency_supported) return null;
|
|
716
|
+
if (isRejected) {
|
|
717
|
+
return /* @__PURE__ */ jsxs3(
|
|
718
|
+
"div",
|
|
719
|
+
{
|
|
720
|
+
role: "alert",
|
|
721
|
+
style: {
|
|
722
|
+
display: "flex",
|
|
723
|
+
alignItems: "flex-start",
|
|
724
|
+
gap: 8,
|
|
725
|
+
padding: "10px 14px",
|
|
726
|
+
background: "#fef2f2",
|
|
727
|
+
border: "1px solid #fecaca",
|
|
728
|
+
borderRadius: 8,
|
|
729
|
+
fontSize: 13,
|
|
730
|
+
color: "#b91c1c",
|
|
731
|
+
lineHeight: 1.5
|
|
732
|
+
},
|
|
733
|
+
children: [
|
|
734
|
+
/* @__PURE__ */ jsx4("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
|
|
735
|
+
/* @__PURE__ */ jsx4("span", { children: "PayPal failed to load. Please refresh the page or try a different payment method." })
|
|
736
|
+
]
|
|
737
|
+
}
|
|
738
|
+
);
|
|
739
|
+
}
|
|
545
740
|
if (!config.client_token) {
|
|
546
741
|
return /* @__PURE__ */ jsx4(
|
|
547
742
|
"div",
|
|
@@ -559,42 +754,143 @@ function PayPalAdvancedCard(props) {
|
|
|
559
754
|
);
|
|
560
755
|
}
|
|
561
756
|
const isSandbox = config.environment === "sandbox";
|
|
757
|
+
if (isPending) {
|
|
758
|
+
return /* @__PURE__ */ jsxs3(
|
|
759
|
+
"div",
|
|
760
|
+
{
|
|
761
|
+
role: "status",
|
|
762
|
+
"aria-label": "Loading PayPal",
|
|
763
|
+
style: {
|
|
764
|
+
display: "flex",
|
|
765
|
+
alignItems: "center",
|
|
766
|
+
justifyContent: "center",
|
|
767
|
+
gap: 10,
|
|
768
|
+
padding: "18px 16px",
|
|
769
|
+
background: "#f9fafb",
|
|
770
|
+
border: "1px solid #e5e7eb",
|
|
771
|
+
borderRadius: 8,
|
|
772
|
+
minHeight: 55
|
|
773
|
+
},
|
|
774
|
+
children: [
|
|
775
|
+
/* @__PURE__ */ jsx4("style", { children: SPIN_STYLE2 }),
|
|
776
|
+
/* @__PURE__ */ jsx4(
|
|
777
|
+
"div",
|
|
778
|
+
{
|
|
779
|
+
style: {
|
|
780
|
+
width: 22,
|
|
781
|
+
height: 22,
|
|
782
|
+
borderRadius: "50%",
|
|
783
|
+
border: "2.5px solid #e5e7eb",
|
|
784
|
+
borderTopColor: "#0070ba",
|
|
785
|
+
animation: "_pp_spin .7s linear infinite",
|
|
786
|
+
flexShrink: 0
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
),
|
|
790
|
+
/* @__PURE__ */ jsx4("div", { style: { fontSize: 13, fontWeight: 500, color: "#6b7280" }, children: "Loading card fields\u2026" })
|
|
791
|
+
]
|
|
792
|
+
}
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
if (!isResolved) return null;
|
|
562
796
|
return /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
|
|
563
797
|
/* @__PURE__ */ jsx4("style", { children: SPIN_STYLE2 }),
|
|
564
798
|
submitting && /* @__PURE__ */ jsxs3(
|
|
565
799
|
"div",
|
|
566
800
|
{
|
|
801
|
+
role: "status",
|
|
802
|
+
"aria-label": "Processing payment",
|
|
567
803
|
style: {
|
|
568
|
-
position: "
|
|
804
|
+
position: "fixed",
|
|
569
805
|
inset: 0,
|
|
570
|
-
zIndex:
|
|
571
|
-
background: "rgba(255,255,255,0.
|
|
572
|
-
borderRadius: 12,
|
|
806
|
+
zIndex: 9999,
|
|
807
|
+
background: "rgba(255,255,255,0.96)",
|
|
573
808
|
display: "flex",
|
|
574
809
|
flexDirection: "column",
|
|
575
810
|
alignItems: "center",
|
|
576
811
|
justifyContent: "center",
|
|
577
|
-
gap:
|
|
578
|
-
minHeight: 180
|
|
812
|
+
gap: 20
|
|
579
813
|
},
|
|
580
814
|
children: [
|
|
815
|
+
/* @__PURE__ */ jsxs3("div", { style: { position: "relative", width: 56, height: 56 }, children: [
|
|
816
|
+
/* @__PURE__ */ jsx4(
|
|
817
|
+
"div",
|
|
818
|
+
{
|
|
819
|
+
style: {
|
|
820
|
+
position: "absolute",
|
|
821
|
+
inset: 0,
|
|
822
|
+
borderRadius: "50%",
|
|
823
|
+
border: "3px solid #e5e7eb"
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
),
|
|
827
|
+
/* @__PURE__ */ jsx4(
|
|
828
|
+
"div",
|
|
829
|
+
{
|
|
830
|
+
style: {
|
|
831
|
+
position: "absolute",
|
|
832
|
+
inset: 0,
|
|
833
|
+
borderRadius: "50%",
|
|
834
|
+
border: "3px solid transparent",
|
|
835
|
+
borderTopColor: "#2563eb",
|
|
836
|
+
animation: "_pp_spin .8s linear infinite"
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ jsx4(
|
|
841
|
+
"svg",
|
|
842
|
+
{
|
|
843
|
+
viewBox: "0 0 24 24",
|
|
844
|
+
fill: "none",
|
|
845
|
+
stroke: "#2563eb",
|
|
846
|
+
strokeWidth: "1.8",
|
|
847
|
+
strokeLinecap: "round",
|
|
848
|
+
strokeLinejoin: "round",
|
|
849
|
+
style: {
|
|
850
|
+
position: "absolute",
|
|
851
|
+
top: "50%",
|
|
852
|
+
left: "50%",
|
|
853
|
+
transform: "translate(-50%, -50%)",
|
|
854
|
+
width: 24,
|
|
855
|
+
height: 24
|
|
856
|
+
},
|
|
857
|
+
children: /* @__PURE__ */ jsx4("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
|
|
858
|
+
}
|
|
859
|
+
)
|
|
860
|
+
] }),
|
|
861
|
+
/* @__PURE__ */ jsxs3("div", { style: { textAlign: "center" }, children: [
|
|
862
|
+
/* @__PURE__ */ jsx4("div", { style: { fontSize: 18, fontWeight: 600, color: "#111827", letterSpacing: "-0.01em" }, children: "Processing your payment" }),
|
|
863
|
+
/* @__PURE__ */ jsxs3("div", { style: { fontSize: 14, color: "#6b7280", marginTop: 6, lineHeight: 1.5 }, children: [
|
|
864
|
+
"This may take a few moments. Please do not close",
|
|
865
|
+
/* @__PURE__ */ jsx4("br", {}),
|
|
866
|
+
"or refresh this page."
|
|
867
|
+
] })
|
|
868
|
+
] }),
|
|
581
869
|
/* @__PURE__ */ jsx4(
|
|
582
870
|
"div",
|
|
583
871
|
{
|
|
584
872
|
style: {
|
|
585
|
-
width:
|
|
586
|
-
height:
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
873
|
+
width: 200,
|
|
874
|
+
height: 3,
|
|
875
|
+
background: "#e5e7eb",
|
|
876
|
+
borderRadius: 3,
|
|
877
|
+
overflow: "hidden",
|
|
878
|
+
marginTop: 4
|
|
879
|
+
},
|
|
880
|
+
children: /* @__PURE__ */ jsx4(
|
|
881
|
+
"div",
|
|
882
|
+
{
|
|
883
|
+
style: {
|
|
884
|
+
width: "40%",
|
|
885
|
+
height: "100%",
|
|
886
|
+
background: "linear-gradient(90deg, #2563eb, #1d4ed8)",
|
|
887
|
+
borderRadius: 3,
|
|
888
|
+
animation: "_pp_progress 1.5s ease-in-out infinite"
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
)
|
|
592
892
|
}
|
|
593
|
-
)
|
|
594
|
-
/* @__PURE__ */ jsxs3("div", { style: { textAlign: "center" }, children: [
|
|
595
|
-
/* @__PURE__ */ jsx4("div", { style: { fontSize: 15, fontWeight: 600, color: "#111827" }, children: "Processing your payment\u2026" }),
|
|
596
|
-
/* @__PURE__ */ jsx4("div", { style: { fontSize: 13, color: "#6b7280", marginTop: 4 }, children: "Please do not close or refresh this page" })
|
|
597
|
-
] })
|
|
893
|
+
)
|
|
598
894
|
]
|
|
599
895
|
}
|
|
600
896
|
),
|
|
@@ -603,6 +899,7 @@ function PayPalAdvancedCard(props) {
|
|
|
603
899
|
{
|
|
604
900
|
style: cardStyle,
|
|
605
901
|
createOrder: async () => {
|
|
902
|
+
if (submittingRef.current) throw new Error("Payment already processing");
|
|
606
903
|
setError(null);
|
|
607
904
|
const r = await api.createOrder(cartId, true);
|
|
608
905
|
return r.id;
|
|
@@ -610,25 +907,32 @@ function PayPalAdvancedCard(props) {
|
|
|
610
907
|
onApprove: async (data) => {
|
|
611
908
|
try {
|
|
612
909
|
setError(null);
|
|
910
|
+
showProcessingOverlay();
|
|
613
911
|
const orderId = String(data?.orderID || "");
|
|
614
912
|
const result = await api.captureOrder(cartId, orderId);
|
|
615
913
|
const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
|
|
616
|
-
onPaid?.({ ...result, ...completeResult });
|
|
914
|
+
await onPaid?.({ ...result, ...completeResult });
|
|
617
915
|
} catch (e) {
|
|
916
|
+
if (isNextRouterError(e)) throw e;
|
|
917
|
+
hideProcessingOverlay();
|
|
918
|
+
submittingRef.current = false;
|
|
919
|
+
setSubmitting(false);
|
|
618
920
|
const msg = e instanceof Error ? e.message : "Card payment failed";
|
|
619
921
|
setError(msg);
|
|
620
922
|
onError?.(msg);
|
|
621
|
-
} finally {
|
|
622
|
-
setSubmitting(false);
|
|
623
923
|
}
|
|
624
924
|
},
|
|
625
925
|
onCancel: () => {
|
|
926
|
+
hideProcessingOverlay();
|
|
927
|
+
submittingRef.current = false;
|
|
626
928
|
setSubmitting(false);
|
|
627
929
|
},
|
|
628
930
|
onError: (e) => {
|
|
629
931
|
const msg = e instanceof Error ? e.message : e?.message || "CardFields error";
|
|
630
932
|
setError(msg);
|
|
631
933
|
onError?.(msg);
|
|
934
|
+
hideProcessingOverlay();
|
|
935
|
+
submittingRef.current = false;
|
|
632
936
|
setSubmitting(false);
|
|
633
937
|
},
|
|
634
938
|
children: /* @__PURE__ */ jsxs3(
|
|
@@ -727,19 +1031,18 @@ function PayPalAdvancedCard(props) {
|
|
|
727
1031
|
disabled: submitting,
|
|
728
1032
|
label: submitting ? "Processing\u2026" : "Pay by Card",
|
|
729
1033
|
onSubmit: () => {
|
|
1034
|
+
if (submittingRef.current) return;
|
|
1035
|
+
submittingRef.current = true;
|
|
730
1036
|
setError(null);
|
|
731
1037
|
setSubmitting(true);
|
|
732
|
-
|
|
733
|
-
onSubmitError: (msg) => {
|
|
734
|
-
setError(msg);
|
|
735
|
-
onError?.(msg);
|
|
736
|
-
setSubmitting(false);
|
|
1038
|
+
showProcessingOverlay();
|
|
737
1039
|
}
|
|
738
1040
|
}
|
|
739
1041
|
),
|
|
740
1042
|
error && /* @__PURE__ */ jsxs3(
|
|
741
1043
|
"div",
|
|
742
1044
|
{
|
|
1045
|
+
role: "alert",
|
|
743
1046
|
style: {
|
|
744
1047
|
display: "flex",
|
|
745
1048
|
alignItems: "flex-start",
|
|
@@ -767,7 +1070,7 @@ function PayPalAdvancedCard(props) {
|
|
|
767
1070
|
}
|
|
768
1071
|
|
|
769
1072
|
// src/adapters/MedusaNextPayPalAdapter.tsx
|
|
770
|
-
import { useCallback } from "react";
|
|
1073
|
+
import { useCallback as useCallback2 } from "react";
|
|
771
1074
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
772
1075
|
var DEFAULT_PAYPAL_PROVIDER_ID = "pp_paypal_paypal";
|
|
773
1076
|
var DEFAULT_PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
|
|
@@ -776,6 +1079,8 @@ function PayPalLoadingCard() {
|
|
|
776
1079
|
return /* @__PURE__ */ jsxs4(
|
|
777
1080
|
"div",
|
|
778
1081
|
{
|
|
1082
|
+
role: "status",
|
|
1083
|
+
"aria-label": "Connecting to PayPal",
|
|
779
1084
|
style: {
|
|
780
1085
|
display: "flex",
|
|
781
1086
|
alignItems: "center",
|
|
@@ -813,6 +1118,7 @@ function PayPalErrorCard({ message }) {
|
|
|
813
1118
|
return /* @__PURE__ */ jsx5(
|
|
814
1119
|
"div",
|
|
815
1120
|
{
|
|
1121
|
+
role: "alert",
|
|
816
1122
|
style: {
|
|
817
1123
|
padding: "12px 16px",
|
|
818
1124
|
background: "#fef2f2",
|
|
@@ -845,17 +1151,12 @@ function MedusaNextPayPalAdapter(props) {
|
|
|
845
1151
|
cartId,
|
|
846
1152
|
enabled: shouldRender
|
|
847
1153
|
});
|
|
848
|
-
const handlePaid =
|
|
849
|
-
(captureResult) => {
|
|
1154
|
+
const handlePaid = useCallback2(
|
|
1155
|
+
async (captureResult) => {
|
|
850
1156
|
onPaid?.(captureResult);
|
|
851
|
-
|
|
852
|
-
if (isNextRedirectError(e)) return;
|
|
853
|
-
onError?.(
|
|
854
|
-
e instanceof Error ? e.message : "Your payment was taken but the order could not be finalized. Please contact support before paying again."
|
|
855
|
-
);
|
|
856
|
-
});
|
|
1157
|
+
await onSuccess?.(cartId);
|
|
857
1158
|
},
|
|
858
|
-
[cartId, onPaid, onSuccess
|
|
1159
|
+
[cartId, onPaid, onSuccess]
|
|
859
1160
|
);
|
|
860
1161
|
if (!shouldRender) return null;
|
|
861
1162
|
if (loading) return /* @__PURE__ */ jsx5(PayPalLoadingCard, {});
|
|
@@ -900,7 +1201,7 @@ function MedusaNextPayPalAdapter(props) {
|
|
|
900
1201
|
}
|
|
901
1202
|
|
|
902
1203
|
// src/components/PayPalPaymentSection.tsx
|
|
903
|
-
import { useCallback as
|
|
1204
|
+
import { useCallback as useCallback3 } from "react";
|
|
904
1205
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
905
1206
|
var PAYPAL_WALLET_PROVIDER_ID = "pp_paypal_paypal";
|
|
906
1207
|
var PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
|
|
@@ -917,6 +1218,8 @@ function SessionInitCard() {
|
|
|
917
1218
|
return /* @__PURE__ */ jsxs5(
|
|
918
1219
|
"div",
|
|
919
1220
|
{
|
|
1221
|
+
role: "status",
|
|
1222
|
+
"aria-label": "Setting up payment",
|
|
920
1223
|
style: {
|
|
921
1224
|
display: "flex",
|
|
922
1225
|
alignItems: "center",
|
|
@@ -952,6 +1255,8 @@ function ConfigLoadingCard() {
|
|
|
952
1255
|
return /* @__PURE__ */ jsxs5(
|
|
953
1256
|
"div",
|
|
954
1257
|
{
|
|
1258
|
+
role: "status",
|
|
1259
|
+
"aria-label": "Connecting to PayPal",
|
|
955
1260
|
style: {
|
|
956
1261
|
display: "flex",
|
|
957
1262
|
alignItems: "center",
|
|
@@ -989,6 +1294,7 @@ function ErrorCard({ message }) {
|
|
|
989
1294
|
return /* @__PURE__ */ jsx6(
|
|
990
1295
|
"div",
|
|
991
1296
|
{
|
|
1297
|
+
role: "alert",
|
|
992
1298
|
style: {
|
|
993
1299
|
padding: "12px 16px",
|
|
994
1300
|
background: "#fef2f2",
|
|
@@ -1018,17 +1324,12 @@ function PayPalPaymentSection({
|
|
|
1018
1324
|
cartId,
|
|
1019
1325
|
enabled: shouldRender
|
|
1020
1326
|
});
|
|
1021
|
-
const handlePaid =
|
|
1022
|
-
(captureResult) => {
|
|
1327
|
+
const handlePaid = useCallback3(
|
|
1328
|
+
async (captureResult) => {
|
|
1023
1329
|
onPaid?.(captureResult);
|
|
1024
|
-
|
|
1025
|
-
if (isNextRedirectError(e)) return;
|
|
1026
|
-
onError?.(
|
|
1027
|
-
e instanceof Error ? e.message : "Your payment was taken but the order could not be finalized. Please contact support before paying again."
|
|
1028
|
-
);
|
|
1029
|
-
});
|
|
1330
|
+
await onSuccess?.(cartId);
|
|
1030
1331
|
},
|
|
1031
|
-
[cartId, onPaid, onSuccess
|
|
1332
|
+
[cartId, onPaid, onSuccess]
|
|
1032
1333
|
);
|
|
1033
1334
|
if (!shouldRender) return null;
|
|
1034
1335
|
if (sessionLoading) return /* @__PURE__ */ jsx6(SessionInitCard, {});
|
|
@@ -1076,9 +1377,17 @@ function PayPalPaymentSection({
|
|
|
1076
1377
|
}
|
|
1077
1378
|
|
|
1078
1379
|
// src/hooks/usePayPalPaymentMethods.ts
|
|
1079
|
-
import { useEffect as useEffect2, useMemo as useMemo5, useRef as
|
|
1380
|
+
import { useEffect as useEffect2, useMemo as useMemo5, useRef as useRef3, useState as useState4 } from "react";
|
|
1381
|
+
var MAX_CACHE_ENTRIES2 = 50;
|
|
1080
1382
|
var _cache2 = /* @__PURE__ */ new Map();
|
|
1081
1383
|
var CACHE_TTL2 = 5 * 60 * 1e3;
|
|
1384
|
+
function cacheSet2(key, value) {
|
|
1385
|
+
if (_cache2.size >= MAX_CACHE_ENTRIES2) {
|
|
1386
|
+
const oldest = _cache2.keys().next().value;
|
|
1387
|
+
if (oldest !== void 0) _cache2.delete(oldest);
|
|
1388
|
+
}
|
|
1389
|
+
_cache2.set(key, value);
|
|
1390
|
+
}
|
|
1082
1391
|
function cacheKey2(baseUrl, cartId) {
|
|
1083
1392
|
return `ppm::${baseUrl}::${cartId ?? ""}`;
|
|
1084
1393
|
}
|
|
@@ -1103,7 +1412,7 @@ function usePayPalPaymentMethods({
|
|
|
1103
1412
|
const hit = _cache2.get(key);
|
|
1104
1413
|
const seed = hit && Date.now() - hit.at < CACHE_TTL2 ? hit.result : null;
|
|
1105
1414
|
const [result, setResult] = useState4(seed ?? { ...DEFAULT_RESULT, loading: enabled });
|
|
1106
|
-
const fetchIdRef =
|
|
1415
|
+
const fetchIdRef = useRef3(0);
|
|
1107
1416
|
useEffect2(() => {
|
|
1108
1417
|
if (!enabled) {
|
|
1109
1418
|
setResult((prev) => ({ ...prev, loading: false }));
|
|
@@ -1130,12 +1439,13 @@ function usePayPalPaymentMethods({
|
|
|
1130
1439
|
cardTitle: typeof cfg.card_title === "string" && cfg.card_title ? cfg.card_title : "Credit or Debit Card",
|
|
1131
1440
|
loading: false
|
|
1132
1441
|
};
|
|
1133
|
-
|
|
1442
|
+
cacheSet2(k, { result: next, at: Date.now() });
|
|
1134
1443
|
setResult(next);
|
|
1135
1444
|
} catch (e) {
|
|
1136
|
-
|
|
1445
|
+
const msg = e instanceof Error ? e.message : "";
|
|
1446
|
+
if (e instanceof Error && e.name === "AbortError") return;
|
|
1137
1447
|
if (!mounted || id !== fetchIdRef.current) return;
|
|
1138
|
-
if (
|
|
1448
|
+
if (msg.includes("403") || msg.includes("Forbidden")) {
|
|
1139
1449
|
const disabled = {
|
|
1140
1450
|
...DEFAULT_RESULT,
|
|
1141
1451
|
paypalEnabled: false,
|
|
@@ -1165,9 +1475,10 @@ export {
|
|
|
1165
1475
|
PayPalProvider,
|
|
1166
1476
|
PayPalSmartButtons,
|
|
1167
1477
|
createPayPalStoreApi,
|
|
1168
|
-
|
|
1478
|
+
hideProcessingOverlay,
|
|
1169
1479
|
isPayPalProviderId,
|
|
1170
1480
|
markPaymentComplete,
|
|
1481
|
+
showProcessingOverlay,
|
|
1171
1482
|
usePayPalConfig,
|
|
1172
1483
|
usePayPalPaymentMethods
|
|
1173
1484
|
};
|