Práctica: Implementar un Flujo Completo de Suscripción

Lectura
30 min~5 min lectura

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:

  1. Frontend: El usuario selecciona un plan (ej: "Premium - $29/mes") y completa un formulario de pago con Stripe Elements, obteniendo un paymentMethodId.
  2. Backend: Recibes el paymentMethodId y creas un Customer en Stripe asociándolo. Luego, creas una Subscription para ese cliente al plan deseado.
  3. Stripe: Genera automáticamente la primera factura (invoice) e intenta cobrarla. Si es exitoso, envía un evento invoice.paid vía webhook.
  4. 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.
  5. 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

  1. Puedo crear un Customer con un método de pago por defecto usando la API de Stripe.
  2. Sé crear una Subscription asociada a un plan y cliente, manejando la respuesta con expand.
  3. He implementado un endpoint de webhook que verifica firmas y procesa invoice.paid, invoice.payment_failed y customer.subscription.deleted.
  4. Puedo listar y actualizar suscripciones (ej: cambiar plan, cancelar) mediante la API.
  5. Entiendo cómo las facturas se generan automáticamente y cómo acceder a su historial.
  6. He probado el flujo completo localmente usando stripe listen y stripe trigger.
  7. 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:

  1. 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).
  2. 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.
  3. 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.
  4. 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.
  5. 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.