Webhooks explicados: payloads firmados y reintentos

Backend2026-10-17TryQuickToolBox

Acabas de integrar una pasarela de pago. La documentación dice: "Enviaremos un webhook a tu endpoint cuando un pago se complete con éxito". Configuras una ruta simple, la pruebas localmente y la despliegas. Entonces, en producción, notas eventos duplicados, notificaciones perdidas y misteriosos errores 500. Los webhooks parecen simples, hasta que dejan de serlo.

Esta guía explica cómo funcionan realmente los webhooks, por qué importan los payloads firmados y cómo construir una lógica de reintentos que mantenga tu sistema sincronizado incluso cuando las cosas salen mal.

¿Qué es un webhook?

Un webhook es un callback HTTP: un sistema envía una solicitud HTTP POST a una URL que tú proporcionas cuando ocurre un evento específico. A diferencia del polling, donde preguntas repetidamente "¿Ocurrió algo?", los webhooks te envían eventos casi en tiempo real.

Los usos comunes incluyen:

El emisor (por ejemplo, Stripe, GitHub) es el proveedor de webhooks; tu servidor es el consumidor de webhooks.

Por qué son esenciales los payloads firmados

Tu endpoint de webhook es de acceso público. Cualquiera que conozca la URL puede enviar eventos falsos. Sin verificación, un atacante podría falsificar un evento de "pago exitoso" y podrías enviar productos gratis.

Los payloads firmados resuelven esto incluyendo una firma criptográfica en las cabeceras de la solicitud, generada con un secreto compartido conocido solo por ti y el proveedor. Cuando recibes un webhook, recalculas la firma y la comparas. Si coinciden, el payload es auténtico.

Cómo funcionan las firmas HMAC

La mayoría de los proveedores usan HMAC-SHA256. El proveedor calcula:

signature = HMAC-SHA256(secret, payload)

y la envía en una cabecera como X-Signature. Tu servidor hace el mismo cálculo y verifica la igualdad. Como el secreto es secreto, solo el proveedor puede generar una firma válida.

Verificar una firma en la práctica

Aquí tienes un ejemplo mínimo en Node.js usando Express:

const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  const payload = req.body; // raw Buffer

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const event = JSON.parse(payload);
  console.log('Received event:', event.type);
  res.status(200).send('OK');
});

app.listen(3000);

Importante: usa siempre el cuerpo de la solicitud en bruto para la verificación de la firma. Si primero parseas el JSON, la cadena serializada puede diferir (espacios en blanco, orden de claves) y la verificación de la firma fallará.

Errores comunes

Reintentos: manejar fallos con elegancia

Las redes fallan, los servidores se reinician y los despliegues ocurren. Los proveedores de webhooks esperan que tu endpoint pueda estar temporalmente no disponible, así que reintentan las entregas fallidas. Pero los reintentos introducen nuevos desafíos: eventos duplicados, entrega desordenada y bucles infinitos si no tienes cuidado.

Cómo reintentan los proveedores

La mayoría de los proveedores usan backoff exponencial: esperan un tiempo corto tras el primer fallo y luego intervalos progresivamente más largos. Por ejemplo, Stripe reintenta hasta 3 días con retrasos crecientes. GitHub reintenta hasta 8 veces. El calendario exacto varía, pero el patrón es consistente.

Tu endpoint debe devolver un código de estado 2xx rápidamente para confirmar la recepción. Cualquier otro estado (4xx, 5xx) señala un fallo y activa un reintento.

Construir manejadores idempotentes

Como los reintentos pueden entregar el mismo evento varias veces, tu manejador debe ser idempotente: procesar el mismo evento dos veces debe tener el mismo efecto que procesarlo una vez.

Estrategias comunes:

Ejemplo usando un conjunto simple en memoria (a modo de ilustración; usa un almacén persistente en producción):

const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const event = req.body;
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed');
  }
  // Process event...
  processedEvents.add(event.id);
  res.status(200).send('OK');
});

Responder rápidamente

Los proveedores suelen tener un timeout (por ejemplo, 5 segundos). Si tu manejador hace un procesamiento pesado, confirma inmediatamente y procesa de forma asíncrona. Por ejemplo, envía el evento a una cola y devuelve 200.

Mejores prácticas para webhooks confiables

  1. Verifica siempre las firmas usando el cuerpo en bruto y una comparación de tiempo constante.
  2. Usa HTTPS para tu endpoint para prevenir escuchas y manipulaciones.
  3. Implementa idempotencia para manejar entregas duplicadas.
  4. Registra todo — payloads entrantes, firmas y tus respuestas — para depuración.
  5. Monitorea los fallos y configura alertas para errores repetidos.
  6. Prueba con eventos reales usando los paneles del proveedor o herramientas CLI.
  7. Maneja eventos desordenados incluyendo timestamps o números de secuencia en tu lógica.

Comparación: polling vs. webhooks

AspectoPollingWebhooks
LatenciaDepende del intervaloCasi en tiempo real
Carga del servidorAlta (solicitudes constantes)Baja (solo en eventos)
ComplejidadSimple de implementarRequiere endpoint, seguridad, reintentos
ConfiabilidadTú controlas los reintentosEl proveedor controla los reintentos

Preguntas frecuentes

¿Qué es una firma de webhook?

Una firma de webhook es un hash criptográfico (normalmente HMAC-SHA256) del payload, generado con un secreto compartido. Permite al receptor verificar que la solicitud provino del emisor esperado y que no fue manipulada.

¿Cómo manejo eventos de webhook duplicados?

Haz tu manejador idempotente rastreando los IDs de eventos o usando operaciones de base de datos que sean naturalmente idempotentes. Comprueba si un evento ya se ha procesado antes de aplicar cambios.

¿Qué debería devolver mi endpoint de webhook?

Devuelve un código de estado 2xx (por ejemplo, 200 OK) tan pronto como hayas recibido y validado el evento. Si necesitas hacer un procesamiento pesado, confirma inmediatamente y maneja el trabajo de forma asíncrona.

¿Listo para probar tus payloads de webhook? Usa nuestro JSON Formatter para inspeccionar y validar la estructura JSON de los eventos entrantes.