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