> ## Documentation Index
> Fetch the complete documentation index at: https://dev.moonpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Handle challenges

> Detect and respond to challenges.

This guide shows you how to detect and handle challenges that require customer
authentication or verification before MoonPay can continue an action.

## Prerequisites

* A [connected customer](/platform/guides/connect-a-customer).
* A UI surface where you can render frames (WebView on mobile, iframe on web).

## When challenges appear

Challenges are extra steps a customer must complete before MoonPay can continue
an action. Common reasons include:

* Strong Customer Authentication (SCA), for example 3D Secure
* Identity verification (KYC)
* Card-specific checks such as CVC re-entry or micro-authorization

Today, challenges are surfaced by frames — frames emit a `challenge` event that
points to the [Challenge frame](/platform/frames/challenge). Other surfaces may
emit challenges in the future (for example, identity APIs).

## Where challenges are emitted

A frame emits a `challenge` event when verification is required. The payload
carries a fully-formed URL that you load into the dedicated
[Challenge frame](/platform/frames/challenge):

* The [Apple Pay frame](/platform/frames/apple-pay) emits `challenge` for
  Apple Pay transactions.
* The [Google Pay frame](/platform/frames/google-pay) emits `challenge` for
  Google Pay transactions.
* The [buy frame](/platform/frames/buy) emits `challenge` for card
  transactions (see [Pay with card](/platform/guides/pay-with-card)).

The event uses the same envelope across all of these frames:

```json Example challenge event theme={null}
{
  "version": 2,
  "meta": { "channelId": "ch_1" },
  "kind": "challenge",
  "payload": {
    "kind": "frame",
    "url": "https://blocks.moonpay.com/platform/v1/challenge?challengeToken=..."
  }
}
```

## How to handle a challenge

1. **Listen for the `challenge` event** on the frame. Treat the action as
   blocked until the challenge resolves.
2. **Mount the Challenge frame** at the URL from the event payload. Pass the
   URL through as-is — do not construct or modify it. See the
   [Challenge frame](/platform/frames/challenge) reference for the frame URL,
   parameters, and events.
3. **Handle the Challenge frame events**:
   * `ready` — the challenge UI is rendered and visible.
   * `complete` — verification resolved. The Challenge frame reports any
     downstream artifacts (for example, the transaction `id` and `status` for
     the buy flow).
   * `cancelled` — the customer dismissed the challenge. Offer a retry path.
   * `error` — the challenge failed. Log the `code` and `message` (both are
     developer-facing and not intended for end-user UI) and show the customer
     a generic next step, such as retrying or choosing a different payment
     method.
4. **Tear down the originating frame** after `complete`, `cancelled`, or
   `error`. For the buy frame, call `buyResult.value.dispose()`.

## Implementation tips

* **Use a full-screen surface on mobile**: challenge flows often involve
  authentication or verification, so treat them like a separate screen or full
  sheet.
* **Validate `postMessage` events**: if you integrate frames manually, validate
  origin and message shape. The [frames protocol](/platform/frames/overview)
  documents the shared envelope format.
* **Handle cancellation and timeouts**: if the customer closes the challenge or
  it fails, show a clear next step (retry, choose a different payment method,
  or exit the flow).
