461 lines
14 KiB
Markdown
461 lines
14 KiB
Markdown
# RF-TENANT-004: Subscripciones y Limites
|
|
|
|
## Identificacion
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | RF-TENANT-004 |
|
|
| **Modulo** | MGN-004 Tenants |
|
|
| **Prioridad** | P1 - Alta |
|
|
| **Estado** | Ready |
|
|
| **Fecha** | 2025-12-05 |
|
|
|
|
---
|
|
|
|
## Descripcion
|
|
|
|
El sistema debe gestionar planes de subscripcion para los tenants, controlando los limites de uso (usuarios, storage, modulos) y el ciclo de facturacion. Cada plan define que funcionalidades y recursos estan disponibles para el tenant.
|
|
|
|
---
|
|
|
|
## Actores
|
|
|
|
| Actor | Descripcion |
|
|
|-------|-------------|
|
|
| Platform Admin | Gestiona planes y subscripciones |
|
|
| Tenant Admin | Ve su subscripcion y puede solicitar upgrades |
|
|
| Sistema | Aplica limites automaticamente |
|
|
| Billing System | Procesa pagos y renovaciones |
|
|
|
|
---
|
|
|
|
## Planes de Subscripcion
|
|
|
|
### Planes Disponibles
|
|
|
|
| Plan | Usuarios | Storage | Modulos | Precio/mes |
|
|
|------|----------|---------|---------|------------|
|
|
| Trial | 5 | 1 GB | Basicos | Gratis (14 dias) |
|
|
| Starter | 10 | 5 GB | Basicos | $29 USD |
|
|
| Professional | 50 | 25 GB | Todos Standard | $99 USD |
|
|
| Enterprise | Ilimitado | 100 GB | Todos + Premium | $299 USD |
|
|
| Custom | Configurable | Configurable | Configurable | Cotizacion |
|
|
|
|
### Modulos por Plan
|
|
|
|
| Modulo | Trial | Starter | Professional | Enterprise |
|
|
|--------|-------|---------|--------------|------------|
|
|
| Auth | ✓ | ✓ | ✓ | ✓ |
|
|
| Users | ✓ | ✓ | ✓ | ✓ |
|
|
| Roles | ✓ | ✓ | ✓ | ✓ |
|
|
| Inventory | ✓ | ✓ | ✓ | ✓ |
|
|
| Financial | - | ✓ | ✓ | ✓ |
|
|
| Reports | - | Basic | Full | Full |
|
|
| CRM | - | - | ✓ | ✓ |
|
|
| Advanced Analytics | - | - | - | ✓ |
|
|
| API Access | - | - | ✓ | ✓ |
|
|
| White Label | - | - | - | ✓ |
|
|
| Priority Support | - | - | - | ✓ |
|
|
|
|
---
|
|
|
|
## Flujo Principal
|
|
|
|
### Ver Subscripcion Actual
|
|
|
|
```
|
|
1. Tenant Admin accede a Configuracion > Subscripcion
|
|
2. Sistema muestra:
|
|
- Plan actual
|
|
- Fecha de inicio
|
|
- Proxima renovacion
|
|
- Uso actual vs limites
|
|
- Historial de facturacion
|
|
3. Admin puede ver planes disponibles para upgrade
|
|
```
|
|
|
|
### Solicitar Upgrade
|
|
|
|
```
|
|
1. Tenant Admin click en "Cambiar Plan"
|
|
2. Sistema muestra comparativa de planes
|
|
3. Admin selecciona nuevo plan
|
|
4. Sistema calcula costo prorrateado
|
|
5. Admin confirma y procede al pago
|
|
6. Sistema procesa pago
|
|
7. Sistema actualiza subscripcion
|
|
8. Nuevos limites aplican inmediatamente
|
|
9. Sistema envia confirmacion por email
|
|
```
|
|
|
|
### Renovacion Automatica
|
|
|
|
```
|
|
1. Sistema detecta subscripcion por vencer (3 dias)
|
|
2. Sistema envia recordatorio de renovacion
|
|
3. En fecha de renovacion:
|
|
a. Sistema intenta cobrar metodo de pago guardado
|
|
b. Si exitoso: renueva subscripcion
|
|
c. Si falla: marca como "payment_pending"
|
|
4. Si pago pendiente por 7 dias:
|
|
a. Sistema envia advertencias
|
|
b. Sistema suspende tenant
|
|
```
|
|
|
|
### Cancelar Subscripcion
|
|
|
|
```
|
|
1. Tenant Admin solicita cancelacion
|
|
2. Sistema muestra encuesta de salida
|
|
3. Admin confirma cancelacion
|
|
4. Sistema programa cancelacion para fin de periodo
|
|
5. Tenant sigue activo hasta fin de periodo pagado
|
|
6. Al terminar periodo: downgrade a Trial o suspension
|
|
```
|
|
|
|
---
|
|
|
|
## Limites y Enforcement
|
|
|
|
### Tipos de Limites
|
|
|
|
| Tipo | Comportamiento |
|
|
|------|----------------|
|
|
| Hard Limit | Bloquea la accion inmediatamente |
|
|
| Soft Limit | Permite con advertencia, bloquea al 110% |
|
|
| Usage Based | Cobra extra por exceso |
|
|
|
|
### Enforcement de Limites
|
|
|
|
```typescript
|
|
// Verificacion de limite de usuarios
|
|
async canCreateUser(tenantId: string): Promise<boolean> {
|
|
const subscription = await this.getSubscription(tenantId);
|
|
const currentUsers = await this.countUsers(tenantId);
|
|
|
|
if (currentUsers >= subscription.limits.maxUsers) {
|
|
throw new PaymentRequiredException(
|
|
`Limite de usuarios alcanzado (${subscription.limits.maxUsers}). ` +
|
|
`Actualiza tu plan para agregar mas usuarios.`
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Verificacion de acceso a modulo
|
|
async canAccessModule(tenantId: string, module: string): Promise<boolean> {
|
|
const subscription = await this.getSubscription(tenantId);
|
|
|
|
if (!subscription.modules.includes(module)) {
|
|
throw new PaymentRequiredException(
|
|
`El modulo "${module}" no esta incluido en tu plan. ` +
|
|
`Actualiza a ${this.getMinPlanForModule(module)} para acceder.`
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Reglas de Negocio
|
|
|
|
| ID | Regla |
|
|
|----|-------|
|
|
| RN-001 | Trial expira en 14 dias sin opcion de extension |
|
|
| RN-002 | Downgrade no permitido durante periodo de facturacion |
|
|
| RN-003 | Upgrade aplica inmediatamente con prorrateo |
|
|
| RN-004 | Cancelacion efectiva al fin del periodo pagado |
|
|
| RN-005 | Datos se conservan 30 dias despues de cancelacion |
|
|
| RN-006 | Exceso de storage: soft limit + cargos adicionales |
|
|
| RN-007 | Pago fallido: 7 dias de gracia antes de suspension |
|
|
| RN-008 | Enterprise puede negociar limites custom |
|
|
|
|
---
|
|
|
|
## Criterios de Aceptacion
|
|
|
|
### Escenario 1: Ver uso actual vs limites
|
|
|
|
```gherkin
|
|
Given tenant con plan Professional (50 usuarios, 25GB)
|
|
And 35 usuarios activos
|
|
And 18GB de storage usado
|
|
When Tenant Admin ve la subscripcion
|
|
Then muestra "Usuarios: 35/50 (70%)"
|
|
And muestra "Storage: 18GB/25GB (72%)"
|
|
And muestra grafico de uso
|
|
```
|
|
|
|
### Escenario 2: Bloqueo por limite de usuarios
|
|
|
|
```gherkin
|
|
Given tenant con plan Starter (10 usuarios)
|
|
And 10 usuarios activos (100%)
|
|
When Admin intenta crear usuario #11
|
|
Then el sistema responde con status 402
|
|
And el mensaje indica limite alcanzado
|
|
And sugiere upgrade a Professional
|
|
```
|
|
|
|
### Escenario 3: Bloqueo por modulo no incluido
|
|
|
|
```gherkin
|
|
Given tenant con plan Starter (sin CRM)
|
|
When usuario intenta acceder a /api/v1/crm/contacts
|
|
Then el sistema responde con status 402
|
|
And el mensaje indica modulo no disponible
|
|
And sugiere planes que incluyen CRM
|
|
```
|
|
|
|
### Escenario 4: Upgrade de plan
|
|
|
|
```gherkin
|
|
Given tenant en Starter ($29/mes) al dia 15 del mes
|
|
When solicita upgrade a Professional ($99/mes)
|
|
Then sistema calcula prorrateo: $35 (15 dias de diferencia)
|
|
And usuario paga $35
|
|
And plan cambia inmediatamente
|
|
And nuevos limites aplican
|
|
And siguiente factura: $99 el dia 1
|
|
```
|
|
|
|
### Escenario 5: Trial expirado
|
|
|
|
```gherkin
|
|
Given tenant en Trial por 14 dias
|
|
When han pasado los 14 dias sin subscription
|
|
Then el estado cambia a "trial_expired"
|
|
And usuarios no pueden acceder
|
|
And datos se conservan
|
|
And Admin puede acceder solo para suscribirse
|
|
```
|
|
|
|
### Escenario 6: Advertencia de renovacion
|
|
|
|
```gherkin
|
|
Given subscripcion que vence en 3 dias
|
|
When el sistema ejecuta job de notificaciones
|
|
Then envia email "Tu subscripcion vence en 3 dias"
|
|
And incluye link para verificar metodo de pago
|
|
```
|
|
|
|
---
|
|
|
|
## Mockup / Wireframe
|
|
|
|
```
|
|
+------------------------------------------------------------------+
|
|
| [Logo] Mi Subscripcion |
|
|
+------------------------------------------------------------------+
|
|
| |
|
|
| ┌─────────────────────────────────────────────────────────────┐ |
|
|
| │ PLAN ACTUAL: Professional [Cambiar Plan] │ |
|
|
| │ │ |
|
|
| │ $99 USD / mes Proxima renovacion: 01/01/2026 │ |
|
|
| │ Facturacion mensual Metodo de pago: •••• 4242 │ |
|
|
| └─────────────────────────────────────────────────────────────┘ |
|
|
| |
|
|
| USO ACTUAL |
|
|
| ┌─────────────────────────────────────────────────────────────┐ |
|
|
| │ │ |
|
|
| │ Usuarios ████████████████████░░░░░░░░░░ 35/50 (70%) │ |
|
|
| │ │ |
|
|
| │ Storage █████████████████████████░░░░░ 18/25GB (72%) │ |
|
|
| │ │ |
|
|
| │ API Calls ██████████░░░░░░░░░░░░░░░░░░░░ 5K/50K (10%) │ |
|
|
| │ │ |
|
|
| └─────────────────────────────────────────────────────────────┘ |
|
|
| |
|
|
| MODULOS INCLUIDOS |
|
|
| ┌─────────────────────────────────────────────────────────────┐ |
|
|
| │ ✓ Auth ✓ Users ✓ Roles ✓ Inventory │ |
|
|
| │ ✓ Financial ✓ Reports (Full) ✓ CRM ✓ API Access │ |
|
|
| │ │ |
|
|
| │ No incluidos: Advanced Analytics, White Label │ |
|
|
| │ [Ver Enterprise para estas funciones] │ |
|
|
| └─────────────────────────────────────────────────────────────┘ |
|
|
| |
|
|
| HISTORIAL DE FACTURACION |
|
|
| ┌─────────────────────────────────────────────────────────────┐ |
|
|
| │ Fecha | Descripcion | Monto | Estado │ |
|
|
| │------------|----------------------|---------|-------------- │ |
|
|
| │ 01/12/2025 | Professional - Dic | $99.00 | ✓ Pagado │ |
|
|
| │ 01/11/2025 | Professional - Nov | $99.00 | ✓ Pagado │ |
|
|
| │ 15/10/2025 | Upgrade Starter->Pro | $35.00 | ✓ Pagado │ |
|
|
| │ 01/10/2025 | Starter - Oct | $29.00 | ✓ Pagado │ |
|
|
| └─────────────────────────────────────────────────────────────┘ |
|
|
| |
|
|
| [Descargar Facturas] [Cancelar Subscripcion] |
|
|
+------------------------------------------------------------------+
|
|
```
|
|
|
|
---
|
|
|
|
## Notas Tecnicas
|
|
|
|
### API Endpoints
|
|
|
|
```typescript
|
|
// Ver subscripcion actual
|
|
GET /api/v1/tenant/subscription
|
|
|
|
// Response 200
|
|
{
|
|
"plan": {
|
|
"id": "plan-professional",
|
|
"name": "Professional",
|
|
"price": 99,
|
|
"currency": "USD",
|
|
"interval": "monthly"
|
|
},
|
|
"status": "active",
|
|
"currentPeriodStart": "2025-12-01",
|
|
"currentPeriodEnd": "2025-12-31",
|
|
"cancelAtPeriodEnd": false,
|
|
"usage": {
|
|
"users": { "current": 35, "limit": 50, "percentage": 70 },
|
|
"storage": { "current": 18000000000, "limit": 25000000000, "percentage": 72 },
|
|
"apiCalls": { "current": 5000, "limit": 50000, "percentage": 10 }
|
|
},
|
|
"modules": ["auth", "users", "roles", "inventory", "financial", "reports", "crm", "api"],
|
|
"paymentMethod": { "type": "card", "last4": "4242", "brand": "visa" }
|
|
}
|
|
|
|
// Ver planes disponibles
|
|
GET /api/v1/subscription/plans
|
|
|
|
// Solicitar upgrade
|
|
POST /api/v1/tenant/subscription/upgrade
|
|
{ "planId": "plan-enterprise" }
|
|
|
|
// Cancelar subscripcion
|
|
POST /api/v1/tenant/subscription/cancel
|
|
{ "reason": "too_expensive", "feedback": "..." }
|
|
|
|
// Verificar limite
|
|
GET /api/v1/tenant/subscription/check-limit?type=users
|
|
|
|
// Response 200
|
|
{
|
|
"type": "users",
|
|
"current": 35,
|
|
"limit": 50,
|
|
"canAdd": true,
|
|
"remaining": 15
|
|
}
|
|
|
|
// Response 402 (limite alcanzado)
|
|
{
|
|
"statusCode": 402,
|
|
"error": "Payment Required",
|
|
"message": "Limite de usuarios alcanzado",
|
|
"upgradeOptions": [
|
|
{ "planId": "plan-enterprise", "name": "Enterprise", "newLimit": "unlimited" }
|
|
]
|
|
}
|
|
```
|
|
|
|
### Modelos de Datos
|
|
|
|
```typescript
|
|
interface Subscription {
|
|
id: string;
|
|
tenantId: string;
|
|
planId: string;
|
|
status: 'trial' | 'active' | 'past_due' | 'canceled' | 'unpaid';
|
|
currentPeriodStart: Date;
|
|
currentPeriodEnd: Date;
|
|
cancelAtPeriodEnd: boolean;
|
|
trialEnd?: Date;
|
|
paymentMethodId?: string;
|
|
}
|
|
|
|
interface Plan {
|
|
id: string;
|
|
name: string;
|
|
price: number;
|
|
currency: string;
|
|
interval: 'monthly' | 'yearly';
|
|
limits: {
|
|
maxUsers: number;
|
|
maxStorageBytes: number;
|
|
maxApiCalls: number;
|
|
};
|
|
modules: string[];
|
|
features: string[];
|
|
isPublic: boolean;
|
|
}
|
|
```
|
|
|
|
### Limit Enforcement Guard
|
|
|
|
```typescript
|
|
@Injectable()
|
|
export class LimitGuard implements CanActivate {
|
|
constructor(
|
|
private subscriptionService: SubscriptionService,
|
|
private reflector: Reflector,
|
|
) {}
|
|
|
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
const limitType = this.reflector.get<string>('checkLimit', context.getHandler());
|
|
if (!limitType) return true;
|
|
|
|
const request = context.switchToHttp().getRequest();
|
|
const tenantId = request.tenantId;
|
|
|
|
const check = await this.subscriptionService.checkLimit(tenantId, limitType);
|
|
|
|
if (!check.canAdd) {
|
|
throw new PaymentRequiredException({
|
|
message: `Limite de ${limitType} alcanzado`,
|
|
upgradeOptions: check.upgradeOptions,
|
|
});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Uso en controller
|
|
@Post()
|
|
@CheckLimit('users')
|
|
create(@Body() dto: CreateUserDto) { }
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
| ID | Descripcion |
|
|
|----|-------------|
|
|
| RF-TENANT-001 | Tenants existentes |
|
|
| Payment Gateway | Stripe/PayPal para pagos |
|
|
| Scheduler | Jobs para renovaciones y notificaciones |
|
|
|
|
---
|
|
|
|
## Estimacion
|
|
|
|
| Tarea | Puntos |
|
|
|-------|--------|
|
|
| Backend: Subscription service | 4 |
|
|
| Backend: Limit enforcement | 4 |
|
|
| Backend: Billing integration | 5 |
|
|
| Backend: Webhooks de pago | 3 |
|
|
| Backend: Tests | 3 |
|
|
| Frontend: SubscriptionPage | 4 |
|
|
| Frontend: PlanComparison | 3 |
|
|
| Frontend: CheckoutFlow | 4 |
|
|
| Frontend: Tests | 2 |
|
|
| **Total** | **32 SP** |
|
|
|
|
---
|
|
|
|
## Historial
|
|
|
|
| Version | Fecha | Autor | Cambios |
|
|
|---------|-------|-------|---------|
|
|
| 1.0 | 2025-12-05 | System | Creacion inicial |
|