Gestión de promociones y bonificaciones a nivel backend
Texto completo del artículo
1) Por qué llevar la promoción en un backend separado
Invariantes monetarios. Bonificación ≠ «balance aplanado»: se trata de un contrato con condiciones (vager, contribución por juego, máximo de apuesta/ganancia).
Velocidad de cambio. Los equipos de marketing lanzan campañas diariamente - se necesita un motor declarativo de reglas y reversión.
Anti-abusivo/cumplimiento. KYC/RG/AML, velocity, segmentación, tasky «cuatro ojos» en offers caros.
Observabilidad e informes. SLO, costo promocional, impacto en GGR/NGR/LTV.
Principio: el núcleo promocional es un servicio separado con sus propias máquinas de estado, y el dinero sólo se mueve a través de la cartera, idempotente.
2) Tipología de bonificaciones e invariantes
Depósito match (100% antes de X): cargado después de capture el depósito, Vager X ×.
Cashback (pérdida-back): calculado por ventana de tiempo/juegos, puede ser sticky/non-sticky.
Tiradas Gratis/Apuestas Gratuitas: cupones/tokens con precio de giro/apuesta, grupo de RTP fijo.
Misiones/misiones: asignación → progreso → recompensa.
Torneos/eventos de vuelo: contribuciones de eventos, clasificación, premios.
Invariantes:- Sticky: no se puede retirar antes de que se cumplan las condiciones.
- Max bet/Max win: límites de apuesta/pago con fondos de bonificación.
- Contribution: contribución por juego (por ejemplo, slots = 100%, live = 10%).
- Expiry: la duración del bono y la ventana del vager.
3) Arquitectura del servicio de bonificación
Admin (campañas/reglas) ─Promo API ─Rules Engine/Elegibility
│
├─Bonus Ledger (estado del offer)
├─Wagering Motor (progreso)
├─Anti -Abuse (límites/frod/velocity)
└─Outbox (events) ─Kafka/Pulsar ─BI/DWH/CRM
Wallet/Ledger── Idempotent Commands ───┘Rules Engine - términos declarativos (segmentos, geo/licencia, canales, KYC/RG).
4) Modelo de datos (simplificado)
`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' (auditoría)
5) Máquinas y sagas de estado
5. 1 Emisión (issue) - saga
1. eligibility. check (segmento, RG/KYC, velocity)
2. grant. create (status=`issued`)
3. wallet. credit [bonus] (idempotente; con sticky - en el sub-balance de bonificación)
4. activate (status=`active`)
5. emit `bonus. issued`
Rollback: al caer en el escalón 3 → 'grant. cancel '+ evento' bonus. revoked`.
5. 2 Progreso del Vagger
Na 'bet. settled 'cuenta la contribución =' stake _ minor contribution_pct' (o según las reglas de win/loss).
Actualizar 'wager _ progress' atomicamente; cuando se alcanza el 100% es 'completo'.
5. 3 Finalización (consumo)
complete → `wallet. convert_bonus_to_cash' (si no es apto) o eliminar las restricciones de salida.
emit `bonus. consumed`.
5. 4 Caducidad/retroalimentación
Por 'expires _ at' o regla de frod → 'revoke' (idempotente), la compensación es posible según la política.
6) Contratos con billetera (sólo a través de API, siempre idempotente)
Acumular un bono
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"}Conversión a caché una vez cumplidas las condiciones
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"}- la consulta 'bets. authorize 'es rechazada por el código' BONUS _ MAX _ BET _ EXCEEDED '.
7) API del servicio promocional (referencias)
Crear 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"}Emitir un bono (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"}Progreso del Vagger (leer)
GET /v1/bonus/grants/gr_123/progress
→ 200 {"required_minor":200000,"contributed_minor":45000,"remaining_minor":155000,"pct":0. 225}Anular/revocar
POST /v1/bonus/grants/gr_123/revoke
Headers: X-Idempotency-Key: revoke_gr_123
{ "reason":"fraud_velocity" }
→ 200 {"status":"revoked"}Todas las llamadas write son con 'X-Idempotency-Key' y 'X-Trace-Id'.
8) Anti-abusivo y cumplimiento
Velocity limites: emisiones/conversiones/intentos de depósito (Redis counters + TTL + Lua).
Dedoop de los desencadenantes: un depósito → un grant por regla.
Segmentación y RG: excluir self-excluded/limited; por licencia de marca/región.
Bloque de conflicto de offer: sólo un bono de bienvenida está activo al mismo tiempo; prioridades.
Detector de anomalías: múltiples cuentas/dispositivos/ASN, «anulaciones» rápidas del vager.
«Cuatro ojos» para grandes subvenciones y ajustes manuales.
Auditoría WORM de todos los cambios de reglas/subvenciones/conversiones.
9) Observabilidad, métricas y SLO
SLO (puntos de referencia):- `grant. issue p95` (issue→credited) ≤ 300–500 мс.
- Actualización 'wager _ progress p95' ≤ 200 ms desde 'bet. settled`.
- Sucesos 'bonus.' en el neumático p95 ≤ 2 min de lo ocurrido.
- «Subvenciones/conversiones perdidas/duplicadas» = 0.
- Rate/latency по `issue/convert/revoke`, error-rate (business/4xx/5xx), `IDEMPOTENCY_MISMATCH`.
- La conversión del vager, la media 'time-to-complete', la proporción de vencidos.
- El coste de la promoción es: 'promo _ cost' (menor) y 'promo _ roi' en las cohortes.
- Anti-Abuse: activaciones velocity rechazadas por max bet/win.
Treking: OpenTelemetry por la cadena 'trigger → grant → wallet. credit → progress. update → convert`.
10) Integración con RGS/juegos
Cupones Free Spins/Free Bets - a través de la API 'entitlements': emisión de tokens, cargo en rantime, telemetría por uso.
Max bet/win - reglas en 'bets. authorize` и `bets. settle`; devuelve los códigos 'BONUS _ RULE _ VIOLATION'.
Contribution - Esquema en el nivel 'bet. settled '(por' game _ type/provider _ id '), versión de circuitos.
11) DWH/BI e informes
Eventos Outbox → Lake (bronze) → Silver (dedoop, SCD2) → vitrinas de Oro:- `fact_bonus_grants`, `fact_wager_progress`, `fact_bonus_cost`, `fact_promo_roi`.
- Frescura SLA: Silver ≤ 15 min, Oro ≤ 30-60 min.
- Paneles: conversión por offer/segmentos, time-to-complete, contribución por juego, incidentes abusivos.
12) Seguridad y residencia
mTLS + OAuth2 CC; scope’ы `promo:issue`, `promo:convert`, `promo:revoke`.
Claves/tokens - por marca/región, de corta duración; secretos en Vault/HSM.
Aislamiento PII: 'player _ id' es un alias; RLS по `brand/region`.
Límites de tasa y cuotas de emisión; protección contra tormentas retraídas.
13) Hojas de cheques
Plataforma/operador
- Todas las transacciones monetarias pasan por Wallet con 'Idempotency-Key'.
- Rules/Elegibilidad versionada; «doble carta» de eventos en migraciones.
- Los circuitos de contratación están centralizados, cubiertos por pruebas.
- Velocity y anti-frod incluidos; «cuatro ojos» para grandes sumas.
- Outbox/CDC, DLQ y replay controlado para 'bonus.'.
- SLO-dashboards, OpenTelemetry, auditoría WORM.
- Vitrinas DWH para ROI y cumplimiento (RG/AML).
Integraciones (RGS/monedero/CRM)
- Compruebo max bet/win; Devuelvo el código de error empresarial.
- Presento 'trace _ id' y 'idempotency _ key'.
- Deduzca los disparadores y las garantías de envío (webhooks firmados).
14) Banderas rojas (anti-patrones)
Acumular el bono «manualmente» directamente en el saldo, pasando por Wallet.
Falta de idempotencia → dobles subvenciones/conversiones.
Vager cuenta por 'bet. placed ', no al final de' bet. settled`.
No hay circuitos contributivos o están «cosidos» en el código de los proveedores.
Las offers en conflicto se activan simultáneamente.
No hay velocity/anti-frod y auditoría WORM.
Los eventos 'bonus.' se publican eludiendo outbox/CDC.
Los indicadores de la promoción no convergen con Ledger/BI (no hay escaparates de ROI).
15) Resultado
Una promoción de backend confiable es contratos e invariantes, no «mejorar el balance». Separa las reglas del dinero, considera el progreso en los resultados reales, garantiza la idempotencia y la observabilidad, protege contra el abusivo y proporciona cumplimiento. Con tal núcleo, el marketing se mueve rápidamente, el jugador ve condiciones honestas, y las finanzas y reguladores obtienen una imagen exacta del costo y efecto de cada offer.
