trading-platform/docs/02-definicion-modulos/OQI-005-payments-stripe/requerimientos/RF-PAY-009-p2p.md
rckrdmrd a7cca885f0 feat: Major platform documentation and architecture updates
Changes include:
- Updated architecture documentation
- Enhanced module definitions (OQI-001 to OQI-008)
- ML integration documentation updates
- Trading strategies documentation
- Orchestration and inventory updates
- Docker configuration updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 05:33:35 -06:00

648 lines
20 KiB
Markdown

---
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)