# MII-014: Sistema de Referidos --- id: MII-014 type: Epic status: Completado priority: P1 phase: 4 story_points: 21 created_date: 2026-01-10 updated_date: 2026-01-13 simco_version: "4.0.0" --- ## Metadata | Campo | Valor | |-------|-------| | **ID** | MII-014 | | **Nombre** | Sistema de Referidos | | **Fase** | 4 - Crecimiento | | **Prioridad** | P1 | | **Story Points** | 21 | | **Estado** | Completado | --- ## 1. Descripcion Implementar un sistema de referidos multinivel que incentive el crecimiento organico, con recompensas justas y mecanismos anti-fraude. ### Objetivo Crear un motor de crecimiento viral donde usuarios existentes inviten a nuevos usuarios y ambos se beneficien. --- ## 2. Requerimientos Relacionados | RF | Descripcion | Prioridad | |----|-------------|-----------| | FR-110 | Codigo de referido unico por usuario | P0 | | FR-111 | Registro atribuido (nuevo usuario con codigo) | P0 | | FR-112 | Condicion de recompensa (compra + primer inventario) | P0 | | FR-113 | Recompensa break-even (1 credito por referido) | P0 | | FR-114 | Multinivel configurable (niveles, %, limites) | P2 | | FR-115 | Anti-fraude (duplicados, topes, holds) | P0 | | FR-116 | Panel de referidos (invitaciones, referidos, creditos) | P1 | --- ## 3. Criterios de Aceptacion ### AC-001: Codigo Unico ```gherkin DADO que soy usuario registrado CUANDO veo mi perfil o panel de referidos ENTONCES tengo un codigo unico para compartir Y puedo copiarlo o compartirlo facilmente ``` ### AC-002: Registro con Codigo ```gherkin DADO que soy nuevo usuario CUANDO me registro con un codigo de referido ENTONCES quedo vinculado a quien me invito Y ambos podemos ver la relacion ``` ### AC-003: Condiciones de Recompensa ```gherkin DADO que invite a un usuario CUANDO el referido completa: 1. Primera compra de creditos 2. Primer inventario exitoso ENTONCES recibo mi recompensa Y el referido tambien recibe bonus ``` ### AC-004: Recompensa de 1 Credito ```gherkin DADO que mi referido cumplio las condiciones CUANDO se activa la recompensa ENTONCES recibo 1 credito en mi wallet Y veo la transaccion en mi historial ``` ### AC-005: Panel de Referidos ```gherkin DADO que he invitado usuarios CUANDO veo mi panel de referidos ENTONCES veo: - Mi codigo de referido - Lista de referidos y su status - Creditos ganados totales - Referidos pendientes de activacion ``` ### AC-006: Anti-fraude ```gherkin DADO que el sistema detecta comportamiento sospechoso CUANDO hay indicios de fraude ENTONCES las recompensas quedan en hold Y se notifica para revision manual Y no se entregan creditos hasta verificar ``` --- ## 4. Tareas Tecnicas | ID | Tarea | Estimacion | Estado | |----|-------|------------|--------| | T-001 | Crear modulo referrals en NestJS | 1 SP | Completado | | T-002 | Implementar generador de codigos | 1 SP | Completado | | T-003 | Crear entidades ReferralCode, ReferralTree | 2 SP | Completado | | T-004 | Implementar vinculacion en registro | 2 SP | Completado | | T-005 | Crear motor de condiciones | 3 SP | Completado | | T-006 | Implementar sistema de recompensas | 2 SP | Completado | | T-007 | Crear reglas anti-fraude | 3 SP | Completado | | T-008 | Implementar panel mobile | 3 SP | Completado | | T-009 | Crear compartir codigo | 1 SP | Completado | | T-010 | Implementar multinivel (P2) | 3 SP | Completado | --- ## 5. Modelo de Datos ### Tabla: referral_codes ```sql CREATE TABLE referral_codes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) UNIQUE, code VARCHAR(10) UNIQUE NOT NULL, total_referrals INT DEFAULT 0, total_activated INT DEFAULT 0, total_rewards DECIMAL(12,4) DEFAULT 0, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT NOW() ); ``` ### Tabla: referral_tree ```sql CREATE TABLE referral_tree ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), referrer_id UUID REFERENCES users(id), referred_id UUID REFERENCES users(id) UNIQUE, referral_code_id UUID REFERENCES referral_codes(id), level INT DEFAULT 1, -- Para multinivel status VARCHAR(20) DEFAULT 'PENDING', first_purchase_at TIMESTAMP, first_session_at TIMESTAMP, activated_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); -- Status: PENDING, PURCHASE_DONE, ACTIVATED, FRAUD_HOLD, REJECTED ``` ### Tabla: referral_rewards ```sql CREATE TABLE referral_rewards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), referral_id UUID REFERENCES referral_tree(id), beneficiary_id UUID REFERENCES users(id), reward_type VARCHAR(20), -- 'REFERRER', 'REFERRED', 'LEVEL_2', etc amount DECIMAL(12,4), status VARCHAR(20) DEFAULT 'PENDING', released_at TIMESTAMP, hold_reason VARCHAR(100), created_at TIMESTAMP DEFAULT NOW() ); -- Status: PENDING, RELEASED, HELD, CANCELLED ``` ### Tabla: referral_fraud_signals ```sql CREATE TABLE referral_fraud_signals ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), referral_id UUID REFERENCES referral_tree(id), signal_type VARCHAR(50), signal_data JSONB, severity VARCHAR(20), -- 'LOW', 'MEDIUM', 'HIGH' reviewed BOOLEAN DEFAULT false, reviewed_by UUID REFERENCES users(id), reviewed_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); ``` --- ## 6. Generador de Codigos ```typescript function generateReferralCode(userId: string): string { // Formato: 3 letras + 4 numeros (ej: MII-A1B2) const letters = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Sin I, O const numbers = '0123456789'; let code = ''; for (let i = 0; i < 3; i++) { code += letters[Math.floor(Math.random() * letters.length)]; } for (let i = 0; i < 4; i++) { code += numbers[Math.floor(Math.random() * numbers.length)]; } return code; // ej: "ABC1234" } ``` --- ## 7. Motor de Condiciones ```typescript interface ActivationConditions { firstPurchase: boolean; firstSession: boolean; minPurchaseAmount?: number; minSessionsCount?: number; } async function checkActivation(referralId: string): Promise { const referral = await this.referralRepo.findOne(referralId); const conditions = getActivationConditions(); const checks = { firstPurchase: referral.first_purchase_at !== null, firstSession: referral.first_session_at !== null }; // Verificar condiciones minimas if (conditions.minPurchaseAmount) { const totalPurchased = await this.getTotalPurchased(referral.referred_id); checks.minPurchase = totalPurchased >= conditions.minPurchaseAmount; } const allMet = Object.values(checks).every(v => v); if (allMet && referral.status === 'PURCHASE_DONE') { await this.activateReferral(referralId); } return allMet; } async function activateReferral(referralId: string) { const referral = await this.referralRepo.findOne(referralId); // Verificar anti-fraude const fraudSignals = await this.checkFraudSignals(referral); if (fraudSignals.length > 0) { await this.holdReferral(referralId, fraudSignals); return; } // Liberar recompensas await this.releaseRewards(referralId); // Actualizar status await this.referralRepo.update(referralId, { status: 'ACTIVATED', activated_at: new Date() }); } ``` --- ## 8. Reglas Anti-fraude | Senal | Severidad | Accion | |-------|-----------|--------| | Mismo dispositivo | HIGH | Hold + revision | | Mismo IP | MEDIUM | Hold si > 3 | | Email temporal | MEDIUM | Hold | | Patron de registro | HIGH | Hold + revision | | Compra minima exacta | LOW | Monitor | | Sesion artificial | HIGH | Bloquear | ```typescript async function checkFraudSignals(referral: ReferralTree): Promise { const signals: FraudSignal[] = []; const referrer = await this.usersRepo.findOne(referral.referrer_id); const referred = await this.usersRepo.findOne(referral.referred_id); // Mismo dispositivo if (referrer.device_fingerprint === referred.device_fingerprint) { signals.push({ type: 'SAME_DEVICE', severity: 'HIGH', data: { fingerprint: referrer.device_fingerprint } }); } // Mismo IP if (referrer.last_ip === referred.registration_ip) { const sameIpCount = await this.countSameIPReferrals(referrer.id); if (sameIpCount > 3) { signals.push({ type: 'SAME_IP_MULTIPLE', severity: 'MEDIUM', data: { ip: referrer.last_ip, count: sameIpCount } }); } } // Email temporal if (isTemporaryEmail(referred.email)) { signals.push({ type: 'TEMPORARY_EMAIL', severity: 'MEDIUM', data: { domain: referred.email.split('@')[1] } }); } return signals; } ``` --- ## 9. Endpoints API | Metodo | Endpoint | Descripcion | Auth | |--------|----------|-------------|------| | GET | /referrals/my-code | Obtener mi codigo | JWT | | GET | /referrals/stats | Estadisticas de referidos | JWT | | GET | /referrals/list | Lista mis referidos | JWT | | POST | /referrals/validate/:code | Validar codigo existe | No | | GET | /referrals/rewards | Historial recompensas | JWT | --- ## 10. Pantallas Mobile | Pantalla | Componentes | |----------|-------------| | **ReferralDashboard** | Codigo, stats, CTA compartir | | **ReferralListScreen** | Lista referidos, status | | **RewardsHistoryScreen** | Recompensas, timeline | | **ShareCodeModal** | Codigo, opciones compartir | --- ## 11. UI del Panel ``` ┌─────────────────────────────────────────┐ │ INVITA Y GANA │ ├─────────────────────────────────────────┤ │ │ │ Tu codigo de invitacion: │ │ ┌─────────────────────────────────┐ │ │ │ ABC1234 │ │ │ │ [📋] │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ 📤 COMPARTIR CODIGO │ │ │ └─────────────────────────────────┘ │ │ │ │ ───────────────────────────────────── │ │ │ │ TUS ESTADISTICAS │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 12 │ │ 8 │ │ 8.0 │ │ │ │Invitados│ │Activados│ │Creditos │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ ───────────────────────────────────── │ │ │ │ REFERIDOS RECIENTES │ │ │ │ 👤 Juan P. ✅ Activado +1.0 │ │ Hace 2 dias │ │ │ │ 👤 Maria L. ⏳ Pendiente │ │ Falta: primer inventario │ │ │ │ 👤 Carlos R. 💰 Compro │ │ Falta: primer inventario │ │ │ │ [Ver todos →] │ │ │ └─────────────────────────────────────────┘ ``` --- ## 12. Configuracion Multinivel (P2) ```yaml referral_config: levels: - level: 1 reward_percent: 100 # 1 credito completo max_rewards: null # Sin limite - level: 2 reward_percent: 25 # 0.25 creditos max_rewards: 10 # Max 10 nivel 2 - level: 3 reward_percent: 10 # 0.1 creditos max_rewards: 5 # Max 5 nivel 3 anti_fraud: same_device_action: "HOLD" same_ip_threshold: 3 min_session_duration: 30 # segundos reward_hold_hours: 24 ``` --- ## 13. Dependencias ### Entrada (Requiere) - MII-002: Autenticacion (registro) - MII-009: Wallet y Creditos (recompensas) - MII-011/012/013: Pagos (primera compra) - MII-005: Procesamiento IA (primera sesion) ### Salida (Bloquea) - MII-015: Admin (moderacion de fraude) --- ## 14. Riesgos | Riesgo | Probabilidad | Impacto | Mitigacion | |--------|--------------|---------|------------| | Fraude masivo | Alta | Alto | Anti-fraude robusto, holds | | Abuso de creditos | Media | Medio | Limites, topes | | Experiencia confusa | Media | Medio | UX clara, FAQs | --- ## 15. Referencias - [REQUERIMIENTOS-FUNCIONALES.md](../00-vision-general/REQUERIMIENTOS-FUNCIONALES.md) - Seccion 5.12 - [VISION-PROYECTO.md](../00-vision-general/VISION-PROYECTO.md) - Modelo crecimiento --- **Ultima Actualizacion:** 2026-01-10