- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones de configuracion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
269 lines
6.8 KiB
Markdown
269 lines
6.8 KiB
Markdown
---
|
|
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: <webhook-uuid>
|
|
X-Webhook-Event: user.created
|
|
X-Webhook-Timestamp: 1704067200000
|
|
X-Webhook-Delivery: <delivery-uuid>
|
|
```
|
|
|
|
### 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
|