## Documentation - Align MCH-029 to MCH-032 with template-saas modules (SAAS-008 to SAAS-015) - Create MCH-034 (Analytics) and MCH-035 (Reports) from SAAS-016/017 - Update PLAN-DESARROLLO.md with Phase 7 and 8 - Update _MAP.md indexes (35 total epics) ## Database (5 new schemas, 14 tables) - Add storage schema: buckets, files, signed_urls - Add webhooks schema: endpoints, deliveries - Add audit schema: logs, retention_policies - Add features schema: flags, tenant_flags (14 seeds) - Add analytics schema: metrics, events, reports, report_schedules - Add auth.oauth_connections for MCH-030 - Add timestamptz_to_date() IMMUTABLE function - Update EXPECTED_SCHEMAS in recreate-database.sh ## Analysis Reports - ANALISIS-INTEGRACION-TEMPLATE-SAAS-2026-01-13.md - VALIDACION-COHERENCIA-2026-01-13.md - GAP-ANALYSIS-BD-2026-01-13.md - REPORTE-EJECUCION-2026-01-13.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
143 lines
4.6 KiB
PL/PgSQL
143 lines
4.6 KiB
PL/PgSQL
-- =============================================================================
|
|
-- MICHANGARRITO - 18 WEBHOOKS (MCH-029: Infraestructura SaaS)
|
|
-- =============================================================================
|
|
-- Sistema de webhooks salientes para integraciones externas
|
|
-- Permite a los tenants recibir notificaciones de eventos en sus sistemas
|
|
-- =============================================================================
|
|
|
|
-- Tipos de evento webhook
|
|
CREATE TYPE webhook_event_type AS ENUM (
|
|
-- Ventas
|
|
'sale.created',
|
|
'sale.completed',
|
|
'sale.cancelled',
|
|
'sale.refunded',
|
|
|
|
-- Productos
|
|
'product.created',
|
|
'product.updated',
|
|
'product.deleted',
|
|
'product.low_stock',
|
|
|
|
-- Clientes
|
|
'customer.created',
|
|
'customer.updated',
|
|
|
|
-- Fiados
|
|
'credit.created',
|
|
'credit.payment',
|
|
'credit.overdue',
|
|
|
|
-- Pedidos
|
|
'order.created',
|
|
'order.confirmed',
|
|
'order.delivered',
|
|
'order.cancelled',
|
|
|
|
-- Suscripciones
|
|
'subscription.created',
|
|
'subscription.renewed',
|
|
'subscription.cancelled',
|
|
'subscription.expired'
|
|
);
|
|
|
|
-- Endpoints de webhook
|
|
CREATE TABLE IF NOT EXISTS webhooks.endpoints (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Configuracion
|
|
name VARCHAR(100) NOT NULL,
|
|
url VARCHAR(500) NOT NULL,
|
|
secret VARCHAR(255) NOT NULL, -- Para firma HMAC-SHA256
|
|
|
|
-- Eventos suscritos
|
|
events webhook_event_type[] NOT NULL,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Rate limiting
|
|
rate_limit INTEGER DEFAULT 100, -- requests por minuto
|
|
|
|
-- Estadisticas
|
|
total_deliveries INTEGER DEFAULT 0,
|
|
failed_deliveries INTEGER DEFAULT 0,
|
|
last_delivery_at TIMESTAMPTZ,
|
|
last_failure_at TIMESTAMPTZ,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_webhooks_endpoints_tenant ON webhooks.endpoints(tenant_id);
|
|
CREATE INDEX idx_webhooks_endpoints_active ON webhooks.endpoints(tenant_id, is_active) WHERE is_active = true;
|
|
|
|
CREATE TRIGGER update_webhooks_endpoints_updated_at
|
|
BEFORE UPDATE ON webhooks.endpoints
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
COMMENT ON TABLE webhooks.endpoints IS 'Endpoints de webhook configurados por tenant';
|
|
COMMENT ON COLUMN webhooks.endpoints.secret IS 'Secret para firma HMAC-SHA256 del payload';
|
|
COMMENT ON COLUMN webhooks.endpoints.events IS 'Array de tipos de evento suscritos';
|
|
|
|
-- Estado de entrega
|
|
CREATE TYPE webhook_delivery_status AS ENUM (
|
|
'pending',
|
|
'success',
|
|
'failed',
|
|
'retrying'
|
|
);
|
|
|
|
-- Entregas de webhook (log)
|
|
CREATE TABLE IF NOT EXISTS webhooks.deliveries (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
endpoint_id UUID NOT NULL REFERENCES webhooks.endpoints(id) ON DELETE CASCADE,
|
|
|
|
-- Evento
|
|
event_type webhook_event_type NOT NULL,
|
|
event_id VARCHAR(100), -- ID del evento original (sale_id, order_id, etc.)
|
|
payload JSONB NOT NULL,
|
|
|
|
-- Estado de entrega
|
|
status webhook_delivery_status DEFAULT 'pending',
|
|
|
|
-- Respuesta
|
|
response_code INTEGER,
|
|
response_body TEXT,
|
|
response_time_ms INTEGER,
|
|
|
|
-- Reintentos
|
|
attempts INTEGER DEFAULT 0,
|
|
max_attempts INTEGER DEFAULT 5,
|
|
next_retry_at TIMESTAMPTZ,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
delivered_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX idx_webhooks_deliveries_endpoint ON webhooks.deliveries(endpoint_id);
|
|
CREATE INDEX idx_webhooks_deliveries_status ON webhooks.deliveries(status) WHERE status IN ('pending', 'retrying');
|
|
CREATE INDEX idx_webhooks_deliveries_retry ON webhooks.deliveries(next_retry_at) WHERE status = 'retrying';
|
|
CREATE INDEX idx_webhooks_deliveries_created ON webhooks.deliveries(created_at DESC);
|
|
|
|
COMMENT ON TABLE webhooks.deliveries IS 'Log de entregas de webhooks';
|
|
COMMENT ON COLUMN webhooks.deliveries.attempts IS 'Numero de intentos realizados';
|
|
COMMENT ON COLUMN webhooks.deliveries.next_retry_at IS 'Timestamp del proximo reintento (backoff exponencial)';
|
|
|
|
-- =============================================================================
|
|
-- FUNCION: Calcular siguiente retry con backoff exponencial
|
|
-- =============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION webhooks.calculate_next_retry(attempts INTEGER)
|
|
RETURNS TIMESTAMPTZ AS $$
|
|
BEGIN
|
|
-- Backoff exponencial: 1min, 5min, 15min, 1hr, 4hr
|
|
RETURN NOW() + (POWER(2, LEAST(attempts, 5)) * INTERVAL '1 minute');
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION webhooks.calculate_next_retry IS 'Calcula el timestamp del siguiente reintento con backoff exponencial';
|