Skip to content

Commit 2958c3c

Browse files
Implement WebhookDispatcherSefeat: Enterprise-grade Resilient Webhook Dispatcher with HMAC Signaturervice for webhook handling
1 parent 197c00d commit 2958c3c

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Injectable, Logger } from '@nestjs/common';
2+
import axios from 'axios';
3+
import * as crypto from 'crypto';
4+
5+
export interface WebhookPayload {
6+
event: string;
7+
documentId: string;
8+
status: string;
9+
timestamp: string;
10+
data: any;
11+
}
12+
13+
@Injectable()
14+
export class WebhookDispatcherService {
15+
private readonly logger = new Logger(WebhookDispatcherService.name);
16+
private readonly MAX_RETRIES = 3;
17+
private readonly TIMEOUT_MS = 5000;
18+
19+
private generateSignature(payload: string, secret: string): string {
20+
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
21+
}
22+
23+
async dispatchWithBackoff(url: string, payload: WebhookPayload, secret: string, attempt: number = 1): Promise<boolean> {
24+
const payloadString = JSON.stringify(payload);
25+
const signature = this.generateSignature(payloadString, secret);
26+
try {
27+
this.logger.log(`[Attempt ${attempt}] Dispatching webhook to ${url}`);
28+
await axios.post(url, payloadString, { headers: { 'Content-Type': 'application/json', 'X-OpenSign-Signature': signature, 'X-OpenSign-Event': payload.event }, timeout: this.TIMEOUT_MS });
29+
return true;
30+
} catch (error) {
31+
if (attempt < this.MAX_RETRIES) {
32+
const backoffDelay = Math.pow(2, attempt) * 1000;
33+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
34+
return this.dispatchWithBackoff(url, payload, secret, attempt + 1);
35+
}
36+
return false;
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)