Exchange Rates
How token prices are fetched, cached, and refreshed.
Overview
When a charge is created the API immediately fetches USD prices for every non-stablecoin token across all configured chains. These rates are stored in the database so the Worker can calculate received USD amounts without hitting CoinGecko on every block.
Rate storage
Each rate is stored as one row in PaymentTokenRate keyed by (paymentId, chain, token):
| Field | Description |
|---|---|
usdRate | Price of 1 token in USD at the time of creation |
requiredAmount | Human-readable token amount needed to cover the charge |
expiresAt | Timestamp after which the rate is considered stale |
Refresh cycle
The Worker runs a background rateRefresher every 60 seconds. It queries for rates where expiresAt is in the past and fetches fresh prices from CoinGecko. The freshness window is controlled by payment.rateRefreshMinutes (default: 5 minutes).
Stablecoins
Tokens without a coingeckoId always receive usdRate = 1. This covers USDC, USDT, DAI, etc. and avoids unnecessary API calls.
Checkout UI
The checkout page shows the customer:
- Exact token amount required (e.g.
0.014285 ETH) - Current rate (e.g.
1 ETH = $3,500.00) - Countdown until the next rate refresh
When the rate refreshes the displayed amount updates automatically without requiring a page reload.
CoinGecko rate limits
The free CoinGecko API has a rate limit of ~30 requests/minute. For high-traffic deployments consider:
- Using the CoinGecko Pro API (set
COINGECKO_API_KEYenv var) - Increasing
payment.rateRefreshMinutesto reduce refresh frequency - Keeping the number of distinct
coingeckoIdvalues small