Skip to main content
Use this guide to add an express checkout button to your app. The buy button is a single MoonPay-hosted button that renders the payment method you set on the quote: Apple Pay, Google Pay, or card. On tap, it opens the native Apple Pay or Google Pay sheet, or runs the card pipeline directly, then completes the transaction. Set up the button after you have a connected customer. The button renders the single payment method from your quote. There is no in-frame payment-method picker. MoonPay handles payment collection and any verification challenges inside hosted frames, so card data and sensitive verification never touch your domain. See the Going Live section for requirements you must meet before taking this integration to production.

When to use the buy button

MoonPay gives you three ways to run a buy. Pick the one that matches how much of the experience you want to own. The key difference between the buy button and headless mode is the UI surface. The buy button renders a visible payment button for you, plus the native payment sheet for Apple Pay and Google Pay. Headless mode renders no UI at all. You supply the button and confirmation screen and call setupBuyButton() with headless: true when the customer confirms. Headless mode supports card and SEPA bank transfer quotes only. Apple Pay and Google Pay need the customer to tap the buy button, so they don’t work with headless: true. Both run the same buy pipeline and use the same events, except that headless mode never sends buttonPressed or cancelled. See headless mode.

Prerequisites

  • A connected customer (via client.getConnection() or client.connect()). See Connect a customer. A guest checkout session, which has no connected customer, can also use the buy button: when the purchase needs the customer to sign in, the buy button raises a challenge.
  • A UI surface where you can render the Buy Button frame.
  • A destination wallet address for the purchased crypto.

Flow overview

1

Get a quote

Request a quote for the amount the customer wants to spend. Pass the destination wallet, the asset the customer will receive, and the paymentMethod you want the button to use. The buy button renders the single button that matches this payment method: Apple Pay, Google Pay, or card.Only quotes with executable: true can be used to execute a transaction. See the quotes API reference for the fields required to receive executable: true.
The paymentMethod you pass sets which button renders. Use apple_pay or google_pay for the wallet buttons, or card with a stored card id for a card button. List the methods available to the customer with getPaymentMethods().On the same confirmation view, before the buy button, you display the contextual quote summary and fee breakdown. You remain responsible for this presentation during the normal hosted flow. See Going Live for the canonical requirements and fee behavior for how fees relate to the source amount. Monitor expiresAt and refresh the quote before it expires. If the button is already rendered, call buyButton.setQuote(newSignature) instead of re-creating it. See Refresh an expiring quote.
2

Render the buy button

Pass the quote signature and a container element to client.setupBuyButton(). The frame renders the single payment button that matches the payment method in the quote (card, Apple Pay, or Google Pay) into your container. On tap, the frame runs the buy pipeline. For Apple Pay and Google Pay, it opens the native payment sheet first.For the frame URL, parameters, and events, see the Buy Button frame reference.
Render the buy button
To match the wallet buttons to your UI, pass applePayButtonSettings and googlePayButtonSettings. The frame applies the one that matches the button it renders. Size the container to match the height you set:
Style the wallet buttons
For every option, see Button settings.
The buy button emits a ready event once the button is rendered and ready to tap. For Apple Pay and Google Pay, it fires after the customer’s device is confirmed to support the wallet.
3

Handle the events

onEvent receives BuyButtonEvent events as the pipeline progresses. Handle each kind:
  • ready — the button is rendered and ready for the customer to tap. Use it to hide any loading placeholder.
  • buttonPressed — the customer tapped the pay button. For Apple Pay and Google Pay, this fires before the native payment sheet appears. For card, it fires before the card pipeline runs. An intent-to-buy signal with no payload. Use it to show a loading state or fire analytics. It is not a purchase outcome. It still fires if the customer then cancels, so listen for complete for the result.
  • complete — the pipeline finished. The payload carries a transaction. Inspect its status first: when status is "failed", branch on failureCode and log failureReason, which is developer-facing; otherwise pass transaction.id to getTransaction() to poll for the final status. After a guest checkout recovery, the payload can also carry sessionUpgraded: true. See Finish on your buy button.
  • challenge — verification is required before the transaction can proceed. Render the challenge frame at the url from the payload. Do not construct the URL yourself.
  • error — the purchase could not run. Log code and message, then tear down the frame. The message is for logs, not for display to customers. The exception is quoteExpired: keep the frame and send a new quote with setQuote().
4

Handle challenges

When the buy button emits a challenge event, the customer must complete one or more verification steps before the transaction can proceed. Set up the challenge frame with the URL from the event payload. For the full flow, see Handle challenges.The challenge frame is self-driving: after initialization, it sequences through all required verification steps, creates the transaction, and emits complete when the pipeline finishes.
Handle challenges
When you receive complete, cancelled, or error from the challenge frame, call buyButton.dispose() to also tear down the buy button.
The challenge frame handles all verification types automatically — KYC, Strong Customer Authentication (SCA), CVC re-entry, wallet ownership, micro-authorization, and 3D Secure (3DS). You never need to distinguish between them.To keep the customer on your buy button once verification is done, see Finish on your buy button.
5

Track the transaction

When the buy button or challenge frame emits complete, the payload includes a transaction with an id and status. The transaction is created and payment is processing. Poll for the final status with getTransaction().
Track the transaction
You can also track transaction status with the existing transaction-updated webhook.

Finish on your buy button

By default, the challenge frame shows its own Apple Pay or Google Pay confirm screen once verification is done, then emits complete. To keep the customer on the buy button you rendered, pass resume: true to setupChallenge(). It needs @moonpay/platform-sdk-web or @moonpay/platform-sdk-react-native 1.27.0 or later. With resume, the challenge frame emits resolved instead of complete. Dispose the challenge frame, not the buy button, and show the buy button again. The customer taps it to pay, and the buy button emits complete. resume only changes Apple Pay and Google Pay purchases. For a card payment, the challenge frame completes the purchase itself and emits complete, with or without resume.

Message flow

When the purchase is handed back, you receive these events in this order: The challenge frame can also end without resolved:
  • complete: the challenge frame couldn’t hand the purchase back, so it finished the purchase itself. Dispose the challenge frame and the buy button, as you do without resume. Check sessionUpgraded here too.
  • cancelled: the customer closed the challenge before it finished. Dispose the challenge frame and offer a retry.
  • error: the challenge failed. Log the code and message, dispose the challenge frame, and show the customer an error.
After resolved, the next tap can emit challenge again. For example, the customer waited too long before tapping, or the quote changed in between. Handle it the same way, with resume. The Challenge frame reference has the resolved payload and the resume URL parameter for integrations without the SDK.

Handle resolved

On the web, dispose the challenge frame yourself, as below. In React Native, setupChallenge() closes its sheet on resolved for you. If you render <MoonPayChallenge>, unmount it. Then show the buy button again.
Finish on your buy button

Guest checkout recovery

resume also covers guest checkout recovery: the challenge the buy button raises when a guest purchase needs the customer to sign in, for example for a second factor. Once the customer has signed in and any further verification is done, the challenge frame emits resolved. Show the buy button again, and the purchase completes as the signed-in customer when the customer taps it. In that case sessionUpgraded: true arrives on the purchase’s final complete: the buy button’s, or the challenge frame’s if the frame finishes the purchase itself. Keep both checks. Create a new session and check the connection again. See Recover the connection after a step-up. Do this on complete. sessionUpgraded is never on resolved, cancelled, or error, and a new session on resolved remounts the buy button in the middle of the purchase.

Refresh an expiring quote

Quotes expire. If the current quote expires before the customer taps the button, fetch a new one and call setQuote() with its signature instead of re-rendering the frame.
Refresh the quote
When you no longer need the button, call buyButton.dispose() to unmount the frame. After you dispose it, no further events reach your onEvent callback.

Transaction statuses

Transactions have the following statuses:
  • Pending: The transaction has been initiated and the payment accepted. The assets are being transferred.
  • Complete: The transaction is finalized. The payment is complete and the assets have been delivered to their destination.
  • Failed: The transaction has failed. The payment was not executed and funds were not transferred.

Next steps

setupBuyButton() reference

Full method signature, parameters, events, and errors.

Buy Button frame

Frame URL, parameters, and protocol messages.

Connect a customer

Connect a customer’s MoonPay account before you start a buy.

Handle challenges

Render the challenge frame when verification is required.