Build the wallet API

Implement the endpoints called by Wuzzlo
View as Markdown

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

RouteCalled whenMutation
POST /auth/loginWuzzlo resolves a launch token to an operator user.No
POST /balanceWuzzlo needs the current available balance.No
POST /betrequestA player places a stake.Debit
POST /resultrequestA bet is settled or corrected.Credit or approved correction
POST /rollbackrequestAn eligible unpaid debit must be restored.Credit back the original debit

The interactive API reference is authoritative for request fields, response schemas, and examples.

Request processing order

For every signed wallet callback:

  1. Read and retain the raw request bytes.
  2. Read the Base64 value from the Signature header.
  3. Verify RSA-SHA256 over the raw bytes using the Wuzzlo public key.
  4. Parse JSON only after verification succeeds.
  5. Validate operatorId, required fields, session ownership, amount, and precision.
  6. Check durable reqId idempotency.
  7. Check the durable transactionId lifecycle.
  8. Lock the wallet and transaction records in one database transaction.
  9. Apply at most one balance mutation and save the idempotency result atomically.
  10. 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:

1{
2 "success": true,
3 "status": 200,
4 "userId": "1001",
5 "username": "Player 1001",
6 "currency": "INR",
7 "expiresAtUtc": "2026-07-16T16:00:00Z"
8}

Balance

POST /balance is read-only. Validate the signed request, operator, session token, and user relationship before returning a JSON number:

1{
2 "balance": 10000.00,
3 "status": "OP_SUCCESS"
4}

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:

  • reqId identifies one delivery attempt and detects an exact replay.
  • transactionId identifies the logical wallet transaction and its lifecycle.
  • roundId groups activity in a game round.
  • userId and operatorId establish 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:

WalletAccount(operatorId, userId, currency, balance, version)
WalletRequest(reqId, endpoint, requestHash, responseStatus, responseBalance, createdAtUtc)
WalletTransaction(transactionId, operatorId, userId, roundId, gameId,
debitAmount, creditAmount, state, createdAtUtc, updatedAtUtc)

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 balance as 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, and status: OP_INVALID_SIGNATURE without touching wallet or idempotency storage.
  • Return only the status assigned to the matched scenario; a plausible alternative fails certification.