Práctica: Implementar un Flujo Completo de Suscripción
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.
Pistas
- 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.