Wallet & Payments
Multi-currency wallets
A player holds one wallet per currency. There is no single blended balance — each wallet is separate, and each is denominated in exactly one currency. A player playing in EUR and betting in USD has two wallets with two independent balances.
Players nominate a lead wallet in their preferences. It is the active wallet: the one the header shows, bets and game rounds spend from, and deposits start from. The player can also switch it on the spot — from the header panel or from the account picker on a game — and every surface follows at once. The bet slip has no wallet picker of its own: it stakes from whichever wallet the header shows. When a game is launched and only one of the player's wallets can fund it, the game opens on that wallet directly instead of showing a one-row picker.
The header is one filled pill showing the active wallet's balance in its own currency, with Deposit seated inside it. It opens into a panel that lists the player's other funded wallets (empty ones are left out), with Deposit and Withdraw actions and the "Show in fiat" switch (below). A player who has no wallet yet sees a zero and Deposit.
The wallet page is laid out as two columns on wider screens: Balance (the total, Deposit and Withdraw, an allocation bar across currencies, one row per wallet, and cards for any bonus the player holds) and Activity (deposits and withdrawals, filterable, with in-progress items pinned at the top and history grouped by day).
The wallet dialog. Deposit, Withdraw and the rest open in one dialog, addressable by link so a section survives a refresh. Its sections are Deposit, Withdraw, Tip (only where the operator has switched tipping on), Transactions and Rewards (rakeback and bonuses, with a badge when a claim is waiting). On a desktop the sections are a menu beside the open one, with the total balance and its split across wallets in view; a desktop opens on Deposit. On a phone the dialog opens as a full-height menu and each section replaces it, with a back arrow; the header's Deposit button skips the menu and opens Deposit directly.
Display currency
Separately from the lead wallet, a player can pick a display currency — USD or EUR today — from the account menu. The wallet page then shows the player's whole balance converted into it, and each wallet row leads with its converted figure, with the wallet's own amount beneath. With no display currency chosen, a player with several wallets sees the total in USD, and a single-wallet player simply sees that wallet in its own currency. Converted figures are approximate; screen readers announce them as such.
Show in fiat is a per-player switch in the header wallet panel. Off by default. When on, a crypto wallet reads in the display currency (or its USD estimate) in the header pill, and crypto rows drop the coin amount beneath the converted figure. Fiat wallets are unchanged, and so is a coin with no usable rate — it keeps showing its own amount. USDT and USDC are dollar stablecoins and already read in dollars.
The conversion is done on the server and is a display convenience only: it never moves money and is never the basis of a transaction. A wallet whose currency has no usable exchange rate is left out of the converted total rather than guessed at. Real currency conversion between a player's wallets is not built; the only conversion that moves money today is the affiliate payout currency (see Affiliate Payouts). Any conversion is recorded as two same-currency ledger legs through a house FX account, so no movement ever crosses currencies inside a single entry — see Money Core.
Exchange rates
Every conversion on the platform — display totals, limit checks, KYC thresholds, withdrawal limits, affiliate bands, reports — goes through one rate lookup:
- Rates are fetched in one place only: a background refresher in the worker. It polls crypto every 30 seconds and fiat every 10 minutes, and no player or staff request ever waits on an exchange feed.
- A published rate is the median of several independent feeds (five crypto venues and four fiat sources), published only when enough of them agree. Staff can tune the per-currency quorum and deviation band, and switch an individual feed off, from the backoffice currencies screen. A failed fetch is never published over a good rate.
- If the live feeds do not produce a currency, the platform falls back to the last stored rate, but only if it is at most twelve hours old (the window is configurable, and some callers — such as crypto game sessions — hold themselves to a much tighter one).
- USDT and USDC are pegged at exactly 1 USD, so dollar-pegged balances keep working with no feed at all. BTC and ETH are never pegged.
- A missing rate is never guessed. There is no 1:1 fallback for anything else. On a money path — a limit check, a KYC threshold, a withdrawal cap, a game-launch funds check, bonus pricing — the action is refused with a "rate unavailable" error rather than decided on a made-up number. Reports and display totals instead skip the currency and name it as unconvertible.
- A stalled refresher is flagged. The backoffice home page fails its "Is the platform live?" check when fiat rates have not updated for 40 minutes or crypto rates for 5 minutes, because stored rates quietly turn into refused deposits, bets and payouts once they age out.
Crypto deposits and withdrawals
The cashier supports crypto alongside fiat: USDT on Tron, USDC and ETH on Ethereum, and BTC. The deposit dialog is a single screen: a tab for fiat or crypto, and for crypto three steps on the same surface — coin, then network, then address. Nothing is fetched until the player picks a coin (unless they came from a wallet's own Deposit button, which opens straight at the address). A coin that rides only one chain settles its network by itself and says so; a coin on several chains asks, because choosing wrongly loses funds. Answered steps collapse into rows with a Change action.
The address is shown with a QR code on a white plate (black on white in every theme, for scanner reliability) with the coin's mark in a cleared centre, and a one-tap copy button. A tinted wrong-network warning heads the address card, so it is read before the address is taken. Deposits are credited once the chain's confirmation count is reached, with an "n of N confirmations" progress line; a "Sent already? Check now" action lets a player prompt a re-check.
Crypto withdrawals are held to the same withdrawal limits and review rules as fiat (see below), plus a per-coin minimum ("dust floor"). Withdrawing to an address the player has never used before requires a recent re-authentication, because a new destination address is exactly what an account takeover needs. The withdrawal form shows a quote (the net amount and the dust floor) before the player confirms, and "withdraw everything" is not offered on crypto, because the amount must be fixed under the quote. Network fees are absorbed by the operator today; charging a withdrawal fee to the player is not wired.
Held deposits. A confirmed crypto deposit that the credit-time responsible-gaming check stops is not lost and not credited: it is recognised on the ledger as player money in the player's held account, and shows to the player as held. A staff member then releases it to the player's cash, returns it to the sender's address, or writes it off, each with a written reason and each recorded in the audit log. The money counts as player funds throughout.
Chain watching, keys and sweeps live in a separate crypto payment service that ships with the platform and pushes normalized deposit events into it. That service is testnet-only by construction — it refuses to boot in a production environment — and the platform only offers it where the money is not real. Going live on crypto means pointing the same adapter contract at a mainnet custody or processing provider.
Playing casino games with crypto
A crypto wallet stays denominated in its coin, and every ledger movement stays in that coin — there is no second, fiat balance. Casino game providers mostly speak fiat, so a round on a crypto wallet is shown to the provider in a shadow currency (USD unless the provider is configured otherwise) at a rate frozen on the round when it opens; the coin amounts moved on win, loss or rollback all use that same frozen rate. The session's rate must be fresh to stake on (10 minutes for crypto, 1 hour for fiat, per-provider settings); an older one asks the player to relaunch, which quotes a new rate. A drift guard stops the session opening new rounds if the market moves beyond a configurable limit (5% by default) from its rate; a round already open always settles at its own frozen rate. See Casino.
Deposits
A deposit runs as an intent, not a single call — because real money moves through a third party on its own schedule.
Player requests deposit
└─► Platform creates a deposit intent, checks responsible-gaming limits
└─► Player completes checkout at the payment provider
└─► Provider's webhook confirms
└─► Platform credits the wallet, once
Three things make this safe:
- Responsible-gaming limits are checked at initiation, and they count in-flight intents as well as settled money. Two concurrent deposits cannot jointly breach a limit that either alone would respect.
- The credit is idempotent. A webhook delivered twice credits once.
- Verification is checked up front. If the player must verify before depositing, the dialog says so and opens document capture in place; a blocked player is pointed to support. Deposits stay open while an uploaded document is still in review.
- A lost webhook does not strand the player's money. A job runs every minute, finds intents still open past a fifteen-minute cutoff, polls the provider, and applies the same resolution the webhook would have. The same pass sends each payout once its one-minute cancel window ends. This is load-bearing: without it a dropped webhook leaves a deposit pending forever.
Withdrawals
A payout is stricter than a deposit, in the ways that matter:
- KYC gate. Every withdrawal requires identity verification, with proof of address added past a cumulative threshold; a declined attempt tells the player exactly which check is missing and opens the document-capture flow in place. Deposits are gated too, past their own cumulative threshold — see KYC for the tiers, thresholds and blocking rules.
- Limits from a USD base. You configure one set of withdrawal limits in USD — minimum, maximum, daily cap and a review threshold — and every currency, fiat or crypto, is held to it, converted at the current rate (rounded down to the currency's precision, the stricter side). A currency that needs different figures gets its own override row. The daily cap counts the player's payouts across all currencies together. If the USD row is missing, payouts in any currency without an override have no limits, and the backoffice home page flags that as a failure, separately for fiat and crypto ("any player can cash out any amount"). No exchange rate means the payout is refused. Limits perks (see Benefits) can only widen what a player gets: the maximum and daily cap are the highest of the global figure and every perk that applies to the player, the minimum the lowest. Perks aimed at tiers count only once the player's identity is approved. The review threshold is a risk setting, not a bound, so a perk that sets one replaces the global figure. The maximum never exceeds the daily cap.
- Manual review. A payout at or above the review threshold (1,000 USD by default) waits in a backoffice review queue and is not sent until a staff member approves it; rejecting it fails the payout and returns the funds. Where the region rule asks for it, a player's first withdrawal waits for review too.
- Funds are held. The amount leaves the available balance when the payout is requested, not when the provider pays — so a player cannot request a withdrawal and then gamble the same money.
- Cancellable. A player can cancel a pending payout during a one-minute window after requesting it, which releases the hold. After that the payout is sent to the provider (or waits in the review queue) and can no longer be cancelled by the player.
- Cents are never lost. A payout is floored to the currency's precision; any sub-unit remainder of the balance goes to the house rounding account in the same ledger entry rather than vanishing.
- Active bonuses are settled first. Cash is always withdrawable, but a withdrawal during a bonus acts on it — see Bonus funds.
Bonus funds
Bonus money lives in a separate bonus balance per currency, alongside cash. How bonuses are defined and cleared is covered in Promotions & Bonuses; what the wallet does with them:
- Cash is spent first. A stake draws on the cash balance, and only what cash cannot cover comes from the bonus balance, so a player with cash to play with never touches the bonus and its winnings stay withdrawable. Every stake still counts toward wagering whichever balance funded it. A stake that drew any bonus pays its whole win into the bonus balance.
- The live bonus balance counts as playable funds. Game providers are told the player's cash plus live bonus, the launch minimum-funds check counts it, and a wallet holding only bonus can still play.
- Progress is visible. The wallet page has a Bonuses card per live bonus: wagering progress, max bet, max win, expiry, which games count and at what rate, and a Cancel action. When wagering is complete the bonus converts to cash.
- Locked bonus. Some rewards arrive as a locked bonus: a pot that cannot be bet and is not yet money. It unlocks into cash as the player wagers, a slice at a time, and the wallet shows what is still locked and how much has unlocked so far. See Benefits.
- Withdrawing during a bonus. Cash is withdrawable at any time, but a withdrawal settles the player's bonus in that currency first. A bonus whose wagering is already met is converted and paid out; a bonus still being wagered is cancelled, and the player must confirm it first (without the confirmation the payout is refused with a bonus-forfeit-required error) — the wallet shows what will be lost (the bonus balance and winnings from it, never the cash). A bonus with nothing left in it is simply closed.
Closed accounts
A player who deletes their account cannot deposit, withdraw or play afterwards, so any balance left in a wallet stays on the ledger untouched. The delete dialog shows the remaining balances first, with a Withdraw shortcut. Payouts, bets and casino rounds already in progress still settle normally.
Disputes and chargebacks
A deposit or payout can be marked disputed — by staff from the backoffice, or by the provider — and later resolved as won or lost. On a deposit, opening the dispute posts the chargeback against the player's wallet, which may take it below zero so the true debt is recorded rather than refused. A won dispute reverses the chargeback. The provider's dispute fee, where one is configured, is booked as the operator's expense — never charged to the player — and is kept even when the dispute is won, as acquirers do. Provider fees on deposits, withdrawals and chargebacks are booked at the moment of the transaction they are charged on, from rates staff configure per provider.
Provider fees, reserve and settlement
Each payment provider holds money for the operator until it remits, so the platform tracks a position per provider and currency: deposits, minus withdrawals, minus chargebacks and refunds, minus what has already been settled with the bank. A positive position means the provider owes the operator; a negative one means the operator must fund it.
- Fees. Staff enter a provider's rate as a percentage, a fixed amount, or both, with the date it takes effect. Rates are never edited in place and cannot be backdated; each transaction is charged at the rate in force when it was created, and the fee is posted in the same movement as the deposit, withdrawal or chargeback. A provider with no rate in force books no fee. When the acquirer's statement arrives, staff record its total fee for the period, and the difference from the platform's estimate posts as a correction.
- Rolling reserve. An acquirer may withhold a slice of each settlement as collateral against future chargebacks. Staff record the amount withheld per settlement; it is booked as an asset the provider is holding, not as a cost, and is later released or forfeited. A provider with no reserve terms withholds nothing, and none of the current providers imposes one.
- Settlements. On the Settlements screen staff propose a settlement for a provider and period (the platform computes the amount and shows any discrepancy against the statement), record it, and resolve it. An inbound settlement lands in a bank client account, named by a settlement destination; an outbound one (the operator funds a short provider) is paid from a chosen bank client account, which cannot go negative.
The operator bank
Player deposits are the players' money; the operator's money is only what players have lost, plus capital the owner puts in. The Bank screen keeps the two apart. A bank must be registered first (a command-line step), and transfers need an adapter for it. Only a simulated bank adapter exists today and it is offered outside production, so on a production deployment settlements can be recorded against a bank but staff cannot yet move money through it.
Per currency there are three places money can be:
| Place | Whose money | Counts as player funds |
|---|---|---|
| At a payment provider (position and reserve) | Players' | Yes |
| Bank client account | Players' | Yes |
| Bank operating account | The operator's | No |
The screen shows, per currency, what is held at providers, what sits in the bank client account, what is owed to players, and the surplus or shortfall between them. Staff actions each need the bank power and a written reason, are recorded in the audit log, and are safe to retry:
- Take profit moves money from the client account to the operating account, and only up to the surplus, so players stay covered. The cap is checked again when the transfer posts.
- Fund moves the operator's own money from the operating account to the client account, to close a shortfall (for example when players have won more than they staked). The screen suggests the amount.
- Capital in / out records the owner putting money into, or taking it out of, the operating account.
A bank account never goes negative. Once a bank is registered, the backoffice home page asks "Are player funds covered?" from the same per-currency figures, and shows any shortfall with a shortcut to fund the client account.
Payment providers
| Provider | Status |
|---|---|
| Trustly | Adapter scaffold only. Request signing and webhook signature verification are unimplemented stubs that throw, so the platform registers Trustly only where the money is not real. Finishing it means implementing the signing against the vendor's documentation. |
| Transak | Adapter with status mapping and HMAC webhook verification; the exact signature header still needs confirming against the vendor's docs before go-live. Points at the staging host by default; unconfigured out of the box. |
| Crypto (testnet) | The bundled crypto payment service described above. Testnet-only, and offered only outside production. |
| Mock provider | A self-completing provider for development and demos. It is never offered in production, and on staging only when its webhook secret is set. |
The machinery around the providers — intents, webhooks, idempotency, reconciliation, holds, limits, review, disputes, cancellation, race handling — is real and tested regardless of which provider sits behind it. Adding a PSP means writing one adapter.
Scope & Roadmap
Implemented. Multi-currency wallets with an active (lead) wallet that the header, casino and bet slip share; a USD/EUR display currency and a "Show in fiat" switch; one FX rate lookup fed by a background refresher (crypto every 30 seconds, fiat every 10 minutes), multi-feed medians, a 12-hour stale-rate window, pegged dollar stablecoins, refusal (never a 1:1 guess) on a missing rate, and a stalled-rate alert on the backoffice home page; the deposit intent lifecycle with webhook confirmation and reconciliation of stranded intents; testnet crypto deposits (coin, network, address, QR with the coin mark) and withdrawals; crypto casino play at a rate frozen per round; payouts with KYC gating, balance holds, USD-based limits shared by fiat and crypto, a cross-currency daily cap, a staff review queue above a threshold, and cancellation; the bonus balance (cash-first stakes, live bonus counted as playable funds, wagering progress, locked bonus unlocking into cash, a confirmation before a withdrawal cancels a live bonus); dispute and chargeback posting with provider fees; a safely gated mock provider; the wallet dialog with its sections; held crypto deposits released, returned or written off by staff; per-provider fee rates with statement correction, rolling-reserve terms and settlements; the operator bank with client and operating accounts, take-profit, fund and capital movements, and a player-funds coverage verdict on the home page; perks that widen withdrawal limits.
Partial. Transak is unconfigured, pointed at its staging host, and has not run against production credentials. Trustly is a scaffold whose signing is not implemented. Crypto runs on testnet only. The only bank adapter is a simulation, so real bank money cannot yet be moved from the platform, and there is no bank statement import. Payout review is a single staff approval, not a two-person sign-off. The display currency offers USD and EUR only.
Not built — and commercially significant. There is no live PSP contract, and card acquiring for gambling merchant codes is a long process in its own right (high-risk merchant account, rolling reserve). There is no representment workflow (evidence packs, deadlines) beyond marking a dispute won or lost, and no payment-fraud controls — no deposit velocity limits, no card-to-account name matching, no multi-accounting or shared-instrument detection. There is no four-eyes (two-approver) workflow on large payouts. There is no mainnet crypto custody. No player-initiated currency exchange between wallets (the display currency is a view; the ledger's FX account is used only for the affiliate payout currency). See What You Still Need.