@easypayment/medusa-payment-paypal 0.9.4 → 0.9.6

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 +190 -233
  4. package/.medusa/server/src/admin/index.js.map +1 -0
  5. package/.medusa/server/src/admin/index.mjs +191 -234
  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 +88 -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 PaymentAction = "capture" | "authorize"
38
9
  type LandingPage = "no_preference" | "login" | "billing"
@@ -53,7 +24,7 @@ const DEFAULT_FORM: AdditionalSettingsForm = {
53
24
  landingPage: "no_preference",
54
25
  requireInstantPayment: false,
55
26
  sendItemDetails: true,
56
- invoicePrefix: "WC-",
27
+ invoicePrefix: "PP-",
57
28
  creditCardStatementName: "PayPal",
58
29
  }
59
30
 
@@ -63,40 +34,15 @@ function mergeWithDefaults(saved?: Partial<AdditionalSettingsForm> | null) {
63
34
  return { ...DEFAULT_FORM, ...(Object.fromEntries(entries) as Partial<AdditionalSettingsForm>) }
64
35
  }
65
36
 
66
- function SectionCard({ title, description, right, children }: { title: string; description?: React.ReactNode; right?: React.ReactNode; children: React.ReactNode }) {
67
- return (
68
- <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
69
- <div className="flex items-start justify-between gap-4 border-b border-ui-border-base p-4">
70
- <div>
71
- <div className="text-base font-semibold text-ui-fg-base">{title}</div>
72
- {description ? <div className="mt-1 text-sm text-ui-fg-subtle">{description}</div> : null}
73
- </div>
74
- {right}
75
- </div>
76
- <div className="p-4">{children}</div>
77
- </div>
78
- )
79
- }
80
-
81
- function FieldRow({ label, hint, children }: { label: string; hint?: React.ReactNode; children: React.ReactNode }) {
82
- return (
83
- <div className="grid grid-cols-12 items-start gap-4 py-3">
84
- <div className="col-span-12 md:col-span-4">
85
- <div className="text-sm font-medium text-ui-fg-base">{label}</div>
86
- {hint ? <div className="mt-1 text-xs text-ui-fg-subtle">{hint}</div> : null}
87
- </div>
88
- <div className="col-span-12 md:col-span-8">{children}</div>
89
- </div>
90
- )
91
- }
92
-
93
37
  export default function AdditionalSettingsTab() {
94
38
  const [form, setForm] = useState<AdditionalSettingsForm>(() => ({ ...DEFAULT_FORM }))
95
39
  const [loading, setLoading] = useState(false)
96
40
  const [saving, setSaving] = useState(false)
97
- const [toast, setToast] = useState<{ type: "success" | "error"; message: string } | null>(null)
41
+ const [toast, setToast] = useState<ToastState>(null)
98
42
  const didInit = useRef(false)
99
43
 
44
+ const dismissToast = useCallback(() => setToast(null), [])
45
+
100
46
  useEffect(() => {
101
47
  if (didInit.current) return
102
48
  didInit.current = true
@@ -122,11 +68,9 @@ export default function AdditionalSettingsTab() {
122
68
  const payload = json?.data ?? json
123
69
  const saved = payload?.additional_settings
124
70
  if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved))
125
- setToast({ type: "success", message: "Settings saved" })
126
- window.setTimeout(() => setToast(null), 2500)
71
+ setToast({ kind: "success", message: "Settings saved" })
127
72
  } catch (e: unknown) {
128
- setToast({ type: "error", message: e instanceof Error ? e.message : "Failed to save settings" })
129
- window.setTimeout(() => setToast(null), 3500)
73
+ setToast({ kind: "error", message: e instanceof Error ? e.message : "Failed to save settings" })
130
74
  } finally {
131
75
  setSaving(false)
132
76
  }
@@ -139,11 +83,7 @@ export default function AdditionalSettingsTab() {
139
83
  <div><h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1></div>
140
84
  </div>
141
85
  <PayPalTabs />
142
- {toast ? (
143
- <div 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" role="status" aria-live="polite">
144
- <span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>{toast.message}</span>
145
- </div>
146
- ) : null}
86
+ <Toast toast={toast} onClose={dismissToast} />
147
87
  <SectionCard
148
88
  title="Additional Settings"
149
89
  description="These settings control checkout behavior and PayPal experience."
@@ -152,22 +92,22 @@ export default function AdditionalSettingsTab() {
152
92
  <button type="button" onClick={onSave} disabled={saving || loading} className="transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5">
153
93
  {saving ? "Saving..." : "Save settings"}
154
94
  </button>
155
- {loading ? <span className="text-sm text-ui-fg-subtle">Loading...</span> : null}
95
+ {loading ? <span className="text-sm text-ui-fg-subtle">Loading…</span> : null}
156
96
  </div>
157
97
  )}
158
98
  >
159
99
  <div className="divide-y divide-ui-border-base">
160
- <FieldRow label="Payment action">
161
- <select value={form.paymentAction} onChange={(e) => setForm((p) => ({ ...p, paymentAction: e.target.value as PaymentAction }))} 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">
100
+ <FieldRow label="Payment action" htmlFor="as-payment-action">
101
+ <select id="as-payment-action" value={form.paymentAction} onChange={(e) => setForm((p) => ({ ...p, paymentAction: e.target.value as PaymentAction }))} 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">
162
102
  <option value="capture">Capture</option>
163
103
  <option value="authorize">Authorize</option>
164
104
  </select>
165
105
  </FieldRow>
166
- <FieldRow label="Brand Name">
167
- <input value={form.brandName} onChange={(e) => setForm((p) => ({ ...p, brandName: e.target.value }))} 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" placeholder="PayPal" />
106
+ <FieldRow label="Brand Name" htmlFor="as-brand-name">
107
+ <input id="as-brand-name" value={form.brandName} onChange={(e) => setForm((p) => ({ ...p, brandName: e.target.value }))} 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" placeholder="PayPal" />
168
108
  </FieldRow>
169
- <FieldRow label="Landing Page">
170
- <select value={form.landingPage} onChange={(e) => setForm((p) => ({ ...p, landingPage: e.target.value as LandingPage }))} 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">
109
+ <FieldRow label="Landing Page" htmlFor="as-landing-page">
110
+ <select id="as-landing-page" value={form.landingPage} onChange={(e) => setForm((p) => ({ ...p, landingPage: e.target.value as LandingPage }))} 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">
171
111
  <option value="no_preference">No Preference</option>
172
112
  <option value="login">Login</option>
173
113
  <option value="billing">Billing</option>
@@ -185,11 +125,11 @@ export default function AdditionalSettingsTab() {
185
125
  <span className="text-sm text-ui-fg-base">Send line item details to PayPal</span>
186
126
  </label>
187
127
  </FieldRow>
188
- <FieldRow label="Invoice prefix">
189
- <input value={form.invoicePrefix} onChange={(e) => setForm((p) => ({ ...p, invoicePrefix: e.target.value }))} 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" placeholder="WC-" />
128
+ <FieldRow label="Invoice prefix" htmlFor="as-invoice-prefix">
129
+ <input id="as-invoice-prefix" value={form.invoicePrefix} onChange={(e) => setForm((p) => ({ ...p, invoicePrefix: e.target.value }))} 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" placeholder="PP-" />
190
130
  </FieldRow>
191
- <FieldRow label="Credit Card Statement Name">
192
- <input value={form.creditCardStatementName} onChange={(e) => setForm((p) => ({ ...p, creditCardStatementName: e.target.value }))} 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" placeholder="PayPal" />
131
+ <FieldRow label="Credit Card Statement Name" htmlFor="as-cc-statement">
132
+ <input id="as-cc-statement" value={form.creditCardStatementName} onChange={(e) => setForm((p) => ({ ...p, creditCardStatementName: e.target.value }))} 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" placeholder="PayPal" />
193
133
  </FieldRow>
194
134
  </div>
195
135
  </SectionCard>
@@ -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 ThreeDSContingency = "sli" | "when_required" | "always"
38
9
 
@@ -60,40 +31,15 @@ const THREE_DS_OPTIONS: { value: ThreeDSContingency; label: string; hint?: strin
60
31
  { value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." },
61
32
  ]
62
33
 
63
- function SectionCard({ title, description, right, children }: { title: string; description?: string; right?: React.ReactNode; children: React.ReactNode }) {
64
- return (
65
- <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
66
- <div className="flex items-start justify-between gap-4 border-b border-ui-border-base p-4">
67
- <div>
68
- <div className="text-base font-semibold text-ui-fg-base">{title}</div>
69
- {description ? <div className="mt-1 text-sm text-ui-fg-subtle">{description}</div> : null}
70
- </div>
71
- {right}
72
- </div>
73
- <div className="p-4">{children}</div>
74
- </div>
75
- )
76
- }
77
-
78
- function FieldRow({ label, hint, children }: { label: string; hint?: React.ReactNode; children: React.ReactNode }) {
79
- return (
80
- <div className="grid grid-cols-12 items-start gap-4 py-3">
81
- <div className="col-span-12 md:col-span-4">
82
- <div className="text-sm font-medium text-ui-fg-base">{label}</div>
83
- {hint ? <div className="mt-1 text-xs text-ui-fg-subtle">{hint}</div> : null}
84
- </div>
85
- <div className="col-span-12 md:col-span-8">{children}</div>
86
- </div>
87
- )
88
- }
89
-
90
34
  export default function AdvancedCardPaymentsTab() {
91
35
  const [form, setForm] = useState<AdvancedCardPaymentsForm>(() => ({ ...DEFAULT_FORM }))
92
36
  const [loading, setLoading] = useState(false)
93
37
  const [saving, setSaving] = useState(false)
94
- const [toast, setToast] = useState<{ type: "success" | "error"; message: string } | null>(null)
38
+ const [toast, setToast] = useState<ToastState>(null)
95
39
  const didInit = useRef(false)
96
40
 
41
+ const dismissToast = useCallback(() => setToast(null), [])
42
+
97
43
  useEffect(() => {
98
44
  if (didInit.current) return
99
45
  didInit.current = true
@@ -121,11 +67,9 @@ export default function AdvancedCardPaymentsTab() {
121
67
  const payload = json?.data ?? json
122
68
  const saved = payload?.advanced_card_payments
123
69
  if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved))
124
- setToast({ type: "success", message: "Settings saved" })
125
- window.setTimeout(() => setToast(null), 2500)
70
+ setToast({ kind: "success", message: "Settings saved" })
126
71
  } catch (e: unknown) {
127
- setToast({ type: "error", message: (e instanceof Error ? e.message : "") || "Failed to save settings." })
128
- window.setTimeout(() => setToast(null), 3500)
72
+ setToast({ kind: "error", message: (e instanceof Error ? e.message : "") || "Failed to save settings." })
129
73
  } finally {
130
74
  setSaving(false)
131
75
  }
@@ -138,11 +82,7 @@ export default function AdvancedCardPaymentsTab() {
138
82
  <div><h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1></div>
139
83
  </div>
140
84
  <PayPalTabs />
141
- {toast ? (
142
- <div 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" role="status" aria-live="polite">
143
- <span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>{toast.message}</span>
144
- </div>
145
- ) : null}
85
+ <Toast toast={toast} onClose={dismissToast} />
146
86
  <SectionCard
147
87
  title="Advanced Card Payments"
148
88
  description="Control card checkout settings and 3D Secure behavior."
@@ -151,7 +91,7 @@ export default function AdvancedCardPaymentsTab() {
151
91
  <button type="button" onClick={onSave} disabled={saving || loading} className="transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5">
152
92
  {saving ? "Saving..." : "Save settings"}
153
93
  </button>
154
- {loading ? <span className="text-sm text-ui-fg-subtle">Loading...</span> : null}
94
+ {loading ? <span className="text-sm text-ui-fg-subtle">Loading…</span> : null}
155
95
  </div>
156
96
  )}
157
97
  >
@@ -162,12 +102,12 @@ export default function AdvancedCardPaymentsTab() {
162
102
  <span className="text-sm text-ui-fg-base">Enable Advanced Credit/Debit Card</span>
163
103
  </label>
164
104
  </FieldRow>
165
- <FieldRow label="Title">
166
- <input value={form.title} onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))} 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" placeholder="Credit or Debit Card" />
105
+ <FieldRow label="Title" htmlFor="acp-title">
106
+ <input id="acp-title" value={form.title} onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))} 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" placeholder="Credit or Debit Card" />
167
107
  </FieldRow>
168
- <FieldRow label="Contingency for 3D Secure" hint="Choose when 3D Secure should be triggered during card payments.">
108
+ <FieldRow label="Contingency for 3D Secure" hint="Choose when 3D Secure should be triggered during card payments." htmlFor="acp-threeds">
169
109
  <div className="flex flex-col gap-2">
170
- <select value={form.threeDS} onChange={(e) => setForm((p) => ({ ...p, threeDS: e.target.value as ThreeDSContingency }))} 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">
110
+ <select id="acp-threeds" value={form.threeDS} onChange={(e) => setForm((p) => ({ ...p, threeDS: e.target.value as ThreeDSContingency }))} 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">
171
111
  {THREE_DS_OPTIONS.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
172
112
  </select>
173
113
  {THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)?.hint
@@ -180,4 +120,4 @@ export default function AdvancedCardPaymentsTab() {
180
120
  </div>
181
121
  </div>
182
122
  )
183
- }
123
+ }
@@ -11,36 +11,11 @@ import PayPalTabs from "../_components/Tabs"
11
11
 
12
12
  export const config = defineRouteConfig({
13
13
  label: "PayPal Connection",
14
- hide: true,
15
14
  })
16
15
 
17
16
 
18
- if (typeof window !== "undefined") {
19
- const preloadHref =
20
- "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
21
-
22
- const existingPreload = document.head.querySelector(
23
- `link[rel="preload"][href="${preloadHref}"]`
24
- )
25
- if (!existingPreload) {
26
- const preloadLink = document.createElement("link")
27
- preloadLink.rel = "preload"
28
- preloadLink.href = preloadHref
29
- preloadLink.as = "script"
30
- document.head.appendChild(preloadLink)
31
- }
32
-
33
- const existingScript = document.getElementById(
34
- "paypal-partner-js"
35
- ) as HTMLScriptElement | null
36
- if (!existingScript) {
37
- const ppScript = document.createElement("script")
38
- ppScript.id = "paypal-partner-js"
39
- ppScript.src = preloadHref
40
- ppScript.async = true
41
- document.head.appendChild(ppScript)
42
- }
43
- }
17
+ const PARTNER_JS_URL =
18
+ "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
44
19
 
45
20
  declare global {
46
21
  interface Window {
@@ -59,7 +34,6 @@ declare global {
59
34
 
60
35
  const SERVICE_URL = "/admin/paypal/onboarding-link"
61
36
  const CACHE_KEY = "pp_onboard_cache"
62
- const RELOAD_KEY = "pp_onboard_reloaded_once"
63
37
  const CACHE_EXPIRY = 10 * 60 * 1000
64
38
 
65
39
  const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete"
@@ -87,7 +61,7 @@ export default function PayPalConnectionPage() {
87
61
  const [env, setEnv] = useState<"sandbox" | "live">("live")
88
62
 
89
63
  useEffect(() => {
90
- fetch("/admin/paypal/environment", { method: "GET" })
64
+ fetch("/admin/paypal/environment", { method: "GET", credentials: "include" })
91
65
  .then((r) => r.json())
92
66
  .then((d) => {
93
67
  const v = d?.environment === "sandbox" ? "sandbox" : "live"
@@ -95,6 +69,44 @@ export default function PayPalConnectionPage() {
95
69
  })
96
70
  .catch(() => {})
97
71
  }, [])
72
+
73
+ useEffect(() => {
74
+ const preloadHref = PARTNER_JS_URL
75
+
76
+ let preloadLink: HTMLLinkElement | null = null
77
+ const existingPreload = document.head.querySelector(
78
+ `link[rel="preload"][href="${preloadHref}"]`
79
+ )
80
+ if (!existingPreload) {
81
+ preloadLink = document.createElement("link")
82
+ preloadLink.rel = "preload"
83
+ preloadLink.href = preloadHref
84
+ preloadLink.as = "script"
85
+ document.head.appendChild(preloadLink)
86
+ }
87
+
88
+ let ppScript: HTMLScriptElement | null = null
89
+ const existingScript = document.getElementById(
90
+ "paypal-partner-js"
91
+ ) as HTMLScriptElement | null
92
+ if (!existingScript) {
93
+ ppScript = document.createElement("script")
94
+ ppScript.id = "paypal-partner-js"
95
+ ppScript.src = preloadHref
96
+ ppScript.async = true
97
+ document.head.appendChild(ppScript)
98
+ }
99
+
100
+ return () => {
101
+ if (preloadLink && preloadLink.parentNode) {
102
+ preloadLink.parentNode.removeChild(preloadLink)
103
+ }
104
+ if (ppScript && ppScript.parentNode) {
105
+ ppScript.parentNode.removeChild(ppScript)
106
+ }
107
+ }
108
+ }, [])
109
+
98
110
  const [connState, setConnState] = useState<
99
111
  "loading" | "ready" | "connected" | "error"
100
112
  >("loading")
@@ -135,11 +147,15 @@ export default function PayPalConnectionPage() {
135
147
  fetch(SERVICE_URL, {
136
148
  method: "POST",
137
149
  headers: { "content-type": "application/json" },
150
+ credentials: "include",
138
151
  body: JSON.stringify({
139
152
  products: ["PPCP"],
140
153
  }),
141
154
  })
142
- .then((r) => r.json())
155
+ .then((r) => {
156
+ if (!r.ok) throw new Error(`Service returned ${r.status}`)
157
+ return r.json()
158
+ })
143
159
  .then((data) => {
144
160
  if (runId !== currentRunId.current) return
145
161
 
@@ -160,12 +176,6 @@ export default function PayPalConnectionPage() {
160
176
  })
161
177
  )
162
178
 
163
- if (!localStorage.getItem(RELOAD_KEY)) {
164
- localStorage.setItem(RELOAD_KEY, "1")
165
- window.location.reload()
166
- return
167
- }
168
-
169
179
  activatePayPal(finalUrl, runId)
170
180
  })
171
181
  .catch(() => {
@@ -196,12 +206,20 @@ export default function PayPalConnectionPage() {
196
206
  }
197
207
  setFinalUrl(url)
198
208
 
209
+ let attempts = 0
210
+ const MAX_ATTEMPTS = 200
211
+
199
212
  const tryInit = () => {
200
213
  if (runId !== currentRunId.current) return
201
214
  if (window.PAYPAL?.apps?.Signup) {
202
215
  showUI()
203
216
  return
204
217
  }
218
+ attempts++
219
+ if (attempts >= MAX_ATTEMPTS) {
220
+ showError("PayPal partner script failed to load. Please refresh and try again.")
221
+ return
222
+ }
205
223
  setTimeout(tryInit, 50)
206
224
  }
207
225
 
@@ -224,6 +242,7 @@ export default function PayPalConnectionPage() {
224
242
  try {
225
243
  const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
226
244
  method: "GET",
245
+ credentials: "include",
227
246
  })
228
247
  const st = await r.json().catch(() => ({}))
229
248
 
@@ -261,7 +280,7 @@ export default function PayPalConnectionPage() {
261
280
  useLayoutEffect(() => {
262
281
  window.onboardingCallback = async function (authCode: string, sharedId: string) {
263
282
  try {
264
- ;(window as any).onbeforeunload = ""
283
+ window.onbeforeunload = null
265
284
  } catch {}
266
285
 
267
286
  setOnboardingInProgress(true)
@@ -278,6 +297,7 @@ export default function PayPalConnectionPage() {
278
297
  const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
279
298
  method: "POST",
280
299
  headers: { "content-type": "application/json" },
300
+ credentials: "include",
281
301
  body: JSON.stringify(payload),
282
302
  })
283
303
 
@@ -299,10 +319,22 @@ export default function PayPalConnectionPage() {
299
319
 
300
320
  try {
301
321
  localStorage.removeItem(CACHE_KEY)
302
- localStorage.removeItem(RELOAD_KEY)
303
322
  } catch {}
304
323
 
305
- window.location.href = window.location.href
324
+ try {
325
+ const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
326
+ method: "GET",
327
+ credentials: "include",
328
+ })
329
+ const refreshedStatus = await statusRes.json().catch(() => ({}))
330
+ setStatusInfo(refreshedStatus || null)
331
+ setConnState("connected")
332
+ setShowManual(false)
333
+ } catch {
334
+ setConnState("connected")
335
+ setShowManual(false)
336
+ }
337
+ setOnboardingInProgress(false)
306
338
  } catch (e: any) {
307
339
  console.error(e)
308
340
  setConnState("error")
@@ -357,6 +389,7 @@ export default function PayPalConnectionPage() {
357
389
  const res = await fetch(SAVE_CREDENTIALS_ENDPOINT, {
358
390
  method: "POST",
359
391
  headers: { "content-type": "application/json" },
392
+ credentials: "include",
360
393
  body: JSON.stringify({
361
394
  clientId: clientId.trim(),
362
395
  clientSecret: secret.trim(),
@@ -371,6 +404,7 @@ export default function PayPalConnectionPage() {
371
404
 
372
405
  const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
373
406
  method: "GET",
407
+ credentials: "include",
374
408
  })
375
409
  const refreshedStatus = await statusRes.json().catch(() => ({}))
376
410
 
@@ -380,7 +414,6 @@ export default function PayPalConnectionPage() {
380
414
 
381
415
  try {
382
416
  localStorage.removeItem(CACHE_KEY)
383
- localStorage.removeItem(RELOAD_KEY)
384
417
  } catch {}
385
418
  } catch (e: any) {
386
419
  console.error(e)
@@ -400,11 +433,13 @@ export default function PayPalConnectionPage() {
400
433
  setError(null)
401
434
  setFinalUrl("")
402
435
  setShowManual(false)
436
+ setStatusInfo(null)
403
437
 
404
438
  try {
405
439
  const res = await fetch(DISCONNECT_ENDPOINT, {
406
440
  method: "POST",
407
441
  headers: { "content-type": "application/json" },
442
+ credentials: "include",
408
443
  body: JSON.stringify({ environment: env }),
409
444
  })
410
445
 
@@ -415,7 +450,7 @@ export default function PayPalConnectionPage() {
415
450
 
416
451
  try {
417
452
  localStorage.removeItem(CACHE_KEY)
418
- localStorage.removeItem(RELOAD_KEY)
453
+
419
454
  } catch {}
420
455
 
421
456
  currentRunId.current = ++runIdRef.current
@@ -439,20 +474,24 @@ export default function PayPalConnectionPage() {
439
474
  await fetch("/admin/paypal/environment", {
440
475
  method: "POST",
441
476
  headers: { "content-type": "application/json" },
477
+ credentials: "include",
442
478
  body: JSON.stringify({ environment: next }),
443
479
  })
444
480
  } catch {}
445
481
 
446
482
  try {
447
483
  localStorage.removeItem(CACHE_KEY)
448
- localStorage.removeItem(RELOAD_KEY)
449
484
  } catch {}
450
485
  }
451
486
 
452
487
  return (
453
488
  <div className="p-6">
454
489
  <div className="flex flex-col gap-6">
455
- <h1 className="text-xl font-semibold">PayPal Gateway By Easy Payment</h1>
490
+ <div className="flex items-start justify-between gap-4">
491
+ <div>
492
+ <h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1>
493
+ </div>
494
+ </div>
456
495
 
457
496
  <PayPalTabs />
458
497
 
@@ -481,6 +520,7 @@ export default function PayPalConnectionPage() {
481
520
  <div className="text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200">
482
521
  ✅ Successfully connected to PayPal!
483
522
  <a
523
+ target="PPFrame"
484
524
  data-paypal-button="true"
485
525
  data-paypal-onboard-complete="onboardingCallback"
486
526
  href="#"
@@ -535,6 +575,7 @@ export default function PayPalConnectionPage() {
535
575
  ppBtnMeasureRef.current = node
536
576
  }}
537
577
  id="paypal-button"
578
+ target="PPFrame"
538
579
  data-paypal-button="true"
539
580
  href={finalUrl || "#"}
540
581
  data-paypal-onboard-complete="onboardingCallback"
@@ -604,8 +645,9 @@ export default function PayPalConnectionPage() {
604
645
  <div className="md:col-span-2">
605
646
  <div className="ml-[260px] max-w-xl mt-4 grid grid-cols-1 gap-3 md:grid-cols-2">
606
647
  <div className="flex flex-col gap-1">
607
- <label className="text-sm font-medium">Client ID</label>
648
+ <label htmlFor="pp-manual-client-id" className="text-sm font-medium">Client ID</label>
608
649
  <input
650
+ id="pp-manual-client-id"
609
651
  type="text"
610
652
  value={clientId}
611
653
  onChange={(e) => setClientId(e.target.value)}
@@ -618,8 +660,9 @@ export default function PayPalConnectionPage() {
618
660
  </div>
619
661
 
620
662
  <div className="flex flex-col gap-1">
621
- <label className="text-sm font-medium">Secret</label>
663
+ <label htmlFor="pp-manual-secret" className="text-sm font-medium">Secret</label>
622
664
  <input
665
+ id="pp-manual-secret"
623
666
  type="password"
624
667
  value={secret}
625
668
  onChange={(e) => setSecret(e.target.value)}