-- ============================================================================= -- 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';