Přeskočit na hlavní obsah

Bonus flow

Bidirectional kontrakt pro vystavování + tracking bonusů na peněžence hráče.

Lifecycle

[bonus.credited]
CRM grant → adapter.grantBonus → ───────────────────→ active

[bonus.wagered] (N×)
←────────────────────────┤

[bonus.completed] │
←────────────────────────┴──→ completed (TERMINÁLNÍ)

alternativní terminály:
[bonus.expired] → expired
[bonus.cancelled] → cancelled (admin / fraud)
[bonus.forfeited] → forfeited (max bet violation, atd.)

CRM → Adapter (outbound)

Když CRM journey, kampaň nebo admin vystaví bonus, casinocrm.io volá váš adapter:

POST <your-base>/v1/bonuses/grant

Headers:

  • Authorization: Bearer <CRM-API-key-or-platform-token> (váš auth scheme)
  • X-casinocrm.io-Signature: <hex-hmac> (casinocrm.io podepisuje outbound volání)
  • Content-Type: application/json

Body:

{
"grant_id": "ucrm-uuid-correlation-key", // ulož + echo na každém webhooku
"tenant_id": "ucrm-tenant-uuid",
"player_external_id": "casino_player_001",
"template": {
"type": "deposit_match",
"amount_subunit": 5000, // €50 v centech (issue value)
"currency": "EUR",
"wagering": {
"multiplier": 30,
"basis": "bonus_plus_deposit",
"max_bet_subunit": 500 // €5 v centech
},
"expires_at": "2026-06-08T...",
"config": { // type-specific config blob
"match_percent": 100,
"cap_amount": 5000,
"deposit_amount": 5000 // pro deposit_match — trigger deposit
}
},
"granted_by": {
"kind": "journey", // manual | api | journey | campaign | auto
"ref": "journey_run_uuid"
}
}

Success response (201):

{
"platform_bonus_id": "platform-side-bonus-id",
"status": "credited" // "credited" pokud synchronně, "pending" pokud async
}

Error responses:

// 4xx — non-retriable
{ "code": "PLAYER_BLOCKED", "message": "Player is self-excluded" }
{ "code": "DUPLICATE_GRANT", "message": "Already issued for grant_id X" }
{ "code": "TEMPLATE_NOT_SUPPORTED", "message": "..." }

// 5xx — retriable
{ "code": "INTERNAL_ERROR", "message": "..." }

POST <your-base>/v1/bonuses/cancel

{
"grant_id": "ucrm-uuid",
"tenant_id": "...",
"platform_bonus_id": "platform-side-id",
"reason": "fraud_review"
}

Vrací 200 { ok: true } a poté pošle bonus.cancelled webhook (asynchronně) až cancel skutečně doběhne na platformě.

GET <your-base>/v1/bonuses/:platform_bonus_id

Používá denní reconciliation pro porovnání CRM state vs platform state.

// 200 response
{
"status": "active", // active | completed | forfeited | expired | cancelled
"wagering_progress_subunit": 60000, // weighted progress v subunit
"total_wagered_subunit": 60000,
"total_won_from_bonus_subunit": 0
}

404 pokud platforma id nezná (znamená, že pohled CRM je špatný — log + audit).

Adapter → CRM (webhooks)

Pošlete je, jak bonus prochází lifecycle. Vždy uveďte grant_id aby CRM mohl korelovat.

POST https://api.casinocrm.io/v1/adapters/<your-slug>/webhooks/bonus-events

Headers:

  • X-casinocrm.io-Signature: <hex-hmac-sha256-over-timestamp.body>
  • X-casinocrm.io-Timestamp: <unix-seconds>

bonus.credited:

{
"event": "bonus.credited",
"event_id": "evt-uuid-unique",
"ts": "2026-05-08T12:00:00Z",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": {
"credited_amount_subunit": 5000,
"currency": "EUR"
}
}

bonus.wagered (pošlete po každé settled sázce, která přispívá k wagering):

{
"event": "bonus.wagered",
"event_id": "evt-uuid",
"ts": "...",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": {
"wagered_amount_subunit": 1000,
"weighted_progress_subunit": 1000, // amount × game_contribution_pct / 100
"bet_id": "bet-uuid"
}
}

bonus.completed:

{
"event": "bonus.completed",
"event_id": "evt-uuid",
"ts": "...",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": {
"completed_at": "...",
"total_wagered_subunit": 150000,
"total_won_from_bonus_subunit": 12000
}
}

bonus.expired:

{
"event": "bonus.expired",
"event_id": "evt-uuid",
"ts": "...",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": {}
}

bonus.cancelled:

{
"event": "bonus.cancelled",
"event_id": "evt-uuid",
"ts": "...",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": { "reason": "fraud_review" }
}

bonus.forfeited:

{
"event": "bonus.forfeited",
"event_id": "evt-uuid",
"ts": "...",
"grant_id": "ucrm-uuid",
"platform_bonus_id": "platform-id",
"data": { "reason": "max_bet_violation:8500_max_500" }
}

Reconciliation

casinocrm.io spouští pnpm reconcile:bonuses denně. Pro každý active grant v tenantech vašeho adapteru:

  1. Volá GET <your-base>/v1/bonuses/:platform_bonus_id
  2. Pokud je status platformy forward transition z CRM statusu, lokálně replayuje chybějící webhook (synthetic event přes stejný state-sync pipeline).
  3. Pokud je status nekompatibilní (CRM active ale platforma completed, zmeškaný webhook), vynutí transition + loguje structured drift entry pro ops.

Tohle gracefully zpracovává dropped webhooks + partial outages — nemusíte implementovat perfektní at-most-once delivery; casinocrm.io zavře gap noční dávkou.