# Casino Integration API This product is a game provider. It does not own player balances. The casino remains the source of truth for player accounts, available balance, debits, credits, refunds, KYC, AML, responsible gaming, and jurisdiction controls. ## V1 Currency Contract Rouge Reels supports configured operator currencies in v1. Supported currency codes are `USD`, `EUR`, `GBP`, `USDT`, `USDC`, `BTC`, `uBTC`, `ETH`, `LTC`, `DOGE`, `BNB`, `SOL`, `XRP`, `ADA`, `TRX`, `XLM`, and `ZEC`. Each operator configuration has one active currency. If a casino wants both USD and uBTC, create separate operator-currency profiles, for example `casino-usd` and `casino-ubtc`. The casino chooses the operator/session currency and all balances, debits, credits, rollbacks, limits, audit records, and reconciliation rows for that session must use the same currency. Rouge Reels does not perform FX conversion, wallet custody, blockchain settlement, or on-chain balance checks. The casino owns conversion, exchange-rate policy, rounding, and player-facing wallet display before calling the provider. Currency precision: - `USD`, `EUR`, `GBP`: 2 decimals - `USDT`, `USDC`: 2 decimals by default - `BTC`, `LTC`, `DOGE`, `BNB`, `SOL`, `ZEC`: 8 decimals - `uBTC`: 2 decimals, where `100.00 uBTC` means 100 micro-BTC - `ETH`: 6 decimals by default - `XRP`, `ADA`, `TRX`: 6 decimals - `XLM`: 7 decimals Example uBTC operator/session values: ```json { "operatorId": "casino-ubtc", "currency": "uBTC", "balance": 100000.00, "minBet": 1.00, "stakeStep": 0.01, "maxPayout": 5000000.00 } ``` ## Sandbox Quick Start Use `docs/sandbox-pilot-offer.md` as the casino-facing package checklist before sending a pilot environment to a partner. It defines the sandbox disclaimer, currency profile scope, Cloudflare/domain stage, acceptance criteria, and hard gates before real-money production. Use `docs/sandbox-integration-guide.md` as the partner-facing path from a clean local checkout to a verified sandbox round. It walks through: - sandbox credentials and local startup - creating a launch token - creating a game session - checking the casino-provided balance - placing a settled dice bet - replaying the same idempotent bet request - forcing an idempotency conflict - forcing a failed round and rollback - looking up the full round dispute packet - reviewing reconciliation rows and operator dashboard evidence Use `docs/postman_collection.json` to run the same sequence against the local provider. A casino partner should be able to complete the sandbox guide and checklist before production credentials are issued. ## Authentication Production casino callbacks must be signed with HMAC-SHA256 using the shared operator secret. Required headers: - `X-Operator-Id` - `X-Timestamp` - `X-Nonce` - `X-Signature` - `X-Idempotency-Key` for money-changing requests Signature payload: ```txt METHOD + "\n" + PATH + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + raw JSON body ``` `PATH` is the request path such as `/wallet/debit`. The signature is lowercase hex HMAC-SHA256 over the payload using the shared operator secret. Casinos should compare signatures with a timing-safe comparison. Reject requests when timestamps are outside the allowed clock skew, nonces are reused, signatures do not match, or idempotency keys conflict. Recommended replay storage is at least the allowed clock-skew window, with a default skew of five minutes. ## Callback Transport Local demo operators can use `sandbox://demo-casino`. Production operators should use an HTTPS `callbackBaseUrl`, for example: ```txt https://casino.example.com ``` The provider sends signed `POST` requests to the required wallet paths under that base URL: - `/wallet/validate-session` - `/wallet/balance` - `/wallet/debit` - `/wallet/credit` - `/wallet/rollback` Default provider behavior is a 3 second callback timeout and 1 retry for timeout, network, HTTP 429, and HTTP 5xx failures. Money-changing retries use the original `X-Idempotency-Key`, so a casino must return the original debit, credit, or rollback response instead of creating a second transaction. ## Casino-Provided Endpoints The casino implements these endpoints so the game provider can communicate with the casino wallet. ### Validate Session `POST /wallet/validate-session` Purpose: validates a launch token and returns a casino player reference. Request: ```json { "operatorId": "demo", "launchToken": "launch-token-from-casino", "gameType": "dice" } ``` Response: ```json { "valid": true, "playerRef": "player-123", "displayName": "Player 123", "currency": "USD" } ``` `currency` must match the configured operator currency. A session returning a different supported currency is rejected with `CURRENCY_MISMATCH`. Unsupported currency codes are rejected with `UNSUPPORTED_CURRENCY`. ### Get Balance `POST /wallet/balance` Request: ```json { "operatorId": "demo", "playerRef": "player-123" } ``` Response: ```json { "balance": 1000.00, "currency": "USD" } ``` `currency` must match the game session currency. The amount is the playable balance available to the game in that exact currency. ### Debit Bet `POST /wallet/debit` Request: ```json { "operatorId": "demo", "playerRef": "player-123", "roundId": "round_abc", "amount": 10.00, "currency": "USD" } ``` Response: ```json { "casinoTransactionId": "casino_debit_abc", "balance": 990.00, "currency": "USD" } ``` ### Credit Win `POST /wallet/credit` Request: ```json { "operatorId": "demo", "playerRef": "player-123", "roundId": "round_abc", "amount": 19.80, "currency": "USD" } ``` Response: ```json { "casinoTransactionId": "casino_credit_abc", "balance": 1009.80, "currency": "USD" } ``` ### Rollback Transaction `POST /wallet/rollback` Request: ```json { "operatorId": "demo", "playerRef": "player-123", "roundId": "round_abc", "amount": 10.00, "currency": "USD", "reason": "Game result failed after successful debit" } ``` Response: ```json { "casinoTransactionId": "casino_rollback_abc", "balance": 1000.00, "currency": "USD" } ``` ## Game Provider Endpoints ### Embedded Currency Switch The casino can switch the embedded game to another player currency by creating a new casino launch token for the matching operator-currency profile and sending it to the iframe. The game does not trust a client-provided currency value; it creates a new session and uses the currency returned by the casino-validated session. Parent page message: ```js gameIframe.contentWindow.postMessage({ type: "ROUGE_REELS_SWITCH_CURRENCY", requestId: "currency-switch-1", operatorId: "casino-ubtc", launchToken: "new-launch-token-from-casino", game: "dice", launchMode: "single" }, "https://games.rougereels.com"); ``` Iframe reply: ```js { type: "ROUGE_REELS_SWITCH_CURRENCY_RESULT", requestId: "currency-switch-1", ok: true, session: { sessionId: "session_...", operatorId: "casino-ubtc", currency: "uBTC", balance: 100000, game: "dice" } } ``` If a round is settling, the iframe waits briefly before switching. If autobet is running, the iframe requests autobet stop and then switches after it becomes safe. Invalid tokens, unsupported currencies, and mismatched casino balances return `ok: false` with an error code. ### Create Demo Launch `POST /sandbox/launch` Creates a sandbox casino launch URL. Use `launchMode: "single"` for a normal casino launch where the session is locked to one game. This is the default and the embedded client hides the game switcher. Use `launchMode: "lobby"` only when the casino wants to embed a provider mini-lobby that can switch among the operator's enabled games. Example request: ```json { "operatorId": "demo", "playerRef": "player-demo-1", "gameType": "dice", "launchMode": "single" } ``` Example single-game launch URL: ```text /game.html?operatorId=demo&game=dice&token=demo_launch_player-demo-1 ``` Example lobby launch URL: ```text /game.html?operatorId=demo&game=dice&token=demo_launch_player-demo-1&mode=lobby ``` For a casino-facing iframe, append `embed=1`. Embed mode hides the demo header, seed hash line, balance badge, and operator/admin link. The game still keeps provably fair details inside the shield control. By default, embed mode uses natural content height so the casino can size the iframe without forced full-screen behavior. ```text /game.html?operatorId=demo&game=dice&token=demo_launch_player-demo-1&embed=1 ``` For a full-height iframe or kiosk-style placement, add `fill=1`. The casino should also give the iframe a real viewport height; on mobile, prefer `100dvh` so browser address bars are handled correctly. ```text /game.html?operatorId=demo&game=dice&token=demo_launch_player-demo-1&embed=1&fill=1 ``` ```html ``` ### Create Session `POST /api/sessions` Validates a launch token and creates a game session. For single-game launches, the returned session includes `gameType` and the provider rejects bets for any other game. For lobby launches, `gameType` is `null`, `launchMode` is `lobby`, and bets may use any game enabled in the returned public operator configuration. ### Place Bet `POST /api/bets` Request: ```json { "sessionId": "session_abc", "gameType": "dice", "amount": 10, "idempotencyKey": "bet_01JRETRYSAFE", "clientSeed": "player-seed", "params": { "target": 50, "direction": "over" } } ``` Response includes the settled bet, result, proof, casino transaction references, and latest casino balance. `idempotencyKey` is required. If the same key is retried with the same session, game, amount, client seed, and params, the API returns the original settled response without debiting or crediting the casino again. If the same key is reused with a different payload, the API returns `IDEMPOTENCY_CONFLICT`. If debit succeeds but settlement later fails, the API attempts a rollback and returns an error containing `roundId` and `roundStatus`. Use that `roundId` to retrieve the failed round and callback trail. ### Round History `GET /api/bets/{roundId}` Returns the stored round record, fairness proof, casino transaction references, and `casinoCallbackAudit` for persisted debit, credit, and rollback callbacks. The callback audit includes method, path, callback URL, idempotency key, attempt count, request body, response body, status code, callback status, and error message when available. Shared secrets and request signatures are not stored. ### Verify Result `POST /api/verify` Recalculates a round from server seed, server seed hash, client seed, nonce, game type, amount, and game parameters. ### Reconciliation `GET /api/reconciliation` Returns debit, credit, and rollback records for casino reconciliation. ## Error Codes - `INVALID_SESSION`: launch token or session id is invalid. - `INSUFFICIENT_FUNDS`: casino balance cannot cover the bet. - `GAME_DISABLED`: operator has disabled the selected game. - `SESSION_GAME_MISMATCH`: single-game launch session tried to place a bet for another game. - `BET_OUT_OF_RANGE`: bet violates min/max limits. - `MAX_PAYOUT_EXCEEDED`: payout exceeds configured risk limit. - `SIGNATURE_INVALID`: callback signature failed. - `SIGNATURE_REQUIRED`: callback signature headers are missing. - `SIGNATURE_STALE`: callback timestamp is invalid or outside allowed skew. - `SIGNATURE_REPLAYED`: callback nonce has already been used. - `IDEMPOTENCY_REQUIRED`: bet placement did not include an idempotency key. - `IDEMPOTENCY_CONFLICT`: duplicate key has a conflicting payload. - `CASINO_TIMEOUT`: casino callback did not respond inside the timeout. - `CASINO_HTTP_ERROR`: casino callback returned a non-retryable HTTP error. - `CASINO_HTTP_RETRYABLE`: casino callback returned HTTP 429 or 5xx and retries were exhausted. - `CASINO_NETWORK_ERROR`: casino callback could not be reached. ## Retry Rules Money-changing requests must be retried with the same idempotency key. A repeated debit, credit, or rollback with the same idempotency key must return the original response instead of creating a second transaction.