@company-manager/docs
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

  1. Sécurité

    • Validation des signatures
    • Timeouts appropriés
    • Rate limiting
  2. Fiabilité

    • Retry avec backoff
    • Gestion des doublons
    • Logging complet
  3. Performance

    • Traitement asynchrone
    • Timeouts courts
    • Monitoring actif