Concepto clave
Implementar un flujo completo de suscripción con Stripe es como gestionar un gimnasio digital. En lugar de fichas de entrada, usas tokens de pago; en lugar de recibos mensuales, generas facturas automáticas; y en lugar de un recepcionista, tienes webhooks que notifican eventos como renovaciones o cancelaciones. El flujo completo abarca desde que el usuario selecciona un plan hasta que recibe su primera factura y se gestionan sus pagos recurrentes.
La arquitectura se basa en tres pilares: Customer (cliente con método de pago), Subscription (suscripción a un plan con ciclo de facturación) y Invoice (factura generada automáticamente). Los webhooks actún como el sistema nervioso, enviando eventos en tiempo real (ej: invoice.paid) para que tu backend sincronice el estado. Es crucial entender que Stripe maneja la lógica de cobro, pero tu aplicación debe gestionar la lógica de negocio (ej: activar acceso al servicio).
Cómo funciona en la práctica
Imagina que implementas suscripciones para una plataforma de cursos online. El flujo paso a paso es:
- Frontend: El usuario selecciona un plan (ej: "Premium - $29/mes") y completa un formulario de pago con Stripe Elements, obteniendo un paymentMethodId.
- Backend: Recibes el paymentMethodId y creas un Customer en Stripe asociándolo. Luego, creas una Subscription para ese cliente al plan deseado.
- Stripe: Genera automáticamente la primera factura (invoice) e intenta cobrarla. Si es exitoso, envía un evento invoice.paid vía webhook.
- Backend: Tu endpoint de webhook recibe invoice.paid, verifica la firma, identifica la suscripción y activa el acceso al curso en tu base de datos.
- Ciclo recurrente: Cada mes, Stripe genera una nueva factura, la cobra y notifica con webhooks. Tu backend actualiza el estado según corresponda (ej: si falla el pago, suspende el acceso).
La clave es que tu backend nunca asume el estado; siempre lo sincroniza desde los webhooks. Por ejemplo, no marques una suscripción como "activa" tras crearla; espera a invoice.paid.
Codigo en accion
Aquí un ejemplo en Node.js con la librería stripe. Primero, crea un cliente y una suscripción:
// Configuración inicial
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// Endpoint para crear suscripción
app.post('/api/subscribe', async (req, res) => {
try {
const { paymentMethodId, planId } = req.body;
// 1. Crear cliente
const customer = await stripe.customers.create({
payment_method: paymentMethodId,
invoice_settings: {
default_payment_method: paymentMethodId
}
});
// 2. Crear suscripción
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ plan: planId }],
expand: ['latest_invoice.payment_intent']
});
res.json({ subscriptionId: subscription.id, status: subscription.status });
} catch (error) {
res.status(400).json({ error: error.message });
}
});Luego, procesa webhooks para manejar eventos. Antes de implementar, así se veía un manejo básico:
// ANTES: Sin verificación de firma (vulnerable)
app.post('/webhook', (req, res) => {
const event = req.body;
if (event.type === 'invoice.paid') {
// Activar acceso
}
res.sendStatus(200);
});Después de refactorizar, con verificación segura:
// DESPUÉS: Con verificación de firma (recomendado)
app.post('/webhook', async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'invoice.paid':
const invoice = event.data.object;
await activateUserAccess(invoice.subscription);
break;
case 'invoice.payment_failed':
const failedInvoice = event.data.object;
await suspendUserAccess(failedInvoice.subscription);
break;
case 'customer.subscription.deleted':
const subscription = event.data.object;
await cancelUserAccess(subscription.id);
break;
}
res.json({ received: true });
});Errores comunes
- No verificar firmas de webhooks: Expones tu backend a eventos falsificados. Siempre usa stripe.webhooks.constructEvent con el secreto.
- Asumir el estado de la suscripción: No confíes en el status devuelto al crear la suscripción; síncroniza desde webhooks para evitar inconsistencias.
- Olvidar expandir recursos: Al crear suscripciones, usa expand para acceder a datos anidados (ej: latest_invoice.payment_intent) sin llamadas adicionales.
- Manejar mal los fallos de pago: No solo escuches invoice.paid; configura retry logic con invoice.payment_failed y notifica al usuario.
- No probar webhooks localmente: Usa el CLI de Stripe para reenviar eventos a tu entorno local y simular flujos completos.
Checklist de dominio
- Puedo crear un Customer con un método de pago por defecto usando la API de Stripe.
- Sé crear una Subscription asociada a un plan y cliente, manejando la respuesta con expand.
- He implementado un endpoint de webhook que verifica firmas y procesa invoice.paid, invoice.payment_failed y customer.subscription.deleted.
- Puedo listar y actualizar suscripciones (ej: cambiar plan, cancelar) mediante la API.
- Entiendo cómo las facturas se generan automáticamente y cómo acceder a su historial.
- He probado el flujo completo localmente usando stripe listen y stripe trigger.
- Sé integrar este flujo con mi base de datos para mantener consistencia entre Stripe y mi estado de negocio.
Implementa un sistema de suscripciones para una SaaS
En este ejercicio, crearás un backend básico que maneje suscripciones recurrentes para una plataforma SaaS ficticia. Sigue estos pasos:
- Configuración inicial: Crea un proyecto Node.js, instala la librería stripe y configura las variables de entorno con tu STRIPE_SECRET_KEY y STRIPE_WEBHOOK_SECRET (usa claves de prueba).
- Endpoint de suscripción: Implementa un endpoint POST /subscribe que reciba paymentMethodId y planId. Dentro:
- Crea un Customer en Stripe con el paymentMethodId como método por defecto.
- Crea una Subscription para ese cliente al plan especificado, expandiendo latest_invoice.payment_intent.
- Devuelve el subscriptionId y status en JSON.
- Webhook handler: Crea un endpoint POST /webhook que:
- Verifique la firma del evento usando stripe.webhooks.constructEvent.
- Procese tres eventos: invoice.paid (registra en consola "Acceso activado para suscripción X"), invoice.payment_failed (registra "Pago fallado para suscripción X"), y customer.subscription.deleted (registra "Suscripción X cancelada").
- Use el campo subscription del objeto invoice para identificar la suscripción.
- Prueba integral: Usa la CLI de Stripe para probar:
- Ejecuta stripe listen --forward-to localhost:3000/webhook para capturar eventos.
- En otra terminal, usa stripe trigger invoice.paid para simular un pago exitoso y verifica que tu endpoint registre el mensaje correcto.
- Extensión opcional: Añade un endpoint GET /subscription/:id que, usando la API de Stripe, recupere los detalles de una suscripción y muestre su status, plan y fecha de próxima factura.
- Usa el método stripe.customers.create con invoice_settings para establecer el payment method por defecto.
- Recuerda que req.rawBody puede ser necesario para webhooks; en Express, usa body-parser con verify para preservar el cuerpo crudo.
- Al expandir latest_invoice.payment_intent, accedes al client_secret para manejar pagos que requieren autenticación adicional.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.