miinventario-v2/docs/01-epicas/MII-014-referidos.md
rckrdmrd c24f889f70
Some checks failed
Build / Build Backend (push) Has been cancelled
Build / Build Mobile (TypeScript Check) (push) Has been cancelled
Lint / Lint Backend (push) Has been cancelled
Lint / Lint Mobile (push) Has been cancelled
Test / Backend E2E Tests (push) Has been cancelled
Test / Mobile Unit Tests (push) Has been cancelled
Build / Build Docker Image (push) Has been cancelled
[MIINVENTARIO] feat: Add exports, reports, integrations modules and CI/CD pipeline
- Add exports module with PDF/CSV/Excel generation
- Add reports module for inventory analytics
- Add POS integrations module
- Add database migrations for exports, movements and integrations
- Add GitHub Actions CI/CD workflow with Docker support
- Add mobile export and reports screens with tests
- Update epic documentation with traceability
- Add deployment and security guides

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 06:06:34 -06:00

13 KiB

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

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

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

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

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

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

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

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

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

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

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

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

interface ActivationConditions {
  firstPurchase: boolean;
  firstSession: boolean;
  minPurchaseAmount?: number;
  minSessionsCount?: number;
}

async function checkActivation(referralId: string): Promise<boolean> {
  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
async function checkFraudSignals(referral: ReferralTree): Promise<FraudSignal[]> {
  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)

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


Ultima Actualizacion: 2026-01-10