Gestion des promotions et des bonus au niveau backend
Texte intégral de l'article
1) Pourquoi porter la promo dans un backend séparé
Invariants monétaires. Le bonus ≠ « l'équilibre » : c'est un contrat avec des conditions (vader, contribution par jeu, maximum de pari/gain).
Vitesse des changements. Les équipes marketing publient des campagnes quotidiennement - vous avez besoin d'un moteur de déclaration des règles et d'un retour en arrière.
Anti-abysse/conformité. KYC/RG/AML, velocity, segmentation, « quatre yeux » sur les offers chers.
L'observation et le rapport. SLO, coût promotionnel, impact sur GGR/NGR/LTV.
Le principe est que le noyau promotionnel est un service distinct avec ses propres machines statutaires, et l'argent ne se déplace que par le portefeuille, idempotent.
2) La typologie des bonus et invariants
Deposit match (100 % avant X) : crédité après capture du dépôt, vader X ×.
Cashback (perds-back) : calculé par la fenêtre temps/jeux, peut être sticky/non-sticky.
Free Spins/Free Bets : coupons/jetons avec prix spin/pari, pool RTP fixe.
Quêtes/missions : mission → progrès → récompense.
Tournois/vols : contribution des événements, classement, prix.
Invariants :- Sticky : vous ne pouvez pas le retirer tant que les conditions sont remplies.
- Max bet/Max win : limites de mise/paiement des fonds bonus.
- Contribution : contribution par jeu (par exemple slots = 100 %, live = 10 %).
- Expiry : durée de validité du bonus et de la fenêtre de vader.
3) L'architecture du service bonus
Admin (campagnes/règles) ─Promo API ─Rules Engine/Eligibility
│
├─Bonus Ledger (état des offers)
├─Wagering Engine (progrès)
├─Anti -Abuse (limites/fred/velocity)
└─Outbox (events) ─Kafka/Pulsar ─BI/DWH/CRM
Wallet/Ledger── Idempotent Commands ───┘Rules Engine - conditions déclaratives (segments, géo/licence, canaux, KYC/RG).
4) Modèle de données (simplifié)
`bonus_grant`
`wager_progress`- `grant_id, required_minor, contributed_minor, remaining_minor, last_update_at`
- `schema_id, rules: [{game_type:"slot", pct:100},{game_type:"live", pct:10}]`
'Bonus _ ledger _ entry '(audit)
5) Machines de statut et sagas
5. 1 Délivrance (issue) - saga
1. eligibility. check (segment, RG/KYC, velocity)
2. grant. create (status=`issued`)
3. wallet. credit [bonus] (idempotent ; avec sticky - en sous-solde bonus)
4. activate (status=`active`)
5. emit `bonus. issued`
Rollback : en cas de chute à 3 pas → 'grant. cancel '+ événement' bonus. revoked`.
5. 2 Progrès du weiger
Sur 'bet. settled 'compte la contribution =' stake _ minor contribution_pct' (ou selon les règles win/loss).
Mettre à jour 'wager _ progress' de manière atomique ; à 100 % - 'complete'.
5. 3 Achèvement (consommation)
complete → `wallet. convert_bonus_to_cash' (si non-sticky) ou suppression des restrictions de sortie.
emit `bonus. consumed`.
5. 4 Expiration/révocation
Par 'expires _ at' ou la règle du frod → 'revoke' (idempotent), une compensation selon la politique est possible.
6) Contrats avec portefeuille (uniquement via API, toujours idempotent)
Accumuler un bonus
POST /v1/wallet/credit
Headers: X-Idempotency-Key: bonus_grant_123
{
"player_id":"p_001",  "amount":{"minor_units":100000,"currency":"EUR"},  "balance_type":"bonus",  "reference":{"grant_id":"gr_123","offer_id":"of_777"}
}
→ 200 {"status":"credited","entry_id":"e_9001"}Conversion en cache une fois les conditions remplies
POST /v1/wallet/convert
Headers: X-Idempotency-Key: bonus_convert_gr_123
{
"player_id":"p_001",  "from_balance":"bonus",  "to_balance":"cash",  "amount_minor":100000,  "reference":{"grant_id":"gr_123"}
}
→ 200 {"status":"converted","entry_id":"e_9010"}- requête 'bets. autorize 'est rejeté par le code' BONUS _ MAX _ BET _ EXCEEDED '.
7) API du service promo (références)
Créer un offer (admin)
POST /v1/offers
{
"name":"Welcome 100% up to 100€",  "type":"deposit_match",  "params":{"match_pct":100,"cap_minor":10000,"wager_x":20,"sticky":true,       "max_bet_minor":200,"max_win_minor":50000,"contribution_schema_id":"c_slot100_live10"},  "eligibility":{"brands":["A"],"regions":["EU"],"segment":"new_depositors"},  "schedule":{"start":"2025-10-20T00:00:00Z","end":"2025-11-30T23:59:59Z"}
}
→ 201 {"offer_id":"of_777"}Émettre un bonus (runtime)
POST /v1/bonus/grants
Headers: X-Idempotency-Key: grant_p001_of777
{
"player_id":"p_001","offer_id":"of_777","trigger":"deposit_captured","amount_minor":10000
}
→ 200 {"grant_id":"gr_123","status":"active"}Progrès du vader (lire)
GET /v1/bonus/grants/gr_123/progress
→ 200 {"required_minor":200000,"contributed_minor":45000,"remaining_minor":155000,"pct":0. 225}Annuler/Retirer
POST /v1/bonus/grants/gr_123/revoke
Headers: X-Idempotency-Key: revoke_gr_123
{ "reason":"fraud_velocity" }
→ 200 {"status":"revoked"}Tous les appels write sont avec 'X-Idempotency-Key' et 'X-Trace-Id'.
8) Anti-abysse et conformité
Limites de Velocity : émission/conversion/tentative de dépôt (Redis counters + TTL + Lua).
Dedup de déclencheur : un dépôt → une subvention selon la règle.
Segmentation et RG : supprimer les auto-excluded/limited ; per brand/region licence.
Bloc de conflit Offer : un seul bonus welcome est actif à la fois ; les priorités.
Détecteur d'anomalie : comptes multiples/appareils/ASN, « zapping » rapide du vader.
« Quatre yeux » sur les grandes subventions et les ajustements manuels.
Audit WORM de toutes les modifications apportées aux règles/subventions/conversions.
9) Observabilité, métriques et SLO
SLO (repères) :- `grant. issue p95` (issue→credited) ≤ 300–500 мс.
- Mise à jour de 'wager _ progress p95' ≤ 200 ms depuis 'bet. settled`.
- Les événements 'bonus.' dans le pneu p95 ≤ 2 min de l'incident.
- « Subventions/conversions perdues/dupliquées » = 0.
- Rate/latency по `issue/convert/revoke`, error-rate (business/4xx/5xx), `IDEMPOTENCY_MISMATCH`.
- Conversion du wager, moyenne « time-to-complete », proportion de retard.
- Coût promo : 'promo _ cost' (mineur) et 'promo _ roi' sur les cohortes.
- Anti-abyse : actionnements velocity rejetés par max bet/win.
Tracing : OpenTelemetry sur la chaîne 'trigger → grant → wallet. credit → progress. update → convert`.
10) Intégration avec RGS/jeux
Coupons Free Spins/Free Bets - via l'API 'entitlements' : émission de tokens, débiter dans le rentime, télémétrie par utilisation.
Max bet/win - règles dans 'bets. authorize` и `bets. settle`; renvoyer les codes 'BONUS _ RULE _ VIOLENCE'.
Contribution : Schéma au niveau 'bet. settled '(par' game _ type/provider _ id'), la version des schémas.
11) DWH/BI et rapports
Outbox de l'événement → Lake (bronze) → Silver (dedup, SCD2) → Gold vitrines :- `fact_bonus_grants`, `fact_wager_progress`, `fact_bonus_cost`, `fact_promo_roi`.
- SLA de fraîcheur : Argent ≤ 15 min, Or ≤ 30-60 min.
- Panels : conversion par offers/segments, time-to-complete, contribution par jeu, incidents d'abyse.
12) Sécurité et résidence
mTLS + OAuth2 CC; scope’ы `promo:issue`, `promo:convert`, `promo:revoke`.
Clés/jetons - per brand/region, courte vie ; secrets dans Vault/HSM.
Isolation PII : 'player _ id' est un alias ; RLS по `brand/region`.
Limites de taux et quotas de délivrance ; protection contre les tempêtes rétrogrades.
13) Chèques-feuilles
Plate-forme/opérateur
- Toutes les transactions monétaires passent par Wallet avec 'Idempotency-Key'.
- Rules/Eligibility sont convertibles ; la « double lettre » des événements sur les migrations.
- Les schémas de contribution sont centralisés et couverts par des tests.
- Velocity et antifrod inclus ; « quatre yeux » sur les sommes importantes.
- Outbox/CDC, DLQ et replay géré pour 'bonus.'.
- SLO-dashboards, OpenTelemetry, audit WORM.
- Vitrines DWH pour RI et Complaens (RG/AML).
Intégration (RGS/portefeuille/CRM)
- Je vérifie max bet/win ; Je retourne le code d'erreur d'entreprise.
- Je projette 'trace _ id' et 'idempotency _ key'.
- Déduplication des déclencheurs et garantie de livraison (webhooks signés).
14) Drapeaux rouges (anti-modèles)
Créditer le bonus « manuellement » directement dans le solde, en contournant Wallet.
Manque d'idempotence → doubles subventions/conversions.
Wager compte pour 'bet. placed ', pas à la suite de' bet. settled`.
Il n'y a pas de schémas contributifs ou ils sont « cousus » dans le code du fournisseur.
Les offers en conflit sont activés simultanément.
Pas d'audit velocity/anti-frod et WORM.
Les événements 'bonus.' sont publiés en contournant outbox/CDC.
Les indicateurs promo ne convergent pas avec Ledger/BI (pas de vitrine ROI).
15) Résultat
Les promos fiables sont des contrats et des invariants, pas des « équilibres ». Il sépare les règles de l'argent, compte les progrès sur les résultats réels, garantit l'idempotence et l'observabilité, protège contre l'abysse et assure la conformité. Avec un tel noyau, le marketing se déplace rapidement, le joueur voit des conditions honnêtes, et les finances et les régulateurs obtiennent une image exacte du coût et de l'effet de chaque offer.
