@easypayment/medusa-paypal-ui 1.2.7 → 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;
297
+ }
298
+ return hit.config;
299
+ }
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);
267
307
  }
268
- _cache.set(key, value);
269
308
  }
270
- function cacheKey(baseUrl, cartId) {
271
- return `${baseUrl}::${cartId ?? ""}`;
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);
272
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,30 +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(
1558
- providerId
1559
- );
1560
- }
1561
-
1562
- // src/adapters/MedusaNextPayPalAdapter.tsx
1563
- var import_jsx_runtime5 = require("react/jsx-runtime");
1564
- var SPIN_STYLE3 = `@keyframes _pp_spin { to { transform: rotate(360deg) } }`;
1461
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1565
1462
  function PayPalLoadingCard() {
1566
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1463
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1567
1464
  "div",
1568
1465
  {
1569
1466
  role: "status",
1570
- "aria-label": "Connecting to PayPal",
1571
1467
  style: {
1572
1468
  display: "flex",
1573
1469
  alignItems: "center",
@@ -1578,31 +1474,35 @@ function PayPalLoadingCard() {
1578
1474
  borderRadius: 10
1579
1475
  },
1580
1476
  children: [
1581
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: SPIN_STYLE3 }),
1582
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1477
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AnimationStyles, {}),
1478
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1583
1479
  "div",
1584
1480
  {
1481
+ className: "_pp-spin-sm",
1482
+ "aria-hidden": "true",
1585
1483
  style: {
1586
1484
  width: 22,
1587
1485
  height: 22,
1588
1486
  borderRadius: "50%",
1589
1487
  border: "2.5px solid #e5e7eb",
1590
1488
  borderTopColor: "#0070ba",
1591
- animation: "_pp_spin .7s linear infinite",
1592
1489
  flexShrink: 0
1593
1490
  }
1594
1491
  }
1595
1492
  ),
1596
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1597
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 13, fontWeight: 500, color: "#111827" }, children: "Connecting to PayPal\u2026" }),
1598
- /* @__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" })
1599
1496
  ] })
1600
1497
  ]
1601
1498
  }
1602
1499
  );
1603
1500
  }
1604
- function PayPalErrorCard({ message }) {
1605
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1501
+ function PayPalErrorCard({
1502
+ message,
1503
+ onRetry
1504
+ }) {
1505
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1606
1506
  "div",
1607
1507
  {
1608
1508
  role: "alert",
@@ -1614,12 +1514,34 @@ function PayPalErrorCard({ message }) {
1614
1514
  fontSize: 13,
1615
1515
  color: "#b91c1c"
1616
1516
  },
1617
- 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
+ ]
1618
1540
  }
1619
1541
  );
1620
1542
  }
1621
1543
  function PayPalUnavailableCard({ label }) {
1622
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1544
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1623
1545
  "div",
1624
1546
  {
1625
1547
  role: "status",
@@ -1644,6 +1566,8 @@ function MedusaNextPayPalAdapter(props) {
1644
1566
  selectedProviderId,
1645
1567
  baseUrl,
1646
1568
  publishableApiKey,
1569
+ credentials,
1570
+ currencyCode,
1647
1571
  providerIds,
1648
1572
  onSuccess,
1649
1573
  onError,
@@ -1652,10 +1576,12 @@ function MedusaNextPayPalAdapter(props) {
1652
1576
  const paypalProviderId = providerIds?.paypal || PAYPAL_WALLET_PROVIDER_ID;
1653
1577
  const paypalCardProviderId = providerIds?.paypalCard || PAYPAL_CARD_PROVIDER_ID;
1654
1578
  const shouldRender = selectedProviderId === paypalProviderId || selectedProviderId === paypalCardProviderId;
1655
- const { config, loading, error } = usePayPalConfig({
1579
+ const { config, loading, error, retry } = usePayPalConfig({
1656
1580
  baseUrl,
1657
1581
  publishableApiKey,
1582
+ credentials,
1658
1583
  cartId,
1584
+ currencyCode,
1659
1585
  enabled: shouldRender
1660
1586
  });
1661
1587
  const handlePaid = (0, import_react5.useCallback)(
@@ -1666,40 +1592,42 @@ function MedusaNextPayPalAdapter(props) {
1666
1592
  [cartId, onPaid, onSuccess]
1667
1593
  );
1668
1594
  if (!shouldRender) return null;
1669
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalLoadingCard, {});
1670
- 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 });
1671
1597
  if (!config) return null;
1672
1598
  const isCardProvider = selectedProviderId === paypalCardProviderId;
1673
1599
  if (config.paypal_enabled === false && !isCardProvider) {
1674
- 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" });
1675
1601
  }
1676
1602
  if (isCardProvider && config.card_enabled === false) {
1677
- 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" });
1678
1604
  }
1679
1605
  const disableFunding = Array.isArray(config.disable_buttons) ? config.disable_buttons.join(",") : void 0;
1680
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
1681
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PayPalCurrencyNotice, { config }),
1682
- /* @__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)(
1683
1609
  PayPalProvider,
1684
1610
  {
1685
1611
  config,
1686
1612
  intent: config.intent === "authorize" ? "authorize" : "capture",
1687
1613
  disableFunding,
1688
- children: isCardProvider ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1614
+ children: isCardProvider ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1689
1615
  PayPalAdvancedCard,
1690
1616
  {
1691
1617
  baseUrl,
1692
1618
  publishableApiKey,
1619
+ credentials,
1693
1620
  cartId,
1694
1621
  config,
1695
1622
  onPaid: handlePaid,
1696
1623
  onError
1697
1624
  }
1698
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1625
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1699
1626
  PayPalSmartButtons,
1700
1627
  {
1701
1628
  baseUrl,
1702
1629
  publishableApiKey,
1630
+ credentials,
1703
1631
  cartId,
1704
1632
  config,
1705
1633
  onPaid: handlePaid,
@@ -1712,14 +1640,12 @@ function MedusaNextPayPalAdapter(props) {
1712
1640
  }
1713
1641
 
1714
1642
  // src/components/PayPalPaymentSection.tsx
1715
- var import_jsx_runtime6 = require("react/jsx-runtime");
1716
- var SPIN_STYLE4 = `@keyframes _pp_section_spin { to { transform: rotate(360deg) } }`;
1643
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1717
1644
  function SessionInitCard() {
1718
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1719
1646
  "div",
1720
1647
  {
1721
1648
  role: "status",
1722
- "aria-label": "Setting up payment",
1723
1649
  style: {
1724
1650
  display: "flex",
1725
1651
  alignItems: "center",
@@ -1731,22 +1657,23 @@ function SessionInitCard() {
1731
1657
  borderRadius: 10
1732
1658
  },
1733
1659
  children: [
1734
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: SPIN_STYLE4 }),
1735
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1660
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AnimationStyles, {}),
1661
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1736
1662
  "div",
1737
1663
  {
1664
+ className: "_pp-spin-sm",
1665
+ "aria-hidden": "true",
1738
1666
  style: {
1739
1667
  width: 20,
1740
1668
  height: 20,
1741
1669
  borderRadius: "50%",
1742
1670
  border: "2.5px solid #e5e7eb",
1743
1671
  borderTopColor: "#0070ba",
1744
- animation: "_pp_section_spin .7s linear infinite",
1745
1672
  flexShrink: 0
1746
1673
  }
1747
1674
  }
1748
1675
  ),
1749
- /* @__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" })
1750
1677
  ]
1751
1678
  }
1752
1679
  );
@@ -1756,20 +1683,24 @@ function PayPalPaymentSection({
1756
1683
  selectedProviderId,
1757
1684
  baseUrl,
1758
1685
  publishableApiKey,
1686
+ credentials,
1687
+ currencyCode,
1759
1688
  sessionLoading = false,
1760
1689
  onSuccess,
1761
1690
  onError,
1762
1691
  onPaid
1763
1692
  }) {
1764
1693
  if (!isPayPalProviderId(selectedProviderId)) return null;
1765
- if (sessionLoading) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SessionInitCard, {});
1766
- 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)(
1767
1696
  MedusaNextPayPalAdapter,
1768
1697
  {
1769
1698
  cartId,
1770
1699
  selectedProviderId,
1771
1700
  baseUrl,
1772
1701
  publishableApiKey,
1702
+ credentials,
1703
+ currencyCode,
1773
1704
  onSuccess,
1774
1705
  onError,
1775
1706
  onPaid
@@ -1779,19 +1710,6 @@ function PayPalPaymentSection({
1779
1710
 
1780
1711
  // src/hooks/usePayPalPaymentMethods.ts
1781
1712
  var import_react6 = require("react");
1782
- var MAX_CACHE_ENTRIES2 = 50;
1783
- var _cache2 = /* @__PURE__ */ new Map();
1784
- var CACHE_TTL2 = 5 * 60 * 1e3;
1785
- function cacheSet2(key, value) {
1786
- if (_cache2.size >= MAX_CACHE_ENTRIES2) {
1787
- const oldest = _cache2.keys().next().value;
1788
- if (oldest !== void 0) _cache2.delete(oldest);
1789
- }
1790
- _cache2.set(key, value);
1791
- }
1792
- function cacheKey2(baseUrl, cartId) {
1793
- return `ppm::${baseUrl}::${cartId ?? ""}`;
1794
- }
1795
1713
  var DEFAULT_RESULT = {
1796
1714
  paypalEnabled: true,
1797
1715
  paypalTitle: "PayPal",
@@ -1800,21 +1718,37 @@ var DEFAULT_RESULT = {
1800
1718
  loading: false,
1801
1719
  error: null
1802
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
+ }
1803
1731
  function usePayPalPaymentMethods({
1804
1732
  baseUrl,
1805
1733
  publishableApiKey,
1806
1734
  cartId,
1735
+ currencyCode,
1736
+ credentials,
1807
1737
  enabled = true
1808
1738
  }) {
1809
1739
  const api = (0, import_react6.useMemo)(
1810
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
1811
- [baseUrl, publishableApiKey]
1740
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey, credentials }),
1741
+ [baseUrl, publishableApiKey, credentials]
1812
1742
  );
1813
- const key = cacheKey2(baseUrl, cartId);
1814
- const hit = _cache2.get(key);
1815
- 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);
1816
1750
  const [result, setResult] = (0, import_react6.useState)(
1817
- seed ?? { ...DEFAULT_RESULT, loading: enabled }
1751
+ seed ? toResult(seed) : { ...DEFAULT_RESULT, loading: enabled }
1818
1752
  );
1819
1753
  const fetchIdRef = (0, import_react6.useRef)(0);
1820
1754
  (0, import_react6.useEffect)(() => {
@@ -1822,43 +1756,33 @@ function usePayPalPaymentMethods({
1822
1756
  setResult((prev) => ({ ...prev, loading: false }));
1823
1757
  return;
1824
1758
  }
1825
- const k = cacheKey2(baseUrl, cartId);
1826
- const cached = _cache2.get(k);
1827
- if (cached && Date.now() - cached.at < CACHE_TTL2) {
1828
- setResult(cached.result);
1759
+ const cached = getCachedPayPalConfig(key);
1760
+ if (cached) {
1761
+ setResult(toResult(cached));
1829
1762
  return;
1830
1763
  }
1831
1764
  const id = ++fetchIdRef.current;
1832
1765
  let mounted = true;
1833
- const controller = new AbortController();
1834
1766
  setResult((prev) => ({ ...prev, loading: true }));
1835
1767
  (async () => {
1836
1768
  try {
1837
- const cfg = await api.getConfig(cartId, controller.signal);
1769
+ const cfg = await fetchPayPalConfigShared(
1770
+ key,
1771
+ () => api.getConfig(cartId)
1772
+ );
1838
1773
  if (!mounted || id !== fetchIdRef.current) return;
1839
- const next = {
1840
- paypalEnabled: cfg.paypal_enabled !== false,
1841
- paypalTitle: typeof cfg.paypal_title === "string" && cfg.paypal_title ? cfg.paypal_title : "PayPal",
1842
- cardEnabled: cfg.card_enabled !== false,
1843
- cardTitle: typeof cfg.card_title === "string" && cfg.card_title ? cfg.card_title : "Credit or Debit Card",
1844
- loading: false,
1845
- error: null
1846
- };
1847
- cacheSet2(k, { result: next, at: Date.now() });
1848
- setResult(next);
1774
+ setResult(toResult(cfg));
1849
1775
  } catch (e) {
1850
- const msg = e instanceof Error ? e.message : "";
1851
- if (e instanceof Error && e.name === "AbortError") return;
1852
1776
  if (!mounted || id !== fetchIdRef.current) return;
1853
- if (msg.includes("403") || msg.includes("Forbidden")) {
1854
- const disabled = {
1777
+ const msg = e instanceof Error ? e.message : "";
1778
+ if (e instanceof HttpError && e.status === 403) {
1779
+ setResult({
1855
1780
  ...DEFAULT_RESULT,
1856
1781
  paypalEnabled: false,
1857
1782
  cardEnabled: false,
1858
1783
  loading: false,
1859
1784
  error: msg || "PayPal is disabled"
1860
- };
1861
- setResult(disabled);
1785
+ });
1862
1786
  return;
1863
1787
  }
1864
1788
  setResult({
@@ -1870,15 +1794,16 @@ function usePayPalPaymentMethods({
1870
1794
  })();
1871
1795
  return () => {
1872
1796
  mounted = false;
1873
- controller.abort();
1874
1797
  };
1875
- }, [api, baseUrl, cartId, enabled]);
1798
+ }, [api, key, cartId, enabled]);
1876
1799
  return result;
1877
1800
  }
1878
1801
  // Annotate the CommonJS export names for ESM import in node:
1879
1802
  0 && (module.exports = {
1803
+ HttpError,
1880
1804
  MedusaNextPayPalAdapter,
1881
1805
  PAYPAL_CARD_PROVIDER_ID,
1806
+ PAYPAL_PROVIDER_IDS,
1882
1807
  PAYPAL_WALLET_PROVIDER_ID,
1883
1808
  PayPalAdvancedCard,
1884
1809
  PayPalCurrencyNotice,
@@ -1886,9 +1811,11 @@ function usePayPalPaymentMethods({
1886
1811
  PayPalProvider,
1887
1812
  PayPalSmartButtons,
1888
1813
  clearCartCaptured,
1814
+ createHttpClient,
1889
1815
  createPayPalStoreApi,
1890
1816
  generateIdempotencyKey,
1891
1817
  hideProcessingOverlay,
1818
+ invalidatePayPalConfigCache,
1892
1819
  isPayPalProviderId,
1893
1820
  markCartCaptured,
1894
1821
  markPaymentComplete,