@easypayment/medusa-paypal-ui 1.2.8 → 1.3.0

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/dist/index.cjs CHANGED
@@ -31,8 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
+ HttpError: () => HttpError,
34
35
  MedusaNextPayPalAdapter: () => MedusaNextPayPalAdapter,
35
36
  PAYPAL_CARD_PROVIDER_ID: () => PAYPAL_CARD_PROVIDER_ID,
37
+ PAYPAL_PROVIDER_IDS: () => PAYPAL_PROVIDER_IDS,
36
38
  PAYPAL_WALLET_PROVIDER_ID: () => PAYPAL_WALLET_PROVIDER_ID,
37
39
  PayPalAdvancedCard: () => PayPalAdvancedCard,
38
40
  PayPalCurrencyNotice: () => PayPalCurrencyNotice,
@@ -40,9 +42,11 @@ __export(index_exports, {
40
42
  PayPalProvider: () => PayPalProvider,
41
43
  PayPalSmartButtons: () => PayPalSmartButtons,
42
44
  clearCartCaptured: () => clearCartCaptured,
45
+ createHttpClient: () => createHttpClient,
43
46
  createPayPalStoreApi: () => createPayPalStoreApi,
44
47
  generateIdempotencyKey: () => generateIdempotencyKey,
45
48
  hideProcessingOverlay: () => hideProcessingOverlay,
49
+ invalidatePayPalConfigCache: () => invalidatePayPalConfigCache,
46
50
  isPayPalProviderId: () => isPayPalProviderId,
47
51
  markCartCaptured: () => markCartCaptured,
48
52
  markPaymentComplete: () => markPaymentComplete,
@@ -104,7 +108,7 @@ function createHttpClient(opts) {
104
108
  if (opts.publishableApiKey) {
105
109
  headers["x-publishable-api-key"] = opts.publishableApiKey;
106
110
  }
107
- const timeoutMs = 3e4;
111
+ const timeoutMs = opts.timeoutMs ?? 3e4;
108
112
  const controller = new AbortController();
109
113
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
110
114
  const callerSignal = init?.signal ?? null;
@@ -161,14 +165,18 @@ function createHttpClient(opts) {
161
165
  }
162
166
  const contentType = res.headers.get("content-type") || "";
163
167
  if (!contentType.includes("application/json")) {
164
- throw new Error(
165
- `[PayPal] Unexpected non-JSON response (${contentType}) from ${path}`
168
+ throw new HttpError(
169
+ `[PayPal] Unexpected non-JSON response (${contentType}) from ${path}`,
170
+ res.status
166
171
  );
167
172
  }
168
173
  try {
169
174
  return JSON.parse(text);
170
175
  } catch {
171
- throw new Error(`[PayPal] Failed to parse JSON response from ${path}`);
176
+ throw new HttpError(
177
+ `[PayPal] Failed to parse JSON response from ${path}`,
178
+ res.status
179
+ );
172
180
  }
173
181
  }
174
182
  async function request(path, init, reqOpts) {
@@ -203,14 +211,21 @@ function generateIdempotencyKey() {
203
211
  }
204
212
  return `pp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
205
213
  }
206
- async function markPaymentComplete(baseUrl, cartId, publishableApiKey) {
207
- const http = createHttpClient({ baseUrl, publishableApiKey });
214
+ async function markPaymentComplete(baseUrl, cartId, options) {
215
+ const opts = typeof options === "string" ? { publishableApiKey: options } : options ?? {};
216
+ const http = createHttpClient({
217
+ baseUrl,
218
+ publishableApiKey: opts.publishableApiKey,
219
+ credentials: opts.credentials,
220
+ timeoutMs: opts.timeoutMs
221
+ });
208
222
  return http.request(
209
223
  `/store/paypal-complete`,
210
224
  {
211
225
  method: "POST",
212
226
  headers: { "Content-Type": "application/json" },
213
- body: JSON.stringify({ cart_id: cartId })
227
+ body: JSON.stringify({ cart_id: cartId }),
228
+ signal: opts.signal
214
229
  },
215
230
  { retries: 3 }
216
231
  );
@@ -222,10 +237,12 @@ function createPayPalStoreApi(opts) {
222
237
  const q = cartId ? `?cart_id=${encodeURIComponent(cartId)}` : "";
223
238
  return http.request(`/store/paypal/config${q}`, { signal });
224
239
  },
225
- getSettings() {
226
- return http.request(`/store/paypal/settings`);
240
+ getSettings(signal) {
241
+ return http.request(`/store/paypal/settings`, {
242
+ signal
243
+ });
227
244
  },
228
- createOrder(cartId, isCardPayment = false) {
245
+ createOrder(cartId, isCardPayment = false, signal) {
229
246
  return http.request(`/store/paypal/create-order`, {
230
247
  method: "POST",
231
248
  headers: {
@@ -238,16 +255,18 @@ function createPayPalStoreApi(opts) {
238
255
  body: JSON.stringify({
239
256
  cart_id: cartId,
240
257
  is_card_payment: isCardPayment
241
- })
258
+ }),
259
+ signal
242
260
  });
243
261
  },
244
- captureOrder(cartId, orderId) {
262
+ captureOrder(cartId, orderId, signal) {
245
263
  return http.request(
246
264
  `/store/paypal/capture-order`,
247
265
  {
248
266
  method: "POST",
249
267
  headers: { "Content-Type": "application/json" },
250
- body: JSON.stringify({ cart_id: cartId, order_id: orderId })
268
+ body: JSON.stringify({ cart_id: cartId, order_id: orderId }),
269
+ signal
251
270
  },
252
271
  { retries: 2 }
253
272
  );
@@ -255,39 +274,97 @@ function createPayPalStoreApi(opts) {
255
274
  };
256
275
  }
257
276
 
258
- // src/hooks/usePayPalConfig.ts
259
- var import_react = require("react");
277
+ // src/client/config-cache.ts
260
278
  var MAX_CACHE_ENTRIES = 50;
261
- var _cache = /* @__PURE__ */ new Map();
262
- var CACHE_TTL = 5 * 60 * 1e3;
263
- function cacheSet(key, value) {
264
- if (_cache.size >= MAX_CACHE_ENTRIES) {
265
- const oldest = _cache.keys().next().value;
266
- if (oldest !== void 0) _cache.delete(oldest);
279
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
280
+ var SEP = "";
281
+ var cache = /* @__PURE__ */ new Map();
282
+ var inflight = /* @__PURE__ */ new Map();
283
+ function configCacheKey(args) {
284
+ return [
285
+ args.baseUrl,
286
+ args.publishableApiKey ?? "",
287
+ args.cartId ?? "",
288
+ (args.currencyCode ?? "").toLowerCase()
289
+ ].join(SEP);
290
+ }
291
+ function getCachedPayPalConfig(key) {
292
+ const hit = cache.get(key);
293
+ if (!hit) return null;
294
+ if (Date.now() - hit.at >= CACHE_TTL_MS) {
295
+ cache.delete(key);
296
+ return null;
267
297
  }
268
- _cache.set(key, value);
298
+ return hit.config;
269
299
  }
270
- function cacheKey(baseUrl, cartId) {
271
- return `${baseUrl}::${cartId ?? ""}`;
300
+ function invalidatePayPalConfigCache(cartId) {
301
+ if (cartId === void 0) {
302
+ cache.clear();
303
+ return;
304
+ }
305
+ for (const key of [...cache.keys()]) {
306
+ if (key.split(SEP)[2] === cartId) cache.delete(key);
307
+ }
272
308
  }
309
+ function fetchPayPalConfigShared(key, fetcher) {
310
+ const existing = inflight.get(key);
311
+ if (existing) return existing;
312
+ const p = (async () => {
313
+ try {
314
+ const config = await fetcher();
315
+ if (cache.size >= MAX_CACHE_ENTRIES && !cache.has(key)) {
316
+ const oldest = cache.keys().next().value;
317
+ if (oldest !== void 0) cache.delete(oldest);
318
+ }
319
+ cache.set(key, { config, at: Date.now() });
320
+ return config;
321
+ } finally {
322
+ inflight.delete(key);
323
+ }
324
+ })();
325
+ inflight.set(key, p);
326
+ return p;
327
+ }
328
+
329
+ // src/constants.ts
330
+ var PAYPAL_WALLET_PROVIDER_ID = "pp_paypal_paypal";
331
+ var PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
332
+ var PAYPAL_PROVIDER_IDS = [
333
+ PAYPAL_WALLET_PROVIDER_ID,
334
+ PAYPAL_CARD_PROVIDER_ID
335
+ ];
336
+ function isPayPalProviderId(providerId) {
337
+ if (!providerId) return false;
338
+ return PAYPAL_PROVIDER_IDS.includes(providerId);
339
+ }
340
+
341
+ // src/hooks/usePayPalConfig.ts
342
+ var import_react = require("react");
273
343
  function usePayPalConfig({
274
344
  baseUrl,
275
345
  publishableApiKey,
276
346
  cartId,
347
+ currencyCode,
348
+ credentials,
277
349
  enabled = true
278
350
  }) {
279
351
  const api = (0, import_react.useMemo)(
280
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
281
- [baseUrl, publishableApiKey]
352
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
353
+ [baseUrl, publishableApiKey, credentials]
282
354
  );
283
- const key = cacheKey(baseUrl, cartId);
284
- const hit = _cache.get(key);
285
- const seedConfig = hit && Date.now() - hit.at < CACHE_TTL ? hit.config : null;
355
+ const key = configCacheKey({
356
+ baseUrl,
357
+ publishableApiKey,
358
+ cartId,
359
+ currencyCode
360
+ });
361
+ const seedConfig = getCachedPayPalConfig(key);
286
362
  const [config, setConfig] = (0, import_react.useState)(seedConfig);
287
363
  const [loading, setLoading] = (0, import_react.useState)(
288
364
  seedConfig === null && enabled
289
365
  );
290
366
  const [error, setError] = (0, import_react.useState)(null);
367
+ const [retryToken, setRetryToken] = (0, import_react.useState)(0);
291
368
  const fetchIdRef = (0, import_react.useRef)(0);
292
369
  (0, import_react.useEffect)(() => {
293
370
  if (!enabled) {
@@ -295,27 +372,26 @@ function usePayPalConfig({
295
372
  setError(null);
296
373
  return;
297
374
  }
298
- const k = cacheKey(baseUrl, cartId);
299
- const cached = _cache.get(k);
300
- if (cached && Date.now() - cached.at < CACHE_TTL) {
301
- setConfig(cached.config);
375
+ const cached = getCachedPayPalConfig(key);
376
+ if (cached) {
377
+ setConfig(cached);
302
378
  setLoading(false);
303
379
  setError(null);
304
380
  return;
305
381
  }
306
382
  const id = ++fetchIdRef.current;
307
383
  let mounted = true;
308
- const controller = new AbortController();
309
384
  setLoading(true);
310
385
  setError(null);
311
386
  (async () => {
312
387
  try {
313
- const cfg = await api.getConfig(cartId, controller.signal);
388
+ const cfg = await fetchPayPalConfigShared(
389
+ key,
390
+ () => api.getConfig(cartId)
391
+ );
314
392
  if (!mounted || id !== fetchIdRef.current) return;
315
- cacheSet(k, { config: cfg, at: Date.now() });
316
393
  setConfig(cfg);
317
394
  } catch (e) {
318
- if (e instanceof Error && e.name === "AbortError") return;
319
395
  if (!mounted || id !== fetchIdRef.current) return;
320
396
  setError(
321
397
  e instanceof Error ? e.message : "Failed to load PayPal config"
@@ -326,17 +402,17 @@ function usePayPalConfig({
326
402
  })();
327
403
  return () => {
328
404
  mounted = false;
329
- controller.abort();
330
405
  };
331
- }, [api, baseUrl, cartId, enabled]);
332
- return { config, loading, error };
406
+ }, [api, key, cartId, enabled, retryToken]);
407
+ const retry = (0, import_react.useMemo)(() => () => setRetryToken((t) => t + 1), []);
408
+ return { config, loading, error, retry };
333
409
  }
334
410
 
335
411
  // src/components/PayPalProvider.tsx
336
412
  var import_react2 = require("react");
337
413
  var import_react_paypal_js = require("@paypal/react-paypal-js");
338
414
  var import_jsx_runtime = require("react/jsx-runtime");
339
- var BN_CODE = "MBJTechnolabs_SI_SPB";
415
+ var BN_CODE = "MBJ_Medusa_SI";
340
416
  function PayPalProvider(props) {
341
417
  const { config, intent = "capture", disableFunding, children } = props;
342
418
  const options = (0, import_react2.useMemo)(() => {
@@ -430,15 +506,19 @@ function showProcessingOverlay() {
430
506
  const overlay = document.createElement("div");
431
507
  overlay.id = OVERLAY_ID;
432
508
  overlay.setAttribute("role", "status");
433
- overlay.setAttribute("aria-label", "Processing payment");
434
509
  overlay.innerHTML = `
435
510
  <style>
436
- @keyframes _pp_spin { to { transform: rotate(360deg) } }
437
- @keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
511
+ @keyframes _pp_ov_spin { to { transform: rotate(360deg) } }
512
+ @keyframes _pp_ov_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
513
+ ._pp-ov-spin { animation: _pp_ov_spin .8s linear infinite }
514
+ ._pp-ov-progress { animation: _pp_ov_progress 1.5s ease-in-out infinite }
515
+ @media (prefers-reduced-motion: reduce) {
516
+ ._pp-ov-spin, ._pp-ov-progress { animation: none }
517
+ }
438
518
  </style>
439
- <div style="position:relative;width:56px;height:56px">
519
+ <div style="position:relative;width:56px;height:56px" aria-hidden="true">
440
520
  <div style="position:absolute;inset:0;border-radius:50%;border:3px solid #e5e7eb"></div>
441
- <div style="position:absolute;inset:0;border-radius:50%;border:3px solid transparent;border-top-color:#0070ba;animation:_pp_spin .8s linear infinite"></div>
521
+ <div class="_pp-ov-spin" style="position:absolute;inset:0;border-radius:50%;border:3px solid transparent;border-top-color:#0070ba"></div>
442
522
  <svg viewBox="0 0 24 24" fill="none" stroke="#0070ba" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"
443
523
  style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:24px;height:24px">
444
524
  <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
@@ -450,8 +530,8 @@ function showProcessingOverlay() {
450
530
  This may take a few moments. Please do not close<br>or refresh this page.
451
531
  </div>
452
532
  </div>
453
- <div style="width:200px;height:3px;background:#e5e7eb;border-radius:3px;overflow:hidden;margin-top:4px">
454
- <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>
533
+ <div style="width:200px;height:3px;background:#e5e7eb;border-radius:3px;overflow:hidden;margin-top:4px" aria-hidden="true">
534
+ <div class="_pp-ov-progress" style="width:40%;height:100%;background:linear-gradient(90deg,#0070ba,#003087);border-radius:3px"></div>
455
535
  </div>
456
536
  `;
457
537
  Object.assign(overlay.style, {
@@ -472,16 +552,7 @@ function showProcessingOverlay() {
472
552
  if (location.href !== startUrl) hideProcessingOverlay();
473
553
  };
474
554
  window.addEventListener("popstate", onNav);
475
- const origPush = history.pushState.bind(history);
476
- const origReplace = history.replaceState.bind(history);
477
- history.pushState = function(...args) {
478
- origPush(...args);
479
- onNav();
480
- };
481
- history.replaceState = function(...args) {
482
- origReplace(...args);
483
- onNav();
484
- };
555
+ const urlWatcher = window.setInterval(onNav, 250);
485
556
  const onPageShow = (e) => {
486
557
  if (e.persisted) hideProcessingOverlay();
487
558
  };
@@ -489,8 +560,7 @@ function showProcessingOverlay() {
489
560
  cleanupNavListener = () => {
490
561
  window.removeEventListener("popstate", onNav);
491
562
  window.removeEventListener("pageshow", onPageShow);
492
- history.pushState = origPush;
493
- history.replaceState = origReplace;
563
+ window.clearInterval(urlWatcher);
494
564
  };
495
565
  }
496
566
  function hideProcessingOverlay() {
@@ -500,12 +570,224 @@ function hideProcessingOverlay() {
500
570
  document.getElementById(OVERLAY_ID)?.remove();
501
571
  }
502
572
 
503
- // src/components/PayPalSmartButtons.tsx
573
+ // src/components/internal/ui.tsx
504
574
  var import_jsx_runtime3 = require("react/jsx-runtime");
505
- var SPIN_STYLE = `
575
+ var ANIMATION_CSS = `
506
576
  @keyframes _pp_spin { to { transform: rotate(360deg) } }
507
577
  @keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
578
+ ._pp-spin { animation: _pp_spin .8s linear infinite }
579
+ ._pp-spin-sm { animation: _pp_spin .7s linear infinite }
580
+ ._pp-progress { animation: _pp_progress 1.5s ease-in-out infinite }
581
+ @media (prefers-reduced-motion: reduce) {
582
+ ._pp-spin, ._pp-spin-sm, ._pp-progress { animation: none }
583
+ }
508
584
  `;
585
+ function AnimationStyles() {
586
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: ANIMATION_CSS });
587
+ }
588
+ function ShieldIcon({
589
+ color,
590
+ size = 24,
591
+ style
592
+ }) {
593
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
594
+ "svg",
595
+ {
596
+ viewBox: "0 0 24 24",
597
+ fill: "none",
598
+ stroke: color,
599
+ strokeWidth: "1.8",
600
+ strokeLinecap: "round",
601
+ strokeLinejoin: "round",
602
+ "aria-hidden": "true",
603
+ style: { width: size, height: size, ...style },
604
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
605
+ }
606
+ );
607
+ }
608
+ function ProcessingOverlayView({ accent }) {
609
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
610
+ "div",
611
+ {
612
+ role: "status",
613
+ style: {
614
+ position: "fixed",
615
+ inset: 0,
616
+ zIndex: 9999,
617
+ background: "rgba(255,255,255,0.96)",
618
+ display: "flex",
619
+ flexDirection: "column",
620
+ alignItems: "center",
621
+ justifyContent: "center",
622
+ gap: 20
623
+ },
624
+ children: [
625
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", width: 56, height: 56 }, children: [
626
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
627
+ "div",
628
+ {
629
+ style: {
630
+ position: "absolute",
631
+ inset: 0,
632
+ borderRadius: "50%",
633
+ border: "3px solid #e5e7eb"
634
+ }
635
+ }
636
+ ),
637
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
638
+ "div",
639
+ {
640
+ className: "_pp-spin",
641
+ style: {
642
+ position: "absolute",
643
+ inset: 0,
644
+ borderRadius: "50%",
645
+ border: "3px solid transparent",
646
+ borderTopColor: accent
647
+ }
648
+ }
649
+ ),
650
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
651
+ ShieldIcon,
652
+ {
653
+ color: accent,
654
+ style: {
655
+ position: "absolute",
656
+ top: "50%",
657
+ left: "50%",
658
+ transform: "translate(-50%, -50%)"
659
+ }
660
+ }
661
+ )
662
+ ] }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center" }, children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
665
+ "div",
666
+ {
667
+ style: {
668
+ fontSize: 18,
669
+ fontWeight: 600,
670
+ color: "#111827",
671
+ letterSpacing: "-0.01em"
672
+ },
673
+ children: "Processing your payment"
674
+ }
675
+ ),
676
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
677
+ "div",
678
+ {
679
+ style: {
680
+ fontSize: 14,
681
+ color: "#6b7280",
682
+ marginTop: 6,
683
+ lineHeight: 1.5
684
+ },
685
+ children: [
686
+ "This may take a few moments. Please do not close",
687
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
688
+ "or refresh this page."
689
+ ]
690
+ }
691
+ )
692
+ ] }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
694
+ "div",
695
+ {
696
+ style: {
697
+ width: 200,
698
+ height: 3,
699
+ background: "#e5e7eb",
700
+ borderRadius: 3,
701
+ overflow: "hidden",
702
+ marginTop: 4
703
+ },
704
+ "aria-hidden": "true",
705
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
706
+ "div",
707
+ {
708
+ className: "_pp-progress",
709
+ style: {
710
+ width: "40%",
711
+ height: "100%",
712
+ background: `linear-gradient(90deg, ${accent}, #003087)`,
713
+ borderRadius: 3
714
+ }
715
+ }
716
+ )
717
+ }
718
+ )
719
+ ]
720
+ }
721
+ );
722
+ }
723
+ function LoadingCard({ label }) {
724
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
725
+ "div",
726
+ {
727
+ role: "status",
728
+ style: {
729
+ display: "flex",
730
+ alignItems: "center",
731
+ justifyContent: "center",
732
+ gap: 10,
733
+ padding: "18px 16px",
734
+ background: "#f9fafb",
735
+ border: "1px solid #e5e7eb",
736
+ borderRadius: 8,
737
+ minHeight: 55
738
+ },
739
+ children: [
740
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
741
+ "div",
742
+ {
743
+ className: "_pp-spin-sm",
744
+ "aria-hidden": "true",
745
+ style: {
746
+ width: 22,
747
+ height: 22,
748
+ borderRadius: "50%",
749
+ border: "2.5px solid #e5e7eb",
750
+ borderTopColor: "#0070ba",
751
+ flexShrink: 0
752
+ }
753
+ }
754
+ ),
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#6b7280" }, children: label })
756
+ ]
757
+ }
758
+ );
759
+ }
760
+ function ErrorBanner({
761
+ children,
762
+ style
763
+ }) {
764
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
765
+ "div",
766
+ {
767
+ role: "alert",
768
+ style: {
769
+ display: "flex",
770
+ alignItems: "flex-start",
771
+ gap: 8,
772
+ padding: "10px 14px",
773
+ background: "#fef2f2",
774
+ border: "1px solid #fecaca",
775
+ borderRadius: 8,
776
+ fontSize: 13,
777
+ color: "#b91c1c",
778
+ lineHeight: 1.5,
779
+ ...style
780
+ },
781
+ children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
783
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children })
784
+ ]
785
+ }
786
+ );
787
+ }
788
+
789
+ // src/components/PayPalSmartButtons.tsx
790
+ var import_jsx_runtime4 = require("react/jsx-runtime");
509
791
  var BUTTON_WIDTH_MAP = {
510
792
  small: "300px",
511
793
  medium: "400px",
@@ -513,24 +795,52 @@ var BUTTON_WIDTH_MAP = {
513
795
  responsive: "100%"
514
796
  };
515
797
  function PayPalSmartButtons(props) {
516
- const { baseUrl, publishableApiKey, cartId, config, onPaid, onError } = props;
798
+ const {
799
+ baseUrl,
800
+ publishableApiKey,
801
+ credentials,
802
+ cartId,
803
+ config,
804
+ onPaid,
805
+ onError
806
+ } = props;
517
807
  const api = (0, import_react3.useMemo)(
518
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
519
- [baseUrl, publishableApiKey]
808
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
809
+ [baseUrl, publishableApiKey, credentials]
520
810
  );
521
811
  const [error, setError] = (0, import_react3.useState)(null);
522
812
  const [processing, setProcessing] = (0, import_react3.useState)(false);
813
+ const processingRef = (0, import_react3.useRef)(false);
814
+ const [domOverlayUp, setDomOverlayUp] = (0, import_react3.useState)(false);
523
815
  const [buttonsReady, setButtonsReady] = (0, import_react3.useState)(false);
524
816
  const [loadTimedOut, setLoadTimedOut] = (0, import_react3.useState)(false);
525
817
  const capturedRef = (0, import_react3.useRef)(null);
526
818
  const [completionPending, setCompletionPending] = (0, import_react3.useState)(false);
527
819
  const [{ isPending, isResolved, isRejected }] = (0, import_react_paypal_js2.usePayPalScriptReducer)();
820
+ const startProcessing = (0, import_react3.useCallback)(() => {
821
+ processingRef.current = true;
822
+ setProcessing(true);
823
+ }, []);
824
+ const stopProcessing = (0, import_react3.useCallback)(() => {
825
+ hideProcessingOverlay();
826
+ setDomOverlayUp(false);
827
+ processingRef.current = false;
828
+ setProcessing(false);
829
+ }, []);
830
+ const showOverlay = (0, import_react3.useCallback)(() => {
831
+ showProcessingOverlay();
832
+ setDomOverlayUp(true);
833
+ }, []);
528
834
  const handleInit = (0, import_react3.useCallback)(() => {
529
835
  setButtonsReady(true);
530
836
  }, []);
531
837
  (0, import_react3.useEffect)(() => {
532
838
  const onPageShow = (e) => {
533
- if (e.persisted) setProcessing(false);
839
+ if (e.persisted) {
840
+ processingRef.current = false;
841
+ setProcessing(false);
842
+ setDomOverlayUp(false);
843
+ }
534
844
  };
535
845
  window.addEventListener("pageshow", onPageShow);
536
846
  return () => window.removeEventListener("pageshow", onPageShow);
@@ -552,234 +862,46 @@ function PayPalSmartButtons(props) {
552
862
  const finalizeCapturedPayment = (0, import_react3.useCallback)(async () => {
553
863
  const captured = capturedRef.current;
554
864
  if (!captured) return;
555
- setProcessing(true);
556
- showProcessingOverlay();
865
+ startProcessing();
866
+ showOverlay();
557
867
  setError(null);
558
868
  try {
559
- const completeResult = await markPaymentComplete(
560
- baseUrl,
561
- cartId,
562
- publishableApiKey
563
- );
869
+ const completeResult = await markPaymentComplete(baseUrl, cartId, {
870
+ publishableApiKey,
871
+ credentials
872
+ });
564
873
  clearCartCaptured(cartId);
565
874
  await onPaid?.({ ...captured, ...completeResult });
566
875
  setCompletionPending(false);
567
876
  } catch (e) {
568
877
  if (isNextRouterError(e)) return;
569
- hideProcessingOverlay();
570
- setProcessing(false);
878
+ stopProcessing();
571
879
  setCompletionPending(true);
572
880
  const msg = "Your payment was received, but finalizing your order failed. Please use the Retry button below \u2014 do not pay again.";
573
881
  setError(msg);
574
882
  onError?.(msg);
575
883
  }
576
- }, [baseUrl, cartId, publishableApiKey, onPaid, onError]);
884
+ }, [
885
+ baseUrl,
886
+ cartId,
887
+ publishableApiKey,
888
+ credentials,
889
+ onPaid,
890
+ onError,
891
+ startProcessing,
892
+ showOverlay,
893
+ stopProcessing
894
+ ]);
577
895
  const showSpinner = (isPending || isResolved && !buttonsReady) && !loadTimedOut;
578
896
  if (!config.currency_supported) return null;
579
897
  const containerWidth = BUTTON_WIDTH_MAP[config.button_width ?? "responsive"] ?? "100%";
580
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { width: containerWidth, position: "relative" }, children: [
581
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: SPIN_STYLE }),
582
- processing && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
583
- "div",
584
- {
585
- role: "status",
586
- "aria-label": "Processing payment",
587
- style: {
588
- position: "fixed",
589
- inset: 0,
590
- zIndex: 9999,
591
- background: "rgba(255,255,255,0.96)",
592
- display: "flex",
593
- flexDirection: "column",
594
- alignItems: "center",
595
- justifyContent: "center",
596
- gap: 20
597
- },
598
- children: [
599
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", width: 56, height: 56 }, children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
601
- "div",
602
- {
603
- style: {
604
- position: "absolute",
605
- inset: 0,
606
- borderRadius: "50%",
607
- border: "3px solid #e5e7eb"
608
- }
609
- }
610
- ),
611
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
612
- "div",
613
- {
614
- style: {
615
- position: "absolute",
616
- inset: 0,
617
- borderRadius: "50%",
618
- border: "3px solid transparent",
619
- borderTopColor: "#0070ba",
620
- animation: "_pp_spin .8s linear infinite"
621
- }
622
- }
623
- ),
624
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
625
- "svg",
626
- {
627
- viewBox: "0 0 24 24",
628
- fill: "none",
629
- stroke: "#0070ba",
630
- strokeWidth: "1.8",
631
- strokeLinecap: "round",
632
- strokeLinejoin: "round",
633
- style: {
634
- position: "absolute",
635
- top: "50%",
636
- left: "50%",
637
- transform: "translate(-50%, -50%)",
638
- width: 24,
639
- height: 24
640
- },
641
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
642
- }
643
- )
644
- ] }),
645
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center" }, children: [
646
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
647
- "div",
648
- {
649
- style: {
650
- fontSize: 18,
651
- fontWeight: 600,
652
- color: "#111827",
653
- letterSpacing: "-0.01em"
654
- },
655
- children: "Processing your payment"
656
- }
657
- ),
658
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
659
- "div",
660
- {
661
- style: {
662
- fontSize: 14,
663
- color: "#6b7280",
664
- marginTop: 6,
665
- lineHeight: 1.5
666
- },
667
- children: [
668
- "This may take a few moments. Please do not close",
669
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
670
- "or refresh this page."
671
- ]
672
- }
673
- )
674
- ] }),
675
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
676
- "div",
677
- {
678
- style: {
679
- width: 200,
680
- height: 3,
681
- background: "#e5e7eb",
682
- borderRadius: 3,
683
- overflow: "hidden",
684
- marginTop: 4
685
- },
686
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
687
- "div",
688
- {
689
- style: {
690
- width: "40%",
691
- height: "100%",
692
- background: "linear-gradient(90deg, #0070ba, #003087)",
693
- borderRadius: 3,
694
- animation: "_pp_progress 1.5s ease-in-out infinite"
695
- }
696
- }
697
- )
698
- }
699
- )
700
- ]
701
- }
702
- ),
703
- showSpinner && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
704
- "div",
705
- {
706
- role: "status",
707
- "aria-label": "Loading PayPal",
708
- style: {
709
- display: "flex",
710
- alignItems: "center",
711
- justifyContent: "center",
712
- gap: 10,
713
- padding: "18px 16px",
714
- background: "#f9fafb",
715
- border: "1px solid #e5e7eb",
716
- borderRadius: 8,
717
- minHeight: 55
718
- },
719
- children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
721
- "div",
722
- {
723
- style: {
724
- width: 22,
725
- height: 22,
726
- borderRadius: "50%",
727
- border: "2.5px solid #e5e7eb",
728
- borderTopColor: "#0070ba",
729
- animation: "_pp_spin .7s linear infinite",
730
- flexShrink: 0
731
- }
732
- }
733
- ),
734
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#6b7280" }, children: "Loading PayPal\u2026" })
735
- ]
736
- }
737
- ),
738
- loadTimedOut && !buttonsReady && !isRejected && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
739
- "div",
740
- {
741
- role: "alert",
742
- style: {
743
- display: "flex",
744
- alignItems: "flex-start",
745
- gap: 8,
746
- padding: "10px 14px",
747
- background: "#fef2f2",
748
- border: "1px solid #fecaca",
749
- borderRadius: 8,
750
- fontSize: 13,
751
- color: "#b91c1c",
752
- lineHeight: 1.5
753
- },
754
- children: [
755
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
756
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "PayPal is not available for this purchase. Please try a different payment method." })
757
- ]
758
- }
759
- ),
760
- isRejected && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
761
- "div",
762
- {
763
- role: "alert",
764
- style: {
765
- display: "flex",
766
- alignItems: "flex-start",
767
- gap: 8,
768
- padding: "10px 14px",
769
- background: "#fef2f2",
770
- border: "1px solid #fecaca",
771
- borderRadius: 8,
772
- fontSize: 13,
773
- color: "#b91c1c",
774
- lineHeight: 1.5
775
- },
776
- children: [
777
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
778
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "PayPal failed to load. Please refresh the page or try a different payment method." })
779
- ]
780
- }
781
- ),
782
- config.environment === "sandbox" && buttonsReady && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
898
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { width: containerWidth, position: "relative" }, children: [
899
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AnimationStyles, {}),
900
+ processing && !domOverlayUp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ProcessingOverlayView, { accent: "#0070ba" }),
901
+ showSpinner && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoadingCard, { label: "Loading PayPal\u2026" }),
902
+ loadTimedOut && !buttonsReady && !isRejected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ErrorBanner, { children: "PayPal is not available for this purchase. Please try a different payment method." }),
903
+ isRejected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ErrorBanner, { children: "PayPal failed to load. Please refresh the page or try a different payment method." }),
904
+ config.environment === "sandbox" && buttonsReady && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
783
905
  "div",
784
906
  {
785
907
  style: {
@@ -796,7 +918,7 @@ function PayPalSmartButtons(props) {
796
918
  marginBottom: 10
797
919
  },
798
920
  children: [
799
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
921
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
800
922
  "svg",
801
923
  {
802
924
  width: "16",
@@ -806,18 +928,19 @@ function PayPalSmartButtons(props) {
806
928
  stroke: "#1e40af",
807
929
  strokeWidth: "2",
808
930
  strokeLinecap: "round",
931
+ "aria-hidden": "true",
809
932
  style: { flexShrink: 0, marginTop: 1 },
810
933
  children: [
811
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
812
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 8v4M12 16h.01" })
934
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 8v4M12 16h.01" })
813
936
  ]
814
937
  }
815
938
  ),
816
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Sandbox mode \u2014 you will not be charged. Use your PayPal sandbox account to complete the payment." })
939
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Sandbox mode \u2014 you will not be charged. Use your PayPal sandbox account to complete the payment." })
817
940
  ]
818
941
  }
819
942
  ),
820
- isResolved && !completionPending && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
943
+ isResolved && !completionPending && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
821
944
  "div",
822
945
  {
823
946
  style: buttonsReady ? void 0 : {
@@ -826,7 +949,7 @@ function PayPalSmartButtons(props) {
826
949
  opacity: 0,
827
950
  pointerEvents: "none"
828
951
  },
829
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
952
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
830
953
  import_react_paypal_js2.PayPalButtons,
831
954
  {
832
955
  forceReRender: [config.currency, config.intent, cartId],
@@ -839,7 +962,8 @@ function PayPalSmartButtons(props) {
839
962
  height: config.button_height
840
963
  },
841
964
  createOrder: async () => {
842
- if (processing) throw new Error("Payment already processing");
965
+ if (processingRef.current)
966
+ throw new Error("Payment already processing");
843
967
  if (capturedRef.current) {
844
968
  void finalizeCapturedPayment();
845
969
  throw new Error(
@@ -847,19 +971,19 @@ function PayPalSmartButtons(props) {
847
971
  );
848
972
  }
849
973
  setError(null);
850
- setProcessing(true);
974
+ startProcessing();
851
975
  try {
852
976
  const r = await api.createOrder(cartId);
853
977
  return r.id;
854
978
  } catch (e) {
855
- setProcessing(false);
979
+ stopProcessing();
856
980
  throw e;
857
981
  }
858
982
  },
859
983
  onApprove: async (data) => {
860
984
  try {
861
- setProcessing(true);
862
- showProcessingOverlay();
985
+ startProcessing();
986
+ showOverlay();
863
987
  setError(null);
864
988
  const orderId = String(data?.orderID || "");
865
989
  if (!orderId)
@@ -871,8 +995,7 @@ function PayPalSmartButtons(props) {
871
995
  markCartCaptured(cartId);
872
996
  } catch (e) {
873
997
  if (isNextRouterError(e)) return;
874
- hideProcessingOverlay();
875
- setProcessing(false);
998
+ stopProcessing();
876
999
  const msg = e instanceof Error ? e.message : "Payment capture failed";
877
1000
  setError(msg);
878
1001
  onError?.(msg);
@@ -881,12 +1004,10 @@ function PayPalSmartButtons(props) {
881
1004
  await finalizeCapturedPayment();
882
1005
  },
883
1006
  onCancel: () => {
884
- hideProcessingOverlay();
885
- setProcessing(false);
1007
+ stopProcessing();
886
1008
  },
887
1009
  onError: (err) => {
888
- hideProcessingOverlay();
889
- setProcessing(false);
1010
+ stopProcessing();
890
1011
  const msg = err instanceof Error ? err.message : err?.message || "PayPal error";
891
1012
  setError(msg);
892
1013
  onError?.(msg);
@@ -895,7 +1016,7 @@ function PayPalSmartButtons(props) {
895
1016
  )
896
1017
  }
897
1018
  ),
898
- completionPending && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1019
+ completionPending && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
899
1020
  "button",
900
1021
  {
901
1022
  type: "button",
@@ -918,40 +1039,14 @@ function PayPalSmartButtons(props) {
918
1039
  children: "Retry \u2014 finish placing my order"
919
1040
  }
920
1041
  ),
921
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
922
- "div",
923
- {
924
- role: "alert",
925
- style: {
926
- marginTop: 10,
927
- display: "flex",
928
- alignItems: "flex-start",
929
- gap: 8,
930
- padding: "10px 14px",
931
- background: "#fef2f2",
932
- border: "1px solid #fecaca",
933
- borderRadius: 8,
934
- fontSize: 13,
935
- color: "#b91c1c",
936
- lineHeight: 1.5
937
- },
938
- children: [
939
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
940
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: error })
941
- ]
942
- }
943
- ) : null
1042
+ error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ErrorBanner, { style: { marginTop: 10 }, children: error }) : null
944
1043
  ] });
945
1044
  }
946
1045
 
947
1046
  // src/components/PayPalAdvancedCard.tsx
948
1047
  var import_react4 = __toESM(require("react"), 1);
949
1048
  var import_react_paypal_js3 = require("@paypal/react-paypal-js");
950
- var import_jsx_runtime4 = require("react/jsx-runtime");
951
- var SPIN_STYLE2 = `
952
- @keyframes _pp_spin { to { transform: rotate(360deg) } }
953
- @keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
954
- `;
1049
+ var import_jsx_runtime5 = require("react/jsx-runtime");
955
1050
  var cardStyle = {
956
1051
  input: {
957
1052
  "font-size": "15px",
@@ -996,18 +1091,19 @@ var labelStyle = {
996
1091
  };
997
1092
  function SubmitButton({
998
1093
  disabled,
1094
+ busy,
999
1095
  label,
1000
1096
  onSubmit,
1001
1097
  onSubmitError
1002
1098
  }) {
1003
1099
  const { cardFieldsForm } = (0, import_react_paypal_js3.usePayPalCardFields)();
1004
1100
  const isDisabled = disabled || !cardFieldsForm;
1005
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1101
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1006
1102
  "button",
1007
1103
  {
1008
1104
  type: "button",
1009
1105
  disabled: isDisabled,
1010
- "aria-busy": disabled,
1106
+ "aria-busy": busy,
1011
1107
  onClick: () => {
1012
1108
  onSubmit();
1013
1109
  Promise.resolve(cardFieldsForm?.submit()).catch((e) => {
@@ -1050,20 +1146,34 @@ function SubmitButton({
1050
1146
  );
1051
1147
  }
1052
1148
  function PayPalAdvancedCard(props) {
1053
- const { baseUrl, publishableApiKey, cartId, config, onPaid, onError } = props;
1149
+ const {
1150
+ baseUrl,
1151
+ publishableApiKey,
1152
+ credentials,
1153
+ cartId,
1154
+ config,
1155
+ onPaid,
1156
+ onError
1157
+ } = props;
1054
1158
  const api = (0, import_react4.useMemo)(
1055
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
1056
- [baseUrl, publishableApiKey]
1159
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
1160
+ [baseUrl, publishableApiKey, credentials]
1057
1161
  );
1058
1162
  const [error, setError] = (0, import_react4.useState)(null);
1059
1163
  const [submitting, setSubmitting] = (0, import_react4.useState)(false);
1060
1164
  const submittingRef = (0, import_react4.useRef)(false);
1061
1165
  const creatingOrderRef = (0, import_react4.useRef)(false);
1166
+ const [domOverlayUp, setDomOverlayUp] = (0, import_react4.useState)(false);
1062
1167
  const capturedRef = (0, import_react4.useRef)(null);
1063
1168
  const [completionPending, setCompletionPending] = (0, import_react4.useState)(false);
1064
1169
  const [{ isPending, isResolved, isRejected }] = (0, import_react_paypal_js3.usePayPalScriptReducer)();
1170
+ const showOverlay = import_react4.default.useCallback(() => {
1171
+ showProcessingOverlay();
1172
+ setDomOverlayUp(true);
1173
+ }, []);
1065
1174
  const resetSubmitState = import_react4.default.useCallback(() => {
1066
1175
  hideProcessingOverlay();
1176
+ setDomOverlayUp(false);
1067
1177
  creatingOrderRef.current = false;
1068
1178
  submittingRef.current = false;
1069
1179
  setSubmitting(false);
@@ -1074,6 +1184,7 @@ function PayPalAdvancedCard(props) {
1074
1184
  creatingOrderRef.current = false;
1075
1185
  submittingRef.current = false;
1076
1186
  setSubmitting(false);
1187
+ setDomOverlayUp(false);
1077
1188
  }
1078
1189
  };
1079
1190
  window.addEventListener("pageshow", onPageShow);
@@ -1091,14 +1202,13 @@ function PayPalAdvancedCard(props) {
1091
1202
  const captured = capturedRef.current;
1092
1203
  if (!captured) return;
1093
1204
  setSubmitting(true);
1094
- showProcessingOverlay();
1205
+ showOverlay();
1095
1206
  setError(null);
1096
1207
  try {
1097
- const completeResult = await markPaymentComplete(
1098
- baseUrl,
1099
- cartId,
1100
- publishableApiKey
1101
- );
1208
+ const completeResult = await markPaymentComplete(baseUrl, cartId, {
1209
+ publishableApiKey,
1210
+ credentials
1211
+ });
1102
1212
  clearCartCaptured(cartId);
1103
1213
  await onPaid?.({ ...captured, ...completeResult });
1104
1214
  setCompletionPending(false);
@@ -1110,34 +1220,22 @@ function PayPalAdvancedCard(props) {
1110
1220
  setError(msg);
1111
1221
  onError?.(msg);
1112
1222
  }
1113
- }, [baseUrl, cartId, publishableApiKey, onPaid, onError, resetSubmitState]);
1223
+ }, [
1224
+ baseUrl,
1225
+ cartId,
1226
+ publishableApiKey,
1227
+ credentials,
1228
+ onPaid,
1229
+ onError,
1230
+ showOverlay,
1231
+ resetSubmitState
1232
+ ]);
1114
1233
  if (!config.currency_supported) return null;
1115
1234
  if (isRejected) {
1116
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1117
- "div",
1118
- {
1119
- role: "alert",
1120
- style: {
1121
- display: "flex",
1122
- alignItems: "flex-start",
1123
- gap: 8,
1124
- padding: "10px 14px",
1125
- background: "#fef2f2",
1126
- border: "1px solid #fecaca",
1127
- borderRadius: 8,
1128
- fontSize: 13,
1129
- color: "#b91c1c",
1130
- lineHeight: 1.5
1131
- },
1132
- children: [
1133
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "PayPal failed to load. Please refresh the page or try a different payment method." })
1135
- ]
1136
- }
1137
- );
1235
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ErrorBanner, { children: "PayPal failed to load. Please refresh the page or try a different payment method." });
1138
1236
  }
1139
1237
  if (!config.client_token) {
1140
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1238
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1141
1239
  "div",
1142
1240
  {
1143
1241
  style: {
@@ -1154,168 +1252,16 @@ function PayPalAdvancedCard(props) {
1154
1252
  }
1155
1253
  const isSandbox = config.environment === "sandbox";
1156
1254
  if (isPending) {
1157
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1158
- "div",
1159
- {
1160
- role: "status",
1161
- "aria-label": "Loading PayPal",
1162
- style: {
1163
- display: "flex",
1164
- alignItems: "center",
1165
- justifyContent: "center",
1166
- gap: 10,
1167
- padding: "18px 16px",
1168
- background: "#f9fafb",
1169
- border: "1px solid #e5e7eb",
1170
- borderRadius: 8,
1171
- minHeight: 55
1172
- },
1173
- children: [
1174
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: SPIN_STYLE2 }),
1175
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1176
- "div",
1177
- {
1178
- style: {
1179
- width: 22,
1180
- height: 22,
1181
- borderRadius: "50%",
1182
- border: "2.5px solid #e5e7eb",
1183
- borderTopColor: "#0070ba",
1184
- animation: "_pp_spin .7s linear infinite",
1185
- flexShrink: 0
1186
- }
1187
- }
1188
- ),
1189
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#6b7280" }, children: "Loading card fields\u2026" })
1190
- ]
1191
- }
1192
- );
1255
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1256
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimationStyles, {}),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoadingCard, { label: "Loading card fields\u2026" })
1258
+ ] });
1193
1259
  }
1194
1260
  if (!isResolved) return null;
1195
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { position: "relative" }, children: [
1196
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: SPIN_STYLE2 }),
1197
- submitting && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1198
- "div",
1199
- {
1200
- role: "status",
1201
- "aria-label": "Processing payment",
1202
- style: {
1203
- position: "fixed",
1204
- inset: 0,
1205
- zIndex: 9999,
1206
- background: "rgba(255,255,255,0.96)",
1207
- display: "flex",
1208
- flexDirection: "column",
1209
- alignItems: "center",
1210
- justifyContent: "center",
1211
- gap: 20
1212
- },
1213
- children: [
1214
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { position: "relative", width: 56, height: 56 }, children: [
1215
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1216
- "div",
1217
- {
1218
- style: {
1219
- position: "absolute",
1220
- inset: 0,
1221
- borderRadius: "50%",
1222
- border: "3px solid #e5e7eb"
1223
- }
1224
- }
1225
- ),
1226
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1227
- "div",
1228
- {
1229
- style: {
1230
- position: "absolute",
1231
- inset: 0,
1232
- borderRadius: "50%",
1233
- border: "3px solid transparent",
1234
- borderTopColor: "#2563eb",
1235
- animation: "_pp_spin .8s linear infinite"
1236
- }
1237
- }
1238
- ),
1239
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1240
- "svg",
1241
- {
1242
- viewBox: "0 0 24 24",
1243
- fill: "none",
1244
- stroke: "#2563eb",
1245
- strokeWidth: "1.8",
1246
- strokeLinecap: "round",
1247
- strokeLinejoin: "round",
1248
- style: {
1249
- position: "absolute",
1250
- top: "50%",
1251
- left: "50%",
1252
- transform: "translate(-50%, -50%)",
1253
- width: 24,
1254
- height: 24
1255
- },
1256
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
1257
- }
1258
- )
1259
- ] }),
1260
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { textAlign: "center" }, children: [
1261
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1262
- "div",
1263
- {
1264
- style: {
1265
- fontSize: 18,
1266
- fontWeight: 600,
1267
- color: "#111827",
1268
- letterSpacing: "-0.01em"
1269
- },
1270
- children: "Processing your payment"
1271
- }
1272
- ),
1273
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1274
- "div",
1275
- {
1276
- style: {
1277
- fontSize: 14,
1278
- color: "#6b7280",
1279
- marginTop: 6,
1280
- lineHeight: 1.5
1281
- },
1282
- children: [
1283
- "This may take a few moments. Please do not close",
1284
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("br", {}),
1285
- "or refresh this page."
1286
- ]
1287
- }
1288
- )
1289
- ] }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1291
- "div",
1292
- {
1293
- style: {
1294
- width: 200,
1295
- height: 3,
1296
- background: "#e5e7eb",
1297
- borderRadius: 3,
1298
- overflow: "hidden",
1299
- marginTop: 4
1300
- },
1301
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1302
- "div",
1303
- {
1304
- style: {
1305
- width: "40%",
1306
- height: "100%",
1307
- background: "linear-gradient(90deg, #2563eb, #1d4ed8)",
1308
- borderRadius: 3,
1309
- animation: "_pp_progress 1.5s ease-in-out infinite"
1310
- }
1311
- }
1312
- )
1313
- }
1314
- )
1315
- ]
1316
- }
1317
- ),
1318
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative" }, children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AnimationStyles, {}),
1263
+ submitting && !domOverlayUp && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProcessingOverlayView, { accent: "#2563eb" }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1319
1265
  import_react_paypal_js3.PayPalCardFieldsProvider,
1320
1266
  {
1321
1267
  style: cardStyle,
@@ -1341,7 +1287,7 @@ function PayPalAdvancedCard(props) {
1341
1287
  onApprove: async (data) => {
1342
1288
  try {
1343
1289
  setError(null);
1344
- showProcessingOverlay();
1290
+ showOverlay();
1345
1291
  const orderId = String(data?.orderID || "");
1346
1292
  if (!orderId)
1347
1293
  throw new Error(
@@ -1361,21 +1307,15 @@ function PayPalAdvancedCard(props) {
1361
1307
  await finalizeCapturedPayment();
1362
1308
  },
1363
1309
  onCancel: () => {
1364
- hideProcessingOverlay();
1365
- creatingOrderRef.current = false;
1366
- submittingRef.current = false;
1367
- setSubmitting(false);
1310
+ resetSubmitState();
1368
1311
  },
1369
1312
  onError: (e) => {
1370
1313
  const msg = e instanceof Error ? e.message : e?.message || "CardFields error";
1371
1314
  setError(msg);
1372
1315
  onError?.(msg);
1373
- hideProcessingOverlay();
1374
- creatingOrderRef.current = false;
1375
- submittingRef.current = false;
1376
- setSubmitting(false);
1316
+ resetSubmitState();
1377
1317
  },
1378
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1318
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1379
1319
  "div",
1380
1320
  {
1381
1321
  style: {
@@ -1385,7 +1325,7 @@ function PayPalAdvancedCard(props) {
1385
1325
  width: "100%"
1386
1326
  },
1387
1327
  children: [
1388
- isSandbox && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1328
+ isSandbox && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1389
1329
  "div",
1390
1330
  {
1391
1331
  style: {
@@ -1401,11 +1341,18 @@ function PayPalAdvancedCard(props) {
1401
1341
  lineHeight: 1.55
1402
1342
  },
1403
1343
  children: [
1404
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 15, flexShrink: 0, marginTop: 1 }, children: "\u{1F9EA}" }),
1405
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
1344
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1345
+ "span",
1346
+ {
1347
+ "aria-hidden": "true",
1348
+ style: { fontSize: 15, flexShrink: 0, marginTop: 1 },
1349
+ children: "\u{1F9EA}"
1350
+ }
1351
+ ),
1352
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
1406
1353
  "Sandbox mode \u2014 use test card",
1407
1354
  " ",
1408
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1355
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1409
1356
  "strong",
1410
1357
  {
1411
1358
  style: { fontFamily: "monospace", letterSpacing: "0.05em" },
@@ -1418,11 +1365,11 @@ function PayPalAdvancedCard(props) {
1418
1365
  ]
1419
1366
  }
1420
1367
  ),
1421
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1422
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: labelStyle, children: "Card number" }),
1423
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_paypal_js3.PayPalNumberField, {})
1368
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { style: labelStyle, id: "_pp-label-card-number", children: "Card number" }),
1370
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_paypal_js3.PayPalNumberField, {})
1424
1371
  ] }),
1425
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1372
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1426
1373
  "div",
1427
1374
  {
1428
1375
  style: {
@@ -1431,18 +1378,18 @@ function PayPalAdvancedCard(props) {
1431
1378
  gap: 16
1432
1379
  },
1433
1380
  children: [
1434
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1435
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: labelStyle, children: "Expiration date" }),
1436
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_paypal_js3.PayPalExpiryField, {})
1381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1382
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { style: labelStyle, id: "_pp-label-expiry", children: "Expiration date" }),
1383
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_paypal_js3.PayPalExpiryField, {})
1437
1384
  ] }),
1438
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1439
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: labelStyle, children: "Security code" }),
1440
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_paypal_js3.PayPalCVVField, {})
1385
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { style: labelStyle, id: "_pp-label-cvv", children: "Security code" }),
1387
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_paypal_js3.PayPalCVVField, {})
1441
1388
  ] })
1442
1389
  ]
1443
1390
  }
1444
1391
  ),
1445
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1392
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1446
1393
  "div",
1447
1394
  {
1448
1395
  style: {
@@ -1453,25 +1400,12 @@ function PayPalAdvancedCard(props) {
1453
1400
  color: "#6b7280"
1454
1401
  },
1455
1402
  children: [
1456
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1457
- "svg",
1458
- {
1459
- width: "14",
1460
- height: "14",
1461
- viewBox: "0 0 24 24",
1462
- fill: "none",
1463
- stroke: "#10b981",
1464
- strokeWidth: "2.5",
1465
- strokeLinecap: "round",
1466
- strokeLinejoin: "round",
1467
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" })
1468
- }
1469
- ),
1470
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Your payment is secured with 256-bit SSL encryption" })
1403
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ShieldIcon, { color: "#10b981", size: 14 }),
1404
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Your payment is secured with 256-bit SSL encryption" })
1471
1405
  ]
1472
1406
  }
1473
1407
  ),
1474
- completionPending ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1408
+ completionPending ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1475
1409
  "button",
1476
1410
  {
1477
1411
  type: "button",
@@ -1493,17 +1427,18 @@ function PayPalAdvancedCard(props) {
1493
1427
  },
1494
1428
  children: "Retry \u2014 finish placing my order"
1495
1429
  }
1496
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1430
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1497
1431
  SubmitButton,
1498
1432
  {
1499
1433
  disabled: submitting,
1434
+ busy: submitting,
1500
1435
  label: submitting ? "Processing\u2026" : "Pay by Card",
1501
1436
  onSubmit: () => {
1502
1437
  if (submittingRef.current) return;
1503
1438
  submittingRef.current = true;
1504
1439
  setError(null);
1505
1440
  setSubmitting(true);
1506
- showProcessingOverlay();
1441
+ showOverlay();
1507
1442
  },
1508
1443
  onSubmitError: (msg) => {
1509
1444
  resetSubmitState();
@@ -1512,28 +1447,7 @@ function PayPalAdvancedCard(props) {
1512
1447
  }
1513
1448
  }
1514
1449
  ),
1515
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1516
- "div",
1517
- {
1518
- role: "alert",
1519
- style: {
1520
- display: "flex",
1521
- alignItems: "flex-start",
1522
- gap: 10,
1523
- padding: "10px 14px",
1524
- background: "#fef2f2",
1525
- border: "1px solid #fecaca",
1526
- borderRadius: 8,
1527
- fontSize: 13,
1528
- color: "#b91c1c",
1529
- lineHeight: 1.5
1530
- },
1531
- children: [
1532
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flexShrink: 0, marginTop: 1 }, children: "\u26A0\uFE0F" }),
1533
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: error })
1534
- ]
1535
- }
1536
- )
1450
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ErrorBanner, { children: error })
1537
1451
  ]
1538
1452
  }
1539
1453
  )
@@ -1544,28 +1458,12 @@ function PayPalAdvancedCard(props) {
1544
1458
 
1545
1459
  // src/adapters/MedusaNextPayPalAdapter.tsx
1546
1460
  var import_react5 = require("react");
1547
-
1548
- // src/constants.ts
1549
- var PAYPAL_WALLET_PROVIDER_ID = "pp_paypal_paypal";
1550
- var PAYPAL_CARD_PROVIDER_ID = "pp_paypal_card_paypal_card";
1551
- var PAYPAL_PROVIDER_IDS = [
1552
- PAYPAL_WALLET_PROVIDER_ID,
1553
- PAYPAL_CARD_PROVIDER_ID
1554
- ];
1555
- function isPayPalProviderId(providerId) {
1556
- if (!providerId) return false;
1557
- return PAYPAL_PROVIDER_IDS.includes(providerId);
1558
- }
1559
-
1560
- // src/adapters/MedusaNextPayPalAdapter.tsx
1561
- var import_jsx_runtime5 = require("react/jsx-runtime");
1562
- var SPIN_STYLE3 = `@keyframes _pp_spin { to { transform: rotate(360deg) } }`;
1461
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1563
1462
  function PayPalLoadingCard() {
1564
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1463
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1565
1464
  "div",
1566
1465
  {
1567
1466
  role: "status",
1568
- "aria-label": "Connecting to PayPal",
1569
1467
  style: {
1570
1468
  display: "flex",
1571
1469
  alignItems: "center",
@@ -1576,31 +1474,35 @@ function PayPalLoadingCard() {
1576
1474
  borderRadius: 10
1577
1475
  },
1578
1476
  children: [
1579
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: SPIN_STYLE3 }),
1580
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1477
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimationStyles, {}),
1478
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1581
1479
  "div",
1582
1480
  {
1481
+ className: "_pp-spin-sm",
1482
+ "aria-hidden": "true",
1583
1483
  style: {
1584
1484
  width: 22,
1585
1485
  height: 22,
1586
1486
  borderRadius: "50%",
1587
1487
  border: "2.5px solid #e5e7eb",
1588
1488
  borderTopColor: "#0070ba",
1589
- animation: "_pp_spin .7s linear infinite",
1590
1489
  flexShrink: 0
1591
1490
  }
1592
1491
  }
1593
1492
  ),
1594
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1595
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Connecting to PayPal\u2026" }),
1596
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 12, color: "#6b7280", marginTop: 2 }, children: "Setting up secure payment" })
1493
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1494
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Connecting to PayPal\u2026" }),
1495
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "#6b7280", marginTop: 2 }, children: "Setting up secure payment" })
1597
1496
  ] })
1598
1497
  ]
1599
1498
  }
1600
1499
  );
1601
1500
  }
1602
- function PayPalErrorCard({ message }) {
1603
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1501
+ function PayPalErrorCard({
1502
+ message,
1503
+ onRetry
1504
+ }) {
1505
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1604
1506
  "div",
1605
1507
  {
1606
1508
  role: "alert",
@@ -1612,12 +1514,34 @@ function PayPalErrorCard({ message }) {
1612
1514
  fontSize: 13,
1613
1515
  color: "#b91c1c"
1614
1516
  },
1615
- children: message
1517
+ children: [
1518
+ message,
1519
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1520
+ "button",
1521
+ {
1522
+ type: "button",
1523
+ onClick: onRetry,
1524
+ style: {
1525
+ display: "block",
1526
+ marginTop: 8,
1527
+ padding: "6px 14px",
1528
+ background: "#ffffff",
1529
+ color: "#b91c1c",
1530
+ border: "1px solid #fecaca",
1531
+ borderRadius: 6,
1532
+ fontSize: 13,
1533
+ fontWeight: 500,
1534
+ cursor: "pointer"
1535
+ },
1536
+ children: "Try again"
1537
+ }
1538
+ )
1539
+ ]
1616
1540
  }
1617
1541
  );
1618
1542
  }
1619
1543
  function PayPalUnavailableCard({ label }) {
1620
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1544
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1621
1545
  "div",
1622
1546
  {
1623
1547
  role: "status",
@@ -1642,6 +1566,8 @@ function MedusaNextPayPalAdapter(props) {
1642
1566
  selectedProviderId,
1643
1567
  baseUrl,
1644
1568
  publishableApiKey,
1569
+ credentials,
1570
+ currencyCode,
1645
1571
  providerIds,
1646
1572
  onSuccess,
1647
1573
  onError,
@@ -1650,10 +1576,12 @@ function MedusaNextPayPalAdapter(props) {
1650
1576
  const paypalProviderId = providerIds?.paypal || PAYPAL_WALLET_PROVIDER_ID;
1651
1577
  const paypalCardProviderId = providerIds?.paypalCard || PAYPAL_CARD_PROVIDER_ID;
1652
1578
  const shouldRender = selectedProviderId === paypalProviderId || selectedProviderId === paypalCardProviderId;
1653
- const { config, loading, error } = usePayPalConfig({
1579
+ const { config, loading, error, retry } = usePayPalConfig({
1654
1580
  baseUrl,
1655
1581
  publishableApiKey,
1582
+ credentials,
1656
1583
  cartId,
1584
+ currencyCode,
1657
1585
  enabled: shouldRender
1658
1586
  });
1659
1587
  const handlePaid = (0, import_react5.useCallback)(
@@ -1664,40 +1592,42 @@ function MedusaNextPayPalAdapter(props) {
1664
1592
  [cartId, onPaid, onSuccess]
1665
1593
  );
1666
1594
  if (!shouldRender) return null;
1667
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalLoadingCard, {});
1668
- if (error) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalErrorCard, { message: error });
1595
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PayPalLoadingCard, {});
1596
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PayPalErrorCard, { message: error, onRetry: retry });
1669
1597
  if (!config) return null;
1670
1598
  const isCardProvider = selectedProviderId === paypalCardProviderId;
1671
1599
  if (config.paypal_enabled === false && !isCardProvider) {
1672
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalUnavailableCard, { label: config.paypal_title || "PayPal" });
1600
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PayPalUnavailableCard, { label: config.paypal_title || "PayPal" });
1673
1601
  }
1674
1602
  if (isCardProvider && config.card_enabled === false) {
1675
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalUnavailableCard, { label: config.card_title || "Card payment" });
1603
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PayPalUnavailableCard, { label: config.card_title || "Card payment" });
1676
1604
  }
1677
1605
  const disableFunding = Array.isArray(config.disable_buttons) ? config.disable_buttons.join(",") : void 0;
1678
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
1679
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalCurrencyNotice, { config }),
1680
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1606
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
1607
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PayPalCurrencyNotice, { config }),
1608
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1681
1609
  PayPalProvider,
1682
1610
  {
1683
1611
  config,
1684
1612
  intent: config.intent === "authorize" ? "authorize" : "capture",
1685
1613
  disableFunding,
1686
- children: isCardProvider ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1614
+ children: isCardProvider ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1687
1615
  PayPalAdvancedCard,
1688
1616
  {
1689
1617
  baseUrl,
1690
1618
  publishableApiKey,
1619
+ credentials,
1691
1620
  cartId,
1692
1621
  config,
1693
1622
  onPaid: handlePaid,
1694
1623
  onError
1695
1624
  }
1696
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1625
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1697
1626
  PayPalSmartButtons,
1698
1627
  {
1699
1628
  baseUrl,
1700
1629
  publishableApiKey,
1630
+ credentials,
1701
1631
  cartId,
1702
1632
  config,
1703
1633
  onPaid: handlePaid,
@@ -1710,14 +1640,12 @@ function MedusaNextPayPalAdapter(props) {
1710
1640
  }
1711
1641
 
1712
1642
  // src/components/PayPalPaymentSection.tsx
1713
- var import_jsx_runtime6 = require("react/jsx-runtime");
1714
- var SPIN_STYLE4 = `@keyframes _pp_section_spin { to { transform: rotate(360deg) } }`;
1643
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1715
1644
  function SessionInitCard() {
1716
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1717
1646
  "div",
1718
1647
  {
1719
1648
  role: "status",
1720
- "aria-label": "Setting up payment",
1721
1649
  style: {
1722
1650
  display: "flex",
1723
1651
  alignItems: "center",
@@ -1729,22 +1657,23 @@ function SessionInitCard() {
1729
1657
  borderRadius: 10
1730
1658
  },
1731
1659
  children: [
1732
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: SPIN_STYLE4 }),
1733
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1660
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AnimationStyles, {}),
1661
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1734
1662
  "div",
1735
1663
  {
1664
+ className: "_pp-spin-sm",
1665
+ "aria-hidden": "true",
1736
1666
  style: {
1737
1667
  width: 20,
1738
1668
  height: 20,
1739
1669
  borderRadius: "50%",
1740
1670
  border: "2.5px solid #e5e7eb",
1741
1671
  borderTopColor: "#0070ba",
1742
- animation: "_pp_section_spin .7s linear infinite",
1743
1672
  flexShrink: 0
1744
1673
  }
1745
1674
  }
1746
1675
  ),
1747
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Setting up payment\u2026" })
1676
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Setting up payment\u2026" })
1748
1677
  ]
1749
1678
  }
1750
1679
  );
@@ -1754,20 +1683,24 @@ function PayPalPaymentSection({
1754
1683
  selectedProviderId,
1755
1684
  baseUrl,
1756
1685
  publishableApiKey,
1686
+ credentials,
1687
+ currencyCode,
1757
1688
  sessionLoading = false,
1758
1689
  onSuccess,
1759
1690
  onError,
1760
1691
  onPaid
1761
1692
  }) {
1762
1693
  if (!isPayPalProviderId(selectedProviderId)) return null;
1763
- if (sessionLoading) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SessionInitCard, {});
1764
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1694
+ if (sessionLoading) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SessionInitCard, {});
1695
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1765
1696
  MedusaNextPayPalAdapter,
1766
1697
  {
1767
1698
  cartId,
1768
1699
  selectedProviderId,
1769
1700
  baseUrl,
1770
1701
  publishableApiKey,
1702
+ credentials,
1703
+ currencyCode,
1771
1704
  onSuccess,
1772
1705
  onError,
1773
1706
  onPaid
@@ -1777,19 +1710,6 @@ function PayPalPaymentSection({
1777
1710
 
1778
1711
  // src/hooks/usePayPalPaymentMethods.ts
1779
1712
  var import_react6 = require("react");
1780
- var MAX_CACHE_ENTRIES2 = 50;
1781
- var _cache2 = /* @__PURE__ */ new Map();
1782
- var CACHE_TTL2 = 5 * 60 * 1e3;
1783
- function cacheSet2(key, value) {
1784
- if (_cache2.size >= MAX_CACHE_ENTRIES2) {
1785
- const oldest = _cache2.keys().next().value;
1786
- if (oldest !== void 0) _cache2.delete(oldest);
1787
- }
1788
- _cache2.set(key, value);
1789
- }
1790
- function cacheKey2(baseUrl, cartId) {
1791
- return `ppm::${baseUrl}::${cartId ?? ""}`;
1792
- }
1793
1713
  var DEFAULT_RESULT = {
1794
1714
  paypalEnabled: true,
1795
1715
  paypalTitle: "PayPal",
@@ -1798,21 +1718,37 @@ var DEFAULT_RESULT = {
1798
1718
  loading: false,
1799
1719
  error: null
1800
1720
  };
1721
+ function toResult(cfg) {
1722
+ return {
1723
+ paypalEnabled: cfg.paypal_enabled !== false,
1724
+ paypalTitle: typeof cfg.paypal_title === "string" && cfg.paypal_title ? cfg.paypal_title : "PayPal",
1725
+ cardEnabled: cfg.card_enabled !== false,
1726
+ cardTitle: typeof cfg.card_title === "string" && cfg.card_title ? cfg.card_title : "Credit or Debit Card",
1727
+ loading: false,
1728
+ error: null
1729
+ };
1730
+ }
1801
1731
  function usePayPalPaymentMethods({
1802
1732
  baseUrl,
1803
1733
  publishableApiKey,
1804
1734
  cartId,
1735
+ currencyCode,
1736
+ credentials,
1805
1737
  enabled = true
1806
1738
  }) {
1807
1739
  const api = (0, import_react6.useMemo)(
1808
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
1809
- [baseUrl, publishableApiKey]
1740
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
1741
+ [baseUrl, publishableApiKey, credentials]
1810
1742
  );
1811
- const key = cacheKey2(baseUrl, cartId);
1812
- const hit = _cache2.get(key);
1813
- const seed = hit && Date.now() - hit.at < CACHE_TTL2 ? hit.result : null;
1743
+ const key = configCacheKey({
1744
+ baseUrl,
1745
+ publishableApiKey,
1746
+ cartId,
1747
+ currencyCode
1748
+ });
1749
+ const seed = getCachedPayPalConfig(key);
1814
1750
  const [result, setResult] = (0, import_react6.useState)(
1815
- seed ?? { ...DEFAULT_RESULT, loading: enabled }
1751
+ seed ? toResult(seed) : { ...DEFAULT_RESULT, loading: enabled }
1816
1752
  );
1817
1753
  const fetchIdRef = (0, import_react6.useRef)(0);
1818
1754
  (0, import_react6.useEffect)(() => {
@@ -1820,43 +1756,33 @@ function usePayPalPaymentMethods({
1820
1756
  setResult((prev) => ({ ...prev, loading: false }));
1821
1757
  return;
1822
1758
  }
1823
- const k = cacheKey2(baseUrl, cartId);
1824
- const cached = _cache2.get(k);
1825
- if (cached && Date.now() - cached.at < CACHE_TTL2) {
1826
- setResult(cached.result);
1759
+ const cached = getCachedPayPalConfig(key);
1760
+ if (cached) {
1761
+ setResult(toResult(cached));
1827
1762
  return;
1828
1763
  }
1829
1764
  const id = ++fetchIdRef.current;
1830
1765
  let mounted = true;
1831
- const controller = new AbortController();
1832
1766
  setResult((prev) => ({ ...prev, loading: true }));
1833
1767
  (async () => {
1834
1768
  try {
1835
- const cfg = await api.getConfig(cartId, controller.signal);
1769
+ const cfg = await fetchPayPalConfigShared(
1770
+ key,
1771
+ () => api.getConfig(cartId)
1772
+ );
1836
1773
  if (!mounted || id !== fetchIdRef.current) return;
1837
- const next = {
1838
- paypalEnabled: cfg.paypal_enabled !== false,
1839
- paypalTitle: typeof cfg.paypal_title === "string" && cfg.paypal_title ? cfg.paypal_title : "PayPal",
1840
- cardEnabled: cfg.card_enabled !== false,
1841
- cardTitle: typeof cfg.card_title === "string" && cfg.card_title ? cfg.card_title : "Credit or Debit Card",
1842
- loading: false,
1843
- error: null
1844
- };
1845
- cacheSet2(k, { result: next, at: Date.now() });
1846
- setResult(next);
1774
+ setResult(toResult(cfg));
1847
1775
  } catch (e) {
1848
- const msg = e instanceof Error ? e.message : "";
1849
- if (e instanceof Error && e.name === "AbortError") return;
1850
1776
  if (!mounted || id !== fetchIdRef.current) return;
1851
- if (msg.includes("403") || msg.includes("Forbidden")) {
1852
- const disabled = {
1777
+ const msg = e instanceof Error ? e.message : "";
1778
+ if (e instanceof HttpError && e.status === 403) {
1779
+ setResult({
1853
1780
  ...DEFAULT_RESULT,
1854
1781
  paypalEnabled: false,
1855
1782
  cardEnabled: false,
1856
1783
  loading: false,
1857
1784
  error: msg || "PayPal is disabled"
1858
- };
1859
- setResult(disabled);
1785
+ });
1860
1786
  return;
1861
1787
  }
1862
1788
  setResult({
@@ -1868,15 +1794,16 @@ function usePayPalPaymentMethods({
1868
1794
  })();
1869
1795
  return () => {
1870
1796
  mounted = false;
1871
- controller.abort();
1872
1797
  };
1873
- }, [api, baseUrl, cartId, enabled]);
1798
+ }, [api, key, cartId, enabled]);
1874
1799
  return result;
1875
1800
  }
1876
1801
  // Annotate the CommonJS export names for ESM import in node:
1877
1802
  0 && (module.exports = {
1803
+ HttpError,
1878
1804
  MedusaNextPayPalAdapter,
1879
1805
  PAYPAL_CARD_PROVIDER_ID,
1806
+ PAYPAL_PROVIDER_IDS,
1880
1807
  PAYPAL_WALLET_PROVIDER_ID,
1881
1808
  PayPalAdvancedCard,
1882
1809
  PayPalCurrencyNotice,
@@ -1884,9 +1811,11 @@ function usePayPalPaymentMethods({
1884
1811
  PayPalProvider,
1885
1812
  PayPalSmartButtons,
1886
1813
  clearCartCaptured,
1814
+ createHttpClient,
1887
1815
  createPayPalStoreApi,
1888
1816
  generateIdempotencyKey,
1889
1817
  hideProcessingOverlay,
1818
+ invalidatePayPalConfigCache,
1890
1819
  isPayPalProviderId,
1891
1820
  markCartCaptured,
1892
1821
  markPaymentComplete,