Balance et portefeuilles : architecture multi-wallet
1) Pourquoi le multi-wallet et quels objectifs
Un seul enregistrement « balance = nombre » ne couvre pas la réalité d'iGaming. Il faut des portefeuilles/sous-comptes distincts : de l'argent réel (cash), des fonds bonus, du Wager Pool, des frispins, des comptoirs, parfois des portefeuilles de devises (EUR/USD/BRL).
Objectifs de l'architecture :- Précision de l'argent (double entrée, audibilité).
- Stratégies de passation par profits et pertes (par exemple, d'abord bonus/Wager, puis cash).
- Vitesse (p95 API ≤ 250-400 ms, mise/settlement en temps réel).
- Sécurité et conformité (KYC/AML, limites du jeu responsable, régulateurs).
- Échelle : pics → dizaines de milliers d'opérations/s, milliards de posts/mois.
2) Modèle de données : « Ledger + Subwallets »
Entités minimales
Compte : joueur/marque/market.
Exemple de tableau (simplifié)
sql
- Comptes de bilan pour double entrée (y compris les comptes de service)
accounts(id, owner_user_id, type, currency, status,...)
-- Câblage (double enregistrement, référence à une opération commerciale)
ledger_entries(id, posting_id, debit_account_id, credit_account_id,        amount_minor, currency, category, operation_id, created_at)
-- Holdes (réserves)
holds(id, account_id, amount_minor, currency, reason, expires_at, state,    operation_id, created_at)
-- Politiques d'annulation (priorités)
spend_policies(id, market, wallet_priority jsonb, updated_at)
- Taux de change croisés fx_rates (ccy_from, ccy_to, taux, précision, valid_from)Règle : la vérité vit dans le journal d'affichage ('ledger _ entries'). L'équilibre actuel est soit un agrégat (un snapshot matérialisé), soit calculé à partir d'un journal (cher, mais seulement vrai).
3) Types de portefeuille et leur comportement
4) Politiques de passation par profits et pertes et ordre de priorité
Formalisez clairement l'algorithme de la source de fonds : Exemple (slots/casino) :1. D'abord débiter de WAGER (si le Wager est actif).
2. Puis de BONUS, jusqu'à épuisement.
3. Le reste vient du CASH.
Exemple (sport) :1. D'abord CASH (régulateur/taxe).
2. Puis BONUS (freebet), en traduisant en WAGER.
Gardez dans Postings la « décision de politique » comme attribut pour que le sappport et l'audit voient « pourquoi c'est débité ».
5) Cycle de vie de l'argent et des opérations
Dépôt
1. 'POST/wallet/deposit' → nous créons un enregistrement pending (inbox de la saucisse PSP).
2. Webhook PSP (signature HMAC, idempotence par 'opération _ id') → credit CASH, category = 'DEPOSIT'.
3. Publiez l'événement 'wallet _ updated'.
Taux
1. 'POST/bet/place' → créer hold (réserve) sur le compte source (CASH/BONUS/WAGER).
2. Lors de la confirmation du taux → transfert hold → debit source, credit du compte de service « calculé » du fournisseur.
3. Si vous annulez, release hold.
Settlment (résultat)
Gain : débit du compte « de règlement » du fournisseur → du credit CASH ou du WAGER→BONUS→CASH de la politique.
Perte : nous fermons avec le câblage « consommation » du fournisseur de → sans crédits au joueur.
1. Vérification KYC/AML, limites du jeu responsable.
2. Hold pour le montant du retrait.
3. Le succès de PSP → le débit final de CASH → le compte de crédit « paiement ».
4. Panne de PSP → release hold.
6) Idempotence et exactly-once « au sens de »
Partout, 'opération _ id' (UUID/ULID amélioré) avec un index unique. Demande répétée → l'état de l'opération précédente.
Webhooks PSP/fournisseur de jeux : Inbox table avec dedupe par 'event _ id + signature'. Le traitement est un worker idempotent (modèle Outbox).
Idempotency-Key sur HTTP pour le client ; TTL stocker ≥ 24-72 h.
7) Réserves et holds (holds)
Le hold n'est pas une annulation, mais un « gel » du résidu disponible.
Règles :- Durée de vie du hold : seconds→minutes (mise) ou heures (retrait).
- Le hold peut être partiellement ou complètement remboursé (settle partial).
- Dans expire, la version automatique et l'événement.
- Gardez la relation 'hold _ id' ↔ 'bet _ id/withdraw _ id'.
8) Devises, FX et arrondis
Les sommes d'argent sont en unités mineures (cents), le type est entier.
Arrondissements bancaires (round half to even) ou par T & C.
FX : 'CASH (EUR)' ↔ 'CASH (USD)' il est préférable de séparer les portefeuilles. Conversion en tant qu'opération distincte :- « debit EUR, credit FX_EURUSD' et » debit FX_EURUSD, credit USD'sont transparents pour l'audit.
- Il est interdit de "passer" par l'automate le cours à la discussion; toutes les règles sont dans la politique FX.
9) Jeu responsable et limites
Deposit/Bet/Loss/Session limites (jour/semaine/mois), « cooling-off », self-exclusion.
Sont implémentés comme pré-check avant hold/debit.
Les logs de refus - dans un journal d'audit séparé, sont disponibles pour le sapport et le régulateur.
10) Signaux antifrod autour du portefeuille
Les clusters des installations/ASN, les dépôts fréquents de la petite somme → de grandes conclusions, отмывочные les patterns.
Limites de velocity pour 'deposit/withdraw' par BIN/pays/périphérique.
Listes des destinataires (portefeuille/IBAN), liste des « mules ».
Les événements de portefeuille → dans la fonctionnalité store scoring (login/dépôt/pari).
11) Cohérence et performance
Vérité du cache vs
La vérité est dans le ledger. Pour l'API « Obtenir l'équilibre », gardez le snapshot matérialisé ('user _ id + wallet _ type → balance_minor, version').
Ecrire : une transaction dans une base de données → handicaper le cache.
Dans le flow « lourd » (live) est approprié short-TTL 1-5 avec + vérification obligatoire de la vérité avant de retirer/grand pari.
Balayage
Chardage par 'user _ id' (module/classement), pools de chardons séparés sous CASH vs BONUS.
Clés chaudes (VIP/bots) : Request coalescing par 'user _ id'.
Agrégations asynchrones (composer « posting » → « snapshot-updater » en arrière-plan).
12) Contrats API (pseudo)
Équilibre
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}
]}Taux (avec hold)
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) Exemples de câblage (double-entrée)
Dépôt de 100 € (PSP fee 1 €, commis. compte - séparé)
Debit: PSP_Settlements(EUR)   10000
Credit: User. CASH(EUR)         10000
Debit: User. CASH (EUR) 100 (transférons-le)
Credit: PSP_Fees(EUR)          100Tarif 5 € de BONUS (virement en WAGER)
Debit: User. BONUS(EUR)       500
Credit: User. WAGER (EUR) 500 (mouvement vers le « wager »)
Debit: User. WAGER(EUR)       500
Credit: Provider. Settlement (EUR) 500 (taux débité)Gagnez 12 €. 5 → en CASH
Debit: Provider. Settlement(EUR)  1250
Credit: User. CASH(EUR)         1250Passation par profits et pertes (mise en œuvre par le compte HOLD)
Debit: User. CASH(EUR)       500
Credit: User. HOLD (EUR) 500 (créé hold)
-- avec settle
Debit: User. HOLD(EUR)       500
Credit: Provider. Settlement(EUR)   500
- lors de l'annulation
Debit: User. HOLD(EUR)       500
Credit: User. CASH(EUR)         50014) Audit, invariabilité et conformité
WORM/immutabilité pour le journal (stockage objet/archives WAL).
Métajournales d'accès : qui a lu/modifié les limites, qui a fait des ajustements manuels (seulement par le biais de « adjustment-posting » avec justification).
GDPR/régulateurs : stockage des transactions 5-10 ans (par juridiction), transparence des calculs pour le joueur (historique des débits/Wager).
15) Résistance aux pannes et DR
Multi-AZ est obligatoire ; Région DR pour portefeuille : réplication sync dans la zone, async dans la région ; PITR est activé.
Promote standby - seulement manuellement par chèque (supprimer split-brain).
Vérifiez chaque semaine la restauration (test-restore) et vérifiez le montant des rapports de contrôle.
16) L'observabilité du portefeuille
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`.
Alertie : baisse du PSP success par marché, croissance de 'hold _ expired _ rate', rassynchrone du fournisseur de jeux (pas de confirmation> N min).
17) Test et contrôle de qualité
Tests contractuels avec PSP/fournisseurs de jeux (webhooks/signatures).
Tests de l'argent basé sur la propriété : montant des débits = = montant des crédits dans chaque poste.
Fuzz/chaos : retards PSP/fournisseur, répétitions de webhooks, flappy réseau.
Charge : burst paris (60-120 s), soaks (4-8 h), controle 'queue _ lag' et p99.
18) Chèque-liste de production-prêt
- Double enregistrement ledger, toutes les opérations via Posting avec 'opération _ id'.
- Politiques de spend claires et ordre de priorité (percé avec post).
- Holds avec TTL/partial settle/expiry, lien avec bet/withdraw.
- Inbox/Outbox, webhooks HMAC, idempotence à toutes les frontières.
- Porte-monnaie CASH/BONUS/WAGER/FS/POINTS ; division par devises.
- FX et arrondis en unités mineures ; la conversion est une opération distincte.
- Limites du jeu responsable jusqu'à hold/debit ; audit des refus.
- Cache de lecture (court TTL) + vérification obligatoire de la vérité avant les actions critiques.
- PITR/backups/DR-scripts ; promotion manuelle, exercice de DR régulier.
- Dashboards/alertes SLI + techniques ; logs WORM et méta-journaux d'accès.
- Tests de charge/de chaos ; rapports de reconnaissance avec PSP/fournisseurs.
Résumé
L'architecture multi-wallet n'est pas un « grand nombre de chiffres de bilan », mais un système financier avec un double enregistrement, des politiques de dépenses, une réserve et une piste transparente pour l'audit et les joueurs. Gardez la vérité dans le journal, utilisez les holds et l'idempotence, séparez les portefeuilles et les devises, automatisez la récupération et le DR. Ainsi, le portefeuille sera rapide pour UX, précis pour l'argent et résistant aux charges de pointe et aux contrôles réglementaires.
