Payment URIs (BIP-21, EIP-681, TRX)
How to build QR payloads that any wallet can scan — and when to fall back to a bare address.
When you render a payment QR for a customer — on a checkout page, a receipt, a kiosk — the QR should encode the address and the expected amount. Wallets read the URI and pre-fill the send screen, so customers can't accidentally type the wrong amount.
The platform's own payment page (on the hosted invoice URL and on the dashboard's Test payment) already does this. This guide is for merchants rendering their own UI.
Scheme per network#
Bitcoin — BIP-21#
bitcoin:bc1q...?amount=0.00082145
amountis in BTC, as a plain decimal.- Any BIP-21 compliant wallet scans and prefills.
Ethereum + L2s — EIP-681#
Native ETH:
ethereum:0xAbc...@1?value=820000000000000
valueis in wei.- The
@<chainId>segment identifies mainnet (1), Sepolia (11155111), etc.
ERC-20 tokens (e.g. USDT on Ethereum):
ethereum:0xTokenContract@1/transfer?address=0xMerchant...&uint256=1000000
uint256is the amount in the token's smallest unit. For USDT-6,1000000=1.00 USDT.addressis the buyer's destination — your invoice'sdeposit_address.
Tron — bare address#
TronLink and most Tron wallets don't currently honour a URI scheme for amount pre-fill. Use a bare address QR and show the expected amount next to it so the customer types it manually.
Amount precision matters#
Never compute the amount in base units via floating-point math — you will drift on the last digits. Work from the string representation of crypto_amount and multiply by 10^decimals as integers.
The safest way#
If you're rendering a QR outside the platform's own page:
- Fetch the invoice:
GET /api/invoices/{id}. - Use
deposit_address+crypto_amount+crypto_networkas inputs. - Apply the scheme rules above.
- Encode as a standard QR — any QR library works; the payload is the URI string.
See also#
- Embed the payment page — if you don't want to render your own.
- Dashboard → Test payment — a working reference you can scan with your wallet.