- The Buy Button frame emits a
challengeevent with a URL. Use that URL to mount the frame. For card and bank transfer quotes, including headless mode, the challenge frame finishes the purchase itself. - The identity flow mounts the frame directly to capture identity data (for example, document capture or liveness checks).
- A buy quote returns a
challengeobject when a guest checkout customer can raise their spending limit. Mount the frame atchallenge.urlto run the limit upgrade.
complete when the pipeline finishes. The complete and
cancelled payloads are discriminated by flow so you can branch on the
originating flow.
Opened with resume, an Apple Pay or Google Pay
buy challenge emits resolved instead of complete. The
customer then pays on your buy button. See Finish on your buy
button.
URL
For thebuy flow, the URL is provided by the buy button’s challenge event
payload. For the identity flow, the URL is provided by the upstream identity
verification call. For the guest_checkout_limit_upgrade flow, the URL is
challenge.url on the buy quote response. In every case, do not construct the
URL yourself — only append the query parameters below before setting it as the
frame src.
Requirements
Size
Render the frame in a modal or full sheet so the customer can complete verification. The frame adapts to any size, but a full-screen or large modal works best for flows like 3D Secure that load bank-hosted pages.Initialization parameters
Events
All events are dispatched using the message pattern described in the frames protocol. Below are the event payloads specific to the Challenge frame.Outbound events
frame->parent These events are sent from this frame to the parent window.handshake
The frame requests that you open a message channel.
ready
The challenge UI is rendered and visible to the customer.
buttonPressed
The customer tapped the native Apple Pay or Google Pay button inside the
challenge frame’s payment sheet. The frame emits this the instant the button is
tapped, before the OS presents the wallet payment sheet. Treat it as an
intent-to-buy signal: use it to react to the tap, for example to show a loading
state or fire analytics.
This event carries no payload. Route it by channelId.
buttonPressed is only emitted by the flows that present a wallet payment sheet
— the buy step-up and the guest_checkout_limit_upgrade flow. The identity
flow does not render a payment sheet and never emits it.
buttonPressed is not a purchase outcome. It still fires if the customer opens
the payment sheet and then cancels, and it fires before any authorization
happens. For the result, listen for complete, which reports the
outcome once the pipeline resolves.
complete
The challenge finished. Remove the challenge frame and branch on the
discriminated payload:
flow: "buy"— the transaction pipeline finished. Remove the buy button too. Iftransaction.statusisfailed, the payment didn’t go through: readfailureCodeandfailureReason, as for the Buy Button frame. Otherwise navigate to the confirmation screen. After a guest checkout recovery, the payload also carriessessionUpgraded: true: create a new session and check the connection again. See Recover the connection after a step-up.flow: "identity"— the customer finished the challenge. Checkstatusfirst. When it is absent, the customer was verified: useidentityIdto continue your onboarding flow.pendingmeans MoonPay is still verifying them, so don’t treat them as verified: see Pending customers.unavailablemeans they can’t be verified (for example, an unsupported region or a closed account), so show your own screen. See Unavailable customers.flow: "guest_checkout_limit_upgrade"— the upgrade reached a terminal outcome. Readstatus.upgradedmeans the limit is raised, so request the quote again to get an executable one.rejectedmeans verification failed, and running the upgrade again does not change that.
resolved
The buy challenges are done and the frame handed the purchase back to your buy
button. The frame sends this only when its URL carries resume=true, and only
for an Apple Pay or Google Pay buy. Remove the Challenge frame, keep the buy
button, and show the buy button again. The customer taps it to pay, and the buy
button emits complete.
The frame sends resolved or complete for a purchase, never both. If it can’t
hand the purchase back, it shows its own confirm screen and emits complete. So
keep your complete handler when you pass resume.
resolved carries no transaction and no sessionUpgraded. After a guest
checkout recovery, sessionUpgraded arrives on the buy button’s complete.
See Finish on your buy
button for every event in
order.
cancelled
The customer dismissed the challenge. Remove the challenge frame and act on
the discriminated payload:
flow: "buy"— remove the buy button too, then offer a retry path. The payload carriestransactionIdandchallengeToken(when known) so you can resume without restarting the pipeline.flow: "identity"— offer a retry path or exit. No extra payload is emitted.flow: "guest_checkout_limit_upgrade"— the customer dismissed the upgrade. Nothing was submitted and the limit is unchanged. Offer a retry path.
error
The challenge failed. Remove the challenge frame (and the buy button, if the
buy flow was driving it), then surface the message to the developer.
Pending customers
A customer can have a check in flight or under review, such as a liveness check. It is not a failure. The customer can’t finish this challenge until the check clears, so don’t mark the transaction or the customer as failed. How the frame reports it depends on the challenge:- Transaction challenges (buy, Apple Pay, Google Pay): an
errorwithcustomer_status_pending, because the transaction can’t continue. - Customer API challenge (
kyc.challenge): acompletewithflow: "identity"andstatus: "pending". While a check is running, the frame can stay open on a waiting screen first and sends nothing. Don’t dispose it until it sends an event.
- Dispose the frame and tell the customer their verification is in progress.
- Poll Get a customer with
GET /customers/{id}, using the customeridfrom the connection result. Authenticate with your secret key, or with the customer’saccessTokenfrom the decrypted credentials. See the Customer API prerequisites for which to use. Back off between requests. Checks usually clear within a few minutes but can take longer. - When
kyc.statusisactive, resume what the customer was doing: continue your onboarding flow, or start the transaction flow again. If it isunavailable, stop and send the customer to an alternate flow.
Unavailable customers
A customer can have a constraint that blocks verification entirely, such as an unsupported region or a closed account. How the frame reports it depends on the challenge:- Transaction challenges (buy, Apple Pay, Google Pay): an
errorwithcustomer_status_unavailable, because the transaction can’t continue. - Customer API challenge (
kyc.challenge): acompletewithflow: "identity"andstatus: "unavailable", the same way the connect frame reports an unavailable customer. Don’t treat thiscompleteas a verified customer: checkstatusfirst.