Баланс і гаманці: архітектура multi-wallet
1) Навіщо multi-wallet і які цілі
Один запис «баланс = число» не покриває реальність iGaming. Потрібні окремі гаманці/субрахунки: реальні гроші (cash), бонусні кошти, вейджер-пул, фріспіни, комп-пойнти, іноді - валютні гаманці (EUR/USD/BRL).
Цілі архітектури:- Точність грошей (double-entry, аудируемость).
- Політики списання (наприклад, спочатку бонус/вейджер, потім cash).
- Швидкість (p95 API ≤ 250-400 мс, ставка/сеттлмент в реальному часі).
- Безпека та комплаєнс (KYC/AML, ліміти відповідальної гри, регулятори).
- Масштаб: піки → десятки тисяч операцій/сек, мільярди постінгів/місяць.
2) Модель даних: «Ledger + Subwallets»
Мінімальні сутності
Account: гравець/бренд/маркет.
Приклад таблиць (спрощено)
sql
-- Балансові рахунки для double-entry (включаючи службові)
accounts(id, owner_user_id, type, currency, status,...)
-- Проводка (подвійний запис, посилання на бізнес-операцію)
ledger_entries(id, posting_id, debit_account_id, credit_account_id,        amount_minor, currency, category, operation_id, created_at)
- Холди (резерви)
holds(id, account_id, amount_minor, currency, reason, expires_at, state,    operation_id, created_at)
- Політики списання (пріоритети)
spend_policies(id, market, wallet_priority jsonb, updated_at)
-- Крос-валютні курси fx_rates (ccy_from, ccy_to, rate, precision, valid_from)Правило: істина живе в журналі проводок («ledger _ entries»). Поточний баланс - або агрегат (матеріалізований снепшот), або обчислюється з журналу (дорого, але єдино вірно).
3) Типи гаманців та їх поведінка
4) Політики списання і порядок пріоритету
Чітко формалізуйте алгоритм джерела засобів: Приклад (слоти/казино):1. Спочатку списувати з WAGER (якщо активний вейджер).
2. Потім з BONUS, поки не вичерпаний.
3. Залишок - з CASH.
Приклад (спорт):1. Спочатку CASH (регулятор/податок).
2. Потім BONUS (freebet), переводячи в WAGER.
Зберігайте в Postings «рішення політики» як атрибут, щоб саппорт і аудит бачили «чому так списали».
5) Життєвий цикл грошей і операцій
Депозит
1.'POST/wallet/deposit'→ створюємо pending запис (inbox колбека PSP).
2. Вебхук PSP (підпис HMAC, ідемпотентність по'operation _ id') → credit CASH, category ='DEPOSIT'.
3. Публікуємо подію'wallet _ updated'.
Ставка
1.'POST/bet/place'→ створюємо hold (резерв) на рахунку джерела (CASH/BONUS/WAGER).
2. При підтвердженні ставки → переказ hold → debit джерела, credit службового «розрахункового» рахунку провайдера.
3. При скасуванні - release hold.
Сетлмент (результат)
Виграш: debit «розрахункового» рахунку провайдера → credit CASH або WAGER→BONUS→CASH з політики.
Програш: закриваємо проводкою «витрата» провайдера → без кредитів гравцеві.
1. Перевірка KYC/AML, ліміти відповідальної гри.
2. Hold на суму виведення.
3. Успіх PSP → фінальна debit CASH → credit рахунок «виплати».
4. Відмова PSP → release hold.
6) Ідемпотентність і exactly-once «за змістом»
Скрізь'operation _ id'( UUID/покращений ULID) з унікальним індексом. Повторний запит → статус минулої операції.
Вебхуки PSP/провайдера ігор: Inbox-таблиця з dedupe по'event _ id + signature'. Обробка - ідемпотентний воркер (Outbox-патерн).
Idempotency-Key на HTTP для клієнта; TTL зберігати ≥ 24-72 год.
7) Резерви і холди (holds)
Холд - не списання, а «заморожування» доступного залишку.
Правила:- Термін життя холда: seconds→minutes (ставка) або годинник (висновок).
- Холд може бути частково або повністю погашений (partial settle).
- При expire - автоматичний release і подія.
- Зберігайте зв'язок'hold _ id'↔'bet _ id/withdraw _ id'.
8) Валюти, FX і округлення
Грошові суми - в мінорних одиницях (cents), тип - ціле.
Округлення банківські (round half to even) або по T&C.
FX: 'CASH (EUR)'↔'CASH (USD)'краще розділяти гаманці. Конверсію робити як окрему операцію:- 'debit EUR, credit FX_EURUSD' і'debit FX_EURUSD, credit USD'- прозоро для аудиту.
- Заборонено автоматом «дотягувати» курс при суперечці; всі правила - в політиці FX.
9) Відповідальна гра і ліміти
Deposit/Bet/Loss/Session ліміти (день/тиждень/місяць), «cooling-off», self-exclusion.
Реалізуються як pre-check перед hold/debit.
Логи відмов - в окремий аудит-журнал, доступні саппорту і регулятору.
10) Антифрод-сигнали навколо гаманця
Кластери пристроїв/ASN, часті депозити малої суми → великі висновки, відмивальні патерни.
Velocity-ліміти на'deposit/withdraw'по BIN/країні/пристрою.
Блок-листи для одержувачів (гаманці/IBAN), список «мулів».
Події гаманця → в feature store скорингу (логін/депозит/ставка).
11) Консистентність і продуктивність
Істина vs кеш
Істина - в ledger. Для API «отримати баланс» - тримайте матеріалізований снепшот ('user _ id + wallet _ type → balance_minor, version').
Писати: транзакція в БД → інвалідувати кеш.
У «важких» флоу (live) доречно short-TTL 1-5 с + обов'язкова перевірка істини перед виведенням/великою ставкою.
Скалювання
Шардування по'user _ id'( модуль/ранжування), окремі шард-пули під CASH vs BONUS.
Гарячі ключі (VIP/боти) - request coalescing по'user _ id'.
Асинхронні агрегації (скомпонуйте'posting'→ «снапшот-апдейтер» в тлі).
12) API-контракти (псевдо)
Баланс
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}
]}Ставка (з холдом)
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}Сетлмент
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) Приклади проводок (double-entry)
Депозит €100 (PSP fee €1, комміс. рахунок - окремий)
Debit: PSP_Settlements(EUR)   10000
Credit: User. CASH(EUR)         10000
Debit: User. CASH (EUR) 100 (fee перекладаємо)
Credit: PSP_Fees(EUR)          100Ставка €5 з BONUS (переклад в WAGER)
Debit: User. BONUS(EUR)       500
Credit: User. WAGER (EUR) 500 (переміщення в «вейджер»)
Debit: User. WAGER(EUR)       500
Credit: Provider. Settlement (EUR) 500 (ставка списана)Виграш €12. 5 → в CASH
Debit: Provider. Settlement(EUR)  1250
Credit: User. CASH(EUR)         1250Холдингове списання (реалізація через службовий рахунок HOLD)
Debit: User. CASH(EUR)       500
Credit: User. HOLD (EUR) 500 (створений hold)
-- при settle
Debit: User. HOLD(EUR)       500
Credit: Provider. Settlement(EUR)   500
-- при скасуванні
Debit: User. HOLD(EUR)       500
Credit: User. CASH(EUR)         50014) Аудит, незмінюваність і відповідність
WORM/immutability для журналу (об'єктне сховище/архів WAL).
Метажурнали доступу: хто читав/змінював ліміти, хто робив ручні коригування (тільки через «adjustment-posting» з обґрунтуванням).
GDPR/регулятори: зберігання транзакцій 5-10 років (за юрисдикцією), прозорість розрахунків для гравця (історія списань/вейджера).
15) Відмовостійкість і DR
Multi-AZ обов'язково; DR-регіон для гаманця: sync-реплікація в зоні, async - в регіон; PITR включено.
Promote standby - тільки вручну за чек-листом (виключити split-brain).
Відновлення перевіряти щотижня (test-restore), звірка суми по контрольних звітах.
16) Спостережуваність гаманця
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`.
Алерти: падіння success PSP по ринку, зростання'hold _ expired _ rate', розсинхрон провайдера ігор (немає підтверджень> N хв).
17) Тестування та контроль якості
Контрактні тести з PSP/ігровими провайдерами (вебхуки/підписи).
Property-based тести грошей: сума дебетів = = сума кредитів у кожній Posting.
Fuzz/chaos: затримки PSP/провайдера, повтори вебхуків, мережеві флаппі.
Навантажувальні: burst ставок (60-120 с), soaks (4-8 ч), контроль'queue _ lag'і p99.
18) Чек-лист продакшен-готовності
- Подвійний запис ledger, всі операції через Posting з'operation _ id'.
- Чіткі spend-policies і порядок пріоритету (персиститься разом з постінгом).
- Холди з TTL/partial settle/expiry, зв'язок з bet/withdraw.
- Inbox/Outbox, HMAC-вебхуки, ідемпотентність на всіх кордонах.
- Окремі гаманці CASH/BONUS/WAGER/FS/POINTS; розподіл за валютами.
- FX і округлення в мінорних одиницях; конверсія - окремою операцією.
- Ліміти відповідальної гри до hold/debit; аудит відмов.
- Кеш для читань (короткий TTL) + обов'язкова перевірка істини перед критичними діями.
- PITR/бекапи/DR-скрипти; ручний promote, регулярні DR-навчання.
- Дашборди/алерти SLI + технічні; логи WORM і метажурнали доступу.
- Навантажувальні/хаос-тести; звіти reconciliation з PSP/провайдерами.
Резюме
Архітектура multi-wallet - це не «багато чисел балансу», а фінансова система з подвійним записом, політиками витрачання, резервацією і прозорим слідом для аудиту і гравців. Тримайте істину в журналі, використовуйте холди і ідемпотентність, розділяйте гаманці і валюти, автоматизуйте reconciliation і DR. так гаманець буде швидким для UX, точним для грошей і стійким під пікові навантаження і регуляторні перевірки.
