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>
19 KiB
19 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| RF-PAY-008 | Sistema de Depositos via SPEI | Requirement | Draft | Alta | OQI-005 | trading-platform | 1.0.0 | 2026-01-04 | 2026-01-04 |
RF-PAY-008: Sistema de Depositos via SPEI
Version: 1.0.0 Fecha: 2026-01-04 Estado: Draft Prioridad: P1 (Alta) Story Points: 8 Epica: OQI-005
Descripcion
El sistema debe permitir a los usuarios en Mexico realizar depositos utilizando SPEI (Sistema de Pagos Electronicos Interbancarios), el sistema de transferencias bancarias instantaneas del pais. Se integrara con un agregador de pagos (Stripe Mexico, Conekta u OpenPay) para generar CLABEs virtuales unicas por usuario y reconciliar automaticamente los depositos recibidos.
Objetivo de Negocio
- Capturar mercado mexicano con metodo de pago preferido (85% de transferencias)
- Reducir costos de transaccion vs tarjetas (1-2% vs 3.5%)
- Eliminar chargebacks (SPEI es irreversible)
- Ofrecer alternativa para usuarios sin tarjeta de credito
- Aumentar ticket promedio (SPEI permite montos mayores)
Casos de Uso
- Deposito Simple: Usuario transfiere $5,000 MXN desde su banca en linea a su CLABE virtual
- Primer Deposito: Usuario obtiene su CLABE unica y realiza primera transferencia
- Deposito Recurrente: Usuario guarda CLABE como beneficiario frecuente en su banco
- Reconciliacion: Sistema detecta deposito y acredita USD equivalente automaticamente
- Notificacion: Usuario recibe push/email confirmando deposito procesado
Integracion con Agregadores
Opciones de Agregador
| Agregador | CLABE Virtual | Fee | Tiempo Integracion |
|---|---|---|---|
| Stripe MX | Si | 1% + $3 MXN | 2-3 semanas |
| Conekta | Si | 1.2% + $4 MXN | 1-2 semanas |
| OpenPay | Si | 0.9% + $2.5 MXN | 2-3 semanas |
Recomendado: Stripe MX por consistencia con otros metodos de pago.
Requisitos Funcionales
RF-PAY-008.1: Generacion de CLABE Virtual
DEBE:
- Generar CLABE de 18 digitos unica por usuario
- Asociar CLABE a
userIden tablaspei_references - CLABE permanente (no expira)
- Mostrar en UI con opcion de copiar
- Incluir nombre del beneficiario estandarizado
Formato CLABE:
Banco (3) + Plaza (3) + Cuenta (11) + Digito verificador (1)
Ejemplo: 646180157000001234
^^^ Banco: STP (646)
^^^ Plaza: 180
^^^^^^^^^^^ Cuenta unica por usuario
^ Digito verificador
Modelo de datos:
@Entity({ name: 'spei_references', schema: 'billing' })
class SpeiReference {
id: string; // UUID
userId: string; // FK a users (UNIQUE)
walletId: string; // FK a wallets
clabe: string; // CLABE 18 digitos (UNIQUE)
beneficiaryName: string; // "TRADING PLATFORM/USER123"
bankName: string; // "STP" o banco del agregador
isActive: boolean; // true
createdAt: Date;
updatedAt: Date;
}
RF-PAY-008.2: Recepcion de Depositos
DEBE:
- Recibir webhook del agregador cuando llega transferencia
- Validar firma del webhook
- Identificar usuario por CLABE destino
- Crear registro en
spei_deposits - Procesar conversion y acreditacion
Datos del webhook:
{
"event": "spei.incoming_transfer",
"data": {
"id": "tr_xxx",
"clabe_destino": "646180157000001234",
"clabe_origen": "012180001234567890",
"monto": 5000.00,
"concepto": "DEPOSITO TRADING",
"referencia_numerica": "1234567",
"nombre_ordenante": "JUAN PEREZ",
"rfc_ordenante": "XAXX010101000",
"fecha_operacion": "2026-01-04T10:30:00Z"
}
}
RF-PAY-008.3: Conversion MXN a USD
DEBE:
- Obtener tipo de cambio actual MXN/USD
- Usar tipo de cambio del Banco de Mexico + spread
- Calcular USD equivalente
- Acreditar al wallet en USD
- Registrar tipo de cambio usado
Calculo:
USD_Amount = MXN_Amount / (Exchange_Rate * (1 + spread))
Ejemplo (spread 0.5%):
$5,000 MXN / (17.50 * 1.005) = $284.21 USD
RF-PAY-008.4: Reconciliacion Automatica
DEBE:
- Procesar depositos automaticamente (sin intervencion manual)
- Manejar depositos duplicados (idempotencia)
- Rechazar depositos de CLABEs no registradas
- Alertar sobre depositos anomalos (montos muy altos)
- Generar reporte diario de reconciliacion
Estados de Deposito:
RECEIVED → Webhook recibido, validando
PROCESSING → Conversion en proceso
COMPLETED → Acreditado al wallet
FAILED → Error en procesamiento
REJECTED → CLABE no encontrada / limite excedido
RF-PAY-008.5: Notificaciones
DEBE:
- Push notification inmediata al recibir deposito
- Email de confirmacion con detalles
- Mostrar en historial de transacciones
- Incluir tipo de cambio usado
Contenido de notificacion:
Asunto: Deposito SPEI recibido - $5,000 MXN
Has recibido un deposito SPEI:
- Monto: $5,000.00 MXN
- Equivalente: $284.21 USD (TC: 17.59)
- Referencia: 1234567
- Fecha: 4 de enero 2026, 10:30 AM
Tu nuevo saldo es: $534.21 USD
Modelo de Datos
Tabla: spei_deposits
@Entity({ name: 'spei_deposits', schema: 'billing' })
class SpeiDeposit {
id: string; // UUID
userId: string; // FK a users
walletId: string; // FK a wallets
speiReferenceId: string; // FK a spei_references
status: SpeiDepositStatus; // received, processing, completed, failed, rejected
// Datos SPEI
clabeOrigen: string; // CLABE del ordenante
clabeDestino: string; // CLABE del usuario
montoMxn: Decimal; // Monto en MXN
concepto: string; // Concepto de la transferencia
referenciaNumerica: string; // Referencia numerica (7 digitos)
nombreOrdenante: string; // Nombre del que envia
rfcOrdenante?: string; // RFC del ordenante
fechaOperacion: Date; // Fecha/hora de la operacion
// Conversion
montoUsd: Decimal; // Monto convertido a USD
tipoCambio: Decimal; // Tipo de cambio usado
spreadAplicado: Decimal; // Spread aplicado (0.005)
// Proveedor
providerRef: string; // ID en Stripe/Conekta/OpenPay
providerData?: object; // Datos raw del proveedor
// Timestamps
createdAt: Date;
processedAt?: Date;
updatedAt: Date;
}
enum SpeiDepositStatus {
RECEIVED = 'received',
PROCESSING = 'processing',
COMPLETED = 'completed',
FAILED = 'failed',
REJECTED = 'rejected'
}
Flujo de Deposito SPEI
+-------------+ +-------------+ +-------------+ +-------------+ +-------------+
| Usuario | | Banco MX | | Agregador | | Backend | | Wallet |
+------+------+ +------+------+ +------+------+ +------+------+ +------+------+
| | | | |
| 1. Obtener CLABE | | | |
|------------------>| | | |
| | | | |
|<------------------| | | |
| CLABE: | | | |
| 646180157000001234| | | |
| | | | |
| 2. Accede a banca | | | |
| en linea | | | |
|------------------>| | | |
| | | | |
| 3. Registra CLABE | | | |
| como beneficiario | | | |
|------------------>| | | |
| | | | |
| 4. Transfiere | | | |
| $5,000 MXN | | | |
|------------------>| | | |
| | | | |
| | 5. SPEI procesa | | |
| | transferencia | | |
| | (5-30 min) | | |
| |------------------>| | |
| | | | |
| | | 6. Webhook: | |
| | | spei.incoming | |
| | |------------------>| |
| | | | |
| | | | 7. Validar CLABE |
| | | | en spei_references|
| | | | |
| | | | 8. Crear |
| | | | spei_deposit |
| | | | status: RECEIVED |
| | | | |
| | | | 9. Obtener TC |
| | | | del dia |
| | | | |
| | | | 10. Convertir |
| | | | $5000 MXN = |
| | | | $284.21 USD |
| | | | |
| | | | 11. Acreditar |
| | | |------------------>|
| | | |<------------------|
| | | | wallet.balance |
| | | | += $284.21 |
| | | | |
| | | | 12. Update status |
| | | | COMPLETED |
| | | | |
|<------------------|-------------------|-------------------| |
| Push: "Deposito | | | |
| recibido! | | | |
| +$284.21 USD" | | | |
| | | | |
|<------------------|-------------------|-------------------| |
| Email: Detalles | | | |
| del deposito | | | |
| | | | |
Reglas de Negocio
RN-001: Limites de Deposito SPEI
| Limite | Valor |
|---|---|
| Deposito minimo | $100 MXN |
| Deposito maximo por transaccion | $500,000 MXN |
| Deposito maximo diario | $1,000,000 MXN |
| Sin limite mensual | - |
RN-002: Tipo de Cambio
- Usar tipo de cambio FIX del Banco de Mexico
- Actualizar tipo de cambio cada 15 minutos
- Aplicar spread de 0.5% sobre tipo de cambio
- Cachear tipo de cambio para consistencia intraday
- Mostrar tipo de cambio antes de depositar
RN-003: Horarios SPEI
| Horario | Disponibilidad |
|---|---|
| Lunes-Viernes 6:00-17:30 | Tiempo real (5-15 min) |
| Lunes-Viernes 17:30-20:00 | Mismo dia (antes 20:00) |
| Sabados 6:00-14:00 | Tiempo real |
| Domingos/Festivos | Siguiente dia habil |
RN-004: Validacion de Depositos
- CLABE destino debe existir en
spei_references - Monto debe estar dentro de limites
- Referencia numerica no debe repetirse (idempotencia)
- Depositos fuera de horario se procesan en siguiente ventana
RN-005: Reconciliacion
- Depositos se reconcilian automaticamente por CLABE
- No se requiere referencia especifica del usuario
- Concepto de transferencia se guarda para auditoria
- Reportes diarios a las 20:00 CST
Integracion con Stripe Mexico
Configuracion
// Crear cuenta SPEI reference
const speiReference = await stripe.customers.createSource(
customerId,
{
type: 'clabe',
clabe: {
// Stripe genera automaticamente
}
}
);
// Resultado
{
id: "src_xxx",
type: "clabe",
clabe: {
clabe: "646180157000001234",
bank_name: "STP",
beneficiary_name: "STRIPE/TRADING PLATFORM"
}
}
Webhook Handler
@Post('/webhooks/stripe/spei')
async handleSpeiWebhook(
@Body() payload: StripeWebhookPayload,
@Headers('stripe-signature') signature: string
) {
// 1. Validar firma
const event = stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_SPEI_WEBHOOK_SECRET
);
// 2. Procesar segun tipo de evento
switch (event.type) {
case 'source.chargeable':
await this.processSpeiDeposit(event.data.object);
break;
case 'source.failed':
await this.handleSpeiFailure(event.data.object);
break;
}
return { received: true };
}
API Endpoints
Obtener CLABE
GET /api/v1/spei/clabe:
description: Obtener CLABE virtual del usuario
response:
clabe: string
bankName: string
beneficiaryName: string
limits:
min: number
max: number
dailyMax: number
exchangeRate:
rate: number
validUntil: string
Historial de Depositos SPEI
GET /api/v1/spei/deposits:
description: Listar depositos SPEI del usuario
query:
page: number
limit: number
status: string
dateFrom: string
dateTo: string
response:
data:
- id: string
status: string
montoMxn: number
montoUsd: number
tipoCambio: number
fechaOperacion: string
nombreOrdenante: string
pagination:
total: number
page: number
pages: number
Tipo de Cambio Actual
GET /api/v1/spei/exchange-rate:
description: Obtener tipo de cambio MXN/USD actual
response:
rate: number
spread: number
effectiveRate: number
validUntil: string
source: string
Seguridad
Validacion de Webhooks
function validateStripeSpeiSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSig = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expectedSig}`)
);
}
Prevencion de Fraude
- Monitorear depositos de misma CLABE origen (lavado)
- Alerta si deposito > $100,000 MXN
- Verificar RFC del ordenante vs usuario registrado
- Bloquear cuenta si patron sospechoso
Idempotencia
- Usar
referenciaNumerica+fechaOperacioncomo clave unica - Ignorar webhooks duplicados
- Registrar todos los intentos para auditoria
Manejo de Errores
| Error | Codigo | Mensaje Usuario |
|---|---|---|
| CLABE no encontrada | 404 | Error interno. Contacta soporte. |
| Monto bajo limite | 400 | El monto minimo de deposito es $100 MXN |
| Monto sobre limite | 400 | El monto maximo por transferencia es $500,000 MXN |
| Limite diario excedido | 400 | Has excedido el limite diario de $1,000,000 MXN |
| Error de conversion | 500 | Error procesando tipo de cambio. Intenta mas tarde. |
| Proveedor no disponible | 503 | Sistema SPEI temporalmente no disponible |
Webhooks Recibidos
| Evento | Accion |
|---|---|
source.chargeable |
Crear spei_deposit, procesar conversion |
source.failed |
Marcar como FAILED, notificar usuario |
source.canceled |
Marcar como REJECTED |
Configuracion Requerida
# Stripe Mexico
STRIPE_MX_SECRET_KEY=sk_xxx
STRIPE_SPEI_WEBHOOK_SECRET=whsec_xxx
# Conekta (alternativo)
CONEKTA_API_KEY=xxx
CONEKTA_WEBHOOK_SECRET=xxx
# Tipo de Cambio
EXCHANGE_RATE_API_URL=https://api.banxico.org.mx/SieAPIRest/service/v1/series/SF43718/datos/oportuno
EXCHANGE_RATE_BANXICO_TOKEN=xxx
EXCHANGE_RATE_CACHE_TTL_MINUTES=15
EXCHANGE_RATE_SPREAD=0.005
# Limites SPEI
SPEI_DEPOSIT_MIN_MXN=100
SPEI_DEPOSIT_MAX_MXN=500000
SPEI_DEPOSIT_MAX_DAILY_MXN=1000000
Metricas de Negocio
KPIs a Rastrear
- Depositos SPEI/dia: Numero de depositos procesados
- Volumen MXN/dia: Total depositado en MXN
- Volumen USD/dia: Total convertido a USD
- Ticket promedio: Monto promedio por deposito
- Tiempo procesamiento: Promedio entre recepcion y acreditacion
- Tasa de error: % de depositos fallidos/rechazados
Criterios de Aceptacion
- Usuario obtiene CLABE unica de 18 digitos
- CLABE se muestra con opcion de copiar
- Depositos se detectan via webhook del agregador
- Conversion MXN a USD aplica tipo de cambio correcto
- Spread de 0.5% se aplica correctamente
- Wallet se acredita en USD automaticamente
- Push notification se envia al confirmar deposito
- Email de confirmacion incluye todos los detalles
- Depositos duplicados se manejan correctamente (idempotencia)
- Limites de monto se validan correctamente
- Historial de depositos SPEI es visible en UI
- Tipo de cambio se actualiza cada 15 minutos