Índice del artículo
Un webhook es la forma en que otro servicio le avisa a tu aplicación de que algo pasó: un pago confirmado, una devolución, un mensaje recibido. En lugar de que tu aplicación pregunte cada rato, el servicio llama a una dirección tuya cuando ocurre algo. Si quieres la diferencia con una API, la tienes en webhook y API.
Esta guía es para quien construye con un agente de código y va a montar su primer webhook. Seguimos un solo caso de principio a fin, con Stripe como ejemplo porque su documentación es de las más completas, y al final tienes el encargo listo para dárselo a Claude Code.
El timbre frente al buzón
Imagina que esperas un paquete importante. Puedes bajar al buzón cada diez minutos a ver si llegó, o puedes esperar a que el repartidor toque el timbre. Lo primero cansa, gasta tiempo y muchas veces encuentras el buzón vacío. Lo segundo te avisa justo cuando pasa.
Un webhook es el timbre. Tu aplicación no pregunta a Stripe cada rato si alguien pagó: Stripe toca el timbre —llama a una dirección de tu servidor— en cuanto el pago se confirma. Lo que viene después es decidir qué haces tú cuando suena: comprobar quién llama, apuntarlo y abrirle la puerta al curso.
El caso: vender un curso y abrir el acceso al pagar
Imagina una academia en línea que vende un curso con Stripe. Cuando alguien paga, tiene que recibir acceso al curso; si se le devuelve el pago completo, el acceso se cierra. La web del curso no se entera de nada de eso por sí sola: el pago ocurre en Stripe. El webhook es el puente.
Lo que hay que decidir no es «cómo recibir un aviso», que es fácil, sino qué hace tu aplicación en cada paso para que ningún pago se quede sin acceso y ningún acceso se dé sin pago.
El recorrido de un aviso de pago
Stripe llama a tu dirección
Registras en Stripe una dirección de tu servidor —por ejemplo, /webhooks/stripe— y eliges qué eventos quieres recibir. Cuando se confirma un pago, Stripe envía a esa dirección un mensaje con los datos del evento.
Compruebas que viene de Stripe
Tu dirección es pública: cualquiera podría enviarle un mensaje falso. Stripe firma cada aviso en la cabecera Stripe-Signature, y tu servidor la verifica con el secreto del punto de conexión y el cuerpo del mensaje tal como llegó, sin tocarlo. Si la firma no cuadra, se rechaza.
Guardas el evento antes de responder
Registra el aviso en tu base de datos o en una cola, de forma que no se pierda si algo falla después. Cada evento trae un identificador único: guardarlo con una restricción de unicidad impide procesar dos veces el mismo.
Respondes rápido que llegó
Stripe espera una respuesta 2xx pronto, antes de cualquier lógica pesada. Si tardas o fallas, lo da por no entregado y lo reintenta.
Procesas el trabajo después
Otro proceso toma los eventos guardados y hace el trabajo: dar el acceso, enviar el correo de bienvenida, anotar la venta. Si un evento necesita el estado actual —¿sigue activa esta compra?—, se consulta en la API de Stripe en ese momento.
Así no
Recibir el aviso, dar el acceso, enviar el correo de bienvenida y, cuando todo termina, responder a Stripe que llegó.
Así sí
Recibir el aviso, comprobar la firma, guardarlo y responder a Stripe al momento. El acceso y el correo los hace otro proceso justo después.
En el primero, si el correo tarda, Stripe cree que el aviso no llegó y lo reintenta: el alumno puede recibir dos bienvenidas. En el segundo, Stripe recibe la respuesta enseguida y el proceso posterior, que evita efectos duplicados y puede reintentar lo que falle, hace el trabajo.
Qué eventos elegir
Un servicio como Stripe emite muchos tipos de eventos, y suscribirse a todos es la forma más rápida de llenar tu servidor de trabajo inútil. Para vender un curso con un pago único, lo esencial suele ser poco: el evento que confirma que el pago del checkout se completó, para abrir el acceso, y el que avisa de una devolución, para cerrarlo. Si vendes suscripciones, entran además los de renovación, pago fallido y cancelación.
La pregunta que conviene hacerse con cada evento es qué decisión de negocio dispara. Si no dispara ninguna, no hace falta recibirlo. Y si dispara una, esa decisión tiene que estar escrita antes de programar: qué pasa con el acceso, qué correo sale, qué se anota y quién se entera si algo falla.
Cómo guardar los avisos
Merece la pena separar dos registros. Uno, de intentos rechazados: avisos cuya firma no cuadró, con la hora y el motivo, para detectar ataques o errores de configuración. Otro, de eventos verificados: cada evento con su identificador, su tipo, el objeto al que se refiere, cuándo llegó y en qué estado está su procesamiento —pendiente, hecho o con error—.
Con esa tabla, el proceso que hace el trabajo sabe qué le queda pendiente, puede reintentar lo que falló sin volver a pedir nada al servicio y deja rastro de cada acceso que abrió o cerró. Cuando un alumno escriba diciendo que pagó y no ve el curso, la respuesta está en esa tabla: si el evento llegó, si se procesó y qué pasó.
Lo que puede salir mal, y cómo se prepara
Un webhook no es una llamada normal: llega cuando llega, puede repetirse y no respeta un orden. La documentación de Stripe lo dice expresamente, y lo mismo vale, con sus variantes, para otros servicios.
Problemas típicos de un webhook
El mismo aviso llega dos veces
Qué ocurre si no lo preparas: El alumno recibe dos correos de bienvenida o se anota la venta dos veces
Cómo se resuelve: Guardar el identificador del evento con unicidad; en Stripe, para casos raros con dos eventos distintos del mismo hecho, comparar también el objeto y el tipo de evento
Los avisos llegan desordenados
Qué ocurre si no lo preparas: Llega la devolución antes que el pago y la aplicación queda en un estado incoherente
Cómo se resuelve: No depender del orden de entrega; cuando falte contexto, consultar el estado actual del objeto en la API. En Stripe, la fecha de creación del evento no sirve para ordenar
Alguien envía un aviso falso
Qué ocurre si no lo preparas: Se abre un acceso sin pago
Cómo se resuelve: Verificar la firma con el cuerpo original del mensaje; la firma de Stripe lleva una marca de tiempo contra reenvíos
Tu servidor tarda o falla
Qué ocurre si no lo preparas: El aviso se da por perdido o se procesa a medias
Cómo se resuelve: Guardar y responder rápido, procesar después; en modo real, Stripe reintenta durante un máximo de tres días
Llegan eventos que no te importan
Qué ocurre si no lo preparas: Trabajo inútil o errores con datos inesperados
Cómo se resuelve: Suscribirse solo a los eventos necesarios e ignorar el resto con una respuesta correcta
Probarlo en tu computadora
Mientras desarrollas, tu aplicación corre en tu computadora y Stripe no puede llamarla desde internet. Hay dos caminos:
- La CLI de Stripe. El comando
stripe listen --forward-to localhost:4242/webhookrecibe los eventos de prueba y los reenvía a tu servidor local, y te da el secreto de firma para esa sesión. No hace falta registrar ninguna dirección pública. - Un túnel. Herramientas como ngrok dan una dirección pública que apunta a tu computadora; sirve para cualquier servicio que no tenga una herramienta propia. Con una dirección temporal, puede cambiar al reiniciar el túnel y hay que actualizarla en el servicio.
Después se disparan eventos de prueba y se comprueba el recorrido entero: que la firma se verifica, que el evento se guarda una sola vez y que el acceso se abre.
Vamos a montar el webhook de Stripe para el curso. Antes de escribir código, lee la documentación oficial de webhooks de Stripe y propón el plan.
Requisitos:
1. Una ruta /webhooks/stripe que verifique la firma (cabecera Stripe-Signature) con el cuerpo original, sin parsearlo antes. Si la firma falla, responde 400 y registra el intento rechazado aparte.
2. Guarda cada evento verificado en una tabla con su identificador único (restricción de unicidad) antes de responder. Si ya existía, responde 200 sin hacer nada más.
3. Responde 200 en cuanto el evento esté guardado. El trabajo (dar o quitar acceso, enviar el correo) lo hace un proceso aparte que lee los eventos pendientes.
4. No dependas del orden de llegada: antes de dar o quitar acceso, consulta en la API de Stripe el estado actual del pago.
5. Procesa checkout.session.completed y checkout.session.async_payment_succeeded (para métodos de pago diferidos) y da acceso solo si el pago figura como confirmado; procesa charge.refunded y cierra el acceso solo si la devolución es por el importe completo. Ignora el resto con 200.
Comprobación: con stripe listen, dispara un pago y una devolución de prueba, repite el mismo evento dos veces y enséñame que el acceso se abre y se cierra una sola vez.Cómo revisar lo que monta el agente
Un agente de código puede montar este webhook en poco tiempo, pero conviene revisarlo con una lista corta antes de darlo por bueno:
- La firma se comprueba con el cuerpo original. Si el código convierte el mensaje a objeto antes de verificar, la verificación puede fallar o, peor, saltarse.
- El evento se guarda antes de responder. Si responde primero y guarda después, una caída en medio pierde el aviso sin reintento.
- La unicidad está en la base de datos, no solo en el código: dos avisos iguales que llegan a la vez no deben pasar los dos.
- El secreto de firma está en las variables de entorno, nunca escrito en el código ni en el repositorio.
- Hay una prueba que repite el mismo evento y comprueba que el efecto ocurre una sola vez.
Si alguno de estos puntos falla, pídele al agente que lo corrija antes de pasar al siguiente.
Lo que un webhook no hace
Un webhook no garantiza que te enteres de todo: si tu servidor está caído más tiempo del que el servicio reintenta, el aviso se pierde. Por eso, en lo que tiene que ver con dinero, conviene una comprobación periódica que compare tus registros con los del servicio —los pagos del día en Stripe frente a los accesos abiertos—. El webhook te entera al momento; la comprobación periódica cubre los huecos.
Tampoco decide nada por ti: qué hacer con cada evento es lógica de tu negocio, y es lo que conviene escribir con detalle antes de pedírselo a un agente. Si vas a construir con Claude Code, la guía completa de Claude Code explica cómo configurarlo para trabajar así.
Términos relacionados
Formación
Aprende a usar la IA en tu trabajo, con criterio.
El nivel 0 es gratis: cuarenta minutos para entender qué pedirle a la IA y qué revisar. El programa completo enseña a implementarla en tu propio negocio, tarea a tarea.
