Implementing Crypto-to-Fiat (Off-Ramp) in a Mobile App
Off-ramp is withdrawing cryptocurrency to fiat money into a bank account or card. Technically more complex than on-ramp: the user sends crypto to the provider first, then receives fiat. KYC is stricter, amounts are higher, and the timeout between sending and receiving money ranges from minutes to days.
Off-Ramp Distinction: Crypto Leaves First
Unlike on-ramp, where users see upfront how much crypto they'll receive, off-ramp requires sending crypto to the provider's deposit address first. This creates an additional trust barrier. The interface must minimize user anxiety.
Process:
- User specifies the crypto amount and bank account details.
- Provider issues a deposit address and confirms the rate (with quote validity time, usually 15–30 minutes).
- The app sends crypto to the deposit address — typically via the wallet's built-in send screen.
- Provider receives confirmation and initiates the fiat transfer.
KYC and Bank Account Verification
Off-ramp AML requirements are stricter. MoonPay and Transak require bank account verification via micro-transactions (two small withdrawals to confirm) or Open Banking (Plaid, TrueLayer).
The app must clearly inform users about KYC timelines — verification may take 1–3 business days on the first off-ramp. Subsequent transactions are instant after approval.
// iOS — launching Transak off-ramp widget
var components = URLComponents(string: "https://global.transak.com")!
components.queryItems = [
.init(name: "apiKey", value: transakApiKey),
.init(name: "walletAddress", value: userWalletAddress),
.init(name: "defaultCryptoCurrency", value: "ETH"),
.init(name: "productsAvailed", value: "SELL"), // off-ramp only
.init(name: "redirectURL", value: "myapp://offramp-callback"),
.init(name: "themeColor", value: "000000")
]
let safariVC = SFSafariViewController(url: components.url!)
present(safariVC, animated: true)
SEPA and Wire Transfer Support
For European users — SEPA Instant (settlement in 10 seconds) and SEPA Credit Transfer (1–2 business days). For international transfers — Wire (SWIFT, 2–5 days). Providers determine available methods by user's country.
Transak supports SEPA for 30+ European countries. MoonPay offers SEPA and UK bank transfers (Faster Payments).
Status and Notifications
Off-ramp transactions pass through multiple statuses: crypto_received → processing → fiat_sent → completed. Each transition should trigger a push notification. Users shouldn't have to guess where their money is.
// Android — webhook handler for status updates (server-side, sent via push)
when (webhookEvent.status) {
"AWAITING_PAYMENT_FROM_USER" -> sendPush("Awaiting $amount $currency")
"PAYMENT_DONE_MARKED_BY_USER" -> sendPush("Crypto received, verifying...")
"ON_HOLD_PENDING_DELIVERY_FROM_TRANSAK" -> sendPush("Transfer initiated")
"COMPLETED" -> sendPush("$fiatAmount ${fiatCurrency} sent to your account")
"FAILED" -> sendPush("Transaction declined: $reason")
}
Timeline: 5 days to integrate a provider, set up off-ramp initiation flow, transfer crypto amount, handle deeplink callbacks, and push notifications for status updates via webhook. Multi-provider off-ramp — 8–12 days.







