--- id: "SAAS-004" title: "Billing y Suscripciones" type: "Module" status: "Published" priority: "P0" module: "billing" version: "1.0.0" created_date: "2026-01-07" updated_date: "2026-01-10" --- # SAAS-004: Billing y Suscripciones ## Metadata - **Codigo:** SAAS-004 - **Modulo:** Billing - **Prioridad:** P0 - **Estado:** Completado - **Fase:** 2 - Billing ## Descripcion Sistema de facturacion completo con Stripe: suscripciones recurrentes, trials, upgrades/downgrades, facturas, y webhooks para sincronizacion. ## Objetivos 1. Integracion completa con Stripe 2. Suscripciones mensuales/anuales 3. Trial period (14 dias) 4. Upgrade/downgrade de plan 5. Facturas y recibos ## Alcance ### Incluido - Stripe Checkout - Suscripciones recurrentes - Trial gratuito - Cambio de plan (prorateo) - Portal de cliente Stripe - Webhooks de Stripe - Historial de facturas ### Excluido - Metered billing - fase posterior - Multiples metodos de pago guardados - Facturacion fiscal mexicana ## Modelo de Datos ### Tablas (schema: billing) **subscriptions** | Columna | Tipo | Descripcion | |---------|------|-------------| | id | UUID | PK | | tenant_id | UUID | FK -> tenants.tenants (unique) | | plan_id | UUID | FK -> plans.plans | | stripe_subscription_id | VARCHAR | ID en Stripe | | stripe_customer_id | VARCHAR | Customer ID Stripe | | status | subscription_status | Estado (ver enums) | | current_period_start | TIMESTAMP | Inicio periodo | | current_period_end | TIMESTAMP | Fin periodo | | trial_start | TIMESTAMP | Inicio trial | | trial_end | TIMESTAMP | Fin trial | | cancel_at | TIMESTAMP | Fecha programada cancelacion | | canceled_at | TIMESTAMP | Fecha real cancelacion | | cancel_reason | VARCHAR | Razon cancelacion | | price_amount | DECIMAL | Precio del plan | | currency | VARCHAR | Moneda (USD, MXN) | | metadata | JSONB | Metadata adicional | **invoices** | Columna | Tipo | Descripcion | |---------|------|-------------| | id | UUID | PK | | tenant_id | UUID | FK -> tenants.tenants | | subscription_id | UUID | FK -> billing.subscriptions | | stripe_invoice_id | VARCHAR | ID en Stripe | | invoice_number | VARCHAR | Numero (INV-YYYYMM-XXXXXX) | | subtotal | DECIMAL | Subtotal | | tax | DECIMAL | Impuestos | | discount | DECIMAL | Descuento aplicado | | total | DECIMAL | Total | | amount_paid | DECIMAL | Monto pagado | | amount_due | DECIMAL | Monto pendiente | | currency | VARCHAR | Moneda | | status | invoice_status | draft, open, paid, void, uncollectible | | invoice_date | DATE | Fecha factura | | due_date | DATE | Fecha vencimiento | | period_start | TIMESTAMP | Inicio periodo facturado | | period_end | TIMESTAMP | Fin periodo facturado | | paid_at | TIMESTAMP | Fecha de pago | | invoice_pdf_url | VARCHAR | URL PDF factura | | hosted_invoice_url | VARCHAR | URL Stripe hosted | | customer_name | VARCHAR | Nombre cliente | | customer_email | VARCHAR | Email cliente | | billing_address | JSONB | Direccion facturacion | **payment_methods** | Columna | Tipo | Descripcion | |---------|------|-------------| | id | UUID | PK | | tenant_id | UUID | FK -> tenants.tenants | | type | payment_method_type | card, bank_transfer, oxxo | | provider | VARCHAR | stripe, conekta, etc. | | external_payment_method_id | VARCHAR | ID en proveedor | | card_brand | VARCHAR | Visa, Mastercard, Amex | | card_last4 | VARCHAR | Ultimos 4 digitos | | card_exp_month | INTEGER | Mes expiracion | | card_exp_year | INTEGER | Año expiracion | | is_default | BOOLEAN | Metodo por defecto | | created_at | TIMESTAMP | Fecha creacion | ## Enums de Billing ### tenants.subscription_status Estado general del tenant respecto a su suscripcion (usado en tenant context): | Valor | Descripcion | |-------|-------------| | trialing | En periodo de prueba | | active | Suscripcion activa | | past_due | Pago atrasado | | cancelled | Cancelado | | unpaid | Sin pago | ### billing.subscription_status Estado interno del sistema de billing: | Valor | Descripcion | |-------|-------------| | trial | En trial | | active | Activo | | past_due | Pago atrasado | | cancelled | Cancelado | | expired | Expirado | **Nota:** El tenant usa `tenants.subscription_status` para el estado visible. El modulo billing usa `billing.subscription_status` internamente. ## Integracion Stripe ### Productos y Precios ```typescript // Configurados en Stripe Dashboard const STRIPE_PRODUCTS = { starter: { monthly: 'price_xxx_monthly', yearly: 'price_xxx_yearly' }, pro: { monthly: 'price_yyy_monthly', yearly: 'price_yyy_yearly' } }; ``` ### Webhooks Manejados - `customer.subscription.created` - `customer.subscription.updated` - `customer.subscription.deleted` - `invoice.paid` - `invoice.payment_failed` - `customer.updated` ## Endpoints API | Metodo | Endpoint | Descripcion | |--------|----------|-------------| | GET | /billing/plans | Planes disponibles | | GET | /billing/subscription | Suscripcion actual | | POST | /billing/checkout | Crear sesion checkout | | POST | /billing/portal | URL portal de cliente | | PUT | /billing/change-plan | Cambiar plan | | POST | /billing/cancel | Cancelar suscripcion | | POST | /billing/resume | Reanudar suscripcion | | GET | /billing/invoices | Historial facturas | | GET | /billing/invoices/:id/pdf | Descargar factura | | POST | /billing/webhook | Webhook Stripe | ## Flujos ### Nueva Suscripcion ``` 1. Usuario selecciona plan 2. Click "Suscribirse" 3. Backend crea Checkout Session 4. Usuario redirigido a Stripe Checkout 5. Usuario completa pago 6. Stripe envia webhook 7. Backend actualiza suscripcion 8. Usuario redirigido a app ``` ### Upgrade de Plan ``` 1. Usuario en plan Starter 2. Click "Upgrade a Pro" 3. Backend llama Stripe API 4. Stripe calcula prorateo 5. Se cobra diferencia 6. Plan actualizado inmediatamente 7. Features Pro desbloqueadas ``` ### Trial Expirando ``` Dia -3: Email "Tu trial termina en 3 dias" Dia -1: Email "Tu trial termina manana" Dia 0: Email "Tu trial ha terminado" Si no hay pago: downgrade a Free ``` ## Estados de Suscripcion ``` trialing ──► active ──► past_due ──► cancelled │ └──► paused ``` | Estado | Acceso | Descripcion | |--------|--------|-------------| | trialing | Completo | En periodo de prueba | | active | Completo | Pagando | | past_due | Limitado | Pago fallido (grace) | | cancelled | Sin acceso | Cancelada | | paused | Sin acceso | Pausada | ## Entregables | Entregable | Estado | Archivo | |------------|--------|---------| | billing.module.ts | Completado | `modules/billing/` | | stripe.service.ts | Completado | `services/` | | billing.controller.ts | Completado | `controllers/` | | DDL billing schema | Completado | `ddl/schemas/billing/` | ## Dependencias ### Depende de - SAAS-001 (Auth) - SAAS-002 (Tenants) - SAAS-005 (Plans) ### Bloquea a - Ninguno (pero habilita features por plan) ## Criterios de Aceptacion - [x] Checkout funciona - [x] Webhooks se procesan - [x] Upgrade/downgrade funciona - [x] Facturas se generan - [x] Trial funciona - [x] Cancelacion funciona ## Configuracion ```typescript { stripe: { secretKey: process.env.STRIPE_SECRET_KEY, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET, portalReturnUrl: 'https://app.example.com/settings/billing' }, trial: { days: 14, requiresCard: false } } ``` --- **Ultima actualizacion:** 2026-01-10