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>
24 KiB
24 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| RF-PAY-007 | Sistema de Depositos y Retiros en Criptomonedas | Requirement | Draft | Alta | OQI-005 | trading-platform | 1.0.0 | 2026-01-04 | 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
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
- Deposito BTC: Usuario envia 0.01 BTC a direccion unica y recibe $450 USD en wallet
- Deposito USDT: Usuario envia 100 USDT (TRC-20) y recibe $100 USD en wallet
- Retiro ETH: Usuario solicita retiro de $200 USD y recibe 0.08 ETH en su wallet externo
- Verificacion KYC: Usuario completa KYC para habilitar retiros crypto
- 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:
- Generar direccion unica de deposito por usuario por moneda/red
- Asociar direccion a
userIden tablacrypto_addresses - Mostrar QR code y direccion copiable en UI
- Detectar red correcta segun direccion (ERC-20 vs TRC-20)
- Reutilizar direccion para depositos futuros del mismo usuario
Modelo de datos:
@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:
- Monitorear direcciones via webhooks del proveedor (Coinbase/BitPay)
- Detectar transacciones entrantes en tiempo real
- Mostrar estado "Pendiente" con contador de confirmaciones
- Convertir a USD al alcanzar confirmaciones requeridas
- 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:
- Obtener tasa de cambio en tiempo real al momento de confirmacion
- Aplicar spread de 0.5% sobre tasa de mercado
- Convertir monto crypto a USD equivalente
- Acreditar USD al wallet interno
- 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:
- Permitir retiros a wallet externo del usuario
- Solicitar direccion de destino y red
- Validar formato de direccion segun red
- Calcular monto crypto segun tasa actual
- Procesar retiro via API del proveedor
- 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:
- Bloquear retiros crypto hasta completar KYC
- Solicitar documentos: ID gubernamental + selfie + comprobante domicilio
- Verificar via proveedor KYC (Jumio, Onfido)
- Aprobar/rechazar en maximo 24 horas
- 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:
- Recibir webhooks de Coinbase Commerce/BitPay
- Validar firma HMAC del webhook
- Actualizar estado de transaccion en BD
- Emitir evento interno para procesamiento
- Responder 200 OK inmediatamente (procesamiento async)
Eventos a manejar:
charge:created- Transaccion detectadacharge:pending- Esperando confirmacionescharge:confirmed- Confirmaciones completascharge:failed- Transaccion fallida/expirada
Modelo de Datos
Tabla: crypto_transactions
@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)
// 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)
// 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
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
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
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
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
# 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