---
id: "RF-PAY-001"
title: "Sistema de Planes y Suscripciones"
type: "Requirement"
status: "Done"
priority: "Alta"
epic: "OQI-005"
project: "trading-platform"
version: "1.0.0"
created_date: "2025-12-05"
updated_date: "2026-01-04"
---

# RF-PAY-001: Sistema de Planes y Suscripciones

**Version:** 1.0.0
**Fecha:** 2025-12-05
**Estado:** ✅ Implementado
**Prioridad:** P0 (Crítica)
**Story Points:** 8
**Épica:** [OQI-005](../_MAP.md)

---

## Descripción

El sistema debe permitir a los usuarios suscribirse a planes mensuales de pago recurrente integrados con Stripe Subscriptions, otorgando acceso diferenciado a funcionalidades premium según el nivel del plan.

---

## Objetivo de Negocio

- Generar ingresos recurrentes predecibles (MRR)
- Segmentar usuarios por valor (freemium → premium)
- Reducir churn con facturación automática
- Facilitar upgrades/downgrades entre planes

---

## Planes Disponibles

| Plan | Precio | Price ID Stripe | Características |
|------|--------|-----------------|-----------------|
| Free | $0/mes | - | Predicciones básicas (5/día), cursos introductorios |
| Basic | $19/mes | `price_1Sb3k64dPtEGmLmpeAdxvmIu` | Predicciones básicas ilimitadas, todos los cursos básicos |
| Pro | $49/mes | `price_1Sb3k64dPtEGmLmpm5n5bbJH` | Predicciones avanzadas, todos los cursos, indicadores técnicos |
| Premium | $99/mes | `price_1Sb3k74dPtEGmLmpHfLpUkvQ` | Todo incluido, soporte prioritario, análisis personalizados |

---

## Requisitos Funcionales

### RF-PAY-001.1: Creación de Suscripción

**DEBE:**
1. Crear Customer en Stripe si no existe
2. Adjuntar método de pago al Customer
3. Crear Subscription con el Price ID correspondiente
4. Guardar registro en tabla `billing.subscriptions`
5. Sincronizar estado inicial (`active`, `incomplete`, `trialing`)

### RF-PAY-001.2: Gestión de Método de Pago

**DEBE:**
1. Permitir agregar/actualizar tarjeta de crédito
2. Soportar Stripe Elements para PCI compliance
3. Guardar PaymentMethod como default del Customer
4. Mostrar últimos 4 dígitos y marca de tarjeta

### RF-PAY-001.3: Cambio de Plan (Upgrade/Downgrade)

**DEBE:**
1. Permitir cambiar de plan en cualquier momento
2. Proration automática de Stripe:
   - **Upgrade:** Cobrar diferencia prorrateada inmediatamente
   - **Downgrade:** Aplicar crédito para próximo ciclo
3. Actualizar `subscription.plan` en BD
4. Mantener `currentPeriodEnd` original

### RF-PAY-001.4: Cancelación de Suscripción

**DEBE:**
1. Permitir cancelar en cualquier momento
2. Mantener acceso hasta `currentPeriodEnd`
3. Marcar `cancelAtPeriodEnd = true`
4. Enviar email de confirmación de cancelación
5. No reembolsar período actual

### RF-PAY-001.5: Renovación Automática

**DEBE:**
1. Stripe intenta cobro automático en `currentPeriodEnd`
2. Si falla, reintentar según configuración Stripe (3 días)
3. Actualizar estado a `past_due` si falla
4. Enviar email de fallo de pago
5. Cancelar automáticamente si falla después de reintentos

### RF-PAY-001.6: Período de Prueba (Trial)

**DEBE:**
1. Ofrecer 7 días gratis en primer suscripción Pro/Premium
2. Crear suscripción con `trial_end` timestamp
3. No cobrar hasta que termine trial
4. Permitir cancelar durante trial sin cargo
5. Convertir a `active` automáticamente al finalizar trial

---

## Flujo de Creación de Suscripción

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Usuario   │     │  Frontend   │     │   Backend   │     │   Stripe    │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                   │                   │                   │
       │ Selecciona plan   │                   │                   │
       │ "Pro"             │                   │                   │
       │──────────────────▶│                   │                   │
       │                   │                   │                   │
       │                   │ POST /payments/   │                   │
       │                   │ subscriptions     │                   │
       │                   │ { plan: "pro",    │                   │
       │                   │   paymentMethodId }│                  │
       │                   │──────────────────▶│                   │
       │                   │                   │                   │
       │                   │                   │ 1. Get/Create     │
       │                   │                   │    Customer       │
       │                   │                   │──────────────────▶│
       │                   │                   │◀──────────────────│
       │                   │                   │ customer_id       │
       │                   │                   │                   │
       │                   │                   │ 2. Attach         │
       │                   │                   │    PaymentMethod  │
       │                   │                   │──────────────────▶│
       │                   │                   │◀──────────────────│
       │                   │                   │                   │
       │                   │                   │ 3. Create         │
       │                   │                   │    Subscription   │
       │                   │                   │──────────────────▶│
       │                   │                   │◀──────────────────│
       │                   │                   │ subscription      │
       │                   │                   │                   │
       │                   │                   │ 4. Save to DB     │
       │                   │                   │ (subscriptions)   │
       │                   │                   │                   │
       │                   │◀──────────────────│                   │
       │                   │ { subscription,   │                   │
       │                   │   status: "active"}│                  │
       │                   │                   │                   │
       │◀──────────────────│                   │                   │
       │ "Suscripción      │                   │                   │
       │ activada!"        │                   │                   │
       │                   │                   │                   │
```

---

## Reglas de Negocio

### RN-001: Un Plan Activo por Usuario

- Un usuario solo puede tener **1 suscripción activa** a la vez
- Cambiar plan cancela la anterior y crea una nueva
- Free plan no genera registro en `subscriptions`

### RN-002: Acceso Según Plan

| Recurso | Free | Basic | Pro | Premium |
|---------|------|-------|-----|---------|
| Predicciones básicas | 5/día | ∞ | ∞ | ∞ |
| Predicciones avanzadas | ❌ | ❌ | ✅ | ✅ |
| Cursos básicos | 3 | ✅ | ✅ | ✅ |
| Cursos avanzados | ❌ | ❌ | ✅ | ✅ |
| Indicadores técnicos | ❌ | ❌ | ✅ | ✅ |
| Soporte prioritario | ❌ | ❌ | ❌ | ✅ |
| Análisis personalizados | ❌ | ❌ | ❌ | ✅ |

### RN-003: Cambio de Plan

**Upgrade (Free → Basic, Basic → Pro, etc.):**
- Aplicar inmediatamente
- Cobrar diferencia prorrateada
- Extender `currentPeriodEnd` proporcionalmente

**Downgrade (Premium → Pro, Pro → Basic, etc.):**
- Aplicar al finalizar período actual
- Generar crédito para próximo ciclo
- Notificar fecha de cambio efectivo

### RN-004: Cancelación y Reactivación

**Cancelación:**
- Mantener acceso hasta `currentPeriodEnd`
- Marcar `cancelAtPeriodEnd = true`
- No permitir reactivación automática después de `currentPeriodEnd`

**Reactivación:**
- Solo permitir si `cancelAtPeriodEnd = true` y antes de `currentPeriodEnd`
- Marcar `cancelAtPeriodEnd = false` en Stripe

---

## Estados de Suscripción

| Estado | Descripción | Acceso |
|--------|-------------|--------|
| `active` | Suscripción activa y al corriente | ✅ Completo |
| `trialing` | En período de prueba | ✅ Completo |
| `past_due` | Pago falló, en reintentos | ⚠️ Limitado |
| `canceled` | Cancelada por usuario o falta de pago | ❌ Free tier |
| `incomplete` | Pago inicial falló | ❌ Sin acceso |
| `incomplete_expired` | Pago falló y expiró (24h) | ❌ Sin acceso |
| `unpaid` | Todos los reintentos fallaron | ❌ Sin acceso |

---

## Manejo de Errores

| Error | Código | Mensaje Usuario | Acción |
|-------|--------|-----------------|--------|
| Tarjeta declinada | 402 | Tu tarjeta fue rechazada. Intenta con otra. | Solicitar nuevo método |
| Customer no existe | 404 | Error de configuración. Contacta soporte. | Crear Customer |
| Ya tiene suscripción | 409 | Ya tienes una suscripción activa. Cancela primero. | Redirigir a /settings |
| Plan inválido | 400 | El plan seleccionado no existe. | Mostrar planes válidos |
| Stripe API error | 502 | Error de procesamiento. Intenta más tarde. | Retry con backoff |

---

## Seguridad

### Validaciones

- Verificar que `userId` del token coincida con Customer
- No permitir modificar suscripciones de otros usuarios
- Validar Price ID contra lista permitida
- Verificar estado de Customer en Stripe antes de cambios

### Datos Sensibles

- **NUNCA** almacenar números de tarjeta completos
- Guardar solo `paymentMethod.last4` y `brand`
- Tokens `pm_xxx` son seguros para guardar
- Usar Stripe Elements para PCI compliance

---

## Configuración Requerida

```env
# Stripe Keys
STRIPE_SECRET_KEY=sk_test_51Sb3k64dPtEGmLmp...
STRIPE_PUBLISHABLE_KEY=pk_test_51Sb3k64dPtEGmLmp...
STRIPE_WEBHOOK_SECRET=whsec_...

# Price IDs (desde Stripe Dashboard)
STRIPE_PRICE_BASIC=price_1Sb3k64dPtEGmLmpeAdxvmIu
STRIPE_PRICE_PRO=price_1Sb3k64dPtEGmLmpm5n5bbJH
STRIPE_PRICE_PREMIUM=price_1Sb3k74dPtEGmLmpHfLpUkvQ

# Configuración de Trial
TRIAL_PERIOD_DAYS=7
```

---

## Webhooks Relacionados

| Evento | Acción |
|--------|--------|
| `customer.subscription.created` | Crear registro en BD |
| `customer.subscription.updated` | Sincronizar estado y plan |
| `customer.subscription.deleted` | Marcar como `canceled` |
| `customer.subscription.trial_will_end` | Enviar email recordatorio (3 días antes) |
| `invoice.payment_succeeded` | Crear registro en `payments` |
| `invoice.payment_failed` | Actualizar a `past_due`, enviar email |

---

## Métricas de Negocio

### KPIs a Rastrear

- **MRR (Monthly Recurring Revenue):** Suma de todas las suscripciones activas
- **Churn Rate:** (Cancelaciones del mes / Suscripciones inicio mes) * 100
- **ARPU (Average Revenue Per User):** MRR / Total usuarios suscritos
- **LTV (Lifetime Value):** ARPU / Churn Rate
- **Conversion Rate:** Trials → Paid

---

## Criterios de Aceptación

- [ ] Usuario puede suscribirse a Basic/Pro/Premium
- [ ] Usuario puede agregar/actualizar método de pago
- [ ] Usuario puede hacer upgrade inmediatamente
- [ ] Usuario puede hacer downgrade al final del período
- [ ] Usuario puede cancelar manteniendo acceso hasta period_end
- [ ] Usuario puede reactivar suscripción cancelada antes de expirar
- [ ] Trial de 7 días se aplica correctamente en primera suscripción
- [ ] Estados se sincronizan correctamente con webhooks
- [ ] Acceso a recursos se controla según plan activo
- [ ] Emails se envían en eventos clave (creación, cancelación, fallo)

---

## Especificación Técnica Relacionada

- [ET-PAY-001: Stripe Subscriptions](../especificaciones/ET-PAY-001-stripe-subscriptions.md)

## Historias de Usuario Relacionadas

- [US-PAY-001: Ver Planes Disponibles](../historias-usuario/US-PAY-001-ver-planes.md)
- [US-PAY-002: Suscribirse a Plan](../historias-usuario/US-PAY-002-suscribirse.md)
- [US-PAY-006: Agregar Método de Pago](../historias-usuario/US-PAY-006-agregar-metodo-pago.md)
