@easypayment/medusa-paypal 0.2.9 → 0.3.0

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 (46) hide show
  1. package/.medusa/server/src/admin/index.js +18 -248
  2. package/.medusa/server/src/admin/index.mjs +18 -248
  3. package/.medusa/server/src/jobs/paypal-reconcile.d.ts.map +1 -1
  4. package/.medusa/server/src/jobs/paypal-reconcile.js +0 -36
  5. package/.medusa/server/src/jobs/paypal-reconcile.js.map +1 -1
  6. package/.medusa/server/src/modules/paypal/index.d.ts +0 -14
  7. package/.medusa/server/src/modules/paypal/index.d.ts.map +1 -1
  8. package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts +6 -0
  9. package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts.map +1 -0
  10. package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js +28 -0
  11. package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js.map +1 -0
  12. package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts +6 -0
  13. package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts.map +1 -0
  14. package/.medusa/server/src/modules/paypal/migrations/{20260301090000_create_paypal_audit_log.js → 20261201090000_remove_paypal_audit_log.js} +10 -10
  15. package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.js.map +1 -0
  16. package/.medusa/server/src/modules/paypal/service.d.ts +2 -48
  17. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  18. package/.medusa/server/src/modules/paypal/service.js +2 -99
  19. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/admin/routes/settings/paypal/_components/Tabs.tsx +0 -2
  22. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +218 -226
  23. package/src/jobs/paypal-reconcile.ts +0 -37
  24. package/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.ts +25 -0
  25. package/src/modules/paypal/migrations/{20260301090000_create_paypal_audit_log.ts → 20261201090000_remove_paypal_audit_log.ts} +8 -8
  26. package/src/modules/paypal/service.ts +2 -112
  27. package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts +0 -3
  28. package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts.map +0 -1
  29. package/.medusa/server/src/api/admin/paypal/audit-logs/route.js +0 -12
  30. package/.medusa/server/src/api/admin/paypal/audit-logs/route.js.map +0 -1
  31. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts +0 -3
  32. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts.map +0 -1
  33. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js +0 -8
  34. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js.map +0 -1
  35. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts +0 -6
  36. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts.map +0 -1
  37. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.js.map +0 -1
  38. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts +0 -7
  39. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts.map +0 -1
  40. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js +0 -10
  41. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js.map +0 -1
  42. package/src/admin/routes/settings/paypal/audit-logs/page.tsx +0 -127
  43. package/src/admin/routes/settings/paypal/reconciliation-status/page.tsx +0 -120
  44. package/src/api/admin/paypal/audit-logs/route.ts +0 -13
  45. package/src/api/admin/paypal/reconciliation-status/route.ts +0 -7
  46. package/src/modules/paypal/models/paypal_audit_log.ts +0 -9
@@ -1,6 +1,5 @@
1
1
  import { MedusaService } from "@medusajs/framework/utils"
2
2
  import PayPalConnection from "./models/paypal_connection"
3
- import PayPalAuditLog from "./models/paypal_audit_log"
4
3
  import PayPalMetric from "./models/paypal_metric"
5
4
  import PayPalSettings from "./models/paypal_settings"
6
5
  import PayPalWebhookEvent from "./models/paypal_webhook_event"
@@ -18,7 +17,6 @@ type Status =
18
17
  | "revoked"
19
18
 
20
19
  class PayPalModuleService extends MedusaService({
21
- PayPalAuditLog,
22
20
  PayPalConnection,
23
21
  PayPalMetric,
24
22
  PayPalSettings,
@@ -97,15 +95,6 @@ class PayPalModuleService extends MedusaService({
97
95
  }
98
96
  }
99
97
 
100
- async getLoggingPreference() {
101
- const data = await this.getSettingsData()
102
- const additional = (data.additional_settings || {}) as Record<string, any>
103
- if (typeof additional.enableLogging === "boolean") {
104
- return additional.enableLogging
105
- }
106
- return true
107
- }
108
-
109
98
  private getAlertWebhookUrls() {
110
99
  return (this.cfg.alertWebhookUrls || []).map((url) => url.trim()).filter(Boolean)
111
100
  }
@@ -1208,57 +1197,12 @@ class PayPalModuleService extends MedusaService({
1208
1197
  })
1209
1198
  }
1210
1199
 
1211
- async recordAuditEvent(eventType: string, metadata?: Record<string, unknown>) {
1212
- const loggingEnabled = await this.getLoggingPreference().catch(() => true)
1213
- if (!loggingEnabled) {
1214
- return null
1215
- }
1216
- try {
1217
- return await this.createPayPalAuditLogs({
1218
- event_type: eventType,
1219
- metadata: metadata ?? {},
1220
- })
1221
- } catch (error) {
1222
- const message = error instanceof Error ? error.message : String(error)
1223
- if (message.includes("paypal_audit_log") && message.includes("does not exist")) {
1224
- console.warn("[paypal_onboarding] audit log table missing; skipping audit event")
1225
- return null
1226
- }
1227
- throw error
1228
- }
1200
+ async recordAuditEvent(_eventType: string, _metadata?: Record<string, unknown>) {
1201
+ return null
1229
1202
  }
1230
1203
 
1231
- async getAuditLogs(limit = 50) {
1232
- const entries = await this.listPayPalAuditLogs({})
1233
- const sorted = [...(entries || [])].sort((a: any, b: any) => {
1234
- const aTime = a?.created_at ? new Date(a.created_at).getTime() : 0
1235
- const bTime = b?.created_at ? new Date(b.created_at).getTime() : 0
1236
- return bTime - aTime
1237
- })
1238
- return sorted.slice(0, limit).map((entry: any) => ({
1239
- id: entry.id,
1240
- event_type: entry.event_type,
1241
- metadata: this.sanitizeAuditMetadata(entry.metadata || {}),
1242
- created_at: entry.created_at || null,
1243
- }))
1244
- }
1245
-
1246
- private sanitizeAuditMetadata(metadata: Record<string, unknown>) {
1247
- const next: Record<string, unknown> = { ...metadata }
1248
- if (typeof next.client_id === "string") {
1249
- next.client_id = this.maskValue(next.client_id)
1250
- }
1251
- if (typeof next.client_secret === "string") {
1252
- next.client_secret = "••••••••"
1253
- }
1254
- return next
1255
- }
1256
1204
 
1257
1205
  async recordMetric(name: string, metadata?: Record<string, unknown>) {
1258
- const loggingEnabled = await this.getLoggingPreference().catch(() => true)
1259
- if (!loggingEnabled) {
1260
- return null
1261
- }
1262
1206
  const existing = await this.listPayPalMetrics({ name })
1263
1207
  const row = existing?.[0]
1264
1208
  const current = (row?.data || {}) as Record<string, any>
@@ -1284,10 +1228,6 @@ class PayPalModuleService extends MedusaService({
1284
1228
  }
1285
1229
 
1286
1230
  async recordPaymentLog(eventType: string, metadata?: Record<string, unknown>) {
1287
- const loggingEnabled = await this.getLoggingPreference().catch(() => true)
1288
- if (!loggingEnabled) {
1289
- return null
1290
- }
1291
1231
  const payload = {
1292
1232
  event_type: eventType,
1293
1233
  metadata: metadata ?? {},
@@ -1297,56 +1237,6 @@ class PayPalModuleService extends MedusaService({
1297
1237
  return await this.recordAuditEvent(`payment_${eventType}`, metadata)
1298
1238
  }
1299
1239
 
1300
- async updateReconciliationStatus(input: {
1301
- status: "success" | "failed"
1302
- sessions_checked?: number
1303
- sessions_updated?: number
1304
- drift_count?: number
1305
- error_message?: string | null
1306
- last_drift_at?: string | null
1307
- last_drift_order_id?: string | null
1308
- }) {
1309
- const existing = await this.listPayPalMetrics({ name: "reconcile_status" })
1310
- const row = existing?.[0]
1311
- const current = (row?.data || {}) as Record<string, any>
1312
- const now = new Date().toISOString()
1313
- const next = {
1314
- ...current,
1315
- runs: Number(current.runs || 0) + 1,
1316
- last_run_at: now,
1317
- last_run_status: input.status,
1318
- last_success_at: input.status === "success" ? now : current.last_success_at,
1319
- last_failure_at: input.status === "failed" ? now : current.last_failure_at,
1320
- sessions_checked: Number(input.sessions_checked ?? current.sessions_checked ?? 0),
1321
- sessions_updated: Number(input.sessions_updated ?? current.sessions_updated ?? 0),
1322
- drift_count: Number(input.drift_count ?? current.drift_count ?? 0),
1323
- last_drift_at: input.last_drift_at ?? current.last_drift_at ?? null,
1324
- last_drift_order_id: input.last_drift_order_id ?? current.last_drift_order_id ?? null,
1325
- last_error: input.error_message ?? current.last_error ?? null,
1326
- }
1327
-
1328
- if (!row) {
1329
- return await this.createPayPalMetrics({
1330
- name: "reconcile_status",
1331
- data: next,
1332
- })
1333
- }
1334
-
1335
- return await this.updatePayPalMetrics({
1336
- id: row.id,
1337
- name: "reconcile_status",
1338
- data: next,
1339
- })
1340
- }
1341
-
1342
- async getReconciliationStatus() {
1343
- const rows = await this.listPayPalMetrics({ name: "reconcile_status" })
1344
- const row = rows?.[0]
1345
- return {
1346
- status: (row?.data as Record<string, any>) || {},
1347
- }
1348
- }
1349
-
1350
1240
  async sendAlert(input: {
1351
1241
  type: string
1352
1242
  message: string
@@ -1,3 +0,0 @@
1
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
- export declare function GET(req: MedusaRequest, res: MedusaResponse): Promise<MedusaResponse>;
3
- //# sourceMappingURL=route.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/admin/paypal/audit-logs/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAG7E,wBAAsB,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,cAAc,2BAShE"}
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GET = GET;
4
- async function GET(req, res) {
5
- const paypal = req.scope.resolve("paypal_onboarding");
6
- const q = (req.query || {});
7
- const limitRaw = Number(q.limit || 50);
8
- const limit = Number.isFinite(limitRaw) ? Math.min(Math.max(limitRaw, 1), 200) : 50;
9
- const logs = await paypal.getAuditLogs(limit);
10
- return res.json({ logs });
11
- }
12
- //# sourceMappingURL=route.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/admin/paypal/audit-logs/route.ts"],"names":[],"mappings":";;AAGA,kBASC;AATM,KAAK,UAAU,GAAG,CAAC,GAAkB,EAAE,GAAmB;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAsB,mBAAmB,CAAC,CAAA;IAC1E,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAwB,CAAA;IAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;IAE7C,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;AAC3B,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
- export declare function GET(req: MedusaRequest, res: MedusaResponse): Promise<MedusaResponse>;
3
- //# sourceMappingURL=route.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/admin/paypal/reconciliation-status/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAG7E,wBAAsB,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,cAAc,2BAGhE"}
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GET = GET;
4
- async function GET(req, res) {
5
- const paypal = req.scope.resolve("paypal_onboarding");
6
- return res.json(await paypal.getReconciliationStatus());
7
- }
8
- //# sourceMappingURL=route.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/admin/paypal/reconciliation-status/route.ts"],"names":[],"mappings":";;AAGA,kBAGC;AAHM,KAAK,UAAU,GAAG,CAAC,GAAkB,EAAE,GAAmB;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAsB,mBAAmB,CAAC,CAAA;IAC1E,OAAO,GAAG,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAA;AACzD,CAAC"}
@@ -1,6 +0,0 @@
1
- import { Migration } from "@medusajs/framework/mikro-orm/migrations";
2
- export declare class Migration20260301090000 extends Migration {
3
- up(): Promise<void>;
4
- down(): Promise<void>;
5
- }
6
- //# sourceMappingURL=20260301090000_create_paypal_audit_log.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"20260301090000_create_paypal_audit_log.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0CAA0C,CAAA;AAEpE,qBAAa,uBAAwB,SAAQ,SAAS;IAC9C,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"20260301090000_create_paypal_audit_log.js","sourceRoot":"","sources":["../../../../../../src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.ts"],"names":[],"mappings":";;;AAAA,yEAAoE;AAEpE,MAAa,uBAAwB,SAAQ,sBAAS;IACpD,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;KAaX,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,MAAM,CAAC;;KAEX,CAAC,CAAA;IACJ,CAAC;CACF;AAvBD,0DAuBC"}
@@ -1,7 +0,0 @@
1
- declare const PayPalAuditLog: import("@medusajs/framework/utils").DmlEntity<import("@medusajs/framework/utils").DMLEntitySchemaBuilder<{
2
- id: import("@medusajs/framework/utils").PrimaryKeyModifier<string, import("@medusajs/framework/utils").IdProperty>;
3
- event_type: import("@medusajs/framework/utils").TextProperty;
4
- metadata: import("@medusajs/framework/utils").NullableModifier<Record<string, unknown>, import("@medusajs/framework/utils").JSONProperty>;
5
- }>, "paypal_audit_log">;
6
- export default PayPalAuditLog;
7
- //# sourceMappingURL=paypal_audit_log.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paypal_audit_log.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/paypal/models/paypal_audit_log.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,cAAc;;;;uBAIlB,CAAA;AAEF,eAAe,cAAc,CAAA"}
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const utils_1 = require("@medusajs/framework/utils");
4
- const PayPalAuditLog = utils_1.model.define("paypal_audit_log", {
5
- id: utils_1.model.id().primaryKey(),
6
- event_type: utils_1.model.text(),
7
- metadata: utils_1.model.json().nullable(),
8
- });
9
- exports.default = PayPalAuditLog;
10
- //# sourceMappingURL=paypal_audit_log.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paypal_audit_log.js","sourceRoot":"","sources":["../../../../../../src/modules/paypal/models/paypal_audit_log.ts"],"names":[],"mappings":";;AAAA,qDAAiD;AAEjD,MAAM,cAAc,GAAG,aAAK,CAAC,MAAM,CAAC,kBAAkB,EAAE;IACtD,EAAE,EAAE,aAAK,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE;IAC3B,UAAU,EAAE,aAAK,CAAC,IAAI,EAAE;IACxB,QAAQ,EAAE,aAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAA;AAEF,kBAAe,cAAc,CAAA"}
@@ -1,127 +0,0 @@
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 AuditLog = {
38
- id: string
39
- event_type: string
40
- metadata?: Record<string, unknown>
41
- created_at?: string | null
42
- }
43
-
44
- function formatDate(value?: string | null) {
45
- if (!value) return ""
46
- const parsed = new Date(value)
47
- if (Number.isNaN(parsed.getTime())) return value
48
- return parsed.toLocaleString()
49
- }
50
-
51
- export default function PayPalAuditLogsPage() {
52
- const [logs, setLogs] = useState<AuditLog[]>([])
53
- const [loading, setLoading] = useState(false)
54
- const [error, setError] = useState<string | null>(null)
55
-
56
- const fetchLogs = useCallback(async () => {
57
- try {
58
- setLoading(true)
59
- setError(null)
60
- const data = await adminFetch<{ logs: AuditLog[] }>("/admin/paypal/audit-logs", { query: { limit: "50" } })
61
- setLogs(data?.logs ?? [])
62
- } catch (fetchError: unknown) {
63
- setError(fetchError instanceof Error ? fetchError.message : "Failed to load audit logs.")
64
- setLogs([])
65
- } finally {
66
- setLoading(false)
67
- }
68
- }, [])
69
-
70
- useEffect(() => { fetchLogs() }, [fetchLogs])
71
-
72
- return (
73
- <div className="p-6">
74
- <div className="flex flex-col gap-6">
75
- <div>
76
- <h1 className="text-xl font-semibold text-ui-fg-base">PayPal Audit Logs</h1>
77
- <p className="mt-1 text-sm text-ui-fg-subtle">
78
- Track administrative changes and credential events for PayPal configuration.
79
- </p>
80
- </div>
81
- <PayPalTabs />
82
- <div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
83
- <div className="border-b border-ui-border-base p-4">
84
- <div className="flex items-center justify-between gap-4">
85
- <div className="text-base font-semibold text-ui-fg-base">Latest events</div>
86
- <button type="button" onClick={fetchLogs} className="rounded-md border border-ui-border-base px-3 py-2 text-sm text-ui-fg-base" disabled={loading}>
87
- {loading ? "Refreshing..." : "Refresh"}
88
- </button>
89
- </div>
90
- </div>
91
- <div className="p-4">
92
- {error ? <div className="rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-600">{error}</div> : null}
93
- {!error && logs.length === 0 ? (
94
- <div className="text-sm text-ui-fg-subtle">{loading ? "Loading audit logs..." : "No audit log entries found yet."}</div>
95
- ) : null}
96
- {logs.length > 0 ? (
97
- <div className="overflow-x-auto">
98
- <table className="min-w-full text-left text-sm">
99
- <thead className="text-ui-fg-muted">
100
- <tr>
101
- <th className="pb-2 pr-4 font-medium">Timestamp</th>
102
- <th className="pb-2 pr-4 font-medium">Event</th>
103
- <th className="pb-2 font-medium">Details</th>
104
- </tr>
105
- </thead>
106
- <tbody>
107
- {logs.map((entry) => (
108
- <tr key={entry.id} className="border-t border-ui-border-base">
109
- <td className="py-3 pr-4 text-ui-fg-base">{formatDate(entry.created_at) || "—"}</td>
110
- <td className="py-3 pr-4 text-ui-fg-base">{entry.event_type || "—"}</td>
111
- <td className="py-3 text-ui-fg-subtle">
112
- <pre className="whitespace-pre-wrap rounded-md bg-ui-bg-subtle p-2 text-xs text-ui-fg-subtle">
113
- {JSON.stringify(entry.metadata || {}, null, 2)}
114
- </pre>
115
- </td>
116
- </tr>
117
- ))}
118
- </tbody>
119
- </table>
120
- </div>
121
- ) : null}
122
- </div>
123
- </div>
124
- </div>
125
- </div>
126
- )
127
- }
@@ -1,120 +0,0 @@
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
- }
@@ -1,13 +0,0 @@
1
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
2
- import type PayPalModuleService from "../../../../modules/paypal/service"
3
-
4
- export async function GET(req: MedusaRequest, res: MedusaResponse) {
5
- const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
6
- const q = (req.query || {}) as Record<string, any>
7
- const limitRaw = Number(q.limit || 50)
8
- const limit = Number.isFinite(limitRaw) ? Math.min(Math.max(limitRaw, 1), 200) : 50
9
-
10
- const logs = await paypal.getAuditLogs(limit)
11
-
12
- return res.json({ logs })
13
- }
@@ -1,7 +0,0 @@
1
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
2
- import type PayPalModuleService from "../../../../modules/paypal/service"
3
-
4
- export async function GET(req: MedusaRequest, res: MedusaResponse) {
5
- const paypal = req.scope.resolve<PayPalModuleService>("paypal_onboarding")
6
- return res.json(await paypal.getReconciliationStatus())
7
- }
@@ -1,9 +0,0 @@
1
- import { model } from "@medusajs/framework/utils"
2
-
3
- const PayPalAuditLog = model.define("paypal_audit_log", {
4
- id: model.id().primaryKey(),
5
- event_type: model.text(),
6
- metadata: model.json().nullable(),
7
- })
8
-
9
- export default PayPalAuditLog