Depurar webhooks sin la fontanería

Toda integración de webhooks empieza con el mismo problema poco glamuroso: el servidor del proveedor necesita llamar a tu código, tu código corre en tu portátil, y nada puede llegar a tu portátil. Así que antes de depurar el trabajo real — la verificación de la firma, la forma de la carga, la lógica del manejador — primero construyes fontanería: un túnel, un despliegue de staging, una URL pública que reenvía a tu máquina. Este artículo trata de saltarse ese paso con un receptor temporal como el Probador de webhooks de este sitio: crea una dirección, apunta el proveedor a ella, dispara el evento y lee exactamente lo que llegó. Después escribe tu manejador contra la verdad, no contra la documentación.

El ciclo de depuración, y dónde se rompe

El ciclo sano de un webhook es corto: ocurre un evento en el proveedor, el proveedor hace POST de una carga firmada a tu endpoint, tu endpoint verifica la firma, analiza el cuerpo, hace el trabajo y responde 200. El ciclo insano — el que todo el mundo reconoce — añade niebla a cada paso. El panel del proveedor dice que la entrega falló con un 500, o un 404, o nada en absoluto. ¿Respondía tu código, un proxy intermedio, o una URL de endpoint con una errata? La reintentada llega con el mismo id de evento — ¿o es un evento nuevo para el mismo pago? La documentación muestra un ejemplo JSON impecable, pero las cargas de producción traen campos extra, objetos anidados que el ejemplo aplanó y, de vez en cuando, un tipo de contenido que el ejemplo nunca mencionó. La documentación de webhooks de Stripe y de GitHub (enlazada abajo) insiste en las mismas disciplinas — verificar firmas, responder rápido, gestionar duplicados — y ninguna puede practicarse hasta que puedes ver una entrega real.

La ruptura casi nunca está en la lógica interesante. Está en la brecha entre lo que crees que se envió y lo que se envió: una cabecera de firma calculada sobre un cuerpo que tu framework ya había analizado y vuelto a serializar, un secreto copiado del panel equivocado, un tipo de evento que tu sentencia switch escribe ligeramente distinto, un manejador que funciona para el evento de prueba del panel y no para el evento real de producción. Todo eso es invisible desde dentro de tus propios registros, porque tus registros empiezan después de que tu código ya ha hecho su primera suposición equivocada. Una entrega capturada, registrada por algo que no es tu código, es la verdad fundamental que acorta el ciclo.

Lo que te compra un receptor temporal

Un receptor temporal es una URL pública cuyo único trabajo es aceptar cualquier cosa y mostrártela: el Probador de webhooks de este sitio crea uno con un clic, lo conserva 30 minutos, registra hasta 50 entregas con su método, ruta y consulta, cabeceras y cuerpo (hasta 256 KB cada uno), y después lo elimina todo automáticamente. Lo que eso te compra es secuenciación. En lugar de desplegar un manejador a medio escribir en una máquina pública solo para observar el tráfico — y depurar después el despliegue y el manejador a la vez —, separas la observación de la implementación. Primero observas cómo llegan varios eventos reales y aprendes su forma verdadera. Después escribes el manejador en local, contra cargas capturadas que puedes repetir a mano en tus propias pruebas. Por último, apuntas el proveedor a tu endpoint real y disparas el evento una vez más, sabiendo ya exactamente cómo es una entrega correcta y qué debería hacer tu manejador con ella.

También te compra evidencia cuando la conversación cruza la frontera de un equipo. «Tu webhook envía el importe como cadena» es una discusión; una entrega capturada que muestra "amount": "2000" es un hecho. La misma captura responde a las preguntas que hacen los tickets de soporte de los proveedores — qué id de evento, qué marca temporal, qué cabecera de firma estaba presente — sin una ronda de arqueología de registros, y aclara si una entrega que falta nunca se envió o se envió a la vieja dirección de prueba que alguien olvidó actualizar. Herramienta vecina, misma filosofía: el trazador de cadenas de estado HTTP muestra lo que hace una URL desde el punto de vista de un servidor cuando la pregunta trata de redirecciones y no de cargas.

El flujo de trabajo, con esta herramienta

Uno: abre el Probador de webhooks y crea un hook. Copia la URL de recepción — y desde este momento trata esa URL como una credencial, por las razones que expone la siguiente sección. Dos: pégala en los ajustes de webhook del proveedor como endpoint de prueba — Stripe, GitHub y la mayoría de los proveedores permiten varios endpoints, así que tu endpoint de producción queda intacto — o en la configuración de tu propio servicio si estás depurando un remitente que controlas. Tres: dispara el evento de la manera honesta. Usa el flujo real del proveedor (un pago en modo de prueba, un push real a una rama desechable) en lugar de solo el botón «enviar evento de prueba» del panel, porque los eventos de prueba a veces difieren de los reales justo en los campos que te importan. Cuatro: lee la captura. Comprueba primero el método y el tipo de contenido, después la firma y las cabeceras del evento, y después el cuerpo como texto sin procesar antes de fiarte de cualquier vista bonita de él.

Cinco: escribe o corrige tu manejador contra esa captura. Verifica la firma sobre el cuerpo sin procesar exactamente como se recibió — por eso la captura muestra los bytes sin analizar — y haz tu manejador idempotente sobre el id de evento que ahora puedes ver que es estable entre reintentos. Seis: elimina el hook (o deja que pasen sus 30 minutos), retira el endpoint de prueba del proveedor y vuelve a apuntar la entrega a tu manejador real. Siete: dispara el evento una vez más y observa cómo tu propio endpoint lo responde. Si ahora falla, la diferencia entre la entrega buena capturada y el comportamiento de tu manejador es todo el error, y es una diferencia que puedes inspeccionar en lugar de adivinar.

Seguridad: la URL es la credencial

Un receptor que cualquiera puede leer no necesita inicio de sesión, y esa comodidad tiene un precio al que debes prestar atención: el id del hook en la URL es la única credencial, y autoriza tanto enviar como ver. Cualquiera que obtenga la URL — de una captura de pantalla, un registro de chat, una pantalla compartida — puede leer cada carga que el hook capture mientras viva. Así que las reglas son las de las contraseñas: nunca publiques la URL, nunca la uses para un feed de producción en vivo, y elimina el hook cuando acabe la sesión en lugar de dejar que las capturas agoten su media hora. Las cargas merecen el mismo respeto desde el otro lado: una carga real de pago o de push puede contener direcciones de correo de clientes, detalles de pedidos y metadatos que no pegarías en un gist público, y una entrega capturada es exactamente esa clase de documento. El modo de prueba, los repositorios desechables y los eventos sintéticos existen precisamente para que los artefactos de depuración no contengan nada que valga la pena robar.

Los propios límites del probador forman parte de la historia de seguridad y están declarados con claridad en la página de la herramienta: las capturas se guardan en el servidor de comprobación solo hasta que el hook caduca o lo eliminas — 30 minutos como máximo —, los cuerpos están limitados a 256 KB y los hooks a 50 solicitudes, y el receptor nunca obtiene, sigue ni repite nada, de modo que una carga capturada no puede convertirse en tráfico saliente del servidor. Aun así, los juicios quedan en tus manos. Si una carga sería un problema en manos de un extraño, no pertenece en absoluto a un visor compartido: usa tu propia infraestructura para esa sesión de depuración, como harías con los secretos de producción. Las herramientas cómodas se ganan su sitio en el trabajo de baja sensibilidad que llena la mayoría de los días de depuración.

Límites: solo recibir e inspeccionar

Lo que esta herramienta deliberadamente no hace es todo lo que viene después de la inspección. No reenvía entregas a tu portátil (ese es el trabajo de un túnel, y los túneles son la herramienta adecuada una vez que el manejador existe y estás iterando sobre él en vivo), no repite una solicitud capturada a tu endpoint real, no transforma ni firma nada, y no conserva un historial más allá de 30 minutos. Esas ausencias son el diseño: un receptor que reenvía es un proxy por el que fluye tu tráfico, un receptor que repite es un remitente, y ambos son confianzas mayores que una bandeja de entrada de 30 minutos. Usa el probador para la fase a la que encaja — ver la verdad antes y mientras construyes el manejador — y deja que tu propio endpoint, tras tu propia autenticación y tus propios registros, tome el relevo en cuanto la integración entre en producción.

Fuentes y lecturas adicionales

Preguntas frecuentes

¿Puedo usar un receptor temporal con Stripe o GitHub?

Sí — ambos te permiten registrar endpoints de webhook adicionales, así que el receptor puede convivir con tu endpoint de producción sin tocarlo. Crea el hook, añade su URL en los ajustes de webhook del proveedor y usa el modo de prueba (Stripe) o un repositorio desechable (GitHub) para disparar eventos reales. La documentación de ambos proveedores, enlazada en las fuentes, cubre el registro de endpoints y sus esquemas de firma; verifica las firmas en tu manejador sobre el cuerpo sin procesar, exactamente como lo muestra la captura.

¿Es seguro apuntar webhooks reales de producción a un receptor compartido?

No. Cualquiera con la URL del hook puede leer todo lo que capture, y las cargas reales suelen llevar datos de clientes. Usa el modo de prueba, cuentas sandbox y eventos sintéticos con un receptor compartido; mantén el tráfico de producción en tu propia infraestructura. El hook también caduca a los 30 minutos, así que un feed de producción apuntado a uno empezaría a fallar en silencio media hora después — otra razón por la que es una herramienta de depuración, no un endpoint.

¿Por qué el probador no reenvía las solicitudes a mi ordenador?

Porque recibir e inspeccionar es una confianza deliberadamente pequeña: el servidor guarda una entrega durante 30 minutos como máximo y nunca inicia tráfico por su cuenta. Reenviar lo convertiría en un proxy de tráfico arbitrario hacia tu máquina, y repetir lo convertiría en un remitente — ambos son servicios distintos y mayores, con riesgos distintos. Una vez que tu manejador existe y estás iterando en vivo, un túnel es la herramienta adecuada para esa fase.