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()orclient.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 achallenge. - 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 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.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 To match the wallet buttons to your UI, pass For every option, see Button
settings.
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
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
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 forcompletefor the result.complete— the pipeline finished. The payload carries atransaction. Inspect itsstatusfirst: whenstatusis"failed", branch onfailureCodeand logfailureReason, which is developer-facing; otherwise passtransaction.idtogetTransaction()to poll for the final status. After a guest checkout recovery, the payload can also carrysessionUpgraded: true. See Finish on your buy button.challenge— verification is required before the transaction can proceed. Render the challenge frame at theurlfrom the payload. Do not construct the URL yourself.error— the purchase could not run. Logcodeandmessage, then tear down the frame. Themessageis for logs, not for display to customers. The exception isquoteExpired: keep the frame and send a new quote withsetQuote().
4
Handle challenges
When the buy button emits a 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.
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
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 emitscomplete. 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 withoutresume. ChecksessionUpgradedhere too.cancelled: the customer closed the challenge before it finished. Dispose the challenge frame and offer a retry.error: the challenge failed. Log thecodeandmessage, dispose the challenge frame, and show the customer an error.
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 callsetQuote() with its signature instead of
re-rendering the frame.
Refresh the quote
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.