@easypayment/medusa-paypal-ui 1.0.51 → 1.0.53

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.
@@ -19,9 +19,18 @@ type Result = {
19
19
  loading: boolean
20
20
  }
21
21
 
22
+ const MAX_CACHE_ENTRIES = 50
22
23
  const _cache = new Map<string, { result: Result; at: number }>()
23
24
  const CACHE_TTL = 5 * 60 * 1000
24
25
 
26
+ function cacheSet(key: string, value: { result: Result; at: number }) {
27
+ if (_cache.size >= MAX_CACHE_ENTRIES) {
28
+ const oldest = _cache.keys().next().value
29
+ if (oldest !== undefined) _cache.delete(oldest)
30
+ }
31
+ _cache.set(key, value)
32
+ }
33
+
25
34
  function cacheKey(baseUrl: string, cartId?: string) {
26
35
  return `ppm::${baseUrl}::${cartId ?? ""}`
27
36
  }
@@ -90,13 +99,14 @@ export function usePayPalPaymentMethods({
90
99
  loading: false,
91
100
  }
92
101
 
93
- _cache.set(k, { result: next, at: Date.now() })
102
+ cacheSet(k, { result: next, at: Date.now() })
94
103
  setResult(next)
95
- } catch (e: any) {
96
- if (e?.name === "AbortError") return
104
+ } catch (e: unknown) {
105
+ const msg = e instanceof Error ? e.message : ""
106
+ if (e instanceof Error && e.name === "AbortError") return
97
107
  if (!mounted || id !== fetchIdRef.current) return
98
108
 
99
- if (e?.message?.includes("403") || e?.message?.includes("Forbidden")) {
109
+ if (msg.includes("403") || msg.includes("Forbidden")) {
100
110
  const disabled: Result = {
101
111
  ...DEFAULT_RESULT,
102
112
  paypalEnabled: false,
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from "./components/PayPalAdvancedCard"
8
8
  export * from "./adapters/MedusaNextPayPalAdapter"
9
9
  export * from "./components/PayPalPaymentSection"
10
10
  export * from "./hooks/usePayPalPaymentMethods"
11
+ export { showProcessingOverlay, hideProcessingOverlay } from "./utils/processing-overlay"
package/src/order.ts CHANGED
@@ -101,13 +101,26 @@ export async function fetchPayPalConfig(opts: {
101
101
  return {}
102
102
  }
103
103
  try {
104
- const res = await fetch(`${baseUrl.replace(/\/$/, "")}/store/paypal/config`, {
105
- headers: {
106
- accept: "application/json",
107
- ...(publishableApiKey ? { "x-publishable-api-key": publishableApiKey } : {}),
108
- },
109
- signal,
110
- })
104
+ const timeoutMs = 30_000
105
+ let effectiveSignal = signal
106
+ let timeoutId: ReturnType<typeof setTimeout> | undefined
107
+ if (!signal) {
108
+ const controller = new AbortController()
109
+ timeoutId = setTimeout(() => controller.abort(), timeoutMs)
110
+ effectiveSignal = controller.signal
111
+ }
112
+ let res: Response
113
+ try {
114
+ res = await fetch(`${baseUrl.replace(/\/$/, "")}/store/paypal/config`, {
115
+ headers: {
116
+ accept: "application/json",
117
+ ...(publishableApiKey ? { "x-publishable-api-key": publishableApiKey } : {}),
118
+ },
119
+ signal: effectiveSignal,
120
+ })
121
+ } finally {
122
+ if (timeoutId !== undefined) clearTimeout(timeoutId)
123
+ }
111
124
  if (!res.ok) {
112
125
  return {}
113
126
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Detect Next.js router errors (redirect / notFound) that must be re-thrown.
3
+ *
4
+ * Next.js implements redirect() and notFound() by throwing special errors with
5
+ * a `digest` property starting with "NEXT_REDIRECT" or "NEXT_NOT_FOUND".
6
+ * Catching these in user-land try/catch blocks prevents the navigation from
7
+ * happening. Because this package can't import from `next` (it's a peer dep),
8
+ * we detect them by duck-typing the digest.
9
+ */
10
+ export function isNextRouterError(e: unknown): boolean {
11
+ if (typeof e !== "object" || e === null || !("digest" in e)) return false
12
+ const digest = (e as { digest: unknown }).digest
13
+ return (
14
+ typeof digest === "string" &&
15
+ (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND"))
16
+ )
17
+ }
@@ -0,0 +1,55 @@
1
+ const OVERLAY_ID = "__pp_processing_overlay"
2
+
3
+ export function showProcessingOverlay() {
4
+ if (typeof document === "undefined") return
5
+ if (document.getElementById(OVERLAY_ID)) return
6
+
7
+ const overlay = document.createElement("div")
8
+ overlay.id = OVERLAY_ID
9
+ overlay.setAttribute("role", "status")
10
+ overlay.setAttribute("aria-label", "Processing payment")
11
+
12
+ overlay.innerHTML = `
13
+ <style>
14
+ @keyframes _pp_spin { to { transform: rotate(360deg) } }
15
+ @keyframes _pp_progress { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
16
+ </style>
17
+ <div style="position:relative;width:56px;height:56px">
18
+ <div style="position:absolute;inset:0;border-radius:50%;border:3px solid #e5e7eb"></div>
19
+ <div style="position:absolute;inset:0;border-radius:50%;border:3px solid transparent;border-top-color:#0070ba;animation:_pp_spin .8s linear infinite"></div>
20
+ <svg viewBox="0 0 24 24" fill="none" stroke="#0070ba" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"
21
+ style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:24px;height:24px">
22
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
23
+ </svg>
24
+ </div>
25
+ <div style="text-align:center">
26
+ <div style="font-size:18px;font-weight:600;color:#111827;letter-spacing:-0.01em">Processing your payment</div>
27
+ <div style="font-size:14px;color:#6b7280;margin-top:6px;line-height:1.5">
28
+ This may take a few moments. Please do not close<br>or refresh this page.
29
+ </div>
30
+ </div>
31
+ <div style="width:200px;height:3px;background:#e5e7eb;border-radius:3px;overflow:hidden;margin-top:4px">
32
+ <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>
33
+ </div>
34
+ `
35
+
36
+ Object.assign(overlay.style, {
37
+ position: "fixed",
38
+ inset: "0",
39
+ zIndex: "99999",
40
+ background: "rgba(255,255,255,0.96)",
41
+ display: "flex",
42
+ flexDirection: "column",
43
+ alignItems: "center",
44
+ justifyContent: "center",
45
+ gap: "20px",
46
+ fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif",
47
+ })
48
+
49
+ document.body.appendChild(overlay)
50
+ }
51
+
52
+ export function hideProcessingOverlay() {
53
+ if (typeof document === "undefined") return
54
+ document.getElementById(OVERLAY_ID)?.remove()
55
+ }