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