--- title: PatePlay for Operators description: Put PatePlay games on your casino through OneHazel — config and seed data only, no PatePlay-specific code on your side. OneHazel runs the connector; you expose your native wallet surface and point launch at the relay. --- # PatePlay for Operators Add **PatePlay** (HTML5 slots + live tables) to your casino through OneHazel. You never speak PatePlay's wire protocol, never hold PatePlay's signing secret, and never build a PatePlay-shaped payload. You expose your platform's **own native** provider surface (`/provider/v1/*`, snake_case, `int64` minor units) and point each game's launch template at PatePlay's relay. OneHazel runs the **connector** that translates PatePlay ⇄ your native shape. This is a **config + seed-data** integration. If your platform already exposes the vendor-neutral `/provider/v1/*` surface (the SIM Casino **reference implementation** of the contract does), it is **near zero-code** on your side — you register the provider, mint a key, sync the catalogue, seed a few test players, and verify. The connector, the HMAC verification, the money translation, session auth, jackpots, and free/bonus bets live on OneHazel + PatePlay. ::: tip Who does what | Party | Owns | |---|---| | **You (operator)** | Registering the provider; a `provider_callback` integrator key; the native `/provider/v1/*` wallet + session surface (your platform feature); the synced catalogue + launch templates on your storefront; a handful of seeded test players; verification. | | **OneHazel** | The PatePlay connector (HMAC verify + money translation + session mapping), the relay/site provisioning, the connection that binds your operator + key + PatePlay environment, and coordinating PatePlay's certification + staging activation. | | **PatePlay** | The game runtime + RGS, the `x-api-key`/`x-api-hmac` credential pair, the game catalogue + `siteCode`, the relay hosts, the hosted certification suite, and activating the staging environment. | You talk to OneHazel. OneHazel talks to PatePlay. You expose native data; OneHazel translates. ::: ## How a spin flows PatePlay hosts the game and runs the RGS. It is a **seamless (transfer) wallet**: during play PatePlay's RGS calls the connector, which calls **your** wallet — there is no pre-funded PatePlay balance. ``` player clicks Play │ your storefront → your backend mints a launch token (plaunch_*) ▼ redirect/iframe → https:///?authCode=plaunch_*&siteCode=&game=&language= │ the plaunch_* IS PatePlay's authCode — no OneHazel launcher hop ▼ PatePlay game runtime ── /authenticate · /balance · /withdraw · /deposit · /cancel ──┐ │ each call signed x-api-key + x-api-hmac (HMAC-SHA256 of raw body) │ ▼ │ OneHazel connector ── verifies HMAC → resolves session → translates ─────────────────┘ │ POST /provider/v1/{session/exchange, session/balance, wallet/bet, wallet/win, wallet/rollback} ▼ Authorization: Bearer YOUR casino wallet (the ledger; int64 minor units) ``` Two things make this work and are the only places operators get bitten: 1. **The launch token IS the authCode.** Your `/session/start` mints a one-time launch token; you drop it straight into the relay URL as `authCode`. There is **no launcher hop** (unlike some aggregated providers) — the browser goes to the relay directly. 2. **Your surface speaks minor units; the connector converts.** You emit `balance_minor: 100000` (int64). The connector turns that into PatePlay's decimal string `"1000.00"`. Never emit decimals from `/provider/v1/*`. ## Prerequisites - You are a OneHazel operator (operator id / brand provisioned). - Your platform exposes the vendor-neutral **`/provider/v1/*`** surface — `session/start`, `session/exchange`, `session/balance`, `wallet/bet`, `wallet/win`, `wallet/rollback`, `catalog/sync`. The full wire contract lives in **`OPERATOR_API.md`** (the SIM Casino reference implementation you can build against). - An admin CMS where you can register game providers and mint integrator keys. - A storefront that renders your game catalogue and can redirect/iframe to a launch URL. - From OneHazel/PatePlay: your **`siteCode`**, the **relay base** for your environment, and the list of **game codes**. ## Step 1 — Register the PatePlay provider In your CMS, register PatePlay as a game provider with the relay base as `launch_base_url` (games get their per-game template in Step 3). ``` POST /api/v1/admin/game-providers { "name": "PatePlay", "slug": "pateplay", "default_currency": "EUR", "launch_base_url": "https:///" } → { "id": "gp_…", "slug": "pateplay", "status": "active" } ``` ## Step 2 — Mint a provider-callback key and hand it to OneHazel Mint an **integrator key** with the **`provider_callback`** scope. You get the key string **once** — copy it immediately. ``` POST /api/v1/admin/integrator-keys { "name": "pateplay-connector", "scopes": ["provider_callback"] } → { "key": "sim_live_…", "last4": "…", "is_active": true } # shown once ``` Give OneHazel, over a secure channel: - the **integrator key** (the credential the connector presents on every `/provider/v1/*` call), - your **casino API base URL** (where your `/provider/v1/*` surface lives — this becomes the connection's `provider_base`), - your **operator identity** (the OneHazel operator the connection binds to). ::: warning The key must carry the `provider_callback` scope A key minted without `provider_callback` authenticates but is rejected on every wallet call (`401`). If wallet calls 401 after go-live, re-check the scope first. ::: ## Step 3 — Catalogue sync Publish PatePlay's games to your catalogue with `POST /provider/v1/catalog/sync` (authed with your integrator key). Each entry's `launch_url_template` points at the relay with `{token}`/`{lang}` placeholders your backend fills at launch time: ``` https:///?authCode={token}&siteCode=&game=&language={lang} ``` Classify **live-table** codes (e.g. the `auto-live-roulette-*` family) as `kind: "casino"` and the rest as `kind: "slot"`. Confirm the games landed in the catalogue your storefront reads: ```bash curl -s 'https:///api/v1/public/v2/games?kind=slot&limit=500' \ | grep -o '"provider_code":"pateplay"' | wc -l # > 0 ``` ::: tip Re-sync to add or update games Adding titles or changing metadata is just another `catalog/sync` — no operator code change. The sync upserts by the provider game code. ::: ## Step 4 — Surface the games and wire launch ### Surface the catalogue Render the synced games on your storefront from your **transactional catalogue API** (the same table `catalog/sync` writes to), not a hand-curated CMS list. ::: warning Large catalogues and the page cap If your storefront requests more games than your catalogue endpoint returns per page, whole providers can drop below the cut and silently vanish — request the full page (e.g. `limit=500`) and page if you exceed it. ::: ### Wire launch (the launch-token = authCode model) When a player clicks Play: 1. Your storefront asks your backend to mint a launch token. The backend substitutes the template placeholders and returns a `launch_url` containing a single-use launch token — which **is** PatePlay's `authCode`. 2. Your storefront **redirects** the browser to that `launch_url` (or iframes it). The browser hits the **relay directly** — there is no launcher exchange hop. 3. PatePlay's runtime calls back with `/authenticate` (carrying the `authCode`), which the connector maps to your `/session/exchange`. Relay query params (PatePlay Wallet 1.1, camelCase): `authCode` (20–255 chars, one-time), `siteCode`, `game` (default `lobby`), `language` (default `en`), plus optional `playerId`, `playerName`, `device` (`desktop|mobile`). ## Step 5 — The wallet leg (what your surface must return) As the player plays, PatePlay's RGS calls the connector, which calls your native surface. You implement nothing PatePlay-specific — you return native shapes and the connector converts: | PatePlay call (RGS → connector) | Reaches your surface as | You return | |---|---|---| | `/authenticate` (authCode) | `POST /provider/v1/session/exchange` | `{ session: { token }, player: { external_id, currency, balance_minor, name, country } }` | | `/balance` | `POST /provider/v1/session/balance` | `{ balance_minor, currency }` | | `/init-game` | `POST /provider/v1/session/balance` (balance echo) | `{ balance_minor, currency }` | | `/withdraw` (bet) | `POST /provider/v1/wallet/bet` | debit; `{ status, balance_minor }` | | `/deposit` (win) | `POST /provider/v1/wallet/win` | credit; `{ status, balance_minor }` | | `/withdraw-and-deposit` | `wallet/bet` only (the 0-value deposit leg is connector bookkeeping; `wallet/win` fires only if the deposit leg is ever nonzero) | debit; `{ status, balance_minor }` | | `/cancel` | `POST /provider/v1/wallet/rollback` — **bets only** (0-value deposits are cancelled connector-locally; a credited win is not cancellable) | reverse the targeted bet | | `/check` | — (connector-local transaction lookup) | — | ::: danger `session.token` and minor units are both load-bearing `/session/exchange` MUST return **both** halves: `session: { token }` (the wallet-capable session the connector uses on every subsequent call — returning only `player` breaks everything downstream) and the player block with **`balance_minor` as an `int64`**: `100000` means **€1000.00**. If you emit `1000.00` or `balance` instead of `balance_minor`, the in-game balance breaks. ::: ## Step 6 — Seed and fund test players PatePlay's certification suite and the first live launches run against real players on your platform. Seed a handful (EUR wallets), fund them, and give OneHazel their `external_id`s — the connector mints certification sessions for them through your existing `/session/start`, so **no bespoke provisioning endpoint is needed**. ```bash # register POST /api/v1/public/auth/register { "email":"pp_test_1@…","username":"pp_test_1","password":"…", "first_name":"Pate","last_name":"Tester1","country":"MT","currency":"EUR" } → { "player": { "external_id": "p_…" } } # fund (login → deposit) POST /api/v1/me/transactions/deposit { "amount_minor": 100000, "method": "bank_transfer" } ``` Hand OneHazel the `external_id`s over the secure channel (never the passwords). ## Step 7 — Verify end-to-end 1. **Config resolves** — mint a launch token for a seeded player and a real game code; confirm the relay URL + balance ride along: ```bash curl -s -X POST 'https:///provider/v1/session/start' \ -H "Authorization: Bearer " -H 'Content-Type: application/json' \ -d '{"idempotency_key":"verify1","player_external_id":"p_…","provider_slug":"pateplay","provider_game_code":"","currency":"EUR","language":"en"}' # → { launch_token.token: "plaunch_…", launch_url: "https:///?authCode=plaunch_…", player.balance_minor: 100000 } ``` 2. **Certification** — OneHazel runs PatePlay's hosted suite against the connector; it should reach 100% with no operator change beyond what you shipped. 3. **First `/authenticate`** — after PatePlay activates the environment, the first real `/authenticate` returns a session + balance; `/balance`, `/withdraw`, `/deposit`, `/cancel` complete the loop. 4. **Wallet reconciles** — check the player's wallet in your admin before and after: a bet debits, a win credits, a cancel restores exactly. ## The deltas that matter (PatePlay-specific rules) - **Auth: `x-api-key` + `x-api-hmac`.** Lowercase-hex `HMAC-SHA256` of the **raw request body**, keyed by the API **secret** (never on the wire). Compute over the exact received bytes — never re-serialize the JSON first, or the digest drifts and verification fails with `NOT_AUTHORIZED`. Reproduce a vector: ```bash SECRET='pateplay_example_secret_0123456789' BODY='{"authToken":"e16e5e30-3fac-49c9-a12f-4f8d735c29d4","siteCode":"testsite","deviceType":"desktop"}' printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1 # → fd0f39cade451125c24b6d42bf3ad732b592a02ba6d2be0e6723dad39e30e2e1 ``` OneHazel supplies the full cross-check vector set during onboarding. - **Money: v1 is 2-decimal currencies only** (EUR/USD/…). Zero-decimal currencies (KRW/VND/CLP/PYG/UGX/…) need an ISO-4217 per-currency exponent map on both the connector and the wallet/UI — a planned fast-follow, out of v1. - **Free/bonus bets:** PatePlay marks free bets inline (`bonus{free:true}`) — the connector does not debit the stake and credits wins per-spin via `/wallet/win`. If your platform mirrors issuance into a `freerounds` module, any settle call must carry **`total_win_minor = 0`** — the winnings were already credited per-spin, and a positive settle value double-pays. - **Jackpots (v1 approximation):** `jackpotContribution` rides inside the stake; a jackpot payout is a normal `/wallet/win` (`round_end`). No native jackpot modelling required. - **Player name/country:** `/session/exchange` carries `player.name` + `country` for PatePlay's `playerName`/`country`. If they come back empty, the data isn't set on the player or your backend build doesn't return the fields yet — it is not a connector bug. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Game won't load: **relay "took too long to respond"** (TCP timeout, no HTTP status) | The PatePlay **environment isn't activated yet** — the relay is firewalled until PatePlay's "request staging setup" step (which comes **after** the certification suite) | Not a casino or connector bug. Complete certification → OneHazel sends PatePlay the setup pack → PatePlay activates the relay → launches resolve. | | Every wallet call `NOT_AUTHORIZED` | HMAC computed over a **re-serialized** body, or wrong secret | Sign the **raw received bytes**; confirm the secret; match against the vector above. | | Every wallet call `401` | Integrator key missing the `provider_callback` scope, or a wrong/revoked key in the connection | Re-mint with the scope; confirm OneHazel has the current key in the connection. | | `/session/exchange` returns **empty `name`/`country`** | The player has no name/country set, or your backend build doesn't return the fields | Set the player fields; if data is present but the response is empty, deploy the backend build that returns them. | | In-game balance shows a wrong number (e.g. `100000.00` or `10.00`) | Units bug: `balance_minor` emitted as a decimal, or ÷100 applied twice | Emit `balance_minor` as an int64; contact OneHazel if your surface is correct and the game still shows a wrong figure. | | Games synced but don't appear on the storefront | Storefront reading a curated CMS list, or hitting the catalogue page cap | Point the storefront at the transactional catalogue and request the full page (`limit=500`). | | Duplicate-looking wallet response with `"duplicate": true` | A retry reused the same `idempotency_key` — success, not failure | The body is the original successful response; treat as accepted. | ## Day-2 operations - **Rotating the integrator key** — mint the new key, give it to OneHazel, have them update the connection, then revoke the old one. Allow **~30 seconds** after OneHazel confirms the update before revoking — the connector briefly caches the connection's decrypted config. - **Adding / updating games** — re-run `catalog/sync`; verify the new titles in your public catalogue. No operator code change. - **Zero-decimal currencies** — before enabling KRW/VND/CLP/…, coordinate with OneHazel: the ISO-4217 exponent handling must land on the connector and your wallet/UI together. Out of v1. - **Staging → production** — a new PatePlay environment means a new relay base and `siteCode`; update `launch_base_url` and re-sync so templates point at the production relay. ## Summary checklist - [ ] PatePlay provider registered (`slug: pateplay`, relay `launch_base_url`) - [ ] `provider_callback` key minted; key + casino API base URL + operator identity handed to OneHazel securely - [ ] `catalog/sync` landed; live-table codes `kind: casino`, rest `kind: slot`; titles visible in your public catalogue - [ ] Storefront reads the transactional catalogue (full page); launch template → `/?authCode={token}&siteCode=…&game=&language={lang}` - [ ] Test players seeded + funded; `external_id`s handed to OneHazel - [ ] HMAC signing cross-checked against the vector set - [ ] Certification 100%; PatePlay environment activated; first `/authenticate` + wallet loop verified against the wallet ledger ## Reference The full vendor-neutral contract — every endpoint, the complete error table, idempotency, session semantics, money rules, and a worked reference implementation — is in **`OPERATOR_API.md`** (the SIM Casino reference implementation you can build against). ::: tip Need help? If a game won't load, the in-game balance looks wrong, or a bet/win isn't reflected in your ledger, contact OneHazel with the game and player — OneHazel owns the PatePlay integration end-to-end and will diagnose it. :::