Un endpoint de webhook que pasa las pruebas locales se rompe en la primera semana de producción. Stripe, Shopify y GitHub entregan eventos con garantía at-least-once: el duplicado es seguro, el orden no tiene garantía alguna y un endpoint caído se convierte en una cola silenciosa de retries. El checklist siguiente cubre idempotencia, retries, firma y monitoreo para recibir eventos sin perder ninguno.
¿Por qué llegan webhooks duplicados?
La garantía at-least-once existe porque el remitente no distingue una falla de red de una respuesta perdida. ¿Tu servidor procesó el evento y el 200 se perdió en el camino? El proveedor reenvía. Stripe mantiene retries con backoff exponencial durante hasta 3 días; otros proveedores configuran entre 3 y 20 intentos. El duplicado es un caso normal, así que el handler debe ser idempotente por construcción.
Idempotencia con índice único
Guarda el ID del evento que entrega el proveedor (el event.id de Stripe) en una tabla con índice único. El flujo seguro cabe en un patrón: abre la transacción, inserta el ID, aborta si ya existe, procesa el evento, haz commit. Procesar y registrar en la misma transacción cierra la ventana en la que un crash entre "procesó" y "guardó" genera retrabajo.
La trampa del orden de los eventos
Bajo retry, un charge.refunded puede llegar antes del charge.succeeded que lo originó. Trata cada transición de estado como sospechosa hasta confirmar: un reembolso que llega de un pago que nunca viste exige consultar la API del proveedor, que tiene la verdad actual. El webhook avisa que vale la pena consultar; la API responde con el estado real.
Validación de firma HMAC
Un endpoint público acepta el payload de cualquier persona. Valida la firma HMAC que el proveedor adjunta (estándar del header Stripe-Signature) con comparación timing-safe: hash_equals() en PHP, Crypto.timingSafeEqual en Node. Rechaza eventos con timestamp mayor a 5 minutos, ventana de tolerancia típica que bloquea el replay de payloads capturados.
Responde rápido, procesa después
Un handler que ejecuta toda la regla de negocio antes del 200 agota el timeout del proveedor, que reenvía y dispara otra ejecución larga. La tormenta de duplicados se retroalimenta. Responde con 200 en segundos y manda el evento a una cola (SQS, Redis Streams, BullMQ); el worker hace el trabajo pesado con las mismas garantías de idempotencia.
Monitoreo y replay
- Tasa de éxito de entrega por endpoint, con alerta por debajo del 99%
- Alerta cuando la dead-letter queue acumula eventos
- Una herramienta de replay manual sobre el log de eventos resuelve tickets de soporte y sesiones de debugging
Emitir webhooks propios
Del lado del envío, firma cada payload, documenta la política de retry, expón un log de eventos con el estado de entrega y soporta rotación de secret sin downtime. El consumidor que depende de tu webhook necesita las cuatro cosas; sin ellas, cada incidente se convierte en ticket.
¿Te gustó el contenido?
Construyo productos web y soluciones con IA de la manera correcta — arquitectura sólida, código sostenible y entrega real.
Hablemos