trading-platform/docs/02-definicion-modulos/OQI-005-payments-stripe/requerimientos/RF-PAY-007-crypto.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

645 lines
24 KiB
Markdown

---
id: "RF-PAY-007"
title: "Sistema de Depositos y Retiros en Criptomonedas"
type: "Requirement"
status: "Draft"
priority: "Alta"
epic: "OQI-005"
project: "trading-platform"
version: "1.0.0"
created_date: "2026-01-04"
updated_date: "2026-01-04"
---
# RF-PAY-007: Sistema de Depositos y Retiros en Criptomonedas
**Version:** 1.0.0
**Fecha:** 2026-01-04
**Estado:** Draft
**Prioridad:** P1 (Alta)
**Story Points:** 13
**Epica:** [OQI-005](../_MAP.md)
---
## Descripcion
El sistema debe permitir a los usuarios realizar depositos y retiros utilizando criptomonedas, integrando con proveedores de pago crypto como Coinbase Commerce o BitPay, soportando multiples monedas y redes blockchain, con conversion automatica a USD para mantener el balance del wallet interno.
---
## Objetivo de Negocio
- Expandir opciones de pago para usuarios globales sin acceso bancario tradicional
- Atraer segmento crypto-nativo con preferencia por pagos descentralizados
- Reducir costos de transaccion en mercados internacionales
- Ofrecer alternativa a usuarios con restricciones bancarias
- Habilitar depositos anonimos (hasta limite KYC)
---
## Casos de Uso
1. **Deposito BTC:** Usuario envia 0.01 BTC a direccion unica y recibe $450 USD en wallet
2. **Deposito USDT:** Usuario envia 100 USDT (TRC-20) y recibe $100 USD en wallet
3. **Retiro ETH:** Usuario solicita retiro de $200 USD y recibe 0.08 ETH en su wallet externo
4. **Verificacion KYC:** Usuario completa KYC para habilitar retiros crypto
5. **Monitoreo de Confirmaciones:** Sistema muestra progreso de confirmaciones on-chain
---
## Monedas y Redes Soportadas
| Moneda | Simbolo | Redes Soportadas | Confirmaciones Requeridas |
|--------|---------|------------------|---------------------------|
| Bitcoin | BTC | Bitcoin Network | 3 confirmaciones |
| Ethereum | ETH | Ethereum Mainnet | 12 confirmaciones |
| Tether | USDT | ERC-20, TRC-20 | 12 (ERC-20), 20 (TRC-20) |
| USD Coin | USDC | ERC-20, Polygon | 12 (ERC-20), 30 (Polygon) |
---
## Requisitos Funcionales
### RF-PAY-007.1: Generacion de Direcciones Unicas
**DEBE:**
1. Generar direccion unica de deposito por usuario por moneda/red
2. Asociar direccion a `userId` en tabla `crypto_addresses`
3. Mostrar QR code y direccion copiable en UI
4. Detectar red correcta segun direccion (ERC-20 vs TRC-20)
5. Reutilizar direccion para depositos futuros del mismo usuario
**Modelo de datos:**
```typescript
@Entity({ name: 'crypto_addresses', schema: 'billing' })
class CryptoAddress {
id: string; // UUID
userId: string; // FK a users
currency: string; // BTC, ETH, USDT
network: string; // bitcoin, ethereum, tron, polygon
address: string; // Direccion de deposito unica
derivationPath?: string; // Para HD wallets
isActive: boolean; // true
createdAt: Date;
updatedAt: Date;
}
```
### RF-PAY-007.2: Deteccion y Procesamiento de Depositos
**DEBE:**
1. Monitorear direcciones via webhooks del proveedor (Coinbase/BitPay)
2. Detectar transacciones entrantes en tiempo real
3. Mostrar estado "Pendiente" con contador de confirmaciones
4. Convertir a USD al alcanzar confirmaciones requeridas
5. Acreditar al wallet interno del usuario
**Flujo de Confirmaciones:**
```
0 confirmaciones → Estado: DETECTED (detectado)
1 confirmacion → Estado: PENDING (pendiente)
3+ confirmaciones → Estado: CONFIRMED (BTC)
12+ confirmaciones → Estado: CONFIRMED (ETH/ERC-20)
20+ confirmaciones → Estado: CONFIRMED (TRC-20)
```
### RF-PAY-007.3: Conversion Automatica a USD
**DEBE:**
1. Obtener tasa de cambio en tiempo real al momento de confirmacion
2. Aplicar spread de 0.5% sobre tasa de mercado
3. Convertir monto crypto a USD equivalente
4. Acreditar USD al wallet interno
5. Registrar tasa de cambio usada en transaccion
**Calculo:**
```
USD_Amount = Crypto_Amount * Market_Rate * (1 - 0.005)
Ejemplo:
0.01 BTC * $45,000/BTC * 0.995 = $447.75 USD
```
### RF-PAY-007.4: Sistema de Retiros Crypto
**DEBE:**
1. Permitir retiros a wallet externo del usuario
2. Solicitar direccion de destino y red
3. Validar formato de direccion segun red
4. Calcular monto crypto segun tasa actual
5. Procesar retiro via API del proveedor
6. Enviar notificacion con TX hash
**Validaciones:**
- KYC aprobado obligatorio para retiros
- Monto minimo: $50 USD equivalente
- Monto maximo: $10,000 USD por transaccion
- Limite diario: $50,000 USD
- Cooldown: 24h para nuevas direcciones
### RF-PAY-007.5: Verificacion KYC para Retiros
**DEBE:**
1. Bloquear retiros crypto hasta completar KYC
2. Solicitar documentos: ID gubernamental + selfie + comprobante domicilio
3. Verificar via proveedor KYC (Jumio, Onfido)
4. Aprobar/rechazar en maximo 24 horas
5. Permitir retiros una vez aprobado
**Niveles KYC:**
| Nivel | Requisitos | Limite Retiro |
|-------|------------|---------------|
| Ninguno | Solo email | $0 (sin retiros) |
| Basico | ID + Selfie | $1,000/dia |
| Verificado | ID + Selfie + Domicilio | $50,000/dia |
### RF-PAY-007.6: Webhooks de Confirmaciones
**DEBE:**
1. Recibir webhooks de Coinbase Commerce/BitPay
2. Validar firma HMAC del webhook
3. Actualizar estado de transaccion en BD
4. Emitir evento interno para procesamiento
5. Responder 200 OK inmediatamente (procesamiento async)
**Eventos a manejar:**
- `charge:created` - Transaccion detectada
- `charge:pending` - Esperando confirmaciones
- `charge:confirmed` - Confirmaciones completas
- `charge:failed` - Transaccion fallida/expirada
---
## Modelo de Datos
### Tabla: crypto_transactions
```typescript
@Entity({ name: 'crypto_transactions', schema: 'billing' })
class CryptoTransaction {
id: string; // UUID
userId: string; // FK a users
walletId: string; // FK a wallets
type: 'deposit' | 'withdrawal';
status: CryptoTxStatus; // detected, pending, confirmed, failed, expired
currency: string; // BTC, ETH, USDT
network: string; // bitcoin, ethereum, tron, polygon
cryptoAmount: Decimal; // Monto en crypto
usdAmount: Decimal; // Monto en USD (convertido)
exchangeRate: Decimal; // Tasa de cambio usada
fromAddress?: string; // Direccion origen (depositos)
toAddress: string; // Direccion destino
txHash?: string; // Hash de transaccion on-chain
confirmations: number; // Confirmaciones actuales
requiredConfirmations: number; // Confirmaciones requeridas
providerRef: string; // ID en Coinbase/BitPay
fee?: Decimal; // Fee de red
metadata?: object;
createdAt: Date;
confirmedAt?: Date;
updatedAt: Date;
}
enum CryptoTxStatus {
DETECTED = 'detected',
PENDING = 'pending',
CONFIRMED = 'confirmed',
FAILED = 'failed',
EXPIRED = 'expired'
}
```
---
## Flujo de Deposito Crypto
```
+-------------+ +-------------+ +-------------+ +-------------+ +-------------+
| Usuario | | Frontend | | Backend | | Coinbase | | Blockchain |
+------+------+ +------+------+ +------+------+ +------+------+ +------+------+
| | | | |
| Selecciona | | | |
| "Depositar BTC" | | | |
|------------------>| | | |
| | | | |
| | GET /crypto/ | | |
| | address?currency= | | |
| | BTC&network=bitcoin| | |
| |------------------>| | |
| | | | |
| | | Get/Create | |
| | | unique address | |
| | |------------------>| |
| | |<------------------| |
| | | | |
| |<------------------| | |
| | { address, qrCode } | |
| | | | |
|<------------------| | | |
| Muestra QR + | | | |
| direccion | | | |
| | | | |
| Usuario envia | | | |
| 0.01 BTC desde | | | |
| su wallet externo | | | |
|---------------------------------------------------------->|----------------->|
| | | | |
| | | | TX broadcast |
| | | |<-----------------|
| | | | |
| | |<------------------| |
| | | Webhook: | |
| | | charge:created | |
| | | | |
| | | Create | |
| | | crypto_transaction| |
| | | status: DETECTED | |
| | | | |
|<------------------|-------------------| | |
| Push: "Deposito | | | |
| detectado, 0/3 | | | |
| confirmaciones" | | | |
| | | | |
| | |<------------------| |
| | | Webhook: | |
| | | charge:confirmed | |
| | | (3 confirmations) | |
| | | | |
| | | BEGIN TX | |
| | | 1. Get exchange | |
| | | rate | |
| | | 2. Convert to USD | |
| | | 3. Credit wallet | |
| | | 4. Update status | |
| | | COMMIT TX | |
| | | | |
|<------------------|-------------------| | |
| Push: "Deposito | | | |
| confirmado! | | | |
| +$447.75 USD" | | | |
| | | | |
```
---
## Flujo de Retiro Crypto
```
+-------------+ +-------------+ +-------------+ +-------------+
| Usuario | | Frontend | | Backend | | Coinbase |
+------+------+ +------+------+ +------+------+ +------+------+
| | | |
| Click "Retirar | | |
| a Crypto" | | |
|------------------>| | |
| | | |
| | GET /user/kyc | |
| |------------------>| |
| |<------------------| |
| | { status: | |
| | "verified" } | |
| | | |
| Ingresa: | | |
| - $200 USD | | |
| - BTC | | |
| - bc1q...xyz | | |
|------------------>| | |
| | | |
| | POST /crypto/ | |
| | withdraw | |
| | { amount: 200, | |
| | currency: "BTC",| |
| | address: "bc1q..| |
| | network: "bitcoin"} |
| |------------------>| |
| | | |
| | | 1. Validate KYC |
| | | 2. Validate addr |
| | | 3. Check limits |
| | | 4. Get rate |
| | | 5. Calculate BTC |
| | | ($200 = 0.0044)|
| | | |
| | | 6. Debit wallet |
| | | 7. Create pending |
| | | withdrawal |
| | | |
| | | 8. Request payout |
| | |------------------>|
| | |<------------------|
| | | { payoutId } |
| | | |
| |<------------------| |
| | { status: "pending", |
| | cryptoAmount: 0.0044, |
| | estimatedTime: "30min" } |
| | | |
|<------------------| | |
| "Retiro procesando| | |
| 0.0044 BTC a | | |
| bc1q...xyz" | | |
| | | |
| | |<------------------|
| | | Webhook: |
| | | payout:completed |
| | | txHash: 0xabc... |
| | | |
|<------------------|-------------------| |
| Email: "Retiro | | |
| completado! | | |
| TX: 0xabc..." | | |
| | | |
```
---
## Reglas de Negocio
### RN-001: Limites de Deposito
| Limite | Valor |
|--------|-------|
| Deposito minimo | $10 USD equivalente |
| Deposito maximo por transaccion | $50,000 USD equivalente |
| Deposito maximo diario | $100,000 USD equivalente |
| Sin limite mensual | - |
### RN-002: Limites de Retiro
| Limite | Sin KYC | KYC Basico | KYC Verificado |
|--------|---------|------------|----------------|
| Por transaccion | $0 | $1,000 | $10,000 |
| Diario | $0 | $1,000 | $50,000 |
| Mensual | $0 | $5,000 | $200,000 |
### RN-003: Fees y Spreads
| Concepto | Valor |
|----------|-------|
| Deposito | GRATIS (usuario paga fee de red) |
| Spread conversion | 0.5% |
| Retiro fee fijo | $5 USD |
| Retiro fee red | Variable (estimado mostrado) |
### RN-004: Expiracion de Transacciones
- Depositos no confirmados expiran en **72 horas**
- Retiros pendientes expiran en **24 horas**
- Transacciones expiradas se marcan como `EXPIRED`
- Fondos de retiros expirados se devuelven al wallet
### RN-005: Direcciones de Retiro
- Primera vez: direccion debe esperar 24h antes de usar
- Direcciones usadas previamente: retiro inmediato
- Maximo 5 direcciones guardadas por moneda
- Cambio de direccion requiere 2FA
---
## Integracion con Proveedores
### Coinbase Commerce (Recomendado)
```typescript
// Configuracion
const coinbase = new CoinbaseCommerce({
apiKey: process.env.COINBASE_API_KEY,
webhookSecret: process.env.COINBASE_WEBHOOK_SECRET
});
// Crear charge para deposito
const charge = await coinbase.charges.create({
name: `Deposito ${userId}`,
description: 'Deposito a wallet trading-platform',
pricing_type: 'no_price', // Usuario decide monto
metadata: {
userId,
walletId,
type: 'deposit'
}
});
```
### BitPay (Alternativa)
```typescript
// Configuracion
const bitpay = new BitPayClient({
token: process.env.BITPAY_TOKEN,
environment: 'prod'
});
// Crear invoice para deposito
const invoice = await bitpay.createInvoice({
price: 0, // Variable
currency: 'USD',
buyer: { email: user.email },
posData: JSON.stringify({ userId, walletId })
});
```
---
## API Endpoints
### Depositos
```yaml
GET /api/v1/crypto/deposit/address:
description: Obtener direccion de deposito
query:
currency: string (BTC, ETH, USDT)
network: string (bitcoin, ethereum, tron, polygon)
response:
address: string
qrCode: string (base64)
currency: string
network: string
minDeposit: number
GET /api/v1/crypto/deposit/status/{txId}:
description: Estado de deposito
response:
status: string
confirmations: number
requiredConfirmations: number
cryptoAmount: number
usdAmount: number
txHash: string
```
### Retiros
```yaml
POST /api/v1/crypto/withdraw:
description: Solicitar retiro crypto
body:
amount: number (USD)
currency: string
network: string
address: string
response:
withdrawalId: string
cryptoAmount: number
exchangeRate: number
fee: number
estimatedTime: string
GET /api/v1/crypto/withdraw/{id}:
description: Estado de retiro
response:
status: string
txHash: string
cryptoAmount: number
address: string
```
### Direcciones Guardadas
```yaml
GET /api/v1/crypto/addresses:
description: Listar direcciones de retiro guardadas
POST /api/v1/crypto/addresses:
description: Agregar direccion de retiro
body:
currency: string
network: string
address: string
label: string
DELETE /api/v1/crypto/addresses/{id}:
description: Eliminar direccion guardada
```
---
## Seguridad
### Validacion de Direcciones
```typescript
function validateCryptoAddress(address: string, network: string): boolean {
switch (network) {
case 'bitcoin':
// Legacy (1...), SegWit (3...), Native SegWit (bc1...)
return /^(1|3)[a-zA-Z0-9]{25,34}$|^bc1[a-z0-9]{39,59}$/.test(address);
case 'ethereum':
case 'polygon':
// ERC-20 format
return /^0x[a-fA-F0-9]{40}$/.test(address);
case 'tron':
// TRC-20 format
return /^T[a-zA-Z0-9]{33}$/.test(address);
default:
return false;
}
}
```
### Prevencion de Fraude
- Monitorear patrones de deposito/retiro inusuales
- Bloquear retiros inmediatos post-deposito (1h cooldown)
- Verificar consistencia de direcciones (no reutilizar entre usuarios)
- Alerta si retiro > 50% del deposito reciente
- Require 2FA para retiros > $1,000
### Hot/Cold Wallet
- Hot wallet: maximo 10% de fondos totales
- Cold wallet: 90% de fondos en almacenamiento offline
- Rebalanceo automatico diario
- Multi-sig para cold wallet (3 de 5 firmas)
---
## Manejo de Errores
| Error | Codigo | Mensaje Usuario |
|-------|--------|-----------------|
| KYC no verificado | 403 | Completa la verificacion de identidad para retirar |
| Direccion invalida | 400 | La direccion ingresada no es valida para {network} |
| Limite excedido | 400 | Excedes el limite de ${limit}/dia. Intenta manana. |
| Saldo insuficiente | 400 | Saldo insuficiente. Disponible: ${balance} |
| Red no soportada | 400 | La red {network} no esta soportada para {currency} |
| Direccion muy nueva | 400 | Nueva direccion. Espera 24h antes de usarla. |
| Proveedor no disponible | 503 | Servicio temporalmente no disponible. Intenta mas tarde. |
---
## Webhooks Recibidos
| Evento | Accion |
|--------|--------|
| `charge:created` | Crear crypto_transaction con status DETECTED |
| `charge:pending` | Actualizar confirmaciones |
| `charge:confirmed` | Convertir a USD, acreditar wallet |
| `charge:failed` | Marcar como FAILED |
| `payout:completed` | Actualizar retiro con txHash |
| `payout:failed` | Revertir debito de wallet |
---
## Configuracion Requerida
```env
# Coinbase Commerce
COINBASE_API_KEY=xxx
COINBASE_WEBHOOK_SECRET=xxx
# BitPay (alternativo)
BITPAY_TOKEN=xxx
BITPAY_WEBHOOK_SECRET=xxx
# Limites
CRYPTO_DEPOSIT_MIN_USD=10
CRYPTO_DEPOSIT_MAX_USD=50000
CRYPTO_DEPOSIT_MAX_DAILY_USD=100000
CRYPTO_WITHDRAWAL_MIN_USD=50
CRYPTO_WITHDRAWAL_FEE_USD=5
CRYPTO_CONVERSION_SPREAD=0.005
# Confirmaciones
CRYPTO_CONFIRMATIONS_BTC=3
CRYPTO_CONFIRMATIONS_ETH=12
CRYPTO_CONFIRMATIONS_TRC20=20
CRYPTO_CONFIRMATIONS_POLYGON=30
# Seguridad
CRYPTO_NEW_ADDRESS_COOLDOWN_HOURS=24
CRYPTO_POST_DEPOSIT_COOLDOWN_HOURS=1
```
---
## Criterios de Aceptacion
- [ ] Usuario puede obtener direccion unica de deposito por moneda/red
- [ ] QR code se genera correctamente para cada direccion
- [ ] Depositos se detectan via webhook en tiempo real
- [ ] Contador de confirmaciones se actualiza correctamente
- [ ] Conversion a USD aplica spread de 0.5%
- [ ] Wallet se acredita automaticamente al confirmar deposito
- [ ] Usuario sin KYC no puede retirar
- [ ] Usuario con KYC puede retirar hasta su limite
- [ ] Validacion de direcciones funciona para todas las redes
- [ ] Retiros se procesan y notifican con TX hash
- [ ] Limites diarios se aplican correctamente
- [ ] Cooldown de 24h para nuevas direcciones funciona
- [ ] Transacciones no confirmadas expiran en 72h
---
## Especificacion Tecnica Relacionada
- [ET-PAY-007: Crypto Integration](../especificaciones/ET-PAY-007-crypto.md)
## Historias de Usuario Relacionadas
- [US-PAY-020: Depositar Crypto](../historias-usuario/US-PAY-020-depositar-crypto.md)
- [US-PAY-021: Retirar Crypto](../historias-usuario/US-PAY-021-retirar-crypto.md)
- [US-PAY-022: Ver Estado Deposito](../historias-usuario/US-PAY-022-estado-deposito.md)