Webhooks explicados: payloads firmados y reintentos

Backend2026-11-08TryQuickToolBox

Acabas de integrar el webhook de un proveedor de pagos. La documentación dice "enviaremos una petición POST a tu endpoint cuando un pago se complete". Sencillo, ¿verdad? Pero luego notas eventos duplicados, notificaciones perdidas y una alerta de seguridad sobre peticiones falsificadas. Los webhooks son simples en concepto pero complicados en producción. Este artículo explica cómo implementar webhooks correctamente usando payloads firmados y reintentos, para que puedas construir integraciones fiables y seguras.

¿Qué son los webhooks?

Un webhook es una API inversa: en lugar de que tu aplicación consulte periódicamente a un servicio para obtener actualizaciones, el servicio envía eventos a tu endpoint mediante HTTP POST. Es eficiente y casi en tiempo real. Algunos usos habituales incluyen notificaciones de pagos, disparadores de CI/CD e integraciones de chat.

Pero los webhooks introducen desafíos: ¿cómo sabes que la petición proviene realmente del proveedor? ¿Qué pasa si tu servidor está caído? ¿Y si el mismo evento llega dos veces? Vamos a abordarlos uno por uno.

Proteger webhooks con payloads firmados

Cualquiera puede enviar una petición HTTP a tu endpoint. Sin verificación, los atacantes podrían falsificar eventos, provocando fraude o corrupción de datos. La solución estándar es firmar el payload con un secreto compartido.

Cómo funcionan las firmas HMAC

El proveedor y tú compartís una clave secreta. Para cada evento, el proveedor calcula un HMAC (código de autenticación de mensajes basado en hash) del cuerpo de la petición usando ese secreto y envía la firma en una cabecera (por ejemplo, X-Signature). Tú recalculas el HMAC por tu lado y lo comparas. Si coinciden, la petición es auténtica y no ha sido manipulada.

Aquí tienes un ejemplo mínimo en Python usando Flask:

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b'your_shared_secret'

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Signature')
    if not signature:
        abort(400, 'Missing signature')
    body = request.get_data()
    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        abort(401, 'Invalid signature')
    # Process the event
    return '', 200

Usa siempre hmac.compare_digest para una comparación en tiempo constante y así prevenir ataques de temporización. Además, verifica la firma antes de parsear el cuerpo JSON para evitar procesamiento innecesario.

Buenas prácticas para las firmas

Gestionar fallos con reintentos

Desde la perspectiva del proveedor, los webhooks son de tipo "dispara y olvida", pero tu endpoint podría no estar disponible temporalmente. Para garantizar la entrega, los proveedores implementan políticas de reintentos. Como receptor, debes estar preparado para reintentos y duplicados.

Estrategias de reintento usadas por los proveedores

La mayoría de los proveedores reintentan con backoff exponencial: por ejemplo, inmediatamente, luego tras 1 minuto, 5 minutos, 30 minutos, etc., hasta un número máximo de intentos (a menudo entre 5 y 10). Algunos también permiten configurar un calendario de reintentos. El comportamiento exacto varía, así que consulta la documentación del proveedor.

Diseñar tu endpoint para los reintentos

Tu endpoint debería:

Ejemplo de comprobación de idempotencia en Python:

processed_events = set()  # In production, use a database or cache

@app.route('/webhook', methods=['POST'])
def webhook():
    # ... signature verification ...
    event = request.json
    event_id = event['id']
    if event_id in processed_events:
        return '', 200  # Already processed
    # Process event
    processed_events.add(event_id)
    return '', 200

Probar tu implementación de webhooks

Antes de pasar a producción, prueba tu endpoint a fondo:

  1. Simula peticiones firmadas: escribe pruebas unitarias que generen firmas válidas e inválidas.
  2. Prueba escenarios de reintento: simula fallos y asegúrate de que tu endpoint gestiona los duplicados.
  3. Usa las herramientas del proveedor: muchos proveedores ofrecen una CLI o un panel para enviar eventos de prueba.
  4. Monitoriza y registra: registra los eventos entrantes (sin datos sensibles) y configura alertas para fallos de firma o tasas de error elevadas.

Comparación: polling vs webhooks

AspectoPollingWebhooks
LatenciaDepende del intervalo (por ejemplo, minutos)Casi en tiempo real
Carga del servidorAlta (peticiones frecuentes)Baja (solo ante eventos)
ComplejidadSencillo de implementarRequiere verificación de firma y gestión de reintentos
FiabilidadTú controlas los reintentosEl proveedor controla los reintentos; tú debes gestionar los duplicados

Preguntas frecuentes

¿Cómo verifico la firma de un webhook?

Calcula el HMAC del cuerpo de la petición sin procesar usando el secreto compartido y compáralo con la cabecera de firma usando una función de comparación en tiempo constante. Verifica siempre antes de procesar el payload.

¿Qué debería devolver mi endpoint para confirmar un webhook?

Devuelve un código de estado 2xx (por ejemplo, 200 OK) en cuanto hayas recibido y encolado el evento para su procesamiento. Evita procesamientos largos en el manejador de la petición para prevenir timeouts.

¿Cómo puedo gestionar entregas duplicadas de webhooks?

Usa una clave de idempotencia del payload del evento (o un ID de evento único) para rastrear los eventos procesados. Almacena estos IDs en una base de datos o caché y omite el procesamiento si el ID ya se ha visto antes.

¿Listo para probar tus payloads de webhook? Usa nuestro JSON Formatter para formatear y validar el cuerpo JSON de los webhooks entrantes, lo que facilita la depuración.