Concepto clave
Los webhooks son mecanismos de comunicacion asincrona que permiten a Stripe notificar a tu aplicacion sobre eventos importantes en tiempo real. Imagina que tienes un restaurante donde los clientes piden comida por telefono: el telefono sonando es el webhook, y tu respuesta al pedido es el manejo del evento. Sin webhooks, tendrias que llamar constantemente a la cocina para preguntar si hay pedidos nuevos, lo que es ineficiente.
En el contexto de Stripe, los webhooks son esenciales porque muchos eventos criticos ocurren fuera del flujo directo de tu codigo, como cuando un cliente cancela una suscripcion desde su portal o cuando falla un pago recurrente. Sin webhooks, tu base de datos podria quedar desincronizada con el estado real en Stripe, llevando a errores como mostrar suscripciones activas que en realidad estan canceladas.
Como funciona en la practica
El proceso comienza cuando configuras un endpoint en tu servidor (por ejemplo, /api/webhooks/stripe) y lo registras en el dashboard de Stripe. Cuando ocurre un evento, como customer.subscription.deleted, Stripe envia una solicitud HTTP POST a tu endpoint con todos los detalles del evento en formato JSON. Tu backend debe verificar la firma del webhook para asegurar que proviene de Stripe, luego procesar el evento segun tu logica de negocio.
Paso a paso: 1) Crea un endpoint seguro en tu aplicacion, 2) Obtiene tu webhook secret desde el dashboard de Stripe, 3) Configura los eventos a escuchar (como pagos exitosos o fallidos), 4) Implementa la verificacion de firma, 5) Maneja los eventos actualizando tu base de datos o enviando notificaciones. Por ejemplo, al recibir invoice.payment_succeeded, podrias actualizar la fecha de renovacion de una suscripcion en tu sistema.
Codigo en accion
Aqui tienes un ejemplo basico en Node.js usando Express y la libreria oficial de Stripe. Primero, el codigo sin verificacion de firma (vulnerable):
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const event = req.body; // ¡Peligro! No verificado
switch (event.type) {
case 'invoice.payment_succeeded':
console.log('Pago exitoso:', event.data.object.id);
break;
default:
console.log(`Evento no manejado: ${event.type}`);
}
res.json({received: true});
});
app.listen(3000, () => console.log('Servidor en puerto 3000'));Ahora, el codigo corregido con verificacion de firma (seguro):
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
// Leer el webhook secret desde variables de entorno
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
console.error('Error de verificacion:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'invoice.payment_succeeded':
const invoice = event.data.object;
// Actualizar base de datos con invoice.subscription
console.log('Pago procesado para suscripcion:', invoice.subscription);
break;
case 'customer.subscription.deleted':
const subscription = event.data.object;
// Desactivar acceso del usuario en tu sistema
console.log('Suscripcion cancelada:', subscription.id);
break;
default:
console.log(`Evento ${event.type} recibido, no accion requerida.`);
}
res.json({received: true});
});
app.listen(3000, () => console.log('Webhook listo en puerto 3000'));Errores comunes
- No verificar la firma del webhook: Esto expone tu endpoint a ataques donde actores maliciosos envian eventos falsos. Siempre usa
stripe.webhooks.constructEvento su equivalente en tu lenguaje. - Manejar eventos duplicados: Stripe puede reenviar webhooks si no responde con un 2xx rapido. Implementa idempotencia guardando IDs de eventos procesados para evitar acciones repetidas como enviar dos emails por un solo pago.
- No probar webhooks localmente: Usa la CLI de Stripe para reenviar eventos a tu maquina local (
stripe listen --forward-to localhost:3000/webhook) antes de desplegar a produccion. - Olvidar eventos criticos: No solo escuches eventos de exito. Maneja
invoice.payment_failedpara notificar a usuarios ycustomer.subscription.updatedpara cambios de plan. - Logs insuficientes: Registra cada evento recibido y cualquier error en el procesamiento para facilitar debugging en produccion.
Checklist de dominio
- Configurar un endpoint HTTPS en tu servidor que acepte POST y registrarlo en el dashboard de Stripe.
- Implementar verificacion de firma usando el webhook secret para asegurar autenticidad.
- Manejar al menos 3 eventos clave: pagos exitosos, pagos fallidos y cancelaciones de suscripcion.
- Probar webhooks localmente con la CLI de Stripe o usando entornos de prueba.
- Agregar logica idempotente para evitar duplicados basada en IDs de eventos.
- Actualizar tu base de datos o estado interno consistentemente al procesar eventos.
- Monitorear fallos de webhooks (Stripe los reintenta hasta 3 dias) y tener alertas configuradas.
Implementa un endpoint de webhook para manejar suscripciones
En este ejercicio, crearas un endpoint de webhook funcional que maneje eventos de suscripciones de Stripe. Usa el codigo de ejemplo como base y extiendelo para un caso real.
- Configura el entorno: Crea un proyecto Node.js con Express e instala la libreria de Stripe (
npm install stripe express). Configura variables de entorno paraSTRIPE_SECRET_KEYySTRIPE_WEBHOOK_SECRET(obten el secret desde el dashboard de Stripe en modo test). - Implementa el endpoint: Crea un archivo
server.jscon un endpoint POST en/webhookque verifique la firma usandostripe.webhooks.constructEvent. Asegurate de usarexpress.rawpara el body. - Maneja eventos especificos: Agrega logica para:
customer.subscription.created: Registrar una nueva suscripcion en un array en memoria (simulando base de datos).invoice.payment_failed: Loggear un mensaje de error e incrementar un contador de intentos fallidos.customer.subscription.deleted: Remover la suscripcion del array y loggear la razon (event.data.object.cancellation_details.reason).
- Prueba con la CLI: Instala la CLI de Stripe y ejecuta
stripe listen --forward-to localhost:3000/webhook. En otra terminal, usastripe trigger customer.subscription.createdpara enviar un evento y verifica que tu endpoint lo maneje correctamente. - Agrega idempotencia: Implementa un Set en memoria para guardar IDs de eventos procesados (
event.id). Antes de procesar, verifica si el ID ya existe y omite el evento si es duplicado.
- Recuerda que el body debe leerse en formato raw, no JSON, para la verificacion de firma. Usa express.raw({type: 'application/json'}) en la ruta.
- Puedes simular una base de datos con un objeto global como let subscriptions = []; para almacenar suscripciones creadas.
- Usa console.log para imprimir detalles de cada evento y verificar que tu codigo se ejecuta correctamente durante las pruebas.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.