48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
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();
|