Concepto clave: Suscripciones y Facturación con Stripe
Stripe es una plataforma de pagos que permite a las empresas gestionar transacciones en línea de manera segura y escalable. Para implementar suscripciones y facturación, Stripe ofrece dos APIs principales: Subscriptions API y Billing API. La Subscriptions API se encarga de crear y gestionar planes recurrentes, mientras que la Billing API maneja facturas, créditos y descuentos. Además, los webhooks son endpoints en tu servidor que reciben notificaciones automáticas de eventos importantes en Stripe, como pagos exitosos o fallidos, lo que permite mantener sincronizada tu base de datos con el estado real de las transacciones.
Una analogía del mundo real sería un gimnasio que ofrece membresías mensuales. Stripe actúa como el sistema de cobro automático que cada mes debita la tarjeta del cliente, genera una factura y notifica al gimnasio si el pago fue exitoso o si hubo un problema. Sin webhooks, el gimnasio tendría que revisar manualmente cada transacción, lo que sería ineficiente y propenso a errores.
Cómo funciona en la práctica: Flujo de una suscripción
El proceso típico para implementar una suscripción con Stripe sigue estos pasos: 1) Configurar un producto y un precio recurrente en el panel de Stripe o mediante API. 2) Crear un cliente en Stripe asociado a un método de pago. 3) Suscribir al cliente a un plan usando la Subscriptions API. 4) Configurar webhooks para escuchar eventos como invoice.payment_succeeded. 5) Procesar las notificaciones en tu backend para actualizar el estado del usuario.
Por ejemplo, al suscribir un cliente, Stripe genera automáticamente una factura inicial y intenta cobrarla. Si el pago es exitoso, envía un webhook a tu servidor, permitiéndote activar el acceso del usuario a tu servicio. Si falla, puedes configurar reintentos o notificar al cliente.
Código en acción: Crear una suscripción con Node.js
Aquí tienes un ejemplo funcional usando la biblioteca oficial de Stripe para Node.js. Este código crea un cliente y lo suscribe a un plan predefinido.
const stripe = require('stripe')('sk_test_tu_clave_secreta');
async function createSubscription(customerEmail, paymentMethodId, priceId) {
try {
// Paso 1: Crear un cliente
const customer = await stripe.customers.create({
email: customerEmail,
payment_method: paymentMethodId,
invoice_settings: {
default_payment_method: paymentMethodId,
},
});
// Paso 2: Crear la suscripción
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [
{ price: priceId },
],
expand: ['latest_invoice.payment_intent'],
});
console.log('Suscripción creada:', subscription.id);
return subscription;
} catch (error) {
console.error('Error al crear suscripción:', error);
throw error;
}
}
// Uso: createSubscription('[email protected]', 'pm_card_visa', 'price_abc123');Antes de refactorizar, el código podría mezclar lógica de cliente y suscripción en una sola función. Después, puedes separar responsabilidades para mejorar mantenibilidad.
// Refactorizado: Separar creación de cliente y suscripción
async function createStripeCustomer(email, paymentMethodId) {
return await stripe.customers.create({
email,
payment_method: paymentMethodId,
invoice_settings: { default_payment_method: paymentMethodId },
});
}
async function subscribeCustomer(customerId, priceId) {
return await stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
});
}
// Uso combinado
async function handleSubscription(email, paymentMethodId, priceId) {
const customer = await createStripeCustomer(email, paymentMethodId);
const subscription = await subscribeCustomer(customer.id, priceId);
return { customer, subscription };
}Errores comunes
- No validar webhooks: Aceptar eventos sin verificar la firma de Stripe puede exponer tu sistema a ataques. Siempre usa
stripe.webhooks.constructEventpara validar. - Ignorar estados de suscripción: Asumir que una suscripción está activa sin verificar estados como
active,past_dueocanceledpuede llevar a dar acceso indebido. - No manejar fallos de pago: No configurar reintentos automáticos o notificaciones para pagos fallidos resulta en pérdida de ingresos y clientes insatisfechos.
- Exponer claves secretas: Incluir
sk_testosk_liveen el frontend o en repositorios públicos compromete la seguridad. Usa variables de entorno. - No probar en modo test: Implementar directamente en producción sin probar con tarjetas de prueba de Stripe puede causar errores costosos.
Checklist de dominio
- Configurar un producto y precio recurrente en Stripe Dashboard o via API.
- Crear un cliente con método de pago predeterminado.
- Suscribir un cliente a un plan y manejar la respuesta.
- Configurar un endpoint de webhook y validar eventos.
- Procesar eventos comunes como
invoice.payment_succeededycustomer.subscription.deleted. - Manejar fallos de pago con reintentos y notificaciones.
- Probar el flujo completo con tarjetas de prueba de Stripe.
Implementar un webhook básico para suscripciones
En este ejercicio, crearás un endpoint de webhook en Node.js que escuche eventos de Stripe y actualice una base de datos simulada. Sigue estos pasos:
- Configura un servidor Express básico con un endpoint POST en
/webhook. - Usa la biblioteca de Stripe para validar la firma del webhook con tu clave de firma.
- Maneja el evento
invoice.payment_succeededpara registrar el pago en un objeto simulado de base de datos. - Maneja el evento
customer.subscription.deletedpara marcar la suscripción como cancelada. - Prueba el webhook usando la CLI de Stripe o el panel de desarrolladores.
Ejemplo de estructura inicial:
const express = require('express');
const stripe = require('stripe')('sk_test_tu_clave');
const app = express();
app.use(express.json());
let database = { subscriptions: {} };
app.post('/webhook', async (req, res) => {
// Implementa aquí
});
app.listen(3000, () => console.log('Webhook corriendo en puerto 3000'));
Pistas- Recuerda obtener tu clave de firma de webhook desde el panel de Stripe.
- Usa stripe.webhooks.constructEvent para validar el payload y la firma.
- Accede al tipo de evento con event.type y a los datos con event.data.object.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.