import crypto from 'crypto'; const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'test_secret_for_webhooks'; // For demo purposes, we log it or hit a dummy URL. const WEBHOOK_URL = process.env.WEBHOOK_URL || 'http://localhost:8081/webhooks/pharmacy'; export class WebhookService { async dispatchEvent(event: string, idempotencyKey: string, payload: any) { const timestamp = Math.floor(Date.now() / 1000); const bodyString = JSON.stringify(payload); const dataToSign = `${timestamp}.${bodyString}`; const signature = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(dataToSign) .digest('hex'); const signatureHeader = `t=${timestamp},v1=${signature}`; console.log(`[WEBHOOK] Dispatching event: ${event} (Idempotency: ${idempotencyKey})`); try { // Assuming Node 20+, native fetch is available const response = await fetch(WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Pharmacy-Event': event, 'X-Pharmacy-Signature': signatureHeader, 'Idempotency-Key': idempotencyKey, }, body: bodyString, }); if (!response.ok) { console.error(`[WEBHOOK] Dispatch failed with status: ${response.status}`); } else { console.log(`[WEBHOOK] Successfully dispatched event: ${event}`); } } catch (error: any) { console.error(`[WEBHOOK] Failed to dispatch webhook: ${error.message}`); // In a real system, this would queue for retry with exponential backoff. } } } export const webhookService = new WebhookService();