How many ways are there to accept stablecoins, and which do I pick?
Two, and they are not variants of one integration — they differ in who the payer is and in what your server does:
Pick on the payer, not on preference: a human with a wallet cannot pay an x402
challenge, and an Agent has no browser to render a checkout in. If you sell to
both, you integrate both.
What do I send on order creation to accept stablecoins?
Three things identify a stablecoin order on the wallet path:
The method is catalogued under the global region code
GLB rather than a single
country, so it is not filtered by the shopper’s country. Pricing works like any
other cross-currency order: keep orderCurrency and orderAmount in your
pricing currency (typically USD), and set userCurrency to USDC or USDT —
the currency the shopper actually pays in. See
Pricing currency strategy.
Do I have to make the shopper pick a chain myself?
Only if you run your own checkout page.- Waffo-hosted cashier — omit
payMethodProperties.cryptoChain. The consultation step returns it as a missing field together with the chain options available for the order, and the cashier collects the shopper’s choice before the order is finalized. - Your own checkout page — present the chain choice yourself and send the
selected value back in
payMethodProperties.cryptoChain.
Can I offer only one chain, so the shopper cannot pick the wrong one?
Yes — that is what the option list is for. The chains offered for an order come from your account’s configuration, so a single-chain configuration produces a single-chain option list and, on the hosted cashier, no chain selector worth speaking of. Ask your integration contact for the chain set you want rather than filtering the list client-side: a client-side filter still leaves the wider set acceptable onorder/create.
Note what this does not prevent. Narrowing the chains removes the choice of
which order the shopper pays; it cannot stop them sending funds on some other
chain from their wallet, which is the WRONG_CHAIN case in
Stablecoin Exceptions & Refunds.
What exactly do I put in the chain field?
The chain’s code, as a string —BASE, not Base, and not a numeric chain
ID. Matching is case-insensitive, and each chain’s short name is accepted as an
alternative to its code, but send the code: it is the value that comes back on
the order and the value the refund path reads.
Testnets are separate codes, usable where your sandbox is configured for them:
Tron and BNB Smart Chain have no testnet code, so those two cannot be exercised
against a testnet.
Two naming points that come up often:
BSCis BNB Smart Chain. The chain’s code isBSCand its network is BNB Smart Chain; “BNB” on its own is the network’s token, not a chain code. SendBSC.- Avalanche means the C-Chain. Only C-Chain (EVM) addresses are valid; X-Chain and P-Chain addresses are rejected outright, including on refunds.
Is the chain field mandatory? What happens if I leave it out?
It is not mandatory on the request, but the order cannot be paid without a chain, so leaving it out has one of two outcomes and never a default:- On the hosted cashier, consultation reports it as a missing field with the available options, and the shopper’s choice supplies it.
- On your own checkout, you get the same missing-field response and it is your job to collect the choice and re-send.
Which stablecoins are supported?
USDC and USDT on the wallet path; USDC only on the x402 path. No other
tokens — no non-stablecoin tokens, no small-cap tokens, and no merchant-issued
tokens.
Anything else sent to a receiving address is not a valid payment: a transfer of
the chain’s native token (ETH, for example) or of any other token does not pay
the order, and neither does a transfer on a chain other than the order’s.
Is there a minimum or maximum amount?
Yes, and it is on your agreement like any other payment method, not a special stablecoin rule. The published payment method catalogue carries the catalogue-level range forUSDC and USDT; the range that actually applies to
you is orderAmountMin / orderAmountMax on your own configuration entry for
the method, readable from /api/v1/paymethodconfig/inquiry or from Service →
Payin in the Portal.
Check it before you create the order. An amount outside the range is rejected at
order creation with A0008 — see
Limits, Currency & Configuration Errors
for how that differs from the two account-level limits.
How many decimal places do stablecoin amounts use?
Two, on every Waffo API field, exactly like a 2-decimal fiat currency — see Currency and amount. On-chain the tokens use 6-decimal atomic units; Waffo handles that conversion, so neither your requests nor your reports carry atomic units, and a block explorer shows the amount in a different unit than the one you sent. The token’s on-chain precision therefore does not change what you send: the sameorderAmount string is correct on every chain, and you never scale it
yourself. The one place atomic units are yours to construct is the x402
signature path, where the challenge and the signature are both in atomic units —
that page spells out the conversion.
Is the receiving address mine, per order, or fixed?
It depends on the path, and the two answers are genuinely different:
On the wallet path an address is bound to one order and released when that order
finishes, which means it can later be issued for a different order. That is
the reason the rule is absolute rather than a preference: a transfer to an
address you cached does not pay the order you cached it for, and once the address
has moved on it cannot be attributed to it at all.
Neither path gives you an address of your own to publish, print on an invoice, or
hard-code. In both, the address is Waffo’s; Waffo receives on-chain and settles
to you per your contract.
The shopper paid to a recycled address. What happens?
Nothing good, and this is worth designing against rather than handling. Once an order’s address has been released, a late transfer to it lands in one of the unmatched cases in Stablecoin Exceptions & Refunds: it does not pay the original order, and it does not pay whichever order holds the address now, because the amount and the order will not correspond. Practical guards: never render a stablecoin address outside the payment page for that order, never let a shopper “reuse the address from last time”, and never show a QR code from an expired order.Does the payment QR code carry the amount?
Do not assume it does. The reliable instruction to a shopper is: send exactly the order amount, in the order’s token, on the order’s chain, before the window closes — all four, from the payment page. A wallet that pre-fills the amount is a convenience, not a guarantee, and an amount the shopper edits produces an overpay or underpay case rather than a paid order.How long does the shopper have to pay?
The order’s expiry, which is the same mechanism as every other payment method: the contract default unless you sendorderExpiredAt on order/create. See
Payment Timeouts & Finality.
Two stablecoin-specific things to plan for:
- The window has to cover the shopper’s action plus the chain’s confirmation time, not just the shopper’s action. A window that would be generous for a wallet redirect can be tight on a chain that confirms slowly.
- A transfer that arrives after the window closes does not pay the order. It
becomes the
LATE_PAIDcase, and there is no way to attach it to the closed order afterwards.
The shopper’s wallet says the transfer succeeded — why is the order still processing?
Because the order advances to success only once the transfer meets the confirmation requirement on-chain, and that wait is a property of the chain, not of Waffo. A wallet showing “sent” is not the same event as the payment being final. How long the wait is varies substantially between chains:- Chains with near-instant deterministic finality (for example Avalanche, Polygon) confirm quickest.
- Ethereum mainnet waits for a block-confirmation depth, so it is noticeably slower.
- Optimistic-rollup L2s (Arbitrum, Base, Optimism) are slowest: the sequencer accepts the transfer in seconds, but confirmation waits for the batch to be confirmed on Ethereum.
The order has been processing for a while. Is it still waiting, or has it failed?
Read the order, not the chain. An order in a non-final state is still in play; the state machine is the same as for every other payment method, so the same rules apply about which states are final and which are not — see Payment lifecycle and Payment Timeouts & Finality. What you must not do is infer failure from the chain being slow, or success from having found the transfer on a block explorer. If the order has not reached a final state by its expiry, it closes; if funds arrived off-spec, it becomes one of the exception cases, which are visible to support with the order id and the transaction hash.Do I receive stablecoins or fiat?
Fiat. The USDC or USDT the shopper pays is converted at payment success through theUSD/USDC and USD/USDT currency pairs, and everything downstream — your
unsettled balance, settlement batches and reports, and withdrawals — stays in
your settlement currency. You never hold the stablecoin, so you carry no token
price exposure and no on-chain fee exposure.
Because conversion happens at payment success, not at order creation, the
rate is the rate at that moment. Stablecoin orders use the same cross-currency
machinery as any other cross-currency order, so the rate mechanics, and whether a
quote can be held, are covered in
Exchange Rates and
Pricing currency strategy
rather than being special here.
Where do stablecoin orders show up in the Portal?
In the ordinary transaction list, filtered like anything else. Two filters get you there:
What the Portal does not give you is any on-chain detail: there is no chain
column, no chain filter, no transaction hash, no confirmation count and no
network-fee figure on the transaction list or the transaction detail. The one
place a chain appears is the refund drawer, where it is shown read-only as the
chain the original payment used.
So if your reconciliation needs the chain or the transaction hash per order, do
not plan to get it from a Portal export. On the x402 path the hash is available
on the API — the settlement receipt returned on success carries it, and that page
documents its shape. On the wallet path, raise the order id with support.
What do I need in order to be enabled for stablecoins?
Two provisioning steps, both arranged with your integration contact: the CRYPTO payment product on your contract, and your chain set. Until both are in place, stablecoin order creation is refused as not permitted by your contract (A0010).
Enrollment itself follows the normal path, including where you watch its status —
see Payment Method Enrollment. Adding a
chain later is the same kind of request as adding a payment method: it is a
configuration change on the Waffo side, not something you can switch on yourself.
Where do I look next?
- Stablecoin Exceptions & Refunds — overpay, underpay, late, wrong chain, wrong token, and what a refund must carry
- x402 stablecoin acquiring — the AI-Agent path end to end
- Payment Timeouts & Finality — order expiry and which states are final
- Reading Your Payment Method Configuration —
your own amount range and availability for
USDC/USDT - Settlement Reports — how a converted stablecoin payment appears in settlement