Coin flow
Your game asks for a coin; the arcade takes it. The game never touches money, cookies or accounts, so the most a buggy (or hostile) game can do is ask.
Step by step#
- YOUR GAME → SDK:
GemsArcadia.insertCoin() - SDK → ARCADE HOST:
postMessage {__ga:1, type:"insertCoin"} - ARCADE HOST:
check event.source + validate - ARCADE HOST → COIN API:
POST /api/play/sessions/:id/insert-coin - COIN API → WALLET:
$transaction: debit + 70% earning - WALLET → COIN API:
new balance - COIN API → ARCADE HOST:
200 {insertionId, balance} - ARCADE HOST → SDK:
response {ok:true, data} - SDK → YOUR GAME:
resolve {ok:true, insertionId} - YOUR GAME → YOUR SERVER:
insertionId (receipt, support) - YOUR SERVER → COIN API:
GET /api/v1/insertions/:id (API key) - COIN API → YOUR SERVER:
{valid:true, coins, playerRef}
Who does what#
Your game (sandboxed iframe)#
Runs in <iframe sandbox="allow-scripts allow-pointer-lock allow-fullscreen">
without allow-same-origin. It has an opaque origin, no access to the
player's cookies, and a Content-Security-Policy that only lets it load its own
files. It talks to the outside world through one channel: postMessage to the
arcade page, wrapped by the SDK.
The SDK#
Wraps each call in an envelope { __ga: 1, id, type, payload }, posts it to the
arcade's origin only, and matches the reply by id. It ignores messages from
anything but its parent window and the arcade origin. Calls that get no answer
time out after 10 seconds.
The arcade host page#
Accepts messages only when event.source is the game's iframe, validates every
payload, and allows one insertion in flight at a time. It then calls the play
API with the player's session cookie, adding an idempotency key so a retried
request can never charge twice.
The coin API and wallet#
POST /api/play/sessions/{id}/insert-coin runs one database transaction that
appends a debit to the player's coin ledger, records the insertion and records
your 70% earning. Balances are derived from the append-only ledger, never
edited in place.
Steps 10–12: server verification (optional)#
Every successful insertion has an insertionId. If your own backend needs to
trust a purchase (a support request quoting the receipt code your game showed,
an unlock you store yourself), it can confirm the id with your API key. See
server verification.