@easypayment/medusa-paypal-ui 1.2.2 → 1.2.3

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.
@@ -1,96 +1,100 @@
1
- "use client"
2
-
3
- import { useEffect, useMemo, useRef, useState } from "react"
4
- import { createPayPalStoreApi } from "../client/paypal"
5
- import type { PayPalConfig } from "../client/types"
6
-
7
- type Args = {
8
- baseUrl: string
9
- publishableApiKey?: string
10
- cartId?: string
11
- enabled?: boolean
12
- }
13
-
14
- const MAX_CACHE_ENTRIES = 50
15
- const _cache = new Map<string, { config: PayPalConfig; at: number }>()
16
- const CACHE_TTL = 5 * 60 * 1000
17
-
18
- function cacheSet(key: string, value: { config: PayPalConfig; at: number }) {
19
- if (_cache.size >= MAX_CACHE_ENTRIES) {
20
- const oldest = _cache.keys().next().value
21
- if (oldest !== undefined) _cache.delete(oldest)
22
- }
23
- _cache.set(key, value)
24
- }
25
-
26
- function cacheKey(baseUrl: string, cartId?: string) {
27
- return `${baseUrl}::${cartId ?? ""}`
28
- }
29
-
30
- export function usePayPalConfig({
31
- baseUrl,
32
- publishableApiKey,
33
- cartId,
34
- enabled = true,
35
- }: Args) {
36
- const api = useMemo(
37
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
38
- [baseUrl, publishableApiKey]
39
- )
40
-
41
- const key = cacheKey(baseUrl, cartId)
42
- const hit = _cache.get(key)
43
- const seedConfig = hit && Date.now() - hit.at < CACHE_TTL ? hit.config : null
44
-
45
- const [config, setConfig] = useState<PayPalConfig | null>(seedConfig)
46
- const [loading, setLoading] = useState<boolean>(seedConfig === null && enabled)
47
- const [error, setError] = useState<string | null>(null)
48
-
49
- const fetchIdRef = useRef(0)
50
-
51
- useEffect(() => {
52
- if (!enabled) {
53
- setLoading(false)
54
- setError(null)
55
- return
56
- }
57
-
58
- const k = cacheKey(baseUrl, cartId)
59
- const cached = _cache.get(k)
60
- if (cached && Date.now() - cached.at < CACHE_TTL) {
61
- setConfig(cached.config)
62
- setLoading(false)
63
- setError(null)
64
- return
65
- }
66
-
67
- const id = ++fetchIdRef.current
68
- let mounted = true
69
- const controller = new AbortController()
70
-
71
- setLoading(true)
72
- setError(null)
73
-
74
- ;(async () => {
75
- try {
76
- const cfg = await api.getConfig(cartId, controller.signal)
77
- if (!mounted || id !== fetchIdRef.current) return
78
- cacheSet(k, { config: cfg, at: Date.now() })
79
- setConfig(cfg)
80
- } catch (e: unknown) {
81
- if (e instanceof Error && e.name === "AbortError") return
82
- if (!mounted || id !== fetchIdRef.current) return
83
- setError(e instanceof Error ? e.message : "Failed to load PayPal config")
84
- } finally {
85
- if (mounted && id === fetchIdRef.current) setLoading(false)
86
- }
87
- })()
88
-
89
- return () => {
90
- mounted = false
91
- controller.abort()
92
- }
93
- }, [api, baseUrl, cartId, enabled])
94
-
95
- return { config, loading, error }
96
- }
1
+ "use client"
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react"
4
+ import { createPayPalStoreApi } from "../client/paypal"
5
+ import type { PayPalConfig } from "../client/types"
6
+
7
+ type Args = {
8
+ baseUrl: string
9
+ publishableApiKey?: string
10
+ cartId?: string
11
+ enabled?: boolean
12
+ }
13
+
14
+ const MAX_CACHE_ENTRIES = 50
15
+ const _cache = new Map<string, { config: PayPalConfig; at: number }>()
16
+ const CACHE_TTL = 5 * 60 * 1000
17
+
18
+ function cacheSet(key: string, value: { config: PayPalConfig; at: number }) {
19
+ if (_cache.size >= MAX_CACHE_ENTRIES) {
20
+ const oldest = _cache.keys().next().value
21
+ if (oldest !== undefined) _cache.delete(oldest)
22
+ }
23
+ _cache.set(key, value)
24
+ }
25
+
26
+ function cacheKey(baseUrl: string, cartId?: string) {
27
+ return `${baseUrl}::${cartId ?? ""}`
28
+ }
29
+
30
+ export function usePayPalConfig({
31
+ baseUrl,
32
+ publishableApiKey,
33
+ cartId,
34
+ enabled = true,
35
+ }: Args) {
36
+ const api = useMemo(
37
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
38
+ [baseUrl, publishableApiKey]
39
+ )
40
+
41
+ const key = cacheKey(baseUrl, cartId)
42
+ const hit = _cache.get(key)
43
+ const seedConfig = hit && Date.now() - hit.at < CACHE_TTL ? hit.config : null
44
+
45
+ const [config, setConfig] = useState<PayPalConfig | null>(seedConfig)
46
+ const [loading, setLoading] = useState<boolean>(
47
+ seedConfig === null && enabled
48
+ )
49
+ const [error, setError] = useState<string | null>(null)
50
+
51
+ const fetchIdRef = useRef(0)
52
+
53
+ useEffect(() => {
54
+ if (!enabled) {
55
+ setLoading(false)
56
+ setError(null)
57
+ return
58
+ }
59
+
60
+ const k = cacheKey(baseUrl, cartId)
61
+ const cached = _cache.get(k)
62
+ if (cached && Date.now() - cached.at < CACHE_TTL) {
63
+ setConfig(cached.config)
64
+ setLoading(false)
65
+ setError(null)
66
+ return
67
+ }
68
+
69
+ const id = ++fetchIdRef.current
70
+ let mounted = true
71
+ const controller = new AbortController()
72
+
73
+ setLoading(true)
74
+ setError(null)
75
+
76
+ ;(async () => {
77
+ try {
78
+ const cfg = await api.getConfig(cartId, controller.signal)
79
+ if (!mounted || id !== fetchIdRef.current) return
80
+ cacheSet(k, { config: cfg, at: Date.now() })
81
+ setConfig(cfg)
82
+ } catch (e: unknown) {
83
+ if (e instanceof Error && e.name === "AbortError") return
84
+ if (!mounted || id !== fetchIdRef.current) return
85
+ setError(
86
+ e instanceof Error ? e.message : "Failed to load PayPal config"
87
+ )
88
+ } finally {
89
+ if (mounted && id === fetchIdRef.current) setLoading(false)
90
+ }
91
+ })()
92
+
93
+ return () => {
94
+ mounted = false
95
+ controller.abort()
96
+ }
97
+ }, [api, baseUrl, cartId, enabled])
98
+
99
+ return { config, loading, error }
100
+ }
@@ -1,145 +1,146 @@
1
- "use client"
2
-
3
-
4
- import { useEffect, useMemo, useRef, useState } from "react"
5
- import { createPayPalStoreApi } from "../client/paypal"
6
-
7
- type Args = {
8
- baseUrl: string
9
- publishableApiKey?: string
10
- cartId?: string
11
- enabled?: boolean
12
- }
13
-
14
- type Result = {
15
- paypalEnabled: boolean
16
- paypalTitle: string
17
- cardEnabled: boolean
18
- cardTitle: string
19
- loading: boolean
20
- /**
21
- * Non-null when the config fetch failed and the returned flags are the
22
- * optimistic defaults rather than the merchant's real settings. Storefronts
23
- * can use this to hide or annotate the PayPal options instead of advertising
24
- * methods that will fail once selected.
25
- */
26
- error: string | null
27
- }
28
-
29
- const MAX_CACHE_ENTRIES = 50
30
- const _cache = new Map<string, { result: Result; at: number }>()
31
- const CACHE_TTL = 5 * 60 * 1000
32
-
33
- function cacheSet(key: string, value: { result: Result; at: number }) {
34
- if (_cache.size >= MAX_CACHE_ENTRIES) {
35
- const oldest = _cache.keys().next().value
36
- if (oldest !== undefined) _cache.delete(oldest)
37
- }
38
- _cache.set(key, value)
39
- }
40
-
41
- function cacheKey(baseUrl: string, cartId?: string) {
42
- return `ppm::${baseUrl}::${cartId ?? ""}`
43
- }
44
-
45
- const DEFAULT_RESULT: Result = {
46
- paypalEnabled: true,
47
- paypalTitle: "PayPal",
48
- cardEnabled: true,
49
- cardTitle: "Credit or Debit Card",
50
- loading: false,
51
- error: null,
52
- }
53
-
54
- export function usePayPalPaymentMethods({
55
- baseUrl,
56
- publishableApiKey,
57
- cartId,
58
- enabled = true,
59
- }: Args): Result {
60
- const api = useMemo(
61
- () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
62
- [baseUrl, publishableApiKey]
63
- )
64
-
65
- const key = cacheKey(baseUrl, cartId)
66
- const hit = _cache.get(key)
67
- const seed = hit && Date.now() - hit.at < CACHE_TTL ? hit.result : null
68
-
69
- const [result, setResult] = useState<Result>(seed ?? { ...DEFAULT_RESULT, loading: enabled })
70
- const fetchIdRef = useRef(0)
71
-
72
- useEffect(() => {
73
- if (!enabled) {
74
- setResult((prev) => ({ ...prev, loading: false }))
75
- return
76
- }
77
-
78
- const k = cacheKey(baseUrl, cartId)
79
- const cached = _cache.get(k)
80
- if (cached && Date.now() - cached.at < CACHE_TTL) {
81
- setResult(cached.result)
82
- return
83
- }
84
-
85
- const id = ++fetchIdRef.current
86
- let mounted = true
87
- const controller = new AbortController()
88
-
89
- setResult((prev) => ({ ...prev, loading: true }))
90
-
91
- ;(async () => {
92
- try {
93
- const cfg = await api.getConfig(cartId, controller.signal)
94
- if (!mounted || id !== fetchIdRef.current) return
95
-
96
- const next: Result = {
97
- paypalEnabled: cfg.paypal_enabled !== false,
98
- paypalTitle:
99
- typeof cfg.paypal_title === "string" && cfg.paypal_title
100
- ? cfg.paypal_title
101
- : "PayPal",
102
- cardEnabled: cfg.card_enabled !== false,
103
- cardTitle:
104
- typeof cfg.card_title === "string" && cfg.card_title
105
- ? cfg.card_title
106
- : "Credit or Debit Card",
107
- loading: false,
108
- error: null,
109
- }
110
-
111
- cacheSet(k, { result: next, at: Date.now() })
112
- setResult(next)
113
- } catch (e: unknown) {
114
- const msg = e instanceof Error ? e.message : ""
115
- if (e instanceof Error && e.name === "AbortError") return
116
- if (!mounted || id !== fetchIdRef.current) return
117
-
118
- if (msg.includes("403") || msg.includes("Forbidden")) {
119
- const disabled: Result = {
120
- ...DEFAULT_RESULT,
121
- paypalEnabled: false,
122
- cardEnabled: false,
123
- loading: false,
124
- error: msg || "PayPal is disabled",
125
- }
126
- setResult(disabled)
127
- return
128
- }
129
-
130
- setResult({
131
- ...DEFAULT_RESULT,
132
- loading: false,
133
- error: msg || "Failed to load PayPal payment methods",
134
- })
135
- }
136
- })()
137
-
138
- return () => {
139
- mounted = false
140
- controller.abort()
141
- }
142
- }, [api, baseUrl, cartId, enabled])
143
-
144
- return result
145
- }
1
+ "use client"
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react"
4
+ import { createPayPalStoreApi } from "../client/paypal"
5
+
6
+ type Args = {
7
+ baseUrl: string
8
+ publishableApiKey?: string
9
+ cartId?: string
10
+ enabled?: boolean
11
+ }
12
+
13
+ type Result = {
14
+ paypalEnabled: boolean
15
+ paypalTitle: string
16
+ cardEnabled: boolean
17
+ cardTitle: string
18
+ loading: boolean
19
+ /**
20
+ * Non-null when the config fetch failed and the returned flags are the
21
+ * optimistic defaults rather than the merchant's real settings. Storefronts
22
+ * can use this to hide or annotate the PayPal options instead of advertising
23
+ * methods that will fail once selected.
24
+ */
25
+ error: string | null
26
+ }
27
+
28
+ const MAX_CACHE_ENTRIES = 50
29
+ const _cache = new Map<string, { result: Result; at: number }>()
30
+ const CACHE_TTL = 5 * 60 * 1000
31
+
32
+ function cacheSet(key: string, value: { result: Result; at: number }) {
33
+ if (_cache.size >= MAX_CACHE_ENTRIES) {
34
+ const oldest = _cache.keys().next().value
35
+ if (oldest !== undefined) _cache.delete(oldest)
36
+ }
37
+ _cache.set(key, value)
38
+ }
39
+
40
+ function cacheKey(baseUrl: string, cartId?: string) {
41
+ return `ppm::${baseUrl}::${cartId ?? ""}`
42
+ }
43
+
44
+ const DEFAULT_RESULT: Result = {
45
+ paypalEnabled: true,
46
+ paypalTitle: "PayPal",
47
+ cardEnabled: true,
48
+ cardTitle: "Credit or Debit Card",
49
+ loading: false,
50
+ error: null,
51
+ }
52
+
53
+ export function usePayPalPaymentMethods({
54
+ baseUrl,
55
+ publishableApiKey,
56
+ cartId,
57
+ enabled = true,
58
+ }: Args): Result {
59
+ const api = useMemo(
60
+ () => createPayPalStoreApi({ baseUrl, publishableApiKey }),
61
+ [baseUrl, publishableApiKey]
62
+ )
63
+
64
+ const key = cacheKey(baseUrl, cartId)
65
+ const hit = _cache.get(key)
66
+ const seed = hit && Date.now() - hit.at < CACHE_TTL ? hit.result : null
67
+
68
+ const [result, setResult] = useState<Result>(
69
+ seed ?? { ...DEFAULT_RESULT, loading: enabled }
70
+ )
71
+ const fetchIdRef = useRef(0)
72
+
73
+ useEffect(() => {
74
+ if (!enabled) {
75
+ setResult((prev) => ({ ...prev, loading: false }))
76
+ return
77
+ }
78
+
79
+ const k = cacheKey(baseUrl, cartId)
80
+ const cached = _cache.get(k)
81
+ if (cached && Date.now() - cached.at < CACHE_TTL) {
82
+ setResult(cached.result)
83
+ return
84
+ }
85
+
86
+ const id = ++fetchIdRef.current
87
+ let mounted = true
88
+ const controller = new AbortController()
89
+
90
+ setResult((prev) => ({ ...prev, loading: true }))
91
+
92
+ ;(async () => {
93
+ try {
94
+ const cfg = await api.getConfig(cartId, controller.signal)
95
+ if (!mounted || id !== fetchIdRef.current) return
96
+
97
+ const next: Result = {
98
+ paypalEnabled: cfg.paypal_enabled !== false,
99
+ paypalTitle:
100
+ typeof cfg.paypal_title === "string" && cfg.paypal_title
101
+ ? cfg.paypal_title
102
+ : "PayPal",
103
+ cardEnabled: cfg.card_enabled !== false,
104
+ cardTitle:
105
+ typeof cfg.card_title === "string" && cfg.card_title
106
+ ? cfg.card_title
107
+ : "Credit or Debit Card",
108
+ loading: false,
109
+ error: null,
110
+ }
111
+
112
+ cacheSet(k, { result: next, at: Date.now() })
113
+ setResult(next)
114
+ } catch (e: unknown) {
115
+ const msg = e instanceof Error ? e.message : ""
116
+ if (e instanceof Error && e.name === "AbortError") return
117
+ if (!mounted || id !== fetchIdRef.current) return
118
+
119
+ if (msg.includes("403") || msg.includes("Forbidden")) {
120
+ const disabled: Result = {
121
+ ...DEFAULT_RESULT,
122
+ paypalEnabled: false,
123
+ cardEnabled: false,
124
+ loading: false,
125
+ error: msg || "PayPal is disabled",
126
+ }
127
+ setResult(disabled)
128
+ return
129
+ }
130
+
131
+ setResult({
132
+ ...DEFAULT_RESULT,
133
+ loading: false,
134
+ error: msg || "Failed to load PayPal payment methods",
135
+ })
136
+ }
137
+ })()
138
+
139
+ return () => {
140
+ mounted = false
141
+ controller.abort()
142
+ }
143
+ }, [api, baseUrl, cartId, enabled])
144
+
145
+ return result
146
+ }
package/src/index.ts CHANGED
@@ -1,16 +1,19 @@
1
- export * from "./client/types"
2
- export * from "./client/paypal"
3
- export * from "./hooks/usePayPalConfig"
4
- export * from "./components/PayPalProvider"
5
- export * from "./components/PayPalCurrencyNotice"
6
- export * from "./components/PayPalSmartButtons"
7
- export * from "./components/PayPalAdvancedCard"
8
- export * from "./adapters/MedusaNextPayPalAdapter"
9
- export * from "./components/PayPalPaymentSection"
10
- export * from "./hooks/usePayPalPaymentMethods"
11
- export { showProcessingOverlay, hideProcessingOverlay } from "./utils/processing-overlay"
12
- export {
13
- markCartCaptured,
14
- wasCartCaptured,
15
- clearCartCaptured,
16
- } from "./utils/captured-state"
1
+ export * from "./client/types"
2
+ export * from "./client/paypal"
3
+ export * from "./hooks/usePayPalConfig"
4
+ export * from "./components/PayPalProvider"
5
+ export * from "./components/PayPalCurrencyNotice"
6
+ export * from "./components/PayPalSmartButtons"
7
+ export * from "./components/PayPalAdvancedCard"
8
+ export * from "./adapters/MedusaNextPayPalAdapter"
9
+ export * from "./components/PayPalPaymentSection"
10
+ export * from "./hooks/usePayPalPaymentMethods"
11
+ export {
12
+ showProcessingOverlay,
13
+ hideProcessingOverlay,
14
+ } from "./utils/processing-overlay"
15
+ export {
16
+ markCartCaptured,
17
+ wasCartCaptured,
18
+ clearCartCaptured,
19
+ } from "./utils/captured-state"