Skip to content

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.

ts
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 code

Options ​

OptionTypeDefaultDescription
settleMsnumber3000Time from "paid" to "completed"
cryptobooleanfalseAdd mock wallet and transfer legs. Use when the real Relay adapter is not configured.
bridgebooleanfalseAdd a mock bridge leg for two-leg pathways. Use when Relay is not configured.
offrampbooleanfalseAdd a mock offramp leg for withdrawals to cash. See Offramp leg.
namestring'Test provider'Name shown to users
idstring'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 5Fee of each leg in basis points
spreadBpsnumber0FX 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 secondssee LegsTime estimate that the user sees before paying
methodsstring[]allOffer only these methods. A leg that keeps no method is removed.
countriesstring[]the regions of each legServe 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.
bankFieldsbooleanfalsebank_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.
exchangebooleanfalseWith crypto: the transfer leg also offers the method exchange_transfer ("From an exchange")
exchangeConnectbooleanfalseThe card leg also offers coinbase_account. With exchange, the widget shows the exchange picker.
standingbooleanfalseWith crypto: give standing deposit addresses (a fake address per user and route, no deposits). Use it with the server option depositAddresses.widget.
standingBankbooleanfalseWith 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? }noneTest 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? }noneTest 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 ​

LegKindMethodsFromToRegionsSurface
cardfiat_onrampcard, apple_pay, google_payany fiatUSDC on BaseallREDIRECT (FORM with cardCheckout: 'form')
localfiat_onrampvietqr, momo, qris, gopay, dana, gcash, qrph, promptpay, duitnow, touchngo, paynow, bank_transferVND, IDR, PHP, THB, MYR, SGDUSDC on BaseVN, ID, PH, TH, MY, SGQR (BANK_FIELDS for bank_transfer with bankFields: true)
payinfiat_payinqris, promptpay, vietqr, qrph, duitnow, paynow, cardany fiatmerchant account (same currency)allQR
walletbridge_swapwalletany crypto in a walletany cryptoallWALLET_TX
transferbridge_swaptransfer (and exchange_transfer with exchange: true)any crypto in a walletany cryptoallDEPOSIT_ADDRESS
bridgebridge_swap(hop)USDC on known chainsany cryptoallnone shown
offrampcrypto_offrampbank_transfer, gcash, momo, promptpayUSDC on known chains (wallet or app address)fiat in the user's accountallFORM, then WALLET_TX
onchainbridge_swapwalletlocalChain.token in a walletthe same token to an addressallWALLET_TX
solana-onchainbridge_swapwalletsolanaLocalChain.mint in a walletthe same token to an addressallWALLET_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:
    1. FORM (detail code payout_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. Transition submit_details.
    2. WALLET_TX (detail code send_crypto): an ERC-20 USDC transfer of the quoted amount to a fake provider address. Transition submit_tx. With custody: 'app', the server's treasury hook sends it.
    3. PROCESSING (detail code settling) for settleMs, then COMPLETED with 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.

ts
mockAdapter({
  localChain: { chain: 'eip155:31337', rpcUrl: 'http://127.0.0.1:8545', token: mockUsdcAddress },
})
  • The destination must be an address on chain in token.
  • Quote: 1:1 with no fee.
  • Step 1: WALLET_TX (detail code send_crypto). The wallet sends an ERC-20 transfer of the quoted amount to the destination address. Transition submit_tx with the hash.
  • Step 2: the adapter reads the receipt with eth_getTransactionReceipt at rpcUrl. The leg is COMPLETED when the receipt shows a Transfer of at least the quoted amount to the destination. It stays PROCESSING while there is no receipt. It is FAILED when 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_TX with two transactions from buildSettlementTxs: approve on the token, then settle for the session id. The destination calls go into settle.
  • Step 2: the adapter checks the session with verifySettlement. It searches the Settled log from the block at which the leg started. The leg is COMPLETED when 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 the settlement transaction.
  • A poll also completes the leg when the contract already has the receipt, for example after a lost submit_tx.
  • The leg is FAILED when 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.

ts
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 chain in mint. Use mint: '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 code send_crypto) with one Solana transfer of the quoted amount to the destination. @openrampkit/solana builds it. Transition submit_tx with the base58 signature.
  • Step 2: the adapter checks the signature at rpcUrl:
    • getSignatureStatuses: the signature must be confirmed or finalized, with no error.
    • getTransaction (jsonParsed): the transaction must be in a slot at or after the start slot. Its SPL transfer and transferChecked instructions (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 PROCESSING while the signature is unknown or not confirmed. It is FAILED when 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% for local, 0.7% for payin (paid in the same currency). Change them with feeBps.
  • spreadBps makes 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: card 60 to 300, local 10 to 120, payin 5 to 60, offramp 60 to 900.
  • Quotes expire after 60 seconds (quoteExpiresAt(1)).
  • Guarantees: the fiat legs (card, local, payin, offramp) use a fixed test rate, so they are firm. A plain transfer (onchain, solana-onchain, and a wallet or transfer leg on one chain and one token) is firm. A bridge leg, and a wallet or transfer leg that changes the chain or the token, is min_output: minOutput is the output less 0.5%, and slippageBps is 50. So you can test the server's min_output path with no real provider.
  • Fees: each leg has one fee line with an amount and included: true. The fiat legs have a provider fee. A plain crypto transfer has a network fee, and a bridge or swap has a bridge fee.
  • providerRef: the leg ref, so you can test the provider reference in the UI and in the admin page.

How to finish each leg ​

LegAction
cardA 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_transferThe same deposit address. The warning tells the user to withdraw from the exchange on the correct network. Press Simulate deposit (test mode).
local, payinPress 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.
transferPress Simulate deposit (test mode) (transition simulate_deposit)
walletSend with a wallet adapter; the client fires submit_tx with the hash
bridgeNothing: it is paid when it starts and settles after settleMs
offrampFill 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 checkoutQR 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=...&currency=...: 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.

ts
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.

MIT licensed