/** * Clip Webhook Controller * * Endpoint público para recibir webhooks de Clip */ import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { ClipService } from '../services/clip.service'; export class ClipWebhookController { public router: Router; private clipService: ClipService; constructor(private dataSource: DataSource) { this.router = Router(); this.clipService = new ClipService(dataSource); this.initializeRoutes(); } private initializeRoutes(): void { // Webhook endpoint (público, sin auth) this.router.post('/:tenantId', this.handleWebhook.bind(this)); } /** * POST /webhooks/clip/:tenantId * Recibir notificaciones de Clip */ private async handleWebhook(req: Request, res: Response, next: NextFunction): Promise { try { const tenantId = req.params.tenantId; const eventType = req.body.event || req.body.type; const data = req.body; // Extraer headers relevantes const headers: Record = { 'x-clip-signature': req.headers['x-clip-signature'] as string || '', 'x-clip-event-id': req.headers['x-clip-event-id'] as string || '', }; // Responder inmediatamente res.status(200).json({ received: true }); // Procesar webhook de forma asíncrona await this.clipService.handleWebhook(tenantId, eventType, data, headers); } catch (error) { console.error('Clip webhook error:', error); if (!res.headersSent) { res.status(200).json({ received: true }); } } } }