Build the wallet API
Build the wallet API
All wallet endpoints accept and return JSON. Except where the authentication contract explicitly uses HTTP 404, wallet business results are returned with HTTP 200 and an exact status in the JSON body.
Required routes
The interactive API reference is authoritative for request fields, response schemas, and examples.
Request processing order
For every signed wallet callback:
- Read and retain the raw request bytes.
- Read the Base64 value from the
Signatureheader. - Verify RSA-SHA256 over the raw bytes using the Wuzzlo public key.
- Parse JSON only after verification succeeds.
- Validate
operatorId, required fields, session ownership, amount, and precision. - Check durable
reqIdidempotency. - Check the durable
transactionIdlifecycle. - Lock the wallet and transaction records in one database transaction.
- Apply at most one balance mutation and save the idempotency result atomically.
- Return the calculated balance and exact status.
Authentication
POST /auth/login validates the token created during launch and identifies the wallet user. Do not infer a user from an untrusted userId hint. Validate the authoritative token, its operator, expiry, and revocation state.
Success returns the canonical operator user:
Balance
POST /balance is read-only. Validate the signed request, operator, session token, and user relationship before returning a JSON number:
Do not return currency-formatted strings or binary floating-point approximations.
Debit a bet
POST /betrequest subtracts amount exactly once. The critical identifiers have different roles:
reqIdidentifies one delivery attempt and detects an exact replay.transactionIdidentifies the logical wallet transaction and its lifecycle.roundIdgroups activity in a game round.userIdandoperatorIdestablish ownership.
Within one database transaction, verify sufficient funds, insert the transaction record, subtract the balance, and store the response. A repeated reqId returns OP_DUPLICATE_REQUEST and the unchanged current balance. A new reqId reusing an existing debit transactionId returns OP_DUPLICATE_TRANSACTION without another debit.
Settle a result
POST /resultrequest settles the referenced transaction exactly once. A win credits the authoritative amount; a loss may settle with zero. Validate that the original debit exists, belongs to the same user, and has not been rolled back or already settled.
Never calculate the payout again from client-side game information. The signed Wuzzlo settlement amount is authoritative after all validation succeeds.
Roll back a debit
POST /rollbackrequest restores only an eligible, unsettled debit. Require the original transactionId, verify ownership and amount, then mark it rolled back and return the restored balance atomically. Do not roll back a settled transaction, an unknown transaction, or the same transaction twice.
Minimum data model
Persist at least:
Use a unique constraint for reqId in its agreed scope and another for the logical transactionId. Use row locking, serializable transactions, or optimistic concurrency with retry so simultaneous callbacks cannot overspend or double-credit.
Response rules
- Always return
balanceas a JSON number. - Successful mutation responses contain the post-transaction balance.
- Duplicate and rejected responses contain the unchanged balance unless the contract explicitly requires zero.
- Invalid signatures return HTTP
200,balance: 0, andstatus: OP_INVALID_SIGNATUREwithout touching wallet or idempotency storage. - Return only the status assigned to the matched scenario; a plausible alternative fails certification.