Crypto-to-Fiat Conversion Implementation in Merchant Mobile App
Merchant accepted payment in USDT, and next day discovered the rate dropped 4% and money sits in wallet. Automatic conversion to fiat immediately after transaction confirmation — not "nice to have," but business requirement. Implementing this in mobile app is harder than it seems: need to sync on-chain events with conversion via CEX or processor without losing transaction on failures.
Conversion Architecture
Two main approaches — via payment processor (Coinbase Commerce, NOWPayments, CryptoCloud) and via direct exchange integration (Binance API, Bybit API). Each has different mobile client logic.
Via Payment Processor
NOWPayments and alike accept crypto and automatically convert to fiat before withdrawal. Mobile client makes POST /v1/payment with parameter payout_currency: "USD" — processor takes on rate difference risk. App responsibility:
- Request payment address and amount
- Show QR code or Deep Link (
bitcoin:addr?amount=0.001) - Poll status via
GET /v1/payment/{id}every 15 seconds or WebSocket - Show status:
waiting → confirming → confirmed → finished
Polling problem — when Bitcoin network overloaded, confirming can hang 40+ minutes. User closes app. Need push notification via FCM/APNs on status change — backend gets webhook from processor and broadcasts push.
Via Binance API
Direct integration: after crypto received on custodial wallet call POST /sapi/v1/convert/getQuote — get quote, then POST /sapi/v1/convert/acceptQuote. Quote valid 10 seconds. If expired — retry. On mobile client this process hidden behind backend; client sees only final fiat balance.
// Example conversion status display (SwiftUI)
struct ConversionStatusView: View {
@StateObject var viewModel: ConversionViewModel
var body: some View {
switch viewModel.status {
case .waitingPayment(let address, let amount):
QRCodeView(address: address, amount: amount)
case .confirming(let confirmations, let required):
ConfirmationProgressView(current: confirmations, required: required)
case .converting:
LoadingView(text: "Converting to USD...")
case .completed(let fiatAmount):
SuccessView(amount: fiatAmount)
case .failed(let error):
ErrorView(error: error)
}
}
}
Working with Rate and Slippage
User sees "get $99.50" — actually arrives $97.80 due to slippage during conversion. Need to explicitly show estimated_rate and locked_rate (if processor supports rate lock for 15–20 minutes). CryptoCloud supports rate fixing; NOWPayments — no.
For live rate display use WebSocket from Binance wss://stream.binance.com:9443/ws/btcusdt@ticker or CoinGecko REST with 30-second caching.
Mobile Client Security
Private wallet keys — never on mobile device. Mobile client talks only to own backend. Exchange API keys — only on server, protected via Vault or environment variables. On iOS use Keychain for session token storage, on Android — EncryptedSharedPreferences from Jetpack Security.
Timeline and Stages
Audit business requirements → choose processor or CEX → implement server-side conversion layer → mobile client with polling/WebSocket → push notifications → testnet/sandbox testing.
3–5 days for integration via ready processor. 2–3 weeks for direct exchange integration with error handling and retries. Cost calculated individually after requirements analysis.







