--- id: "SAAS-010" title: "Webhooks" type: "Module" status: "Published" priority: "P2" module: "webhooks" version: "1.0.0" created_date: "2026-01-07" updated_date: "2026-01-10" --- # SAAS-010: Webhooks ## Metadata - **Codigo:** SAAS-010 - **Modulo:** Webhooks - **Prioridad:** P2 - **Estado:** Implementado - **Fase:** 5 - Integraciones ## Descripcion Sistema de webhooks outbound: configuracion de endpoints por tenant, eventos suscribibles, firma de payloads, reintentos automaticos, y logs de entregas. ## Objetivos 1. Configuracion de webhooks por tenant 2. Eventos suscribibles 3. Firma HMAC de payloads 4. Reintentos con backoff 5. Dashboard de entregas ## Implementacion ### Estado de Entregables | Entregable | Estado | Archivo | |------------|--------|---------| | webhooks.module.ts | Implementado | `apps/backend/src/modules/webhooks/webhooks.module.ts` | | webhooks.controller.ts | Implementado | `apps/backend/src/modules/webhooks/webhooks.controller.ts` | | webhook.service.ts | Implementado | `apps/backend/src/modules/webhooks/services/webhook.service.ts` | | webhook.processor.ts | Implementado | `apps/backend/src/modules/webhooks/processors/webhook.processor.ts` | | DDL webhooks schema | Implementado | `apps/database/ddl/schemas/webhooks/` | ### Archivos Creados **Backend:** ``` apps/backend/src/modules/webhooks/ ├── webhooks.module.ts # NestJS module with BullMQ ├── webhooks.controller.ts # REST endpoints (10 endpoints) ├── index.ts # Module barrel export ├── entities/ │ ├── webhook.entity.ts # WebhookEntity, WebhookDeliveryEntity │ └── index.ts ├── dto/ │ ├── webhook.dto.ts # Request/Response DTOs │ └── index.ts ├── processors/ │ ├── webhook.processor.ts # BullMQ worker for deliveries │ └── index.ts └── services/ ├── webhook.service.ts # Business logic └── index.ts ``` **Database:** ``` apps/database/ddl/schemas/webhooks/tables/ ├── 01-webhooks.sql # Main webhooks table └── 02-deliveries.sql # Delivery logs, retry functions ``` ### Tablas de Base de Datos | Tabla | Descripcion | |-------|-------------| | webhooks.webhooks | Configuracion de webhooks por tenant | | webhooks.deliveries | Historial de entregas y reintentos | ### Funciones de Base de Datos | Funcion | Descripcion | |---------|-------------| | webhooks.calculate_next_retry() | Calcula tiempo de siguiente reintento | | webhooks.get_pending_retries() | Obtiene entregas pendientes de retry | | webhooks.get_webhook_stats() | Estadisticas de un webhook | ## Eventos Disponibles | Evento | Descripcion | Payload | |--------|-------------|---------| | user.created | Usuario creado | User object | | user.updated | Usuario actualizado | User + changes | | user.deleted | Usuario eliminado | { userId } | | subscription.created | Nueva suscripcion | Subscription | | subscription.updated | Suscripcion cambiada | Subscription | | subscription.cancelled | Suscripcion cancelada | Subscription | | invoice.paid | Factura pagada | Invoice | | invoice.failed | Pago fallido | Invoice | | file.uploaded | Archivo subido | File object | | file.deleted | Archivo eliminado | { fileId } | | tenant.updated | Tenant actualizado | Tenant object | ## Endpoints API | Metodo | Endpoint | Descripcion | Implementado | |--------|----------|-------------|--------------| | GET | /webhooks/events | Listar eventos disponibles | ✓ | | GET | /webhooks | Listar webhooks | ✓ | | GET | /webhooks/:id | Obtener webhook | ✓ | | POST | /webhooks | Crear webhook | ✓ | | PUT | /webhooks/:id | Actualizar webhook | ✓ | | DELETE | /webhooks/:id | Eliminar webhook | ✓ | | POST | /webhooks/:id/regenerate-secret | Regenerar secret | ✓ | | POST | /webhooks/:id/test | Enviar test | ✓ | | GET | /webhooks/:id/deliveries | Historial entregas | ✓ | | POST | /webhooks/:id/deliveries/:did/retry | Reintentar | ✓ | ## Firma de Payloads ### Generacion ```typescript function signPayload(payload: object, secret: string): string { const timestamp = Date.now(); const body = JSON.stringify(payload); const signature = crypto .createHmac('sha256', secret) .update(`${timestamp}.${body}`) .digest('hex'); return `t=${timestamp},v1=${signature}`; } ``` ### Headers Enviados ``` X-Webhook-Signature: t=1704067200000,v1=abc123... X-Webhook-Id: X-Webhook-Event: user.created X-Webhook-Timestamp: 1704067200000 X-Webhook-Delivery: ``` ### Verificacion (lado receptor) ```typescript function verifySignature(payload: string, signature: string, secret: string): boolean { const [timestamp, hash] = parseSignature(signature); // Verificar timestamp (5 min tolerance) if (Date.now() - timestamp > 300000) return false; const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${payload}`) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(hash), Buffer.from(expected) ); } ``` ## Logica de Reintentos ``` Intento 1: Inmediato Intento 2: +1 minuto Intento 3: +5 minutos Intento 4: +30 minutos Intento 5: +2 horas Intento 6: +6 horas Despues: Marcar como fallido ``` ### Status de Entrega | Status | Descripcion | |--------|-------------| | pending | En cola | | delivered | Entregado (2xx) | | failed | Fallo permanente | | retrying | En reintento | ## Limites por Plan | Plan | Webhooks | Eventos/mes | |------|----------|-------------| | Free | 0 | 0 | | Starter | 0 | 0 | | Pro | 5 | 10,000 | | Enterprise | 20 | 100,000 | ## Dependencias ### Tecnologicas - BullMQ para queue (Redis) - NestJS @nestjs/bullmq - ioredis ### Depende de - SAAS-002 (Tenants) - SAAS-005 (Plans - feature flag) ### Bloquea a - Integraciones de terceros - Automatizaciones externas ## Criterios de Aceptacion - [x] CRUD webhooks funciona - [x] Eventos se disparan - [x] Firma es correcta - [x] Reintentos funcionan - [x] Test endpoint funciona - [x] Logs se registran ## Configuracion ### Variables de Entorno ```bash # Redis for BullMQ REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= # Optional ``` ## Seguridad - Secrets generados aleatoriamente (whsec_...) - HTTPS requerido para URLs - Timeout de 30 segundos - Rate limit por tenant - No seguir redirects ## Uso ### Crear Webhook ```typescript POST /webhooks { "name": "My Webhook", "url": "https://example.com/webhook", "events": ["user.created", "subscription.created"], "headers": { "X-Custom-Header": "value" } } ``` ### Dispatch Event (desde otros servicios) ```typescript // Inyectar WebhookService constructor(private webhookService: WebhookService) {} // Disparar evento await this.webhookService.dispatch( tenantId, 'user.created', { id: user.id, email: user.email } ); ``` --- **Ultima actualizacion:** 2026-01-10