Skip to main content
<MoonPayChallenge> renders a 3DS or identity challenge frame inline in your layout. It is the declarative alternative to client.setupChallenge(). Mount this component when a payment frame (Apple Pay, Google Pay, buy button, or buy frame) emits a "challenge" event. Pass the url from the event payload directly. Unmounting the component disposes the frame and stops event delivery.
PaymentScreen.tsx
import React from "react";
import {
  MoonPayApplePayButton,
  MoonPayChallenge,
  type ApplePayEvent,
  type ChallengeEvent,
} from "@moonpay/platform-sdk-react-native";
import { StyleSheet, View } from "react-native";

export function PaymentScreen({ quoteSignature }: { quoteSignature: string }) {
  const [challengeUrl, setChallengeUrl] = React.useState<string | null>(null);

  const handlePayEvent = (event: ApplePayEvent) => {
    if (event.kind === "challenge") {
      setChallengeUrl(event.payload.url);
    }
  };

  const handleChallengeEvent = (event: ChallengeEvent) => {
    switch (event.kind) {
      case "complete":
        console.log(event.payload);
        setChallengeUrl(null);
        break;
      case "cancelled":
        console.log("Challenge cancelled", event.payload);
        setChallengeUrl(null);
        break;
      case "error":
        console.error(event.payload.code, event.payload.message);
        setChallengeUrl(null);
        break;
    }
  };

  return (
    <View style={styles.container}>
      {challengeUrl ? (
        <MoonPayChallenge url={challengeUrl} onEvent={handleChallengeEvent} />
      ) : (
        <MoonPayApplePayButton
          quote={quoteSignature}
          onEvent={handlePayEvent}
        />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
});

Props

PropTypeRequiredDefaultDescription
urlstringFull challenge URL from a payment frame’s "challenge" event. Mount-time only.
onEvent(event: ChallengeEvent) => voidCallback for challenge lifecycle events. See ChallengeEvent.
styleViewStyleflex: 1Container style.

ChallengeEvent

Use event.kind to handle each event.
kindPayloadWhen you receive it
"ready"The challenge UI is rendered and ready.
"complete"CompleteResultThe challenge finished. See CompleteResult shape below.
"cancelled"CancellationThe customer dismissed the challenge. See Cancellation below.
"error"{ code: string; message: string }The challenge encountered an error.
CompleteResult is a discriminated union on flow:
flowExtra fields
"buy"transaction: FrameTransaction
"identity"identityId: string
Cancellation is a discriminated union on flow:
flowExtra fields
"buy"transactionId?: string, challengeToken?: string
"identity"
On "cancelled", carry transactionId and challengeToken to resume the transaction if needed.