Mock
@openrampkit/adapter-mock is a test provider for local development, demos and tests. It moves no money. It exercises every common surface: a hosted redirect checkout, QR codes, a deposit address and a wallet transaction.
Test mode only
Anyone who knows an order ref can mark a mock payment as paid on the mock checkout page. So the mock is a sandbox adapter (env: 'sandbox'): a server with livemode: true does not start with it. It also refuses live sessions: with livemode: true, quote() and start() fail with PROVIDER_UNAVAILABLE. Do not configure it in production.
import { mockAdapter } from '@openrampkit/adapter-mock'
mockAdapter() // fiat legs only
mockAdapter({ crypto: true, bridge: true }) // also wallet, transfer and bridge
mockAdapter({ settleMs: 0, name: 'Sandbox provider' }) // instant, custom name
mockAdapter({ crypto: true, offramp: true }) // also withdraw to a wallet and to cash
mockAdapter({ crypto: true, exchange: true }) // also "From an exchange"
mockAdapter({ cardCheckout: 'form' }) // card fields in the widget, no hosted page
mockAdapter({ bankFields: true }) // bank transfer shows bank details, not a QR codeOptions
| Option | Type | Default | Description |
|---|---|---|---|
settleMs | number | 3000 | Time from "paid" to "completed" |
crypto | boolean | false | Add mock wallet and transfer legs. Use when the real Relay adapter is not configured. |
bridge | boolean | false | Add a mock bridge leg for two-leg pathways. Use when Relay is not configured. |
offramp | boolean | false | Add a mock offramp leg for withdrawals to cash. See Offramp leg. |
name | string | 'Test provider' | Name shown to users |
id | string | 'mock' | Adapter id. Give each instance its own id when you configure more than one mock. See Several mock providers. |
feeBps | { card?, local?, payin?, offramp?, crypto? } | card 250, local 100, payin 70, offramp 100, crypto 5 | Fee of each leg in basis points |
spreadBps | number | 0 | FX spread in basis points on the test rate of the onramp and offramp legs. A higher spread gives a worse rate. |
eta | { card?, local?, payin?, offramp? }, each { min, max } in seconds | see Legs | Time estimate that the user sees before paying |
methods | string[] | all | Offer only these methods. A leg that keeps no method is removed. |
countries | string[] | the regions of each leg | Serve only these countries on the fiat legs (card, local, payin, offramp) |
cardCheckout | 'redirect' | 'form' | 'redirect' | redirect: a hosted checkout page in a new tab. form: test card fields in the widget (surface FORM). Use form on a static site that cannot serve the hosted page. |
bankFields | boolean | false | bank_transfer on the local leg shows bank details with copy buttons (surface BANK_FIELDS) instead of a QR code. The other methods keep the QR code. |
exchange | boolean | false | With crypto: the transfer leg also offers the method exchange_transfer ("From an exchange") |
exchangeConnect | boolean | false | The card leg also offers coinbase_account. With exchange, the widget shows the exchange picker. |
standing | boolean | false | With crypto: give standing deposit addresses (a fake address per user and route, no deposits). Use it with the server option depositAddresses.widget. |
standingBank | boolean | false | With standing: also give standing bank accounts in USD and EUR (test bank details, no deposits), for the widget's "Bank account" tab. |
localChain | { chain, rpcUrl, token, symbol?, decimals? } | none | Test only. Add an onchain leg that pays with a real ERC-20 transfer on a local chain (for example Anvil). See Local chain leg. |
solanaLocalChain | { chain, rpcUrl, mint, symbol?, decimals? } | none | Test only. Add a solana-onchain leg that pays with a real SPL (or SOL) transfer on a Solana cluster, for example devnet. See Solana local chain leg. |
Do not turn on crypto or bridge next to the real Relay adapter: both would offer the same methods.
Legs
| Leg | Kind | Methods | From | To | Regions | Surface |
|---|---|---|---|---|---|---|
card | fiat_onramp | card, apple_pay, google_pay | any fiat | USDC on Base | all | REDIRECT (FORM with cardCheckout: 'form') |
local | fiat_onramp | vietqr, momo, qris, gopay, dana, gcash, qrph, promptpay, duitnow, touchngo, paynow, bank_transfer | VND, IDR, PHP, THB, MYR, SGD | USDC on Base | VN, ID, PH, TH, MY, SG | QR (BANK_FIELDS for bank_transfer with bankFields: true) |
payin | fiat_payin | qris, promptpay, vietqr, qrph, duitnow, paynow, card | any fiat | merchant account (same currency) | all | QR |
wallet | bridge_swap | wallet | any crypto in a wallet | any crypto | all | WALLET_TX |
transfer | bridge_swap | transfer (and exchange_transfer with exchange: true) | any crypto in a wallet | any crypto | all | DEPOSIT_ADDRESS |
bridge | bridge_swap | (hop) | USDC on known chains | any crypto | all | none shown |
offramp | crypto_offramp | bank_transfer, gcash, momo, promptpay | USDC on known chains (wallet or app address) | fiat in the user's account | all | FORM, then WALLET_TX |
onchain | bridge_swap | wallet | localChain.token in a wallet | the same token to an address | all | WALLET_TX |
solana-onchain | bridge_swap | wallet | solanaLocalChain.mint in a wallet | the same token to an address | all | WALLET_TX (Solana) |
Because the fiat legs deliver USDC on Base, a destination on another chain (for example Monad) gets a two-leg pathway through the bridge leg (or Relay).
Solana destinations work too. A destination of USDC on Solana gets the two-leg pathway (fiat to Base, then bridge). The wallet leg from a Solana token asks for a Solana transfer (SolanaTxRequest, type: 'transfer'). The transfer leg shows a base58 test address for a Solana source. The offramp leg takes EVM USDC only.
The wallet leg (with crypto: true) also serves withdrawals to a wallet: it asks for one mock transaction to the target address.
Offramp leg
offramp: true adds a crypto_offramp leg for withdrawals to cash. It moves no money.
- Methods:
bank_transfer,gcash,momo,promptpay(local methods only in their countries). - Currencies: the test FX currencies below. Limits: 5 to 5000 USD.
- Quote: 1 USDC is 1 USD, minus a 1% fee in USDC, converted at the test FX rate.
- Steps:
FORM(detail codepayout_account): the payout account. Bank transfer asks for the account holder name, the bank name and the account number. GCash, MoMo and PromptPay ask for the name and a phone number. Transitionsubmit_details.WALLET_TX(detail codesend_crypto): an ERC-20 USDCtransferof the quoted amount to a fake provider address. Transitionsubmit_tx. Withcustody: 'app', the server's treasury hook sends it.PROCESSING(detail codesettling) forsettleMs, thenCOMPLETEDwith the quoted payout as the output.
Local chain leg
localChain adds an onchain leg for tests on a local dev chain. This leg sends a real transaction. Use it only with a test chain and a test token.
mockAdapter({
localChain: { chain: 'eip155:31337', rpcUrl: 'http://127.0.0.1:8545', token: mockUsdcAddress },
})- The destination must be an address on
chainintoken. - Quote: 1:1 with no fee.
- Step 1:
WALLET_TX(detail codesend_crypto). The wallet sends an ERC-20transferof the quoted amount to the destination address. Transitionsubmit_txwith the hash. - Step 2: the adapter reads the receipt with
eth_getTransactionReceiptatrpcUrl. The leg isCOMPLETEDwhen the receipt shows aTransferof at least the quoted amount to the destination. It staysPROCESSINGwhile there is no receipt. It isFAILEDwhen the transaction reverted, pays less, pays another address or was already used for another payment.
The leg reports the hash as the source transaction, and also as the destination transaction once the check passes. This is the same check that the Relay adapter does for a same-chain wallet payment. See Testing with mocks.
With a settlement contract
When the destination has settlement, the leg pays through OpenRampSettlement instead. The leg declares the settlement capability.
- Step 1:
WALLET_TXwith two transactions frombuildSettlementTxs:approveon the token, thensettlefor the session id. The destinationcallsgo intosettle. - Step 2: the adapter checks the session with
verifySettlement. It searches theSettledlog from the block at which the leg started. The leg isCOMPLETEDwhen the receipt pays the recipient at least the quoted amount with the session's calls. The transaction hash comes from the log, not from the browser. The leg reports it as thesettlementtransaction. - A poll also completes the leg when the contract already has the receipt, for example after a lost
submit_tx. - The leg is
FAILEDwhen the transaction reverted, did not settle this session, or settled a different amount, recipient or call bundle.
This works on any EVM chain with an RPC, for example a testnet. The playground uses it on Arbitrum Sepolia, Robinhood Chain Testnet and Tempo Testnet.
Solana local chain leg
solanaLocalChain adds a solana-onchain leg. It is the Solana version of the local chain leg. This leg sends a real transaction. Use it only with Solana devnet (or a local validator) and a test token.
import { SOLANA_DEVNET, SOLANA_DEVNET_USDC_MINT } from '@openrampkit/core'
mockAdapter({
methods: ['wallet'],
solanaLocalChain: { chain: SOLANA_DEVNET, rpcUrl: 'https://api.devnet.solana.com', mint: SOLANA_DEVNET_USDC_MINT },
})- The destination must be an owner address on
chaininmint. Usemint: 'native'for SOL (9 decimals). - Quote: 1:1 with no fee.
- At start, the leg reads the current slot with
getSlot. - Step 1:
WALLET_TX(detail codesend_crypto) with one Solanatransferof the quoted amount to the destination.@openrampkit/solanabuilds it. Transitionsubmit_txwith the base58 signature. - Step 2: the adapter checks the signature at
rpcUrl:getSignatureStatuses: the signature must beconfirmedorfinalized, with no error.getTransaction(jsonParsed): the transaction must be in a slot at or after the start slot. Its SPLtransferandtransferCheckedinstructions (or System Program transfers, for SOL) must move at least the quoted amount into a token account of the mint that the destination owns.- One signature completes one payment only.
- The leg stays
PROCESSINGwhile the signature is unknown or not confirmed. It isFAILEDwhen the transaction failed, is older than the leg, pays less, pays another mint or account, or was already used.
The check reads the instructions, not the balance change. Thus a transfer from the destination to itself counts. The playground uses this: the visitor pays devnet USDC back to their own wallet, so no funds are lost.
solanaPaidTo(tx, owner, mint) from @openrampkit/adapter does the amount check. Use it in your own adapter.
Quotes
- Fiat legs use fixed test FX rates to USD (USD, EUR, GBP, SGD, MYR, THB, PHP, IDR, VND, INR, BRL, AUD, CAD, JPY, KRW). Other currencies fail with
NO_QUOTES. - Fees (defaults): 2.5% for
card, 1% forlocal, 0.7% forpayin(paid in the same currency). Change them withfeeBps. spreadBpsmakes the test rate worse by that many basis points. The spread is not a fee line: it shows in the rate and in the amount the user gets.- Crypto legs: 1:1 minus 5 basis points (
feeBps.crypto). - Default time estimates in seconds:
card60 to 300,local10 to 120,payin5 to 60,offramp60 to 900. - Quotes expire after 60 seconds (
quoteExpiresAt(1)). - Guarantees: the fiat legs (
card,local,payin,offramp) use a fixed test rate, so they arefirm. A plain transfer (onchain,solana-onchain, and awalletortransferleg on one chain and one token) isfirm. Abridgeleg, and awalletortransferleg that changes the chain or the token, ismin_output:minOutputis the output less 0.5%, andslippageBpsis 50. So you can test the server'smin_outputpath with no real provider. - Fees: each leg has one fee line with an amount and
included: true. The fiat legs have aproviderfee. A plain crypto transfer has anetworkfee, and a bridge or swap has abridgefee. providerRef: the leg ref, so you can test the provider reference in the UI and in the admin page.
How to finish each leg
| Leg | Action |
|---|---|
card | A new tab opens the mock checkout at {baseUrl}/adapters/{id}/checkout. Pay marks the order paid; Decline payment fails it. |
card with cardCheckout: 'form' | Fill in the test card fields in the widget, then press Pay (test mode) (transition pay_card). Any card number with 12 to 19 digits pays, for example 4242 4242 4242 4242. The card 4000 0000 0000 0002 is declined. The expiry is MM/YY and the CVC has 3 or 4 digits. |
transfer with exchange_transfer | The same deposit address. The warning tells the user to withdraw from the exchange on the correct network. Press Simulate deposit (test mode). |
local, payin | Press Simulate payment (test mode) (transition simulate_payment). With bankFields: true, bank_transfer shows a test bank account, the amount and a reference, with the same button. |
transfer | Press Simulate deposit (test mode) (transition simulate_deposit) |
wallet | Send with a wallet adapter; the client fires submit_tx with the hash |
bridge | Nothing: it is paid when it starts and settles after settleMs |
offramp | Fill in the payout form (submit_details), then send with a wallet adapter or the treasury (submit_tx) |
After "paid", the leg is PROCESSING (detail code settling) until settleMs has passed, then COMPLETED. A leg with a crypto output gets a fake destination transaction. A fiat payout has none.
| Hosted checkout | QR with the simulate button |
|---|---|
![]() | ![]() |
Routes and webhooks
The mock adapter serves two routes under {baseUrl}/adapters/{id}/ (id is mock by default):
GET checkout?ref=...&amount=...¤cy=...: the mock checkout page.POST pay: the form target. It updates the order and applies a leg event to the session, like a webhook would.
It has no webhook handler.
Several mock providers
OpenRampKit compares the quotes of all providers for a route. To show this without real providers, configure more than one mock. Give each one its own id and a clear mock name. Do not use the name of a real provider.
adapters: [
mockAdapter({ id: 'mock', name: 'Mock Onramp A', crypto: true, exchange: true }),
mockAdapter({ id: 'mock-b', name: 'Mock Onramp B', feeBps: { card: 199, local: 60 }, spreadBps: 60, methods: ['card', 'vietqr', 'qris'] }),
mockAdapter({ id: 'mock-local', name: 'Mock Local Rails', feeBps: { local: 40 }, spreadBps: 30, methods: ['vietqr', 'qris'], countries: ['VN', 'ID'] }),
mockAdapter({ id: 'mock-card', name: 'Mock Card Onramp', feeBps: { card: 149 }, spreadBps: 50, methods: ['card', 'apple_pay', 'google_pay'] }),
]A VietQR deposit in Vietnam then gets three quotes (A, B and Local Rails). The widget shows them in order and marks the best one with "Best price". Turn on crypto, bridge and exchange on one instance only: the crypto legs do not need a comparison. The playground uses a setup like this one.
Wallets
Pair it with createMockWallet() from @openrampkit/client to test "Pay with wallet" without a real wallet. See Testing with mocks.

