@easypayment/medusa-payment-paypal 0.9.4 → 0.9.5

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.
Files changed (190) hide show
  1. package/.medusa/server/src/admin/index.d.ts +2 -0
  2. package/.medusa/server/src/admin/index.d.ts.map +1 -0
  3. package/.medusa/server/src/admin/index.js +194 -239
  4. package/.medusa/server/src/admin/index.js.map +1 -0
  5. package/.medusa/server/src/admin/index.mjs +195 -240
  6. package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts +8 -0
  7. package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts.map +1 -0
  8. package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js +8 -0
  9. package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js.map +1 -0
  10. package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts +8 -0
  11. package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts.map +1 -0
  12. package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js +8 -0
  13. package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js.map +1 -0
  14. package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts +2 -0
  15. package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts.map +1 -0
  16. package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js +25 -0
  17. package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js.map +1 -0
  18. package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts +12 -0
  19. package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts.map +1 -0
  20. package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js +22 -0
  21. package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js.map +1 -0
  22. package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts +7 -0
  23. package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts.map +1 -0
  24. package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js +40 -0
  25. package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js.map +1 -0
  26. package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts +2 -0
  27. package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts.map +1 -0
  28. package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js +76 -0
  29. package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js.map +1 -0
  30. package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts +2 -0
  31. package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts.map +1 -0
  32. package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js +81 -0
  33. package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js.map +1 -0
  34. package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts +2 -0
  35. package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts.map +1 -0
  36. package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js +9 -0
  37. package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js.map +1 -0
  38. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts +20 -0
  39. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -0
  40. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +440 -0
  41. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -0
  42. package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts +2 -0
  43. package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts.map +1 -0
  44. package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js +9 -0
  45. package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js.map +1 -0
  46. package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts +4 -0
  47. package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts.map +1 -0
  48. package/.medusa/server/src/admin/routes/settings/paypal/page.js +14 -0
  49. package/.medusa/server/src/admin/routes/settings/paypal/page.js.map +1 -0
  50. package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts +2 -0
  51. package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts.map +1 -0
  52. package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js +9 -0
  53. package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js.map +1 -0
  54. package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts +2 -0
  55. package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts.map +1 -0
  56. package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js +109 -0
  57. package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js.map +1 -0
  58. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts +1 -1
  59. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
  60. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js +24 -9
  61. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js.map +1 -1
  62. package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts.map +1 -1
  63. package/.medusa/server/src/api/admin/paypal/disconnect/route.js +10 -3
  64. package/.medusa/server/src/api/admin/paypal/disconnect/route.js.map +1 -1
  65. package/.medusa/server/src/api/admin/paypal/environment/route.js +2 -2
  66. package/.medusa/server/src/api/admin/paypal/environment/route.js.map +1 -1
  67. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -1
  68. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +3 -2
  69. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -1
  70. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +1 -1
  71. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -1
  72. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +4 -4
  73. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -1
  74. package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts.map +1 -1
  75. package/.medusa/server/src/api/admin/paypal/save-credentials/route.js +21 -8
  76. package/.medusa/server/src/api/admin/paypal/save-credentials/route.js.map +1 -1
  77. package/.medusa/server/src/api/admin/paypal/settings/route.d.ts.map +1 -1
  78. package/.medusa/server/src/api/admin/paypal/settings/route.js +34 -4
  79. package/.medusa/server/src/api/admin/paypal/settings/route.js.map +1 -1
  80. package/.medusa/server/src/api/admin/paypal/status/route.d.ts.map +1 -1
  81. package/.medusa/server/src/api/admin/paypal/status/route.js +11 -5
  82. package/.medusa/server/src/api/admin/paypal/status/route.js.map +1 -1
  83. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
  84. package/.medusa/server/src/api/store/paypal/capture-order/route.js +21 -3
  85. package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
  86. package/.medusa/server/src/api/store/paypal/config/route.js +2 -2
  87. package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -1
  88. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -1
  89. package/.medusa/server/src/api/store/paypal/create-order/route.js +13 -9
  90. package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -1
  91. package/.medusa/server/src/api/store/paypal/settings/route.js +3 -3
  92. package/.medusa/server/src/api/store/paypal/settings/route.js.map +1 -1
  93. package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -1
  94. package/.medusa/server/src/api/store/paypal/webhook/route.js +10 -3
  95. package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -1
  96. package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -1
  97. package/.medusa/server/src/api/store/paypal-complete/route.js +7 -4
  98. package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -1
  99. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js +3 -3
  100. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js.map +1 -1
  101. package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts +1 -1
  102. package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts.map +1 -1
  103. package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js +1 -1
  104. package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js.map +1 -1
  105. package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts +1 -1
  106. package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts.map +1 -1
  107. package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js +1 -1
  108. package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js.map +1 -1
  109. package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts +1 -1
  110. package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts.map +1 -1
  111. package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js +1 -1
  112. package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js.map +1 -1
  113. package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.d.ts +6 -0
  114. package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.d.ts.map +1 -0
  115. package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.js +25 -0
  116. package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.js.map +1 -0
  117. package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts +1 -1
  118. package/.medusa/server/src/modules/paypal/models/paypal_connection.js +3 -3
  119. package/.medusa/server/src/modules/paypal/models/paypal_connection.js.map +1 -1
  120. package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts +1 -1
  121. package/.medusa/server/src/modules/paypal/models/paypal_metric.js +1 -1
  122. package/.medusa/server/src/modules/paypal/models/paypal_metric.js.map +1 -1
  123. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts +1 -1
  124. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js +1 -1
  125. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js.map +1 -1
  126. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -1
  127. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +95 -76
  128. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -1
  129. package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
  130. package/.medusa/server/src/modules/paypal/payment-provider/service.js +39 -89
  131. package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
  132. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts.map +1 -1
  133. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js +7 -0
  134. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js.map +1 -1
  135. package/.medusa/server/src/modules/paypal/service.d.ts +10 -8
  136. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  137. package/.medusa/server/src/modules/paypal/service.js +56 -51
  138. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  139. package/.medusa/server/src/modules/paypal/types/config.d.ts.map +1 -1
  140. package/.medusa/server/src/modules/paypal/types/config.js +11 -2
  141. package/.medusa/server/src/modules/paypal/types/config.js.map +1 -1
  142. package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts.map +1 -1
  143. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js +1 -0
  144. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js.map +1 -1
  145. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -1
  146. package/.medusa/server/src/modules/paypal/webhook-processor.js +1 -1
  147. package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -1
  148. package/.medusa/server/src/subscribers/paypal-order-invoice.js +4 -4
  149. package/.medusa/server/src/subscribers/paypal-order-invoice.js.map +1 -1
  150. package/package.json +1 -1
  151. package/src/admin/index.ts +1 -7
  152. package/src/admin/routes/settings/paypal/_components/FieldRow.tsx +29 -0
  153. package/src/admin/routes/settings/paypal/_components/SectionCard.tsx +26 -0
  154. package/src/admin/routes/settings/paypal/_components/Toast.tsx +4 -3
  155. package/src/admin/routes/settings/paypal/_utils/adminFetch.ts +32 -0
  156. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +23 -83
  157. package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +17 -77
  158. package/src/admin/routes/settings/paypal/connection/page.tsx +86 -45
  159. package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +23 -101
  160. package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +26 -9
  161. package/src/api/admin/paypal/disconnect/route.ts +9 -3
  162. package/src/api/admin/paypal/environment/route.ts +2 -2
  163. package/src/api/admin/paypal/onboard-complete/route.ts +4 -3
  164. package/src/api/admin/paypal/onboarding-link/route.ts +1 -1
  165. package/src/api/admin/paypal/onboarding-status/route.ts +5 -5
  166. package/src/api/admin/paypal/save-credentials/route.ts +24 -9
  167. package/src/api/admin/paypal/settings/route.ts +35 -4
  168. package/src/api/admin/paypal/status/route.ts +10 -5
  169. package/src/api/store/paypal/capture-order/route.ts +27 -3
  170. package/src/api/store/paypal/config/route.ts +2 -2
  171. package/src/api/store/paypal/create-order/route.ts +16 -10
  172. package/src/api/store/paypal/settings/route.ts +3 -3
  173. package/src/api/store/paypal/webhook/route.ts +13 -3
  174. package/src/api/store/paypal-complete/route.ts +8 -4
  175. package/src/modules/paypal/clients/paypal-seller.client.ts +3 -3
  176. package/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.ts +1 -1
  177. package/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.ts +1 -1
  178. package/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.ts +1 -1
  179. package/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.ts +44 -0
  180. package/src/modules/paypal/models/paypal_connection.ts +3 -3
  181. package/src/modules/paypal/models/paypal_metric.ts +1 -1
  182. package/src/modules/paypal/models/paypal_webhook_event.ts +1 -1
  183. package/src/modules/paypal/payment-provider/card-service.ts +120 -80
  184. package/src/modules/paypal/payment-provider/service.ts +44 -93
  185. package/src/modules/paypal/payment-provider/webhook-utils.ts +9 -0
  186. package/src/modules/paypal/service.ts +79 -66
  187. package/src/modules/paypal/types/config.ts +18 -2
  188. package/src/modules/paypal/utils/paypal-auth.ts +1 -0
  189. package/src/modules/paypal/webhook-processor.ts +8 -5
  190. package/src/subscribers/paypal-order-invoice.ts +4 -4
@@ -1,38 +1,9 @@
1
- import React, { useEffect, useRef, useState } from "react"
1
+ import React, { useCallback, useEffect, useRef, useState } from "react"
2
2
  import PayPalTabs from "../_components/Tabs"
3
-
4
- type AdminFetchOptions = {
5
- method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
6
- body?: Record<string, unknown>
7
- query?: Record<string, string>
8
- }
9
-
10
- async function adminFetch<T = unknown>(path: string, opts: AdminFetchOptions = {}): Promise<T> {
11
- const { method = "GET", body, query } = opts
12
- let url = path
13
- if (query && Object.keys(query).length > 0) {
14
- const params = new URLSearchParams(query)
15
- url = `${path}?${params.toString()}`
16
- }
17
- const headers: Record<string, string> = { Accept: "application/json" }
18
- if (body !== undefined) headers["Content-Type"] = "application/json"
19
- if (typeof window !== "undefined") {
20
- const token = (window as any).__medusa__?.token
21
- if (token) headers["Authorization"] = `Bearer ${token}`
22
- }
23
- const res = await fetch(url, {
24
- method, headers, credentials: "include",
25
- body: body !== undefined ? JSON.stringify(body) : undefined,
26
- })
27
- const text = await res.text().catch(() => "")
28
- if (!res.ok) {
29
- if (res.status === 401) throw new Error("Unauthorized (401) - session may have expired. Please reload and log in again.")
30
- if (res.status === 403) throw new Error("Forbidden (403) - you do not have permission to perform this action.")
31
- throw new Error(text || `Request failed with status ${res.status}`)
32
- }
33
- if (!text) return {} as T
34
- try { return JSON.parse(text) as T } catch { return {} as T }
35
- }
3
+ import SectionCard from "../_components/SectionCard"
4
+ import FieldRow from "../_components/FieldRow"
5
+ import Toast, { ToastState } from "../_components/Toast"
6
+ import { adminFetch } from "../_utils/adminFetch"
36
7
 
37
8
  type ButtonColor = "gold" | "blue" | "silver" | "black" | "white"
38
9
  type ButtonShape = "rect" | "pill"
@@ -79,51 +50,6 @@ const LABEL_OPTIONS: { value: ButtonLabel; label: string }[] = [
79
50
  { value: "pay", label: "Pay" },
80
51
  ]
81
52
 
82
- function SectionCard({
83
- title,
84
- description,
85
- children,
86
- right,
87
- }: {
88
- title: string
89
- description?: string
90
- children: React.ReactNode
91
- right?: React.ReactNode
92
- }) {
93
- return (
94
- <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
95
- <div className="flex items-start justify-between gap-4 border-b border-ui-border-base p-4">
96
- <div>
97
- <div className="text-base font-semibold text-ui-fg-base">{title}</div>
98
- {description ? <div className="mt-1 text-sm text-ui-fg-subtle">{description}</div> : null}
99
- </div>
100
- {right}
101
- </div>
102
- <div className="p-4">{children}</div>
103
- </div>
104
- )
105
- }
106
-
107
- function FieldRow({
108
- label,
109
- hint,
110
- children,
111
- }: {
112
- label: string
113
- hint?: React.ReactNode
114
- children: React.ReactNode
115
- }) {
116
- return (
117
- <div className="grid grid-cols-12 items-start gap-4 py-3">
118
- <div className="col-span-12 md:col-span-4">
119
- <div className="text-sm font-medium text-ui-fg-base">{label}</div>
120
- {hint ? <div className="mt-1 text-xs text-ui-fg-subtle">{hint}</div> : null}
121
- </div>
122
- <div className="col-span-12 md:col-span-8">{children}</div>
123
- </div>
124
- )
125
- }
126
-
127
53
  export default function PayPalSettingsTab() {
128
54
  const [form, setForm] = useState<PayPalSettingsForm>({
129
55
  enabled: true,
@@ -137,9 +63,11 @@ export default function PayPalSettingsTab() {
137
63
  })
138
64
  const [loading, setLoading] = useState(false)
139
65
  const [saving, setSaving] = useState(false)
140
- const [toast, setToast] = useState<{ type: "success" | "error"; message: string } | null>(null)
66
+ const [toast, setToast] = useState<ToastState>(null)
141
67
  const didInit = useRef(false)
142
68
 
69
+ const dismissToast = useCallback(() => setToast(null), [])
70
+
143
71
  useEffect(() => {
144
72
  if (didInit.current) return
145
73
  didInit.current = true
@@ -184,16 +112,14 @@ export default function PayPalSettingsTab() {
184
112
  ...saved,
185
113
  }))
186
114
  }
187
- setToast({ type: "success", message: "Settings saved" })
188
- window.setTimeout(() => setToast(null), 2500)
115
+ setToast({ kind: "success", message: "Settings saved" })
189
116
  } catch (e: unknown) {
190
117
  setToast({
191
- type: "error",
118
+ kind: "error",
192
119
  message:
193
120
  (e instanceof Error ? e.message : "") ||
194
121
  "Failed to save settings.",
195
122
  })
196
- window.setTimeout(() => setToast(null), 3500)
197
123
  } finally {
198
124
  setSaving(false)
199
125
  }
@@ -213,17 +139,7 @@ export default function PayPalSettingsTab() {
213
139
 
214
140
  <PayPalTabs />
215
141
 
216
- {toast ? (
217
- <div
218
- className="fixed right-6 top-6 z-50 rounded-md border border-ui-border-base bg-ui-bg-base px-4 py-3 text-sm shadow-lg"
219
- role="status"
220
- aria-live="polite"
221
- >
222
- <span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>
223
- {toast.message}
224
- </span>
225
- </div>
226
- ) : null}
142
+ <Toast toast={toast} onClose={dismissToast} />
227
143
 
228
144
  <SectionCard
229
145
  title="PayPal Settings"
@@ -255,8 +171,9 @@ export default function PayPalSettingsTab() {
255
171
  </label>
256
172
  </FieldRow>
257
173
 
258
- <FieldRow label="Title">
174
+ <FieldRow label="Title" htmlFor="pp-title">
259
175
  <input
176
+ id="pp-title"
260
177
  value={form.title}
261
178
  onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))}
262
179
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -273,8 +190,9 @@ export default function PayPalSettingsTab() {
273
190
  >
274
191
  <div className="divide-y divide-ui-border-base">
275
192
 
276
- <FieldRow label="Button Color">
193
+ <FieldRow label="Button Color" htmlFor="pp-btn-color">
277
194
  <select
195
+ id="pp-btn-color"
278
196
  value={form.buttonColor}
279
197
  onChange={(e) => setForm((p) => ({ ...p, buttonColor: e.target.value as ButtonColor }))}
280
198
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -287,8 +205,9 @@ export default function PayPalSettingsTab() {
287
205
  </select>
288
206
  </FieldRow>
289
207
 
290
- <FieldRow label="Button Shape">
208
+ <FieldRow label="Button Shape" htmlFor="pp-btn-shape">
291
209
  <select
210
+ id="pp-btn-shape"
292
211
  value={form.buttonShape}
293
212
  onChange={(e) => setForm((p) => ({ ...p, buttonShape: e.target.value as ButtonShape }))}
294
213
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -301,8 +220,9 @@ export default function PayPalSettingsTab() {
301
220
  </select>
302
221
  </FieldRow>
303
222
 
304
- <FieldRow label="Button Width">
223
+ <FieldRow label="Button Width" htmlFor="pp-btn-width">
305
224
  <select
225
+ id="pp-btn-width"
306
226
  value={form.buttonWidth}
307
227
  onChange={(e) => setForm((p) => ({ ...p, buttonWidth: e.target.value as ButtonWidth }))}
308
228
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -315,8 +235,9 @@ export default function PayPalSettingsTab() {
315
235
  </select>
316
236
  </FieldRow>
317
237
 
318
- <FieldRow label="Button Height">
238
+ <FieldRow label="Button Height" htmlFor="pp-btn-height">
319
239
  <select
240
+ id="pp-btn-height"
320
241
  value={String(form.buttonHeight)}
321
242
  onChange={(e) => setForm((p) => ({ ...p, buttonHeight: Number(e.target.value) }))}
322
243
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -329,8 +250,9 @@ export default function PayPalSettingsTab() {
329
250
  </select>
330
251
  </FieldRow>
331
252
 
332
- <FieldRow label="Button Label">
253
+ <FieldRow label="Button Label" htmlFor="pp-btn-label">
333
254
  <select
255
+ id="pp-btn-label"
334
256
  value={form.buttonLabel}
335
257
  onChange={(e) => setForm((p) => ({ ...p, buttonLabel: e.target.value as ButtonLabel }))}
336
258
  className="w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive"
@@ -7,18 +7,35 @@ type CreatePaymentSessionBody = {
7
7
  customer_id?: string
8
8
  }
9
9
 
10
+ const ALLOWED_PROVIDERS = new Set([
11
+ "pp_paypal_paypal",
12
+ "pp_paypal_card_paypal_card",
13
+ ])
14
+
10
15
  export async function POST(req: MedusaRequest, res: MedusaResponse) {
11
16
  const collectionId = req.params.id
12
17
  const { provider_id, data, customer_id } = req.body as CreatePaymentSessionBody
13
18
 
14
- const { result } = await createPaymentSessionsWorkflow(req.scope).run({
15
- input: {
16
- payment_collection_id: collectionId,
17
- provider_id,
18
- customer_id,
19
- data,
20
- },
21
- })
19
+ if (!provider_id || !ALLOWED_PROVIDERS.has(provider_id)) {
20
+ return res.status(400).json({ message: "Invalid or unsupported provider_id" })
21
+ }
22
+
23
+ if (!collectionId || typeof collectionId !== "string") {
24
+ return res.status(400).json({ message: "Invalid collection id" })
25
+ }
26
+
27
+ try {
28
+ const { result } = await createPaymentSessionsWorkflow(req.scope).run({
29
+ input: {
30
+ payment_collection_id: collectionId,
31
+ provider_id,
32
+ customer_id,
33
+ data,
34
+ },
35
+ })
22
36
 
23
- res.status(200).json({ payment_session: result })
37
+ res.status(200).json({ payment_session: result })
38
+ } catch {
39
+ res.status(500).json({ message: "Failed to create payment session" })
40
+ }
24
41
  }
@@ -2,7 +2,13 @@ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
2
2
  import type PayPalModuleService from "../../../../modules/paypal/service"
3
3
 
4
4
  export async function POST(req: MedusaRequest, res: MedusaResponse) {
5
- const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
6
- await paypal.disconnect()
7
- return res.json({ ok: true })
5
+ try {
6
+ const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
7
+ await paypal.disconnect()
8
+ return res.json({ ok: true })
9
+ } catch (e: unknown) {
10
+ const message = e instanceof Error ? e.message : String(e)
11
+ console.error("[PayPal] disconnect failed:", message)
12
+ return res.status(500).json({ message: "Failed to disconnect PayPal" })
13
+ }
8
14
  }
@@ -19,7 +19,7 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
19
19
  await paypal.setEnvironment(env)
20
20
  const status = await paypal.getStatus()
21
21
  return res.json(status)
22
- } catch (e: any) {
23
- return res.status(500).json({ message: e?.message || "Unknown error" })
22
+ } catch {
23
+ return res.status(500).json({ message: "Failed to update environment" })
24
24
  }
25
25
  }
@@ -25,10 +25,11 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
25
25
  })
26
26
 
27
27
  return res.json({ ok: true })
28
- } catch (e: any) {
29
- console.error("[PayPal] onboard-complete failed", e)
28
+ } catch (e: unknown) {
29
+ const message = e instanceof Error ? e.message : String(e)
30
+ console.error("[PayPal] onboard-complete failed:", message)
30
31
  return res.status(500).json({
31
- message: e?.message || "Failed to exchange and save PayPal credentials",
32
+ message: "Failed to exchange and save PayPal credentials",
32
33
  })
33
34
  }
34
35
  }
@@ -33,7 +33,7 @@ export async function POST(req: AuthenticatedMedusaRequest, res: MedusaResponse)
33
33
  } catch (e: any) {
34
34
  console.error("[paypal_onboarding] onboarding-link error:", e?.message || e, e?.stack)
35
35
  return res.status(500).json({
36
- message: e?.message || "Unknown error",
36
+ message: "Failed to generate PayPal onboarding link",
37
37
  })
38
38
  }
39
39
  }
@@ -6,12 +6,12 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
6
6
  const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
7
7
  const status = await paypal.getStatus()
8
8
  return res.json(status)
9
- } catch (e: any) {
10
- console.error("[paypal_onboarding] onboarding-status error:", e?.message || e, e?.stack)
11
- return res.json({
12
- environment: "live",
9
+ } catch (e: unknown) {
10
+ const message = e instanceof Error ? e.message : String(e)
11
+ console.error("[paypal_onboarding] onboarding-status error:", message)
12
+ return res.status(500).json({
13
13
  status: "disconnected",
14
- error: e?.message || "Unknown error",
14
+ message: "Failed to retrieve PayPal status",
15
15
  })
16
16
  }
17
17
  }
@@ -6,17 +6,32 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
6
6
  const body = req.body as {
7
7
  clientId?: string
8
8
  clientSecret?: string
9
- environment?: "sandbox" | "live"
9
+ environment?: string
10
10
  }
11
11
 
12
- if (!body?.clientId || !body?.clientSecret) {
13
- return res.status(400).json({ message: "Missing clientId/clientSecret" })
12
+ if (!body?.clientId || typeof body.clientId !== "string" || body.clientId.length > 200) {
13
+ return res.status(400).json({ message: "Invalid or missing clientId" })
14
14
  }
15
15
 
16
- await paypal.saveAndHydrateSellerCredentials({
17
- clientId: body.clientId,
18
- clientSecret: body.clientSecret,
19
- environment: body.environment,
20
- })
21
- return res.json({ ok: true })
16
+ if (!body?.clientSecret || typeof body.clientSecret !== "string" || body.clientSecret.length > 200) {
17
+ return res.status(400).json({ message: "Invalid or missing clientSecret" })
18
+ }
19
+
20
+ const environment =
21
+ body.environment === "sandbox" || body.environment === "live"
22
+ ? body.environment
23
+ : undefined
24
+
25
+ try {
26
+ await paypal.saveAndHydrateSellerCredentials({
27
+ clientId: body.clientId.trim(),
28
+ clientSecret: body.clientSecret.trim(),
29
+ environment,
30
+ })
31
+ return res.json({ ok: true })
32
+ } catch (e: unknown) {
33
+ const message = e instanceof Error ? e.message : String(e)
34
+ console.error("[PayPal] save-credentials failed:", message)
35
+ return res.status(500).json({ message: "Failed to save PayPal credentials" })
36
+ }
22
37
  }
@@ -2,13 +2,44 @@ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
2
2
  import type PayPalModuleService from "../../../../modules/paypal/service"
3
3
 
4
4
  export async function GET(req: MedusaRequest, res: MedusaResponse) {
5
- const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
6
- return res.json(await paypal.getSettings())
5
+ try {
6
+ const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
7
+ return res.json(await paypal.getSettings())
8
+ } catch (e: unknown) {
9
+ console.error("[PayPal] settings GET failed:", e instanceof Error ? e.message : e)
10
+ return res.status(500).json({ message: "Failed to load PayPal settings" })
11
+ }
7
12
  }
8
13
 
14
+ const ALLOWED_SETTINGS_KEYS = new Set([
15
+ "additional_settings",
16
+ "paypal_settings",
17
+ "advanced_card_payments",
18
+ "pay_later_messaging",
19
+ "apple_pay",
20
+ "google_pay",
21
+ "api_details",
22
+ ])
23
+
9
24
  export async function POST(req: MedusaRequest, res: MedusaResponse) {
10
25
  const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
11
26
 
12
- const patch = (req.body && typeof req.body === "object") ? (req.body as Record<string, any>) : {}
13
- return res.json(await paypal.saveSettings(patch))
27
+ const raw = (req.body && typeof req.body === "object") ? (req.body as Record<string, unknown>) : {}
28
+ const patch: Record<string, unknown> = {}
29
+ for (const key of Object.keys(raw)) {
30
+ if (ALLOWED_SETTINGS_KEYS.has(key)) {
31
+ patch[key] = raw[key]
32
+ }
33
+ }
34
+
35
+ if (Object.keys(patch).length === 0) {
36
+ return res.status(400).json({ message: "No valid settings fields provided" })
37
+ }
38
+
39
+ try {
40
+ return res.json(await paypal.saveSettings(patch as Record<string, Record<string, unknown>>))
41
+ } catch (e: unknown) {
42
+ console.error("[PayPal] settings POST failed:", e instanceof Error ? e.message : e)
43
+ return res.status(500).json({ message: "Failed to save PayPal settings" })
44
+ }
14
45
  }
@@ -2,11 +2,16 @@ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
2
2
  import type PayPalModuleService from "../../../../modules/paypal/service"
3
3
 
4
4
  export async function GET(req: MedusaRequest, res: MedusaResponse) {
5
- const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
5
+ try {
6
+ const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
6
7
 
7
- const q = (req.query || {}) as Record<string, any>
8
- const envParam = (q.environment || q.env) as string | undefined
9
- const env = envParam === "live" ? "live" : envParam === "sandbox" ? "sandbox" : undefined
8
+ const q = (req.query || {}) as Record<string, any>
9
+ const envParam = (q.environment || q.env) as string | undefined
10
+ const env = envParam === "live" ? "live" : envParam === "sandbox" ? "sandbox" : undefined
10
11
 
11
- return res.json(await paypal.getStatus(env))
12
+ return res.json(await paypal.getStatus(env))
13
+ } catch (e: unknown) {
14
+ console.error("[PayPal] status GET failed:", e instanceof Error ? e.message : e)
15
+ return res.status(500).json({ message: "Failed to retrieve PayPal status" })
16
+ }
12
17
  }
@@ -8,6 +8,8 @@ import { isPayPalProviderId } from "../../../../modules/paypal/utils/provider-id
8
8
 
9
9
  const BN_CODE = "MBJTechnolabs_SI_SPB"
10
10
 
11
+ const PAYPAL_ORDER_ID_RE = /^[A-Z0-9]{10,25}$/
12
+
11
13
  type Body = {
12
14
  cart_id: string
13
15
  order_id: string
@@ -188,6 +190,27 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
188
190
  return res.status(400).json({ message: "cart_id and order_id are required" })
189
191
  }
190
192
 
193
+ if (typeof cartId !== "string" || !cartId.startsWith("cart_")) {
194
+ return res.status(400).json({ message: "Invalid cart_id format" })
195
+ }
196
+
197
+ if (!PAYPAL_ORDER_ID_RE.test(orderId)) {
198
+ return res.status(400).json({ message: "Invalid order_id format" })
199
+ }
200
+
201
+ const session = await findPayPalSessionForCart(cartId, scope)
202
+ if (session) {
203
+ const sessionOrderId = String(
204
+ (session.session_data?.paypal as Record<string, unknown>)?.order_id ||
205
+ session.session_data?.order_id || ""
206
+ )
207
+ if (sessionOrderId && sessionOrderId !== orderId) {
208
+ return res.status(400).json({
209
+ message: "order_id does not match the payment session for this cart",
210
+ })
211
+ }
212
+ }
213
+
191
214
  const existingCapture = await getExistingCapture(cartId, orderId, scope)
192
215
  if (existingCapture) {
193
216
  return res.json({ capture: existingCapture })
@@ -207,10 +230,11 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
207
230
  : "capture"
208
231
 
209
232
  const requestId = resolveIdempotencyKey(req, "capture-order", `pp-capture-${orderId}`)
233
+ const safeOrderId = encodeURIComponent(orderId)
210
234
  const endpoint =
211
235
  paymentAction === "authorize"
212
- ? `${base}/v2/checkout/orders/${orderId}/authorize`
213
- : `${base}/v2/checkout/orders/${orderId}/capture`
236
+ ? `${base}/v2/checkout/orders/${safeOrderId}/authorize`
237
+ : `${base}/v2/checkout/orders/${safeOrderId}/capture`
214
238
 
215
239
  const ppResp = await fetch(endpoint, {
216
240
  method: "POST",
@@ -260,6 +284,6 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
260
284
  await paypal.recordMetric("capture_order_failed")
261
285
  } catch {
262
286
  }
263
- return res.status(500).json({ message: e?.message || "Failed to capture PayPal order" })
287
+ return res.status(500).json({ message: "Failed to capture PayPal order" })
264
288
  }
265
289
  }
@@ -94,7 +94,7 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
94
94
  button_height: paypalSettings.buttonHeight ?? 45,
95
95
  button_label: paypalSettings.buttonLabel || "paypal",
96
96
  })
97
- } catch (e: any) {
98
- return res.status(500).json({ message: e?.message || "Failed to load PayPal config" })
97
+ } catch {
98
+ return res.status(500).json({ message: "Failed to load PayPal config" })
99
99
  }
100
100
  }
@@ -111,10 +111,14 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
111
111
  const cartId = body.cart_id
112
112
  const isCardPayment = !!(body as any).is_card_payment
113
113
 
114
- if (!cartId) {
114
+ if (!cartId || typeof cartId !== "string") {
115
115
  return res.status(400).json({ message: "cart_id is required" })
116
116
  }
117
117
 
118
+ if (!cartId.startsWith("cart_")) {
119
+ return res.status(400).json({ message: "Invalid cart_id format" })
120
+ }
121
+
118
122
  const existingOrderId = await getExistingPayPalOrderId(req, cartId)
119
123
  if (existingOrderId) {
120
124
  return res.json({ id: existingOrderId })
@@ -290,11 +294,13 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
290
294
  )
291
295
  const roundedItemSumFixed = parseFloat(roundedItemSum.toFixed(exponent))
292
296
 
293
- const adjustedItemTotal = roundedItemSumFixed
297
+ const finalPurchaseItems = purchaseItemsRaw.map((item: any) => item.paypalItem)
294
298
 
295
- const diff = parseFloat((adjustedItemTotal - roundedItemSumFixed).toFixed(exponent))
299
+ const adjustedItemTotal = sendItemDetails && finalPurchaseItems.length > 0
300
+ ? roundedItemSumFixed
301
+ : subtotalMajor
296
302
 
297
- const finalPurchaseItems = purchaseItemsRaw.map((item: any) => item.paypalItem)
303
+ const diff = parseFloat((subtotalMajor - roundedItemSumFixed).toFixed(exponent))
298
304
 
299
305
  if (Math.abs(diff) > 0.000001 && sendItemDetails && finalPurchaseItems.length > 0) {
300
306
  finalPurchaseItems.push({
@@ -438,12 +444,12 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
438
444
  await paypal.recordMetric("create_order_failed")
439
445
  } catch {
440
446
  }
441
- const message = e?.message || "Failed to create PayPal order"
442
- const status = message.includes("PayPal does not support currency")
443
- ? 400
444
- : message.includes("PayPal is configured for")
445
- ? 400
446
- : 500
447
+ const rawMessage = e?.message || ""
448
+ const isCurrencyError =
449
+ rawMessage.includes("PayPal does not support currency") ||
450
+ rawMessage.includes("PayPal is configured for")
451
+ const status = isCurrencyError ? 400 : 500
452
+ const message = isCurrencyError ? rawMessage : "Failed to create PayPal order"
447
453
  return res.status(status).json({ message })
448
454
  }
449
455
  }
@@ -11,9 +11,9 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
11
11
 
12
12
  return res.json({
13
13
  paymentAction: additionalSettings.paymentAction === "authorize" ? "authorize" : "capture",
14
- advancedCardEnabled: advancedCard.enabled === true,
14
+ advancedCardEnabled: advancedCard.enabled !== false,
15
15
  })
16
- } catch (e: any) {
17
- return res.status(500).json({ message: e?.message || "Failed to load PayPal settings" })
16
+ } catch {
17
+ return res.status(500).json({ message: "Failed to load PayPal settings" })
18
18
  }
19
19
  }
@@ -140,6 +140,16 @@ async function verifyWebhookSignature(
140
140
  webhook_event: body,
141
141
  }
142
142
 
143
+ const certUrl = verifyPayload.cert_url
144
+ if (
145
+ certUrl &&
146
+ !certUrl.startsWith("https://www.paypal.com/") &&
147
+ !certUrl.startsWith("https://api-m.paypal.com/") &&
148
+ !certUrl.startsWith("https://api-m.sandbox.paypal.com/")
149
+ ) {
150
+ throw new Error("Invalid paypal-cert-url: must originate from paypal.com")
151
+ }
152
+
143
153
  const missing = Object.entries(verifyPayload)
144
154
  .filter(([k, v]) => k !== "webhook_id" && k !== "webhook_event" && !v)
145
155
  .map(([k]) => k)
@@ -208,7 +218,7 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
208
218
  console.error("[PayPal] webhook: signature verification failed:", e?.message)
209
219
  return res
210
220
  .status(401)
211
- .json({ message: e?.message || "Webhook signature verification failed" })
221
+ .json({ message: "Webhook signature verification failed" })
212
222
  }
213
223
 
214
224
  const eventVersion = normalizeEventVersion(payload)
@@ -316,10 +326,10 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
316
326
  await paypal.recordMetric("webhook_failed").catch(() => {})
317
327
 
318
328
  if (!retryable) {
319
- return res.status(200).json({ ok: false, message: e?.message })
329
+ return res.status(200).json({ ok: false, message: "Webhook processing failed" })
320
330
  }
321
331
  return res
322
332
  .status(500)
323
- .json({ message: e?.message || "PayPal webhook processing error" })
333
+ .json({ message: "PayPal webhook processing error" })
324
334
  }
325
335
  }
@@ -6,8 +6,12 @@ import type PayPalModuleService from "../../../modules/paypal/service"
6
6
  export async function POST(req: MedusaRequest, res: MedusaResponse) {
7
7
  const { cart_id } = req.body as { cart_id: string }
8
8
 
9
- if (!cart_id) {
10
- return res.status(400).json({ error: "cart_id is required" })
9
+ if (!cart_id || typeof cart_id !== "string") {
10
+ return res.status(400).json({ message: "cart_id is required" })
11
+ }
12
+
13
+ if (!cart_id.startsWith("cart_")) {
14
+ return res.status(400).json({ message: "Invalid cart_id format" })
11
15
  }
12
16
 
13
17
  try {
@@ -36,7 +40,7 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
36
40
  )[0]
37
41
 
38
42
  if (!session) {
39
- return res.status(400).json({ error: "No PayPal payment session found for cart" })
43
+ return res.status(400).json({ message: "No PayPal payment session found for cart" })
40
44
  }
41
45
 
42
46
  const currentStatus = String(session.status || "")
@@ -90,6 +94,6 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
90
94
  return res.json({ success: true, session_id: session.id })
91
95
  } catch (e: any) {
92
96
  console.error("[paypal-complete] error:", e?.message || e)
93
- return res.status(500).json({ error: e?.message || "Internal error" })
97
+ return res.status(500).json({ message: "Internal error" })
94
98
  }
95
99
  }