@easypayment/medusa-paypal 0.2.7 → 0.2.9

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 (90) hide show
  1. package/.medusa/server/src/admin/index.js +536 -938
  2. package/.medusa/server/src/admin/index.mjs +536 -938
  3. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
  4. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js +1 -0
  5. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js.map +1 -1
  6. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
  7. package/.medusa/server/src/api/store/paypal/capture-order/route.js +61 -74
  8. package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
  9. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -1
  10. package/.medusa/server/src/api/store/paypal/create-order/route.js +3 -24
  11. package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -1
  12. package/.medusa/server/src/api/store/paypal/settings/route.d.ts.map +1 -1
  13. package/.medusa/server/src/api/store/paypal/settings/route.js +7 -1
  14. package/.medusa/server/src/api/store/paypal/settings/route.js.map +1 -1
  15. package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -1
  16. package/.medusa/server/src/api/store/paypal/webhook/route.js +1 -1
  17. package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -1
  18. package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -1
  19. package/.medusa/server/src/api/store/paypal-complete/route.js +46 -24
  20. package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -1
  21. package/.medusa/server/src/jobs/paypal-reconcile.d.ts.map +1 -1
  22. package/.medusa/server/src/jobs/paypal-reconcile.js +19 -5
  23. package/.medusa/server/src/jobs/paypal-reconcile.js.map +1 -1
  24. package/.medusa/server/src/jobs/paypal-webhook-retry.d.ts.map +1 -1
  25. package/.medusa/server/src/jobs/paypal-webhook-retry.js +1 -1
  26. package/.medusa/server/src/jobs/paypal-webhook-retry.js.map +1 -1
  27. package/.medusa/server/src/modules/paypal/index.d.ts +0 -14
  28. package/.medusa/server/src/modules/paypal/index.d.ts.map +1 -1
  29. package/.medusa/server/src/modules/paypal/service.d.ts +56 -93
  30. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  31. package/.medusa/server/src/modules/paypal/service.js +34 -47
  32. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  33. package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts +14 -0
  34. package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts.map +1 -0
  35. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js +32 -0
  36. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js.map +1 -0
  37. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts +2 -15
  38. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -1
  39. package/.medusa/server/src/modules/paypal/webhook-processor.js +17 -100
  40. package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/admin/routes/settings/paypal/_components/Tabs.tsx +0 -1
  43. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +226 -346
  44. package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +227 -381
  45. package/src/admin/routes/settings/paypal/audit-logs/page.tsx +127 -131
  46. package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +599 -557
  47. package/src/admin/routes/settings/paypal/reconciliation-status/page.tsx +120 -165
  48. package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +12 -1
  49. package/src/api/store/paypal/capture-order/route.ts +276 -284
  50. package/src/api/store/paypal/create-order/route.ts +2 -32
  51. package/src/api/store/paypal/settings/route.ts +8 -1
  52. package/src/api/store/paypal/webhook/route.ts +1 -2
  53. package/src/api/store/paypal-complete/route.ts +75 -45
  54. package/src/jobs/paypal-reconcile.ts +21 -6
  55. package/src/jobs/paypal-webhook-retry.ts +1 -2
  56. package/src/modules/paypal/service.ts +39 -62
  57. package/src/modules/paypal/utils/paypal-auth.ts +32 -0
  58. package/src/modules/paypal/webhook-processor.ts +18 -116
  59. package/tsconfig.json +1 -1
  60. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.d.ts +0 -3
  61. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.d.ts.map +0 -1
  62. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.js +0 -17
  63. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.js.map +0 -1
  64. package/.medusa/server/src/api/admin/paypal/disputes/route.d.ts +0 -3
  65. package/.medusa/server/src/api/admin/paypal/disputes/route.d.ts.map +0 -1
  66. package/.medusa/server/src/api/admin/paypal/disputes/route.js +0 -27
  67. package/.medusa/server/src/api/admin/paypal/disputes/route.js.map +0 -1
  68. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.d.ts +0 -3
  69. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.d.ts.map +0 -1
  70. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.js +0 -17
  71. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.js.map +0 -1
  72. package/.medusa/server/src/api/store/paypal/disputes/route.d.ts +0 -3
  73. package/.medusa/server/src/api/store/paypal/disputes/route.d.ts.map +0 -1
  74. package/.medusa/server/src/api/store/paypal/disputes/route.js +0 -46
  75. package/.medusa/server/src/api/store/paypal/disputes/route.js.map +0 -1
  76. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.d.ts +0 -6
  77. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.d.ts.map +0 -1
  78. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.js +0 -43
  79. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.js.map +0 -1
  80. package/.medusa/server/src/modules/paypal/models/paypal_dispute.d.ts +0 -16
  81. package/.medusa/server/src/modules/paypal/models/paypal_dispute.d.ts.map +0 -1
  82. package/.medusa/server/src/modules/paypal/models/paypal_dispute.js +0 -19
  83. package/.medusa/server/src/modules/paypal/models/paypal_dispute.js.map +0 -1
  84. package/src/admin/routes/settings/paypal/disputes/page.tsx +0 -259
  85. package/src/api/admin/paypal/disputes/[id]/route.ts +0 -19
  86. package/src/api/admin/paypal/disputes/route.ts +0 -30
  87. package/src/api/admin/paypal/disputes/summary/route.ts +0 -18
  88. package/src/api/store/paypal/disputes/route.ts +0 -67
  89. package/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.ts +0 -40
  90. package/src/modules/paypal/models/paypal_dispute.ts +0 -18
@@ -1,165 +1,120 @@
1
- import React, { useCallback, useEffect, useState } from "react"
2
- import PayPalTabs from "../_components/Tabs"
3
-
4
- type ReconciliationStatus = {
5
- runs?: number
6
- last_run_at?: string | null
7
- last_run_status?: string | null
8
- last_success_at?: string | null
9
- last_failure_at?: string | null
10
- sessions_checked?: number
11
- sessions_updated?: number
12
- drift_count?: number
13
- last_drift_at?: string | null
14
- last_drift_order_id?: string | null
15
- last_error?: string | null
16
- }
17
-
18
- function formatDate(value?: string | null) {
19
- if (!value) {
20
- return "—"
21
- }
22
- const parsed = new Date(value)
23
- if (Number.isNaN(parsed.getTime())) {
24
- return value
25
- }
26
- return parsed.toLocaleString()
27
- }
28
-
29
- export default function PayPalReconciliationStatusPage() {
30
- const [status, setStatus] = useState<ReconciliationStatus>({})
31
- const [loading, setLoading] = useState(false)
32
- const [error, setError] = useState<string | null>(null)
33
-
34
- const fetchStatus = useCallback(async () => {
35
- try {
36
- setLoading(true)
37
- setError(null)
38
- const response = await fetch("/admin/paypal/reconciliation-status", {
39
- credentials: "include",
40
- headers: {
41
- Accept: "application/json",
42
- },
43
- })
44
-
45
- if (!response.ok) {
46
- const message = await response.text().catch(() => "")
47
- throw new Error(message || "Failed to load reconciliation status.")
48
- }
49
-
50
- const data = await response.json().catch(() => ({}))
51
- setStatus((data?.status as ReconciliationStatus) || {})
52
- } catch (fetchError: any) {
53
- setError(fetchError?.message || "Failed to load reconciliation status.")
54
- setStatus({})
55
- } finally {
56
- setLoading(false)
57
- }
58
- }, [])
59
-
60
- useEffect(() => {
61
- fetchStatus()
62
- }, [fetchStatus])
63
-
64
- return (
65
- <div className="p-6">
66
- <div className="flex flex-col gap-6">
67
- <div>
68
- <h1 className="text-xl font-semibold text-ui-fg-base">
69
- PayPal Reconciliation Status
70
- </h1>
71
- <p className="mt-1 text-sm text-ui-fg-subtle">
72
- Monitor reconciliation health and drift detection for PayPal payment sessions.
73
- </p>
74
- </div>
75
-
76
- <PayPalTabs />
77
-
78
- <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
79
- <div className="border-b border-ui-border-base p-4">
80
- <div className="flex items-center justify-between gap-4">
81
- <div className="text-base font-semibold text-ui-fg-base">
82
- Latest reconciliation run
83
- </div>
84
- <button
85
- type="button"
86
- onClick={fetchStatus}
87
- className="rounded-md border border-ui-border-base px-3 py-2 text-sm text-ui-fg-base"
88
- disabled={loading}
89
- >
90
- {loading ? "Refreshing..." : "Refresh"}
91
- </button>
92
- </div>
93
- </div>
94
- <div className="p-4">
95
- {error ? (
96
- <div className="rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-600">
97
- {error}
98
- </div>
99
- ) : null}
100
-
101
- {!error && Object.keys(status).length === 0 ? (
102
- <div className="text-sm text-ui-fg-subtle">
103
- {loading ? "Loading reconciliation status..." : "No reconciliation data yet."}
104
- </div>
105
- ) : null}
106
-
107
- {Object.keys(status).length > 0 ? (
108
- <div className="grid gap-4 text-sm text-ui-fg-base md:grid-cols-2">
109
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
110
- <div className="text-ui-fg-subtle">Last run</div>
111
- <div className="mt-1 font-medium">{formatDate(status.last_run_at)}</div>
112
- </div>
113
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
114
- <div className="text-ui-fg-subtle">Last run status</div>
115
- <div className="mt-1 font-medium capitalize">
116
- {status.last_run_status || "—"}
117
- </div>
118
- </div>
119
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
120
- <div className="text-ui-fg-subtle">Last success</div>
121
- <div className="mt-1 font-medium">{formatDate(status.last_success_at)}</div>
122
- </div>
123
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
124
- <div className="text-ui-fg-subtle">Last failure</div>
125
- <div className="mt-1 font-medium">{formatDate(status.last_failure_at)}</div>
126
- </div>
127
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
128
- <div className="text-ui-fg-subtle">Sessions checked</div>
129
- <div className="mt-1 font-medium">{status.sessions_checked ?? 0}</div>
130
- </div>
131
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
132
- <div className="text-ui-fg-subtle">Sessions updated</div>
133
- <div className="mt-1 font-medium">{status.sessions_updated ?? 0}</div>
134
- </div>
135
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
136
- <div className="text-ui-fg-subtle">Drift detections</div>
137
- <div className="mt-1 font-medium">{status.drift_count ?? 0}</div>
138
- </div>
139
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3">
140
- <div className="text-ui-fg-subtle">Last drift order</div>
141
- <div className="mt-1 font-medium">
142
- {status.last_drift_order_id || "—"}
143
- </div>
144
- <div className="mt-1 text-xs text-ui-fg-subtle">
145
- {formatDate(status.last_drift_at)}
146
- </div>
147
- </div>
148
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 md:col-span-2">
149
- <div className="text-ui-fg-subtle">Last error</div>
150
- <div className="mt-1 font-medium">
151
- {status.last_error || "No errors recorded."}
152
- </div>
153
- </div>
154
- <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 md:col-span-2">
155
- <div className="text-ui-fg-subtle">Total runs</div>
156
- <div className="mt-1 font-medium">{status.runs ?? 0}</div>
157
- </div>
158
- </div>
159
- ) : null}
160
- </div>
161
- </div>
162
- </div>
163
- </div>
164
- )
165
- }
1
+ import React, { useCallback, useEffect, useState } from "react"
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
+ }
36
+
37
+ type ReconciliationStatus = {
38
+ runs?: number
39
+ last_run_at?: string | null
40
+ last_run_status?: string | null
41
+ last_success_at?: string | null
42
+ last_failure_at?: string | null
43
+ sessions_checked?: number
44
+ sessions_updated?: number
45
+ drift_count?: number
46
+ last_drift_at?: string | null
47
+ last_drift_order_id?: string | null
48
+ last_error?: string | null
49
+ }
50
+
51
+ function formatDate(value?: string | null) {
52
+ if (!value) return "—"
53
+ const parsed = new Date(value)
54
+ if (Number.isNaN(parsed.getTime())) return value
55
+ return parsed.toLocaleString()
56
+ }
57
+
58
+ export default function PayPalReconciliationStatusPage() {
59
+ const [status, setStatus] = useState<ReconciliationStatus>({})
60
+ const [loading, setLoading] = useState(false)
61
+ const [error, setError] = useState<string | null>(null)
62
+
63
+ const fetchStatus = useCallback(async () => {
64
+ try {
65
+ setLoading(true)
66
+ setError(null)
67
+ const data = await adminFetch<{ status: ReconciliationStatus }>("/admin/paypal/reconciliation-status")
68
+ setStatus(data?.status ?? {})
69
+ } catch (fetchError: unknown) {
70
+ setError(fetchError instanceof Error ? fetchError.message : "Failed to load reconciliation status.")
71
+ setStatus({})
72
+ } finally {
73
+ setLoading(false)
74
+ }
75
+ }, [])
76
+
77
+ useEffect(() => { fetchStatus() }, [fetchStatus])
78
+
79
+ return (
80
+ <div className="p-6">
81
+ <div className="flex flex-col gap-6">
82
+ <div>
83
+ <h1 className="text-xl font-semibold text-ui-fg-base">PayPal Reconciliation Status</h1>
84
+ <p className="mt-1 text-sm text-ui-fg-subtle">Monitor reconciliation health and drift detection for PayPal payment sessions.</p>
85
+ </div>
86
+ <PayPalTabs />
87
+ <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
88
+ <div className="border-b border-ui-border-base p-4">
89
+ <div className="flex items-center justify-between gap-4">
90
+ <div className="text-base font-semibold text-ui-fg-base">Latest reconciliation run</div>
91
+ <button type="button" onClick={fetchStatus} className="rounded-md border border-ui-border-base px-3 py-2 text-sm text-ui-fg-base" disabled={loading}>
92
+ {loading ? "Refreshing..." : "Refresh"}
93
+ </button>
94
+ </div>
95
+ </div>
96
+ <div className="p-4">
97
+ {error ? <div className="rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-600">{error}</div> : null}
98
+ {!error && Object.keys(status).length === 0 ? (
99
+ <div className="text-sm text-ui-fg-subtle">{loading ? "Loading reconciliation status..." : "No reconciliation data yet."}</div>
100
+ ) : null}
101
+ {Object.keys(status).length > 0 ? (
102
+ <div className="grid gap-4 text-sm text-ui-fg-base md:grid-cols-2">
103
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Last run</div><div className="mt-1 font-medium">{formatDate(status.last_run_at)}</div></div>
104
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Last run status</div><div className="mt-1 font-medium capitalize">{status.last_run_status || "—"}</div></div>
105
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Last success</div><div className="mt-1 font-medium">{formatDate(status.last_success_at)}</div></div>
106
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Last failure</div><div className="mt-1 font-medium">{formatDate(status.last_failure_at)}</div></div>
107
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Sessions checked</div><div className="mt-1 font-medium">{status.sessions_checked ?? 0}</div></div>
108
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Sessions updated</div><div className="mt-1 font-medium">{status.sessions_updated ?? 0}</div></div>
109
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Drift detections</div><div className="mt-1 font-medium">{status.drift_count ?? 0}</div></div>
110
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3"><div className="text-ui-fg-subtle">Last drift order</div><div className="mt-1 font-medium">{status.last_drift_order_id || "—"}</div><div className="mt-1 text-xs text-ui-fg-subtle">{formatDate(status.last_drift_at)}</div></div>
111
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 md:col-span-2"><div className="text-ui-fg-subtle">Last error</div><div className="mt-1 font-medium">{status.last_error || "No errors recorded."}</div></div>
112
+ <div className="rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 md:col-span-2"><div className="text-ui-fg-subtle">Total runs</div><div className="mt-1 font-medium">{status.runs ?? 0}</div></div>
113
+ </div>
114
+ ) : null}
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ )
120
+ }
@@ -2,6 +2,15 @@ import { createPaymentSessionsWorkflow } from "@medusajs/core-flows"
2
2
  import { MedusaResponse, MedusaStoreRequest, refetchEntity } from "@medusajs/framework/http"
3
3
  import { MedusaError } from "@medusajs/framework/utils"
4
4
 
5
+ // FIX 6: Removed the broken import of isPayPalProviderId from provider-ids.
6
+ // That import caused TS2307 because the relative path was wrong, and the
7
+ // isPayPalProviderId guard was conflicting with the typed body cast below,
8
+ // producing two const declarations of provider_id in the same scope.
9
+ // This route does not need to filter by provider — it accepts any provider_id
10
+ // and delegates to the standard createPaymentSessionsWorkflow. The
11
+ // isPayPalProviderId guard (Fix 6b) belongs only in routes that need to
12
+ // short-circuit for non-PayPal providers.
13
+
5
14
  type CreatePaymentSessionBody = {
6
15
  provider_id: string
7
16
  data?: Record<string, unknown>
@@ -16,6 +25,8 @@ const defaultPaymentCollectionFields = [
16
25
 
17
26
  export async function POST(req: MedusaStoreRequest, res: MedusaResponse) {
18
27
  const collectionId = req.params.id
28
+
29
+ // Single typed destructure — no redeclaration, req.body cast once
19
30
  const { provider_id, data } = req.body as CreatePaymentSessionBody
20
31
 
21
32
  if (!provider_id || typeof provider_id !== "string") {
@@ -52,4 +63,4 @@ export async function POST(req: MedusaStoreRequest, res: MedusaResponse) {
52
63
  res.status(200).json({
53
64
  payment_collection: paymentCollection,
54
65
  })
55
- }
66
+ }