Integrations
Webhooks
Guide d'implémentation et de gestion des webhooks dans Company Manager.
Webhooks
Guide complet pour l'implémentation et la gestion des webhooks dans Company Manager.
Configuration
Routes API
// pages/api/webhooks/[type].ts
import { NextApiRequest, NextApiResponse } from 'next';
import { WebhookHandler } from '@/lib/webhooks';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { type } = req.query;
const handler = new WebhookHandler(type as string);
try {
await handler.verify(req);
await handler.process(req.body);
res.json({ success: true });
} catch (error) {
res.status(400).json({ error: error.message });
}
}Gestionnaire de Webhooks
// lib/webhooks/handler.ts
export class WebhookHandler {
constructor(private type: string) {}
async verify(req: NextApiRequest) {
const signature = req.headers['x-webhook-signature'];
if (!signature) {
throw new Error('Missing signature');
}
const isValid = await this.verifySignature(
req.body,
signature as string
);
if (!isValid) {
throw new Error('Invalid signature');
}
}
async process(payload: any) {
const processor = this.getProcessor();
await processor.handle(payload);
}
}Sécurité
Validation des Signatures
// lib/webhooks/security.ts
import { createHmac } from 'crypto';
export class WebhookSecurity {
constructor(private secret: string) {}
verifySignature(payload: string, signature: string): boolean {
const hmac = createHmac('sha256', this.secret);
const digest = hmac.update(payload).digest('hex');
return signature === digest;
}
generateSignature(payload: string): string {
const hmac = createHmac('sha256', this.secret);
return hmac.update(payload).digest('hex');
}
}Traitement des Événements
Processeur d'Événements
// lib/webhooks/processor.ts
export class WebhookProcessor {
async handle(event: WebhookEvent) {
await this.validateEvent(event);
await this.processEvent(event);
await this.saveEvent(event);
}
private async validateEvent(event: WebhookEvent) {
const schema = this.getValidationSchema(event.type);
await schema.parseAsync(event);
}
private async processEvent(event: WebhookEvent) {
const handler = this.getEventHandler(event.type);
await handler.process(event);
}
private async saveEvent(event: WebhookEvent) {
await prisma.webhookEvent.create({
data: {
type: event.type,
payload: event,
processedAt: new Date(),
},
});
}
}Gestion des Erreurs
Retry Logic
// lib/webhooks/retry.ts
export class WebhookRetry {
private maxAttempts = 3;
private backoffMs = 1000;
async withRetry<T>(fn: () => Promise<T>): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
await this.wait(attempt);
}
}
throw lastError;
}
private wait(attempt: number): Promise<void> {
const delay = this.backoffMs * Math.pow(2, attempt - 1);
return new Promise(resolve => setTimeout(resolve, delay));
}
}Monitoring
Métriques
// lib/webhooks/metrics.ts
export class WebhookMetrics {
private metrics = {
received: new Counter('webhook_received_total'),
processed: new Counter('webhook_processed_total'),
failed: new Counter('webhook_failed_total'),
processingTime: new Histogram('webhook_processing_time'),
};
recordReceived(type: string) {
this.metrics.received.inc({ type });
}
recordProcessed(type: string, durationMs: number) {
this.metrics.processed.inc({ type });
this.metrics.processingTime.observe(durationMs);
}
recordFailure(type: string, error: string) {
this.metrics.failed.inc({ type, error });
}
}Envoi de Webhooks
Client Webhook
// lib/webhooks/actions.ts
export class WebhookClient {
constructor(private endpoint: string, private secret: string) {}
async send(event: WebhookEvent): Promise<void> {
const payload = JSON.stringify(event);
const signature = this.generateSignature(payload);
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature,
},
body: payload,
});
if (!response.ok) {
throw new Error(`Webhook failed: ${response.statusText}`);
}
}
private generateSignature(payload: string): string {
const security = new WebhookSecurity(this.secret);
return security.generateSignature(payload);
}
}Tests
Tests d'Intégration
// tests/webhooks.test.ts
describe('Webhook Handler', () => {
let handler: WebhookHandler;
beforeEach(() => {
handler = new WebhookHandler('test');
});
it('should verify valid signature', async () => {
const payload = { type: 'test', data: {} };
const signature = generateTestSignature(payload);
const req = mockRequest(payload, signature);
await expect(handler.verify(req)).resolves.not.toThrow();
});
it('should reject invalid signature', async () => {
const payload = { type: 'test', data: {} };
const signature = 'invalid';
const req = mockRequest(payload, signature);
await expect(handler.verify(req)).rejects.toThrow();
});
});Bonnes Pratiques
-
Sécurité
- Validation des signatures
- Timeouts appropriés
- Rate limiting
-
Fiabilité
- Retry avec backoff
- Gestion des doublons
- Logging complet
-
Performance
- Traitement asynchrone
- Timeouts courts
- Monitoring actif