erp-retail-backend-v2/src/modules/payment-terminals/controllers/clip-webhook.controller.ts
Adrian Flores Cortes 9de89aab5a [PROP-CORE-004] feat: Add Phase 6 modules from erp-core
Propagated modules:
- payment-terminals: MercadoPago + Clip TPV integration
- ai: Role-based AI access (ADMIN, GERENTE_TIENDA, CAJERO, CLIENTE)
- mcp: 18 ERP tools for AI assistants

71 files added. Critical for POS operations (P0).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 02:43:47 -06:00

55 lines
1.6 KiB
TypeScript

/**
* 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<void> {
try {
const tenantId = req.params.tenantId;
const eventType = req.body.event || req.body.type;
const data = req.body;
// Extraer headers relevantes
const headers: Record<string, string> = {
'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 });
}
}
}
}