template-saas/docs/architecture/adr/ADR-003-billing-stripe.md
rckrdmrd 50a821a415
Some checks failed
CI / Backend CI (push) Has been cancelled
CI / Frontend CI (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / CI Summary (push) Has been cancelled
[SIMCO-V38] feat: Actualizar a SIMCO v3.8.0
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Actualizaciones de configuracion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:08 -06:00

5.2 KiB

id title type status priority supersedes superseded_by version created_date updated_date
ADR-003 Billing Integration con Stripe ADR Accepted P0 N/A N/A 1.0.0 2026-01-07 2026-01-10

ADR-003: Billing Integration con Stripe

Metadata

Campo Valor
ID ADR-003
Estado Accepted
Fecha 2026-01-10
Supersede N/A

Contexto

Template SaaS necesita un sistema de billing para:

  • Planes de suscripción (mensual/anual)
  • Trial periods
  • Múltiples métodos de pago
  • Facturación automática
  • Portal de autoservicio
  • Webhooks para sincronización

Opciones Consideradas

Opción 1: Implementación Custom

Descripción: Sistema de billing propio conectado a pasarelas de pago.

Pros:

  • Control total
  • Sin comisiones de plataforma
  • Personalización completa

Contras:

  • Complejidad extrema (PCI compliance)
  • Time-to-market largo
  • Mantenimiento costoso
  • Riesgos de seguridad

Opción 2: Stripe ✓

Descripción: Plataforma de pagos completa con Billing.

Pros:

  • Rápido de implementar
  • PCI compliance incluido
  • Múltiples países/monedas
  • Portal de cliente built-in
  • Webhooks robustos
  • Excelente documentación

Contras:

  • Comisiones (~2.9% + 30¢)
  • Dependencia de tercero
  • Algunas limitaciones en personalización

Opción 3: Paddle/Lemonsqueezy

Descripción: Merchant of Record (MoR) completo.

Pros:

  • Maneja impuestos automáticamente
  • Sin necesidad de entidad legal por país
  • Más simple que Stripe

Contras:

  • Comisiones más altas (~5-8%)
  • Menos control
  • Menos features

Decisión

Elegimos Stripe por:

  1. Balance: Buen equilibrio entre control y simplicidad
  2. Features: Billing, Invoicing, Customer Portal, Tax
  3. Ecosistema: SDKs, webhooks, testing, documentación
  4. Escalabilidad: Crece con el negocio
  5. Confiabilidad: Uptime excelente, soporte 24/7

Implementación

Arquitectura

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Frontend  │────▶│   Backend   │────▶│   Stripe    │
└─────────────┘     └─────────────┘     └─────────────┘
                           │                    │
                           │◀───────────────────┘
                           │      Webhooks
                    ┌──────▼──────┐
                    │  PostgreSQL │
                    └─────────────┘

Modelos de Datos

// Local (sincronizado con Stripe)
Subscription {
  id: UUID
  tenant_id: UUID
  stripe_subscription_id: string
  stripe_customer_id: string
  plan_id: UUID
  status: 'trialing' | 'active' | 'past_due' | 'cancelled'
  current_period_start: Date
  current_period_end: Date
}

Plan {
  id: UUID
  stripe_product_id: string
  stripe_price_id_monthly: string
  stripe_price_id_yearly: string
  name: string
  price_monthly: number
  price_yearly: number
  features: JSON
  limits: JSON
}

Flujo de Suscripción

1. Usuario selecciona plan
2. Backend crea Checkout Session
3. Redirect a Stripe Checkout
4. Usuario completa pago
5. Stripe envía webhook 'checkout.session.completed'
6. Backend crea/actualiza Subscription local
7. Redirect a success page

Webhooks Críticos

Evento Acción
checkout.session.completed Crear suscripción
customer.subscription.updated Actualizar estado
customer.subscription.deleted Marcar cancelada
invoice.paid Registrar pago
invoice.payment_failed Notificar, retry

Customer Portal

Stripe Customer Portal permite:

  • Ver/descargar facturas
  • Actualizar método de pago
  • Cambiar plan
  • Cancelar suscripción
// Crear session del portal
const session = await stripe.billingPortal.sessions.create({
  customer: tenant.stripe_customer_id,
  return_url: 'https://app.example.com/billing',
});

Plan Limits Enforcement

// Verificar límite antes de acción
async canAddUser(tenantId: string): Promise<boolean> {
  const subscription = await this.getSubscription(tenantId);
  const plan = await this.getPlan(subscription.plan_id);
  const currentUsers = await this.countUsers(tenantId);

  return currentUsers < plan.limits.max_users;
}

Consecuencias

Positivas

  • Time-to-market rápido
  • Sin preocupaciones PCI
  • Billing Portal listo para usar
  • Webhooks confiables
  • Soporte multi-moneda

Negativas

  • Comisiones por transacción
  • Dependencia de Stripe
  • Sincronización de datos necesaria
  • Algunas features requieren Stripe Tax adicional

Testing

  • Stripe Test Mode para desarrollo
  • Webhook testing con Stripe CLI
  • Cards de prueba documentadas

Referencias


Fecha decision: 2026-01-10 Autores: Claude Code (Arquitectura)