Balance y carteras: arquitectura multi-wallet
1) Por qué multi-wallet y qué objetivos
Una entrada «balance = número» no cubre la realidad de iGaming. Se necesitan monederos/subcuentas individuales: dinero real (cash), fondos de bonificación, pool vager, giros gratis, puntos integrados, a veces, monederos de divisas (EUR/USD/BRL).
Objetivos de la arquitectura:- Precisión del dinero (doble entrada, auditabilidad).
- Políticas de cancelación (por ejemplo, primero bonus/vager, luego cash).
- Velocidad (p95 API ≤ 250-400 ms, apuesta/settlement en tiempo real).
- Seguridad y cumplimiento (KYC/AML, límites de juego responsable, reguladores).
- Escala: picos → decenas de miles de operaciones/segundos, miles de millones de postings/mes.
2) Modelo de datos: «Ledger + Subwallets»
Entidades mínimas
Cuenta: jugador/marca/mercado.
Ejemplo de tablas (simplificado)
sql
-- Cuentas de balance para doble entrada (incluidas las cuentas de servicio)
accounts(id, owner_user_id, type, currency, status,...)
-- Cableado (entrada doble, referencia a la operación empresarial)
ledger_entries(id, posting_id, debit_account_id, credit_account_id,        amount_minor, currency, category, operation_id, created_at)
-- Holds (reservas)
holds(id, account_id, amount_minor, currency, reason, expires_at, state,    operation_id, created_at)
- Políticas de cancelación (prioridades)
spend_policies(id, market, wallet_priority jsonb, updated_at)
-- Tipos de cambio cruzados fx_rates (ccy_from, ccy_to, tasa, precisión, valid_from)Regla: la verdad vive en el registro de cableado ('ledger _ entries'). El balance actual es un agregado (snapshot materializado) o se calcula a partir de un registro (caro, pero el único cierto).
3) Tipos de billeteras y su comportamiento
4) Políticas de cancelación y orden de prioridad
Formalice claramente el algoritmo de fuente de fondos: Ejemplo (ranuras/casinos):1. Cancelar primero de WAGER (si el Vager está activo).
2. Luego de BONUS, aún no se ha agotado.
3. El resto es de CASH.
Ejemplo (deporte):1. Primero CASH (regulador/impuesto).
2. Luego BONUS (freebet), traduciendo a WAGER.
Guarde en Postings la «solución de políticas» como un atributo para que el zapport y la auditoría vean «por qué se ha cargado tanto».
5) Ciclo de vida del dinero y las operaciones
Depósito
1. 'POST/wallet/deposite' → creamos una grabación pending (inbox salchicha PSP).
2. Webhook PSP (firma HMAC, idempotencia por 'operation _ id') → credit CASH, categoría = 'DEPÓSITO'.
3. Publicamos el evento 'wallet _ updated'.
1. 'POST/bet/place' → creamos un hold (reserva) en la cuenta de origen (CASH/BONUS/WAGER).
2. Al confirmar la apuesta → la transferencia hold → debit de la fuente, credit de la cuenta de «liquidación» de servicio del proveedor.
3. Cuando se cancela - release hold.
Settlment (resultado)
Ganancia: debit la cuenta «calculada» del proveedor → credit CASH o WAGER→BONUS→CASH de políticas.
Pérdida: cerramos con un cableado el «gasto» del proveedor → sin créditos al jugador.
1. Verificación de KYC/AML, límites del juego responsable.
2. Hold por la cantidad de salida.
3. Éxito PSP → debit final CASH → credit cuenta de «pago».
4. Falla PSP → release hold.
6) Idempotencia y exactly-once «en el sentido»
En todas partes 'operation _ id' (UUID/ULID mejorado) con un índice único. Volver a preguntar → el estado de la operación pasada.
Webhooks PSP/proveedor de juegos: Mesa de entrada con dedupe por 'event _ id + signature'. El procesamiento es un worker idempotente (patrón de Outbox).
Idempotency-Key en HTTP para el cliente; TTL almacenar ≥ 24-72 h.
7) Reservas y colinas (holds)
Hold no es una cancelación, sino una «congelación» del saldo disponible.
Reglas:- Duración de la vida de la colina: seconds→minutes (apuesta) u reloj (retirada).
- La colina se puede canjear parcial o totalmente (settle parcial).
- Con expire: release automático y evento.
- Almacene la relación 'hold _ id' ↔ 'bet _ id/withdraw _ id'.
8) Monedas, FX y redondeo
Las sumas de dinero son en unidades menores (cents), el tipo es un todo.
Los redondeos son bancarios (round half to even) o por T & C.
FX: 'CASH (EUR)' ↔ 'CASH (USD)' es mejor separar las carteras. La conversión se realiza como una operación independiente:- 'debit EUR, credit FX_EURUSD' y' debit FX_EURUSD, credit USD '- transparente para la auditoría.
- Está prohibido por la máquina «alcanzar» el curso en caso de disputa; todas las reglas están en la política FX.
9) Juego responsable y límites
Deposition/Bet/Loss/Session limites (día/semana/mes), «cooling-off», self-exclusion.
Se implementan como pre-check antes de hold/debit.
Registros de rebote: en un registro de auditoría independiente, disponible para el sapport y el regulador.
10) Señales antifraude alrededor de la cartera
Clusters de dispositivos/ASN, depósitos frecuentes de pequeñas cantidades → grandes retiros, patrones de lavado.
Velocity Limits on 'deposite/withdraw' por BIN/país/dispositivo.
Hojas de flujo para destinatarios (monederos/IBAN), lista de «mulas».
Eventos de billetera → en la puntuación de feature store (inicio de sesión/depósito/apuesta).
11) Consistencia y rendimiento
Verdad vs caché
La verdad está en el ledger. Para la API «obtener balance» - mantenga el snapshot materializado ('user _ id + wallet _ type → balance_minor, version').
Escribir: una transacción en la DB → invalidar la caché.
En el flow «pesado» (en vivo), es apropiado el short-TTL 1-5 con + verificación obligatoria de la verdad antes de retirar/apuesta grande.
Skalirovanie
Charding por 'user _ id' (módulo/clasificación), grupos shard individuales bajo CASH vs BONUS.
Teclas calientes (VIP/bots) - request coalescing por 'user _ id'.
Agregaciones asíncronas (compilar 'posting' → 'snapshot updater' en el fondo).
12) API-contratos (pseudo)
Balance
http
GET /v1/wallets? types=CASH,BONUS
→ 200 {"wallets":[
{"type":"CASH","currency":"EUR","available":12050,"hold":500,"version":1942},  {"type":"BONUS","currency":"EUR","available":3000,"wager_req":15000}
]}Apuesta (con la colina)
http
POST /v1/bets/place
{"bet_id":"b_123","amount":500,"currency":"EUR","source_policy":"casino_default", "idempotency_key":"ik_abc"}
→ 201 {"status":"HELD","hold_id":"h_789","expires_in":30}Settlment
http
POST /v1/bets/settle
{"bet_id":"b_123","result":"WIN","payout":1250}
→ 200 {"status":"SETTLED","cash_delta":+1250}http
POST /v1/withdrawals
{"withdraw_id":"w_456","amount":10000,"currency":"EUR","method":"sepa", "idempotency_key":"ik_def"}
→ 202 {"state":"PENDING","next_check_sec":2,"status_url":"/v1/withdrawals/w_456"}13) Ejemplos de cableado (doble entrada)
Depósito €100 (PSP fee €1, commis. cuenta - separada)
Debit: PSP_Settlements(EUR)   10000
Credit: User. CASH(EUR)         10000
Debit: User. CASH (EUR) 100 (fee transferible)
Credit: PSP_Fees(EUR)          100Tarifa €5 de BONUS (traducción a WAGER)
Debit: User. BONUS(EUR)       500
Credit: User. WAGER (EUR) 500 (movimiento a «Vager»)
Debit: User. WAGER(EUR)       500
Credit: Provider. Settlement (EUR) 500 (tasa cargada)Ganar 12 €. 5 → en CASH
Debit: Provider. Settlement(EUR)  1250
Credit: User. CASH(EUR)         1250Débito hold (implementación a través de la cuenta de servicio HOLD)
Debit: User. CASH(EUR)       500
Credit: User. HOLD (EUR) 500 (creado hold)
-- con settle
Debit: User. HOLD(EUR)       500
Credit: Provider. Settlement(EUR)   500
-- cuando se cancela
Debit: User. HOLD(EUR)       500
Credit: User. CASH(EUR)         50014) Auditoría, inmutabilidad y cumplimiento
WORM/immutability para el registro (almacén de objetos/archivo WAL).
Metahormales de acceso: quién leyó/cambió los límites, quién hizo los ajustes manuales (solo a través de «adjustment-posting» con justificación).
GDPR/reguladores: almacenamiento de transacciones de 5-10 años (por jurisdicción), transparencia de liquidación para el jugador (historial de cargos/vager).
15) Tolerancia a fallas y DR
Multi-AZ es obligatorio; Región DR para monedero: replicación sync en la zona, async en la región; PITR incluido.
Promote standby - sólo manualmente en la lista de comprobación (excluir split-brain).
Restaurar comprobar semanalmente (test-restore), conciliar la cantidad con los informes de control.
16) Observabilidad de la billetera
SLI: `deposit_success_ratio`, `withdraw_success_ratio`, `bet_hold_latency_p95`, `settlement_latency_p95`.
Тех: `ledger_postings_rate`, `db_connections_saturation`, `queue_lag_seconds`, `hold_expired_rate`.
Alertas: caída de PSP success por mercado, crecimiento de 'hold _ expired _ rate', proveedor de juegos de rassinchron (no hay confirmaciones> N min).
17) Pruebas y control de calidad
Pruebas contractuales con proveedores de PSP/juegos (webhooks/firmas).
Pruebas de dinero basado en propiedades: suma de adeudos = = suma de créditos en cada Posting.
Fuzz/chaos: retrasos PSP/proveedor, repeticiones de webhooks, flappies de red.
Carga: burst apuestas (60-120 s), soaks (4-8 h), control 'queue _ lag' y p99.
18) Lista de comprobación de disponibilidad de producción
- Doble registro ledger, todas las operaciones a través de Posting con 'operation _ id'.
- Claro spend-policies y orden de prioridad (persistido junto con el posting).
- Holds con TTL/settle/expiry parcial, enlace con bet/withdraw.
- Inbox/Outbox, HMAC-webhooks, idempotencia en todas las fronteras.
- Monederos CASH/BONUS/WAGER/FS/POINTS; división por monedas.
- FX y redondeos en unidades menores; la conversión es una operación independiente.
- Límites de juego responsable hasta hold/debit; Auditoría de fallos.
- Caché de lectura (TTL corto) + Verificación obligatoria de la verdad antes de acciones críticas.
- PITR/backups/scripts DR; promote manual, ejercicios regulares de DR.
- Dashboards/alertas SLI + técnicas; los registros WORM y los registros de acceso.
- Pruebas de carga/caos; informes de reconciliación con PSP/proveedores.
Resumen
La arquitectura multi-wallet no es «un montón de números de balance», sino un sistema financiero con doble registro, políticas de gasto, reservas y una pista transparente para auditorías y jugadores. Mantenga la verdad en el registro, use las colinas y la idempotencia, comparta las billeteras y las monedas, automatice la reconciliación y el DR.El monedero será rápido para UX, preciso para el dinero y resistente a cargas máximas y controles regulatorios.
