Fiat pricing
Price an invoice in fiat and let DuckyPay size the stablecoin amount at a live rate — your customer sees ฿350, the chain settles the exact tokens.
Supported currencies#
This is the complete list — any other currency code is rejected with a 400 before anything is quoted:
currency | Currency | Rate source |
|---|---|---|
THB | Thai baht | Bitkub (THB markets), CoinGecko fallback |
USD | US dollar | CoinGecko |
// fiat: { currency: "EUR", amount: "25" } →
{
"success": false,
"data": null,
"error": "Invalid enum value. Expected 'THB' | 'USD', received 'EUR'"
}Pricing in another currency today? Convert to USD on your side and pass fiat: { currency: 'USD', … }, or price directly in token units with amount — see Amounts: token or fiat.
How to price in fiat#
Send fiat instead of amount when creating a charge. amount and fiat are mutually exclusive — exactly one is required.
const charge = await duckypay.charges.create({
chainId: 137,
fiat: { currency: 'THB', amount: '350' }, // ฿350 — a decimal string
// no `amount` — you provide EITHER fiat OR a token amount, never both
});The response carries the token amount DuckyPay sized, plus the full quote breakdown in data.fiat so nothing about the price is a black box:
{
"amount": "10120000", // ← authoritative: what the buyer actually pays (token, smallest unit)
"fiat": {
"currency": "THB",
"amount": "350", // your original price
"marketRate": 35.10, // fiat per 1 whole token at quote time
"buffer": 0.50, // subtracted from the rate in YOUR favor
"effectiveRate": 34.60, // marketRate − buffer → used to size the payment
"tokenAmount": "10120000", // = ceil(350 / 34.60 × 10^6)
"source": "bitkub",
"asOf": 1789550000
}
}The token amount is authoritative — it is what the buyer pays, what the contract splits, and what invoice.paid reports. fiat is display metadata; the hosted checkout uses it to show the buyer “฿350” alongside the exact token amount and rate.
How the token amount is computed#
- Fetch the live market rate for the invoice’s token in your currency (fiat per 1 whole token).
- Subtract a per-currency buffer from the rate (THB default: ฿0.50):
effectiveRate = marketRate − buffer. The buffer works in your favor — it cushions rate movement between quote and payment. - Size the payment, rounding up to the token’s smallest unit:
tokenAmount = ceil(fiatAmount ÷ effectiveRate × 10^decimals).
Both the buffer and the round-up lean the same way: when the quote is slightly off, you receive marginally more than the fiat price — never less.
Where the rates come from#
- THB — Bitkub’s public THB markets first (the reference price Thai users actually trade at), CoinGecko as fallback.
- USD — CoinGecko.
- If a provider is briefly unreachable, the last known good rate is reused for a short window. If no trustworthy rate exists, charge creation fails with
503— DuckyPay never invents a rate to keep an invoice minting.
Rules & edge cases#
- Stablecoins only. Fiat pricing requires an ERC-20 stablecoin token — combining
fiatwithtoken: "NATIVE"is rejected. - Multi-asset charges quote per token. On a
both/transfercharge with several accepted tokens, each token gets its own fiat-sized amount, so the buyer pays the equivalent price whichever asset they pick. - The quote is frozen at creation. A charge’s token amount never changes after minting; a stale unpaid invoice simply expires (expiry) and you mint a fresh one at the current rate.