---
id: "RF-PAY-009"
title: "Sistema de Transferencias P2P"
type: "Requirement"
status: "Draft"
priority: "Media"
epic: "OQI-005"
project: "trading-platform"
version: "1.0.0"
created_date: "2026-01-04"
updated_date: "2026-01-04"
---

# RF-PAY-009: Sistema de Transferencias P2P

**Version:** 1.0.0
**Fecha:** 2026-01-04
**Estado:** Draft
**Prioridad:** P2 (Media)
**Story Points:** 5
**Epica:** [OQI-005](../_MAP.md)

---

## Descripcion

El sistema debe permitir a los usuarios realizar transferencias de fondos peer-to-peer (P2P) entre wallets internos de la plataforma, de manera instantanea y sin comisiones. Los usuarios podran enviar dinero a otros usuarios identificandolos por email, username o wallet ID.

---

## Objetivo de Negocio

- Fomentar comunidad activa y engagement entre usuarios
- Facilitar pagos entre traders (apuestas amistosas, compartir senales)
- Reducir friccion para onboarding de nuevos usuarios (amigos invitan)
- Retener fondos dentro del ecosistema (no salen a bancos)
- Habilitar futuros marketplaces internos (venta de cursos, senales)

---

## Casos de Uso

1. **Envio por Email:** Usuario A envia $50 USD a usuario B usando su email
2. **Envio por Username:** Usuario A envia $100 USD a @trader_pro
3. **Envio por Wallet ID:** Usuario A pega wallet ID y envia $25 USD
4. **Con Mensaje:** Usuario A envia $10 USD con nota "Para el cafe de ayer"
5. **Recepcion:** Usuario B recibe notificacion y ve fondos en wallet
6. **Historial:** Usuario revisa todas las transferencias enviadas/recibidas

---

## Requisitos Funcionales

### RF-PAY-009.1: Identificacion de Destinatario

**DEBE:**
1. Buscar usuario por email exacto
2. Buscar usuario por username (con o sin @)
3. Buscar usuario por wallet ID (UUID corto de 8 caracteres)
4. Mostrar preview del destinatario antes de confirmar
5. Validar que destinatario tiene cuenta activa

**Preview de destinatario:**
```
Enviar a:
+---------------------------+
| @trader_pro               |
| Juan P****z               |  (nombre parcialmente oculto)
| Miembro desde 2024        |
| [x] Cuenta verificada     |
+---------------------------+
```

### RF-PAY-009.2: Creacion de Transferencia

**DEBE:**
1. Validar saldo suficiente en wallet del remitente
2. Validar monto dentro de limites ($1 - $5,000)
3. Validar limite diario acumulado ($10,000)
4. Permitir mensaje opcional (max 140 caracteres)
5. Requerir 2FA para montos > $500 USD

**Modelo de datos:**
```typescript
@Entity({ name: 'p2p_transfers', schema: 'billing' })
class P2PTransfer {
  id: string;                    // UUID
  shortId: string;               // ID corto 8 chars para referencia
  senderId: string;              // FK a users
  senderWalletId: string;        // FK a wallets
  receiverId: string;            // FK a users
  receiverWalletId: string;      // FK a wallets
  amount: Decimal;               // Monto en USD
  message?: string;              // Mensaje opcional (140 chars max)
  status: P2PTransferStatus;     // pending, completed, failed, reversed
  requires2FA: boolean;          // true si amount > 500
  verified2FA: boolean;          // true si 2FA completado
  createdAt: Date;
  completedAt?: Date;
  updatedAt: Date;
}

enum P2PTransferStatus {
  PENDING = 'pending',       // Esperando 2FA
  COMPLETED = 'completed',   // Transferido exitosamente
  FAILED = 'failed',         // Error en proceso
  REVERSED = 'reversed'      // Revertido por admin
}
```

### RF-PAY-009.3: Procesamiento de Transferencia

**DEBE:**
1. Debitar wallet del remitente atomicamente
2. Acreditar wallet del destinatario atomicamente
3. Crear registros en `wallet_transactions` para ambos
4. Marcar transferencia como `completed`
5. Emitir eventos para notificaciones

**Transaccion atomica:**
```typescript
await db.transaction(async (tx) => {
  // 1. Lock wallets en orden consistente (evitar deadlock)
  const [senderWallet, receiverWallet] = await Promise.all([
    tx.wallet.findOne({ id: senderWalletId }, { lock: true }),
    tx.wallet.findOne({ id: receiverWalletId }, { lock: true })
  ]);

  // 2. Validar saldo
  if (senderWallet.balance < amount) {
    throw new InsufficientFundsError();
  }

  // 3. Debitar remitente
  await tx.walletTransaction.create({
    walletId: senderWallet.id,
    type: 'debit',
    amount: -amount,
    reference: transferId,
    description: `Transferencia P2P a ${receiverUsername}`
  });
  await tx.wallet.decrement(senderWallet.id, 'balance', amount);

  // 4. Acreditar destinatario
  await tx.walletTransaction.create({
    walletId: receiverWallet.id,
    type: 'credit',
    amount: amount,
    reference: transferId,
    description: `Transferencia P2P de ${senderUsername}`
  });
  await tx.wallet.increment(receiverWallet.id, 'balance', amount);

  // 5. Actualizar estado
  await tx.p2pTransfer.update(transferId, {
    status: 'completed',
    completedAt: new Date()
  });
});
```

### RF-PAY-009.4: Verificacion 2FA

**DEBE:**
1. Requerir 2FA para transferencias > $500 USD
2. Soportar TOTP (Google Authenticator)
3. Soportar SMS como fallback
4. Codigo valido por 5 minutos
5. Maximo 3 intentos fallidos

**Flujo 2FA:**
```
1. Usuario crea transferencia de $600
2. Backend crea transfer con status: PENDING, requires2FA: true
3. Frontend muestra modal de 2FA
4. Usuario ingresa codigo
5. Backend valida codigo
6. Si valido: procesar transferencia
7. Si invalido: incrementar intentos, bloquear si > 3
```

### RF-PAY-009.5: Notificaciones

**DEBE:**
1. Push notification instantanea al destinatario
2. Email de confirmacion a ambas partes
3. In-app notification en historial
4. Mostrar mensaje del remitente si existe

**Contenido de notificacion (destinatario):**
```
@juan_trader te envio $50.00 USD

"Para el cafe de ayer"

Tu nuevo saldo: $150.00 USD
```

**Contenido de notificacion (remitente):**
```
Transferencia exitosa

Enviaste $50.00 USD a @trader_pro
Referencia: ABC12345
Tu nuevo saldo: $200.00 USD
```

### RF-PAY-009.6: Historial de Transferencias

**DEBE:**
1. Mostrar transferencias enviadas y recibidas
2. Filtrar por tipo (enviadas/recibidas/todas)
3. Filtrar por rango de fechas
4. Buscar por destinatario/remitente
5. Mostrar mensaje asociado

**Vista de historial:**
```
+--------------------------------------------+
| Transferencias P2P                         |
+--------------------------------------------+
| [Todas] [Enviadas] [Recibidas]             |
+--------------------------------------------+
| 04 Ene 2026  @trader_pro                   |
| Enviado     -$50.00 USD    "Para el cafe"  |
+--------------------------------------------+
| 03 Ene 2026  @maria_fx                     |
| Recibido    +$100.00 USD   "Gracias!"      |
+--------------------------------------------+
| 02 Ene 2026  @carlos_btc                   |
| Enviado     -$25.00 USD                    |
+--------------------------------------------+
```

---

## Flujo de Transferencia P2P

```
+-------------+     +-------------+     +-------------+     +-------------+
|  Remitente  |     |  Frontend   |     |   Backend   |     | Destinatario|
+------+------+     +------+------+     +------+------+     +------+------+
       |                   |                   |                   |
       | Click "Enviar     |                   |                   |
       | Dinero"           |                   |                   |
       |------------------>|                   |                   |
       |                   |                   |                   |
       | Ingresa:          |                   |                   |
       | - @trader_pro     |                   |                   |
       | - $50 USD         |                   |                   |
       | - "Para el cafe"  |                   |                   |
       |------------------>|                   |                   |
       |                   |                   |                   |
       |                   | GET /users/lookup |                   |
       |                   | ?q=@trader_pro    |                   |
       |                   |------------------>|                   |
       |                   |<------------------|                   |
       |                   | { username,       |                   |
       |                   |   displayName,    |                   |
       |                   |   walletId }      |                   |
       |                   |                   |                   |
       |<------------------|                   |                   |
       | Preview:          |                   |                   |
       | "Juan P****z"     |                   |                   |
       |                   |                   |                   |
       | Confirma          |                   |                   |
       |------------------>|                   |                   |
       |                   |                   |                   |
       |                   | POST /p2p/transfer|                   |
       |                   | { receiverId,     |                   |
       |                   |   amount: 50,     |                   |
       |                   |   message }       |                   |
       |                   |------------------>|                   |
       |                   |                   |                   |
       |                   |                   | 1. Validate user  |
       |                   |                   | 2. Validate       |
       |                   |                   |    balance        |
       |                   |                   | 3. Validate       |
       |                   |                   |    limits         |
       |                   |                   | 4. Check 2FA req  |
       |                   |                   |    ($50 < $500)   |
       |                   |                   |    NO 2FA needed  |
       |                   |                   |                   |
       |                   |                   | BEGIN TX          |
       |                   |                   | - Debit sender    |
       |                   |                   | - Credit receiver |
       |                   |                   | - Create records  |
       |                   |                   | COMMIT TX         |
       |                   |                   |                   |
       |                   |<------------------|                   |
       |                   | { status:         |                   |
       |                   |   "completed",    |                   |
       |                   |   transferId }    |                   |
       |                   |                   |                   |
       |<------------------|                   |------------------>|
       | "Enviado!"        |                   | Push: "Recibiste  |
       |                   |                   | $50 de @juan"     |
       |                   |                   |                   |
       |<------------------|                   |------------------>|
       | Email:            |                   | Email:            |
       | Confirmacion      |                   | Confirmacion      |
       |                   |                   |                   |
```

---

## Flujo con 2FA (Monto > $500)

```
+-------------+     +-------------+     +-------------+
|  Remitente  |     |  Frontend   |     |   Backend   |
+------+------+     +------+------+     +------+------+
       |                   |                   |
       | Enviar $600 a     |                   |
       | @trader_pro       |                   |
       |------------------>|                   |
       |                   |                   |
       |                   | POST /p2p/transfer|
       |                   | { amount: 600 }   |
       |                   |------------------>|
       |                   |                   |
       |                   |                   | Detecta: amount >
       |                   |                   | $500, requiere 2FA
       |                   |                   |
       |                   |                   | Crear transfer:
       |                   |                   | status: PENDING
       |                   |                   | requires2FA: true
       |                   |                   |
       |                   |<------------------|
       |                   | { transferId,     |
       |                   |   requires2FA:    |
       |                   |   true }          |
       |                   |                   |
       |<------------------|                   |
       | Modal: "Ingresa   |                   |
       | codigo 2FA"       |                   |
       |                   |                   |
       | Ingresa: 123456   |                   |
       |------------------>|                   |
       |                   |                   |
       |                   | POST /p2p/transfer|
       |                   | /{id}/verify      |
       |                   | { code: 123456 }  |
       |                   |------------------>|
       |                   |                   |
       |                   |                   | Validar TOTP
       |                   |                   | Procesar transfer
       |                   |                   |
       |                   |<------------------|
       |                   | { status:         |
       |                   |   "completed" }   |
       |                   |                   |
       |<------------------|                   |
       | "Enviado!"        |                   |
       |                   |                   |
```

---

## Reglas de Negocio

### RN-001: Limites de Transferencia

| Limite | Valor |
|--------|-------|
| Monto minimo | $1.00 USD |
| Monto maximo por transferencia | $5,000.00 USD |
| Limite diario acumulado | $10,000.00 USD |
| Limite mensual | Sin limite |

### RN-002: Comisiones

| Concepto | Fee |
|----------|-----|
| Transferencia P2P | **0% (gratis)** |
| Sin fees ocultos | - |

### RN-003: Seguridad 2FA

| Monto | Requiere 2FA |
|-------|--------------|
| $0 - $500 | No |
| $500.01+ | Si |

### RN-004: Restricciones

- No se puede enviar a si mismo
- Ambas cuentas deben estar activas
- No se permite enviar a cuentas suspendidas
- Wallet del remitente debe tener saldo suficiente
- No se pueden revertir transferencias (excepto admin)

### RN-005: Mensajes

- Mensaje opcional
- Maximo 140 caracteres
- No se permiten URLs
- Filtro de palabras prohibidas
- Mensaje visible para ambas partes

---

## API Endpoints

### Buscar Usuario

```yaml
GET /api/v1/users/lookup:
  description: Buscar usuario para transferencia P2P
  query:
    q: string (email, username o walletId)
  response:
    id: string
    username: string
    displayName: string (parcialmente oculto)
    walletId: string
    verified: boolean
    memberSince: string
```

### Crear Transferencia

```yaml
POST /api/v1/p2p/transfers:
  description: Crear transferencia P2P
  body:
    receiverId: string
    amount: number
    message?: string
  response:
    transferId: string
    shortId: string
    status: string
    requires2FA: boolean
    amount: number
    receiver:
      username: string
      displayName: string
```

### Verificar 2FA

```yaml
POST /api/v1/p2p/transfers/{id}/verify:
  description: Verificar 2FA para transferencia pendiente
  body:
    code: string (6 digitos)
  response:
    transferId: string
    status: "completed"
    completedAt: string
```

### Historial de Transferencias

```yaml
GET /api/v1/p2p/transfers:
  description: Listar transferencias P2P del usuario
  query:
    type: string (sent | received | all)
    page: number
    limit: number
    dateFrom: string
    dateTo: string
  response:
    data:
      - id: string
        shortId: string
        type: string (sent | received)
        amount: number
        otherUser:
          username: string
          displayName: string
        message: string
        status: string
        createdAt: string
    pagination:
      total: number
      page: number
      pages: number
```

### Detalle de Transferencia

```yaml
GET /api/v1/p2p/transfers/{id}:
  description: Obtener detalle de una transferencia
  response:
    id: string
    shortId: string
    type: string
    amount: number
    message: string
    status: string
    sender:
      username: string
      displayName: string
    receiver:
      username: string
      displayName: string
    createdAt: string
    completedAt: string
```

---

## Seguridad

### Prevencion de Fraude

- Rate limit: maximo 10 transferencias por hora
- Alerta si multiples transferencias al mismo destinatario
- Bloqueo temporal si patron sospechoso
- Monitoreo de transferencias circulares (A→B→A)
- Limite diario reduce riesgo de robo de cuenta

### Validaciones

```typescript
async function validateP2PTransfer(
  senderId: string,
  receiverId: string,
  amount: number
): Promise<ValidationResult> {
  // 1. No enviar a si mismo
  if (senderId === receiverId) {
    throw new Error('No puedes enviarte dinero a ti mismo');
  }

  // 2. Destinatario existe y activo
  const receiver = await userRepo.findOne({ id: receiverId });
  if (!receiver || receiver.status !== 'active') {
    throw new Error('Usuario no encontrado o inactivo');
  }

  // 3. Saldo suficiente
  const wallet = await walletRepo.findOne({ userId: senderId });
  if (wallet.balance < amount) {
    throw new Error(`Saldo insuficiente. Disponible: $${wallet.balance}`);
  }

  // 4. Dentro de limites
  if (amount < 1 || amount > 5000) {
    throw new Error('Monto debe ser entre $1 y $5,000 USD');
  }

  // 5. Limite diario
  const todayTotal = await getDailyTransferTotal(senderId);
  if (todayTotal + amount > 10000) {
    throw new Error(`Excedes limite diario. Disponible hoy: $${10000 - todayTotal}`);
  }

  return { valid: true, requires2FA: amount > 500 };
}
```

### Auditoria

- Log de todas las transferencias
- Guardar IP y User-Agent del remitente
- Registro de intentos fallidos de 2FA
- Alertas a seguridad por patrones anomalos

---

## Manejo de Errores

| Error | Codigo | Mensaje Usuario |
|-------|--------|-----------------|
| Usuario no encontrado | 404 | No encontramos un usuario con ese email/username |
| Cuenta inactiva | 403 | Este usuario no puede recibir transferencias |
| Saldo insuficiente | 400 | Saldo insuficiente. Tienes $X disponible |
| Monto muy bajo | 400 | El monto minimo es $1 USD |
| Monto muy alto | 400 | El monto maximo por transferencia es $5,000 USD |
| Limite diario | 400 | Has alcanzado tu limite diario de $10,000 |
| Auto-transferencia | 400 | No puedes enviarte dinero a ti mismo |
| 2FA invalido | 401 | Codigo 2FA incorrecto. Te quedan X intentos |
| 2FA expirado | 401 | Codigo expirado. Solicita uno nuevo |
| Rate limit | 429 | Demasiadas transferencias. Espera 1 hora |

---

## Configuracion Requerida

```env
# Limites P2P
P2P_TRANSFER_MIN_USD=1
P2P_TRANSFER_MAX_USD=5000
P2P_TRANSFER_DAILY_LIMIT_USD=10000
P2P_2FA_THRESHOLD_USD=500

# Rate Limits
P2P_MAX_TRANSFERS_PER_HOUR=10
P2P_MAX_TRANSFERS_PER_DAY=50

# 2FA
P2P_2FA_CODE_EXPIRY_MINUTES=5
P2P_2FA_MAX_ATTEMPTS=3

# Mensajes
P2P_MESSAGE_MAX_LENGTH=140
```

---

## Metricas de Negocio

### KPIs a Rastrear

- **Transferencias P2P/dia:** Numero de transferencias completadas
- **Volumen P2P/dia:** Total transferido en USD
- **Usuarios activos P2P:** Usuarios unicos que envian/reciben
- **Ticket promedio:** Monto promedio por transferencia
- **Ratio envio/recepcion:** Balance de actividad
- **Tasa de conversion 2FA:** % de transferencias >$500 completadas

---

## Criterios de Aceptacion

- [ ] Usuario puede buscar destinatario por email
- [ ] Usuario puede buscar destinatario por username
- [ ] Usuario puede buscar destinatario por wallet ID
- [ ] Preview muestra nombre parcialmente oculto
- [ ] Transferencia de $50 se procesa sin 2FA
- [ ] Transferencia de $600 requiere 2FA
- [ ] Saldo se actualiza instantaneamente en ambos wallets
- [ ] Mensaje opcional se muestra a destinatario
- [ ] Push notification llega al destinatario
- [ ] Email de confirmacion llega a ambas partes
- [ ] Historial muestra transferencias enviadas y recibidas
- [ ] Limites de monto se validan correctamente
- [ ] Limite diario se calcula correctamente
- [ ] No se permite enviar a si mismo
- [ ] No se permite enviar a cuenta suspendida

---

## Especificacion Tecnica Relacionada

- [ET-PAY-009: P2P Transfers](../especificaciones/ET-PAY-009-p2p.md)

## Historias de Usuario Relacionadas

- [US-PAY-030: Enviar Dinero P2P](../historias-usuario/US-PAY-030-enviar-p2p.md)
- [US-PAY-031: Recibir Dinero P2P](../historias-usuario/US-PAY-031-recibir-p2p.md)
- [US-PAY-032: Ver Historial P2P](../historias-usuario/US-PAY-032-historial-p2p.md)
