Prerequisites
- A MoonPay account with bank transfers enabled. Contact your MoonPay account team to enable it.
- A connected customer (via
client.getConnection()orclient.connect()). - A UI surface where you can render MoonPay frames (iframe on web, or WebView on mobile).
- A destination wallet address for the purchased crypto.
Flow overview
1
Confirm bank transfer is available
Fetch the customer’s available payment method types and check for Show bank transfer in your payment method picker only when a
sepa.sepa config is
present, availability.active is true, and capabilities.requiresWidget is
false. A requiresWidget: true bank transfer isn’t eligible for the headless
flow and must complete in the MoonPay widget instead.SEPA doesn’t create a stored payment method, so it never appears in
paymentMethods. That array still lists the customer’s stored cards, so it can
be non-empty even when the customer pays by bank transfer.2
Get a bank-transfer quote
Request a quote with Bank transfers are a floating payment method: the quote returns
paymentMethod.type set to "sepa".exchangeRateType: "floating" and the crypto amount is an estimate until the
customer’s funds settle. Render the estimated amount with a tilde (for example,
~0.0234 ETH) and tell the customer the final amount is set at settlement. See
Exchange rate type.Show the contextual quote summary and fee breakdown before the customer
confirms. See Going Live for
the canonical requirements. Monitor expiresAt and refresh the quote before it
expires.3
Execute the transaction
Call A bank transfer can still require a challenge, such as a KYC step-up. Handle it
exactly as for cards, but route the result back into the deposit flow: the
Challenge frame creates the transaction and emits it on
client.setupBuy() with the quote signature, exactly as you do for cards.
The headless frame creates the transaction and emits complete with the
transaction id. For the frame URL, parameters, and events, see the
Buy frame reference.setupBuy
complete, so call
showDepositDetails with that transaction id. See
Handle challenges.Challenge handling
4
Read and render the deposit details
Call
client.getTransaction() with the id. For a bank transfer, the response
includes a bankTransferDepositInfo object with the account details and the
payment reference, plus the exact fiat amount to transfer in source.amount (the
gross amount, including fees). Render these fields in your own UI so the customer
can pay from their banking app. MoonPay does not render the deposit UI.Read deposit details
bankTransferDepositInfo contains:5
Track the transaction
A bank transfer can take from a few minutes to a few days, and the transaction
stays Bank-transfer transactions have the following statuses:
pending until settlement. While it’s pending, the transaction’s
destination.amount is 0, so keep rendering the estimate from the original
quote’s destination.amount with a tilde, and make clear the crypto amount is
only an estimate until the transfer lands. After settlement, switch to the
transaction’s final destination.amount.Poll client.getTransaction() to track status. Because settlement can run for
days, you can also subscribe to the
transaction-updated
webhook instead of long-lived polling.Track the transaction
- Pending: The transaction is waiting for the customer’s deposit, or the deposit has arrived and is being processed.
- Complete: The funds have been received and the crypto delivered to the destination wallet.
- Failed: The transfer timed out or was cancelled.
status stays pending from the moment the transaction is created until it
settles, so it doesn’t tell you whether the deposit has arrived yet. For
finer-grained progress, read the stages array: each stage has a kind and a
status. When the waiting_payment stage’s status is "success", the
customer’s deposit has arrived. Use that to move your UI from awaiting-transfer
to processing before the transaction reaches a terminal status.Requotes and cancellations for bank transfers are handled by MoonPay’s hosted
flow, not the headless flow. If the settled amount differs from the estimate,
the customer is emailed and completes the requote there. A cancellation
surfaces to your app as a
failed transaction when you poll
client.getTransaction().Polling is the documented way to track status. If you already consume MoonPay
webhooks, you can also use the existing
transaction-updated
webhook. See the webhooks overview.