Wallet Aggregator

The wallet aggregator facade exposes a versioned inbound wallet endpoint for casino game aggregators:

POST /api/wallet/v1/providers/{slug}/callback

Providers are configured in Admin -> Finance -> Wallet Aggregator. Each provider selects one adapter (mock, softswiss, pragmatic, or evoplay), a wallet currency (GOLD or DIAMOND), rollback window, rate limit, optional IP allowlist, and encrypted adapter config.

Canonical Operations

All adapters normalize callbacks to the same internal contract.

OperationEffect
balanceResolves the wallet session and returns the current wallet balance. No mutation.
debitDebits a bet from the provider currency wallet, writes a core Transaction with type AGGREGATOR_BET, writes WalletTransaction, and opens/appends a GameRound.
creditCredits a win, writes AGGREGATOR_WIN, updates round credit totals, and optionally settles the round when the adapter payload sets roundFinished.
rollbackReverses a prior debit or credit by referenceTxId, writes AGGREGATOR_ROLLBACK, and marks the original wallet transaction as rolled back.

Amounts are decimal strings with at most two fractional digits. Responses serialize balances as strings.

Idempotency And Replay

  • providerTxId is the idempotency anchor.
  • WalletTransaction has a unique (providerId, providerTxId) constraint.
  • Core ledger rows use paymentMethod = wallet_aggregator and idempotencyKey = wallet:{providerId}:{providerTxId}.
  • Duplicate callbacks with the same operation, amount, and reference return the persisted response with duplicate: true.
  • Duplicate callbacks with a different amount, operation, or reference are rejected with DUPLICATE_TX_MISMATCH and create a TX_MISMATCH discrepancy.
  • Redis replay reservations use wallet:replay:{providerId}:{providerTxId} with a TTL at least as long as the rollback window.
  • When Redis is configured and unavailable, callback guards fail closed with REPLAY_REJECTED.

Sessions

Callbacks never create users. A wallet session maps a provider token or external user ID to an existing platform User.

Admin/dev session creation:

POST /api/v1/admin/wallet-aggregator/sessions
Content-Type: application/json

{
  "providerId": "provider-uuid",
  "userId": "user-uuid",
  "ttlSeconds": 3600,
  "externalUserId": "optional-provider-player-id"
}

The response returns the plaintext token once. Only its SHA-256 hash is stored.

Rollbacks

  • Rollbacks require referenceTxId.
  • Repeated rollback of the same reference returns the original rollback result.
  • Rollback-before-debit creates a zero-amount tombstone. If the late debit later arrives, it is rejected.
  • Expired rollback windows return ROLLBACK_WINDOW_EXPIRED.
  • Settled debit rounds reject rollback by default with ROUND_FINALIZED.
  • Credit rollback clamps at zero if the user lacks the credited amount and creates a CLAMPED_ROLLBACK discrepancy.

Error Vocabulary

Canonical errors are mapped by each adapter:

Canonical CodeMeaning
INSUFFICIENT_FUNDSWallet balance cannot cover a debit.
USER_NOT_FOUNDSession token or mapped user is missing/expired.
USER_BLOCKEDUser is blocked or frozen.
TX_NOT_FOUNDReferenced round or transaction is missing.
ROUND_FINALIZEDRound cannot accept the requested mutation.
ROLLBACK_WINDOW_EXPIREDRollback is outside provider window.
DUPLICATE_TX_MISMATCHProvider transaction ID was reused with different payload.
INVALID_SIGNATUREAdapter signature check failed.
REPLAY_REJECTEDReplay/rate guard rejected or guard storage was unavailable.
PROVIDER_DISABLEDProvider is missing, inactive, disallowed by IP, or adapter is unavailable.
INVALID_REQUESTPayload is malformed or violates the canonical contract.
INTERNAL_ERRORTemporary internal failure. Aggregators should retry.

Adapter Examples

Mock

Config:

{ "secret": "test-secret" }

Signature: X-Wallet-Signature = hex(hmac_sha256(secret, rawBody))

Debit:

{
  "operation": "debit",
  "providerTxId": "mock-bet-1",
  "providerRoundId": "mock-round-1",
  "externalUserId": "session-token",
  "amount": "10.00",
  "currency": "GOLD",
  "gameId": "slot-1"
}

Success:

{
  "status": "ok",
  "balance": "90.00",
  "currency": "GOLD",
  "transactionId": "core-transaction-id",
  "walletTransactionId": "wallet-transaction-id",
  "duplicate": false
}

SoftSwiss-style

Config:

{ "secret": "softswiss-secret" }

Signature: X-REQUEST-SIGN = hex(hmac_sha256(secret, rawBody))

The adapter accepts a single action object or an actions array for combined bet/win calls.

{
  "action": "play",
  "transactionId": "sw-bet-1",
  "roundId": "sw-round-1",
  "playerId": "session-token",
  "amount": "5.00",
  "currency": "GOLD"
}

Errors are returned with HTTP status plus { "status": "error", "code": "INSUFFICIENT_FUNDS" }.

Pragmatic-style

Config:

{ "secret": "pragmatic-secret" }

Signature: form field hash = md5(sorted_params_without_hash + secret).

action=bet&transactionId=pp-bet-1&roundId=pp-round-1&playerId=session-token&amount=7.50&currency=GOLD&hash=...

Responses always use HTTP 200 with numeric error:

{ "error": 0, "description": "OK", "balance": "92.50", "transactionId": "core-transaction-id", "duplicate": false }

EvoPlay-style

Config:

{ "projectKey": "evoplay-key" }

Signature: signature = hex(hmac_sha256(projectKey, sorted_ordered_fields)).

{
  "jsonrpc": "2.0",
  "method": "win",
  "id": "rpc-1",
  "params": {
    "transactionId": "evo-win-1",
    "roundId": "evo-round-1",
    "playerId": "session-token",
    "amount": "12.00",
    "currency": "GOLD",
    "signature": "..."
  }
}

Operations And Reconciliation

Admin tooling exposes:

  • Provider CRUD, activation kill switch, config validation, and test action.
  • Session token creation for staging and certification.
  • Round and wallet transaction explorers with raw payload access.
  • Discrepancy queue with resolve/write-off actions.
  • Health summary for active providers, callback decisions, and open discrepancies.

Automated reconciliation covers stuck rounds, wallet transaction versus core ledger drift, and provider statement diffing into matched, missing-ours, missing-theirs, and amount-mismatch buckets.

Callback signing update (2026-10-29)

EvoPlay-style JSON callbacks authenticate the method and request ID together with typed parameters. Sign the UTF-8 encoding of JSON.stringify(['v1', body.method, body.id ?? null, sortedParamsWithoutSignature]) with HMAC-SHA256 using projectKey. Sort top-level parameter keys lexicographically and exclude only signature. Preserve parameter value types. Params-only signatures are rejected. Coordinate this update with the provider before enabling callbacks.

Mock callbacks are available only in development/test environments. Replace any previously saved placeholder secret before enabling a real provider. Production signing secrets require at least 32 bytes. Provider mutations require a super administrator; reads require administrator and finance-view permission.

Try the live demo Pricing

Have questions about Poker Engine? We reply fast.