Going to production
The demo omits several things a production integration needs. This page lists the gap between it and something you would put money through.
What the demo skips
| Missing | Why it matters |
|---|---|
| Player accounts and sessions | The demo takes a playerRef from a text box |
| Wallet, balance, entry debit | Nobody pays to enter, and nobody gets paid |
| Real provider sessions | demoSessionUrl() returns a fake URL |
| Persistence | Settlements live in a Map and die on restart |
| A queue behind the webhook | It settles inline; a real one enqueues and returns |
| Authentication | Every page is public |
| KYC, responsible gaming, jurisdiction | Which players may play at all |
All of those are on your side of the boundary, and SlotBattle will not grow them. See What SlotBattle does not do.
Do these before you take money
Debit the entry before opening the battle, and make it reversible. Battles can end
failed or cancelled. Decide your refund policy before you need one, and make a
battle.failed webhook trigger it automatically rather than through a support ticket.
Settle inside a transaction, keyed for idempotency. Delivery is at-least-once, so a duplicate will arrive eventually and must not pay twice.
Never settle from the socket. It is best-effort and has no replay.
Check ok per seat. A completed battle can contain a failed seat.
Mint provider sessions per player, server-side. game_url is a live money session. It
appears in no SlotBattle response, log, event or webhook; hold the same line in your own logs.
Verify the boundary holds
npm run build
grep -r "sbk_" dist/ && echo "LEAK" || echo "clean"
Run that in CI. A key in a client bundle is not something code review reliably catches; it is something you catch by grepping the built artifact.
Also confirm lib/slotbattle.ts is never reachable from a client component. The runtime guard
catches it in development; the grep catches what the guard cannot.
Operational reality
503 unavailable is normal under load. The instance caps in-flight battles. Retry with
exponential backoff and jitter; this is the one routinely retryable error.
Viewer tokens expire in 15 minutes. Re-mint on reconnect, and when a page sits open longer.
Signing-key rotations should be invisible. The operator appoints a new key and leaves the
old one verifying until it retires, so tokens already in browsers keep working. Viewers
suddenly taking 401 on the socket means the rotation was done without a window. Re-minting
is your mitigation, and the rotation is worth reporting.
Deploys can fail battles in flight. A battle mid-recording depends on recorders that are
already running. Handle battle.failed as a routine outcome, not an exception.
Watch a real recording end to end. A recorder without GPU acceleration falls back to about 2 fps: the recording succeeds and is unwatchable. No automated check catches this, so a human has to look.
Final checklist
- No
sbk_in any client bundle, checked in CI - Webhook signature verification rejects a tampered body, tested rather than assumed
- Settlement is idempotent under duplicate delivery
- Seats with
ok: falseare excluded from payouts -
battle.failedandbattle.cancelledtrigger refunds automatically -
503is retried with backoff;400/409are not - A late-joining viewer renders a correct lobby
- Viewer tokens are re-minted on reconnect
-
game_urlappears in no log you control - A full recording has been watched by a human
Where to go next
- API reference: every route, with Try It
- Errors: every code and what to do about it
- Keys and credentials: every credential and how it rotates
- What the host controls: which side a problem is on