@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.
- package/.medusa/server/src/admin/index.js +18 -248
- package/.medusa/server/src/admin/index.mjs +18 -248
- package/.medusa/server/src/jobs/paypal-reconcile.d.ts.map +1 -1
- package/.medusa/server/src/jobs/paypal-reconcile.js +0 -36
- package/.medusa/server/src/jobs/paypal-reconcile.js.map +1 -1
- package/.medusa/server/src/modules/paypal/index.d.ts +0 -14
- package/.medusa/server/src/modules/paypal/index.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js +28 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/{20260301090000_create_paypal_audit_log.js → 20261201090000_remove_paypal_audit_log.js} +10 -10
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.js.map +1 -0
- package/.medusa/server/src/modules/paypal/service.d.ts +2 -48
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/service.js +2 -99
- package/.medusa/server/src/modules/paypal/service.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/routes/settings/paypal/_components/Tabs.tsx +0 -2
- package/src/admin/routes/settings/paypal/additional-settings/page.tsx +218 -226
- package/src/jobs/paypal-reconcile.ts +0 -37
- package/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.ts +25 -0
- package/src/modules/paypal/migrations/{20260301090000_create_paypal_audit_log.ts → 20261201090000_remove_paypal_audit_log.ts} +8 -8
- package/src/modules/paypal/service.ts +2 -112
- package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts +0 -3
- package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts.map +0 -1
- package/.medusa/server/src/api/admin/paypal/audit-logs/route.js +0 -12
- package/.medusa/server/src/api/admin/paypal/audit-logs/route.js.map +0 -1
- package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts +0 -3
- package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts.map +0 -1
- package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js +0 -8
- package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js.map +0 -1
- package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts +0 -6
- package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts.map +0 -1
- package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.js.map +0 -1
- package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts +0 -7
- package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts.map +0 -1
- package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js +0 -10
- package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js.map +0 -1
- package/src/admin/routes/settings/paypal/audit-logs/page.tsx +0 -127
- package/src/admin/routes/settings/paypal/reconciliation-status/page.tsx +0 -120
- package/src/api/admin/paypal/audit-logs/route.ts +0 -13
- package/src/api/admin/paypal/reconciliation-status/route.ts +0 -7
- 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(
|
|
1212
|
-
|
|
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 +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 +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"}
|
package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts.map
DELETED
|
@@ -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"}
|
package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.js.map
DELETED
|
@@ -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
|
-
}
|