# MII-009: Wallet y Creditos --- id: MII-009 type: Epic status: Completado priority: P0 phase: 3 story_points: 13 created_date: 2026-01-10 updated_date: 2026-01-13 simco_version: "4.0.0" --- ## Metadata | Campo | Valor | |-------|-------| | **ID** | MII-009 | | **Nombre** | Wallet y Creditos | | **Fase** | 3 - Monetizacion | | **Prioridad** | P0 | | **Story Points** | 13 | | **Estado** | Completado | --- ## 1. Descripcion Implementar el sistema de wallet de creditos que permite a los usuarios consumir creditos por sesion de inventario, con registro transparente de costos y precios. ### Objetivo Crear un sistema de monetizacion justo y transparente basado en consumo, donde el usuario paga el doble del costo real de la IA. --- ## 2. Requerimientos Relacionados | RF | Descripcion | Prioridad | |----|-------------|-----------| | FR-080 | Cartera de creditos por usuario/tienda | P0 | | FR-081 | Consumo de creditos por sesion (configurable) | P0 | | FR-082 | Motor de costos COGS IA (proveedor, frames, costo) | P0 | | FR-083 | Regla de precio: 2x COGS IA | P0 | | FR-084 | Transparencia: mostrar creditos y equivalencia MXN | P1 | --- ## 3. Criterios de Aceptacion ### AC-001: Wallet de Usuario ```gherkin DADO que soy un usuario registrado CUANDO veo mi wallet ENTONCES veo mi saldo de creditos Y veo el equivalente aproximado en MXN Y veo el historial de transacciones ``` ### AC-002: Consumo por Sesion ```gherkin DADO que tengo creditos suficientes CUANDO inicio una sesion de inventario ENTONCES se reservan creditos estimados Y al completar, se cobra el monto real Y si falla, se liberan los creditos reservados ``` ### AC-003: Creditos Insuficientes ```gherkin DADO que no tengo creditos suficientes CUANDO intento iniciar una sesion ENTONCES veo un mensaje indicando falta de saldo Y me ofrecen comprar un paquete Y no puedo continuar sin creditos ``` ### AC-004: Calculo de COGS ```gherkin DADO que una sesion se proceso CUANDO el sistema calcula el costo ENTONCES registra: - Proveedor de IA usado - Numero de frames procesados - Tokens/llamadas consumidas - Costo en USD y MXN ``` ### AC-005: Precio = 2x COGS ```gherkin DADO que el COGS de una sesion es X CUANDO se calcula el precio al usuario ENTONCES el precio es 2 * X Y se muestra de forma transparente ``` ### AC-006: Transparencia ```gherkin DADO que complete una sesion CUANDO veo el detalle de consumo ENTONCES veo: - Creditos consumidos - Costo base (COGS) - Precio cobrado (2x) - Equivalente en MXN ``` --- ## 4. Tareas Tecnicas | ID | Tarea | Estimacion | Estado | |----|-------|------------|--------| | T-001 | Crear modulo credits en NestJS | 1 SP | Completado | | T-002 | Implementar entidad CreditWallet | 1 SP | Completado | | T-003 | Implementar transacciones atomicas | 2 SP | Completado | | T-004 | Crear motor de COGS | 2 SP | Completado | | T-005 | Implementar regla de pricing 2x | 1 SP | Completado | | T-006 | Crear pantalla de wallet en mobile | 2 SP | Completado | | T-007 | Implementar validacion pre-sesion | 1 SP | Completado | | T-008 | Crear historial de transacciones | 2 SP | Completado | | T-009 | Implementar jobs de reconciliacion | 1 SP | Completado | --- ## 5. Modelo de Datos ### Tabla: credit_wallets ```sql CREATE TABLE credit_wallets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) UNIQUE, balance DECIMAL(12,4) DEFAULT 0, reserved DECIMAL(12,4) DEFAULT 0, lifetime_credits DECIMAL(12,4) DEFAULT 0, lifetime_spent DECIMAL(12,4) DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- balance: creditos disponibles -- reserved: creditos reservados para sesiones en progreso -- lifetime_credits: total creditos comprados -- lifetime_spent: total creditos consumidos ``` ### Tabla: credit_transactions ```sql CREATE TABLE credit_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), wallet_id UUID REFERENCES credit_wallets(id), type VARCHAR(20), -- 'PURCHASE', 'CONSUMPTION', 'REFUND', 'BONUS', 'REFERRAL' amount DECIMAL(12,4) NOT NULL, balance_after DECIMAL(12,4), reference_type VARCHAR(50), -- 'PAYMENT', 'SESSION', 'REFERRAL_REWARD' reference_id UUID, metadata JSONB, created_at TIMESTAMP DEFAULT NOW() ); ``` ### Tabla: cogs_records ```sql CREATE TABLE cogs_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id UUID REFERENCES inventory_sessions(id), provider VARCHAR(50) NOT NULL, frames_processed INT, api_calls INT, tokens_used INT, cost_usd DECIMAL(10,6), exchange_rate DECIMAL(10,4), cost_mxn DECIMAL(10,4), price_to_user DECIMAL(10,4), -- 2x cost_mxn margin DECIMAL(10,4), calculated_at TIMESTAMP DEFAULT NOW() ); ``` ### Tabla: pricing_rules ```sql CREATE TABLE pricing_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100), type VARCHAR(50), -- 'MULTIPLIER', 'FIXED', 'TIERED' config JSONB, is_active BOOLEAN DEFAULT true, priority INT DEFAULT 0, valid_from TIMESTAMP, valid_until TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); ``` --- ## 6. Motor de COGS ### Calculo de Costo ```typescript interface COGSCalculation { provider: string; framesProcessed: number; apiCalls: number; tokensUsed: number; // Costos costPerFrame: number; // USD costPerToken: number; // USD fixedCost: number; // USD por sesion // Totales totalCostUSD: number; exchangeRate: number; // USD/MXN totalCostMXN: number; // Precio usuario multiplier: number; // 2x por defecto priceToUser: number; // MXN margin: number; // MXN } function calculateCOGS(session: InventorySession): COGSCalculation { const provider = getActiveProvider(); const costs = provider.getCosts(); const totalCostUSD = (session.framesProcessed * costs.perFrame) + (session.tokensUsed * costs.perToken) + costs.fixed; const rate = getExchangeRate('USD', 'MXN'); const totalCostMXN = totalCostUSD * rate; const multiplier = getPricingMultiplier(); const priceToUser = totalCostMXN * multiplier; return { provider: provider.name, framesProcessed: session.framesProcessed, tokensUsed: session.tokensUsed, totalCostUSD, exchangeRate: rate, totalCostMXN, multiplier, priceToUser, margin: priceToUser - totalCostMXN }; } ``` --- ## 7. Endpoints API | Metodo | Endpoint | Descripcion | Auth | |--------|----------|-------------|------| | GET | /credits/wallet | Obtener wallet actual | JWT | | GET | /credits/transactions | Historial transacciones | JWT | | GET | /credits/balance | Solo balance | JWT | | POST | /credits/reserve | Reservar para sesion | JWT | | POST | /credits/consume | Consumir despues de sesion | JWT | | POST | /credits/release | Liberar reserva | JWT | | GET | /credits/estimate | Estimar costo sesion | JWT | --- ## 8. Pantallas Mobile | Pantalla | Componentes | |----------|-------------| | **WalletScreen** | Balance, equivalente MXN, botones | | **TransactionHistoryScreen** | Lista transacciones, filtros | | **TransactionDetailScreen** | Detalle, COGS, breakdown | | **InsufficientCreditsModal** | Mensaje, CTA comprar | | **ConsumptionSummaryModal** | Post-sesion, desglose | --- ## 9. UI de Wallet ``` ┌─────────────────────────────────────────┐ │ MI WALLET │ ├─────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ │ │ │ │ │ │ │ 12.5 │ │ │ │ CREDITOS │ │ │ │ │ │ │ │ ≈ $156.25 MXN │ │ │ └─────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ [+] RECARGAR CREDITOS │ │ │ └─────────────────────────────────┘ │ │ │ │ MOVIMIENTOS RECIENTES │ │ ───────────────────── │ │ │ │ 📥 Recarga $100 MXN +8.0 │ │ Ayer, 3:45 PM │ │ │ │ 📤 Inventario Tienda 1 -1.2 │ │ Ayer, 10:30 AM │ │ │ │ 📥 Bono de referido +1.0 │ │ 8 Ene, 5:00 PM │ │ │ │ [Ver todo el historial →] │ │ │ └─────────────────────────────────────────┘ ``` --- ## 10. Flujo de Consumo ``` ┌─────────────────────────────────────────────────────────────────┐ │ FLUJO DE CONSUMO │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Iniciar │──▶│ Reservar │──▶│ Procesar │──▶│ Calcular │ │ │ │ Sesion │ │ Creditos │ │ Video │ │ COGS │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ │ │ │ │ balance -= estimated │ │ │ │ │ reserved += estimated │ │ │ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ Consumir │ │ │ │ Real │ │ │ └──────────┘ │ │ │ │ │ reserved -= estimated │ │ │ balance += (estimated - real) │ │ lifetime_spent += real │ │ │ │ SI FALLA: │ │ ───────── │ │ reserved -= estimated │ │ balance += estimated (devolucion) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 11. Dependencias ### Entrada (Requiere) - MII-002: Autenticacion (usuario) - MII-005: Procesamiento IA (COGS) ### Salida (Bloquea) - MII-010: Paquetes de Recarga - MII-011, 012, 013: Pagos - MII-014: Referidos (rewards) --- ## 12. Configuracion | Parametro | Valor Default | Descripcion | |-----------|---------------|-------------| | PRICING_MULTIPLIER | 2.0 | Factor sobre COGS | | MIN_RESERVE | 2.0 | Creditos minimos a reservar | | EXCHANGE_RATE_TTL | 1h | Cache de tipo de cambio | | FREE_CREDITS_SIGNUP | 3.0 | Creditos gratis al registrar | --- ## 13. Riesgos | Riesgo | Probabilidad | Impacto | Mitigacion | |--------|--------------|---------|------------| | Costos IA suben | Media | Alto | Pricing dinamico, alertas | | Race conditions | Media | Alto | Transacciones atomicas | | Tipo cambio volatil | Baja | Medio | Cache, margen buffer | --- ## 14. Notas de Implementacion - Usar transacciones DB para atomicidad - Implementar lock optimista en wallet - Cache de tipo de cambio con TTL - Considerar creditos con expiracion (futuro) - Log detallado de cada transaccion --- ## 15. Referencias - [REQUERIMIENTOS-FUNCIONALES.md](../00-vision-general/REQUERIMIENTOS-FUNCIONALES.md) - Seccion 5.9 - [ADR-0001](../97-adr/ADR-0001-modelo-creditos-tokens.md) - Modelo de Creditos - [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md) --- **Ultima Actualizacion:** 2026-01-10