Wallets
How Billium assigns payment addresses — direct wallets vs. xpub-derived wallets.
Billium is non-custodial — we never hold your funds, and we never control a private key. Every payment lands in a wallet that you own. Before you can accept a cryptocurrency on a given network, you need to register the wallet that should receive it.
Each merchant can register one wallet per (cryptocurrency, network) pair. For example, you can have one (USDT, ETH) wallet and a separate (USDT, TRX) wallet, but not two USDT wallets on Ethereum.
The two wallet types
Billium supports two wallet types, and the right choice depends on volume and which coin you're accepting.
| Feature | DIRECT_WALLET | XPUB_WALLET |
|---|---|---|
| What you register | A single address | An extended public key (xpub/ypub/zpub) |
| Addresses per invoice | Same address reused | Unique address derived per invoice |
| Matching strategy | Amount + time window + salt | Unambiguous — each invoice gets its own address |
| Supported coins | All supported chains | BTC and LTC only |
| Best for | Smart-contract chains (ETH, Polygon, BSC, Cronos, TRON) or very low BTC/LTC volume | Any BTC or LTC merchant with non-trivial volume |
On EVM chains and TRON, xpub wallets aren't offered because they're not a natural fit — address derivation on those chains works differently and Billium's matching algorithm already disambiguates payments reliably via amount and salt. Direct wallets on those chains are equivalent to xpub wallets on BTC/LTC for all practical purposes.
DIRECT_WALLET
A direct wallet holds a single address. Every invoice you create for that (cryptocurrency, network) pair tells the customer to pay to that same address. Billium's matching algorithm then figures out which incoming transaction corresponds to which invoice based on amount, time window, and an optional salt.
{
"walletType": "DIRECT_WALLET",
"cryptocurrency": "USDT",
"network": "ETH",
"address": "0xAbCdEf0123456789abcdef0123456789aBcDeF01"
}When to use it:
- Always, for EVM chains (ETH, Polygon, BSC, Cronos) and TRON — these don't support xpub wallets.
- Optionally, for BTC or LTC when your volume is low enough that two simultaneous invoices for the same amount are extraordinarily unlikely.
Validation: Billium verifies the address format at registration against the network you selected. An invalid EVM address or a mainnet-format Bitcoin address on a testnet wallet (or vice versa) is rejected immediately.
XPUB_WALLET
An xpub wallet gives Billium an extended public key from which it can derive a fresh address for every invoice. Each payment lands at a unique address that nobody else on your merchant is using, so matching is trivially unambiguous — the right address means the right invoice, full stop.
{
"walletType": "XPUB_WALLET",
"cryptocurrency": "BTC",
"network": "BTC",
"xpub": "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs",
"derivationPath": "m/84'/0'/0'"
}Supported coins: BTC and LTC only.
Supported derivation schemes:
| BIP | Prefix | Address type | Notes |
|---|---|---|---|
| BIP44 | xpub | P2PKH (legacy) | Most compatible, highest transaction fees. |
| BIP49 | ypub | P2SH-P2WPKH (nested SegWit) | Good middle ground for wallets that don't support native SegWit. |
| BIP84 | zpub | P2WPKH (native SegWit) | Recommended. Lowest fees, widely supported by modern wallets. |
The BIP32 coin types are 0 for BTC and 2 for LTC, matching the standards. Billium infers the derivation scheme from the xpub prefix — you don't need to pass it explicitly.
How derivation works under load
Every invoice created against an xpub wallet gets its own derived address. Billium serializes address generation internally, so even with thousands of invoices created in parallel you'll never see two of them share an address — derivation and index tracking happen atomically.
Registering a wallet
POST /api/v1/merchants/merchant/{merchantId}/wallets accepts either shape depending on walletType. Minimal example for an xpub BTC wallet:
curl -X POST https://api.billium.to/api/v1/merchants/merchant/$MERCHANT_ID/wallets \
-H "x-api-key: $BILLIUM_SECRET_KEY" \
-H "content-type: application/json" \
-d '{
"walletType": "XPUB_WALLET",
"cryptocurrency": "BTC",
"network": "BTC",
"xpub": "zpub6r…",
"derivationPath": "m/84'\''/0'\''/0'\''",
"requiredConfirmations": 3
}'You can tune requiredConfirmations per wallet (default 1). Higher values mean Billium waits longer before marking a payment as confirmed — safer for high-value invoices, slower for customer UX.
Deletion is guarded
You can't delete a wallet that has active invoices pointing at it. If any invoice in AWAITING_PAYMENT or PENDING_CONFIRMATION is still tied to the wallet, the delete returns an error:
Cannot delete a wallet with active payments
For a DIRECT_WALLET the check matches on the payment's destination address; for an XPUB_WALLET it matches on (cryptocurrency, network). To retire an xpub wallet, wait for all in-flight invoices to reach a terminal state (paid, expired, or cancelled), then delete. You can create a fresh wallet for the same pair the moment the old one is gone.
Rotating an xpub wallet
Rotating an xpub (e.g. because your hardware wallet was replaced) is a delete-then-create:
- Disable the old wallet (
PATCHwithisEnabled: false) so no new invoices pick it up. - Wait for in-flight invoices to settle — or expire the stuck ones manually via the dashboard.
- Delete the old wallet.
- Register the new xpub.
The lastUsedIndex of the old wallet doesn't transfer — the new wallet starts fresh at 0. This is fine because the two wallets have different xpubs and therefore different address spaces; there's no collision risk.