Webhooks de Stripe: configurar, verificar y probar

Guía de webhooks de Stripe

Webhooks de Stripe: endpoint, firma y pruebas con la CLI | We.Inc

Configura un endpoint de webhook de Stripe, verifica Stripe-Signature con el cuerpo sin procesar, prueba con stripe listen y stripe trigger, y completa los pedidos de Checkout una sola vez.

Un webhook de Stripe es un endpoint HTTPS en tu servidor al que Stripe envía por POST el JSON de un evento, como checkout.session.completed tras un pago. Tu handler debe verificar la cabecera Stripe-Signature contra el cuerpo sin procesar e intacto usando el secreto whsec_ del endpoint, devolver un 2xx rápido y tolerar que el mismo evento llegue más de una vez.

Lo esencial de los webhooks de Stripe (comprobado con la documentación de Stripe, octubre de 2026)

La mayoría de las integraciones rotas con Stripe fallan en uno de tres puntos: el framework procesó el cuerpo antes de la verificación, el handler hizo trabajo lento y agotó el tiempo, o un evento reintentado completó un pedido dos veces. Esta guía recorre un handler que evita los tres, con un comprobador abajo que verifica una cabecera Stripe-Signature en tu navegador para que veas exactamente lo que calcula la librería.

Preguntas frecuentes

¿Por qué recibo "No signatures found matching the expected signature"?

Casi siempre porque el cuerpo se modificó antes de la verificación, por ejemplo con express.json() o un body parser que vuelve a codificar el JSON. Monta express.raw({ type: "application/json" }) solo en la ruta del webhook, o lee request.text() en un route handler de Next.js. Un secreto incorrecto provoca el mismo mensaje.

¿Qué eventos necesito para Stripe Checkout?

checkout.session.completed para pagos con tarjeta, más checkout.session.async_payment_succeeded para métodos diferidos como los adeudos bancarios. Stripe también sugiere gestionar checkout.session.async_payment_failed para poder avisar al cliente. Comprueba el payment_status de la sesión antes de completar el pedido.

¿Puedo fiarme de la página de éxito en lugar de un webhook?

No. La propia documentación de Stripe dice que un cliente puede pagar y cerrar la pestaña antes de que cargue tu página de éxito. Usa la página de éxito para una confirmación rápida si quieres, pero el webhook también debe activar la entrega, y ambos caminos deben poder ejecutarse dos veces sin problema.

¿Los eventos de Stripe llegan en orden?

Stripe no garantiza el orden. invoice.paid puede llegar antes que customer.subscription.created. Registra los ids de evento para detectar duplicados y obtén el objeto relacionado desde la API cuando necesites su estado más reciente.

¿Cómo reenvío un evento fallido?

Haz clic en Resend en el evento dentro del Dashboard (funciona hasta 15 días después de su creación) o ejecuta stripe events resend <event_id> --webhook-endpoint=<endpoint_id> con la CLI (hasta 30 días). Los reintentos automáticos continúan de todos modos.

La regla del cuerpo sin procesar

Responde primero, trabaja después

Completa el pedido exactamente una vez

Código del handler

Express con stripe-node

express.raw se monta solo en esta ruta, así el cuerpo llega a constructEvent con los bytes originales.

Route handler del App Router de Next.js

request.text() da el cuerpo sin procesar. Si constructEvent lanza un error, la firma o el secreto son incorrectos.

Pruebas con la CLI de Stripe

Reenviar y lanzar eventos en local

listen muestra un secreto whsec_ para esta sesión. Ponlo en tu entorno local y luego lanza eventos en una segunda terminal.

Eventos que conviene gestionar

Tipos de evento comunes de Stripe y qué hacer

Comprobador de Stripe-Signature

Pega una cabecera Stripe-Signature, el cuerpo sin procesar y tu secreto whsec_ para ver la misma comprobación que hacen las librerías. Se ejecuta en tu navegador con Web Crypto y no envía nada. Usa un secreto de modo de prueba, nunca uno de producción.

Cabecera Stripe-Signature

Cuerpo de la petición sin procesar

Secreto del endpoint (whsec_...)

Tolerancia en segundos

Firmar este cuerpo ahora

Verificar

Resultado

La cabecera no tiene timestamp t=.

La cabecera no tiene firma v1=.

v1 esperado: {v}

La firma coincide.

La firma no coincide: el cuerpo se modificó o el secreto es incorrecto.

El timestamp tiene {n} segundos, dentro de la tolerancia.

El timestamp tiene {n} segundos, fuera de la tolerancia: una librería lo rechazaría.

Empieza gratis · Sin tarjeta de crédito

Relacionado: Guías

Producto

Para quién es

Funciones

Recursos

Empresa

Mapa del sitio