Skip to main content
Render the Add Card frame so the customer can save a new credit or debit card. The provider presents the frame in a full-screen native modal. Card data is captured inside a PCI-compliant MoonPay-hosted UI and never touches your app code.
Setup Add Card
import {
  useMoonPay,
  type AddCardEvent,
} from "@moonpay/platform-sdk-react-native";

export function AddCardScreen() {
  const { client } = useMoonPay();

  const start = async () => {
    const addCardResult = await client.setupAddCard({
      onEvent: (event: AddCardEvent) => {
        switch (event.kind) {
          case "complete":
            // Card added. Use event.payload.card.id to get a quote.
            console.log(event.payload.card);
            break;
          case "error":
            console.error(event.payload.code, event.payload.message);
            break;
        }
      },
    });

    if (!addCardResult.ok) {
      // Handle error
      console.error(addCardResult.error.kind, addCardResult.error.message);
      return;
    }

    const addCard = addCardResult.value;
  };

  // ...
}

Parameters

PropertyTypeRequiredDescription
onEvent(event: AddCardEvent) => voidCallback invoked for Add Card flow events. See AddCardEvent.
This method does not require a separate auth token. The client uses stored credentials from an active connection.

AddCardEvent

onEvent receives events as the Add Card flow progresses. Use event.kind to decide how to handle each event.
kindPayloadWhen you receive it
"ready"The frame finished loading and the card form is visible.
"complete"{ card: CardResponse }The card was saved successfully.
"error"AddCardEventErrorThe flow encountered an error.

CardResponse

The card object returned when the customer finishes adding a card. Use id directly in getQuote — there is no need to re-fetch payment methods.
FieldTypeRequiredDescription
idstringThe stored card identifier.
typestringThe payment method type, typically "card".
cardTypestringThe card sub-type (for example, "credit" or "debit").
brandstringThe card network brand (for example, "visa", "mastercard").
last4stringThe last four digits of the card number.
expirationMonthstringThe card expiration month, as a two-digit string (for example, "04").
expirationYearstringThe card expiration year, as a four-digit string (for example, "2030").
availability{ active: true }The card is available for new transactions when the frame returns it.

AddCardEventError

FieldTypeRequiredDescription
code"configurationError" | "generic"The error category.
messagestringDeveloper-friendly details.

Result

client.setupAddCard() returns a Result<AddCardFrame, SetupAddCardError>.

Result envelope

Result<AddCardFrame, SetupAddCardError>
FieldTypeRequiredDescription
okbooleanWhether the operation succeeded.
valueAddCardFramePresent when ok is true.
errorSetupAddCardErrorPresent when ok is false.

AddCardFrame

FieldTypeRequiredDescription
dispose() => voidUnmounts the frame. After you call this, no further events are dispatched to your onEvent callback.
The Add Card frame does not expose a setQuote method — quotes are issued after the card is saved.

SetupAddCardError

FieldTypeRequiredDescription
kind"configurationError" | "genericError"The error category.
messagestringDeveloper-friendly details.

Example: Add a card, then buy

After the complete event fires, pass card.id to getQuote and use the returned signature with setupBuy to execute the transaction. For the full end-to-end walkthrough, see the Pay with card guide.
Add card, then buy
import {
  useMoonPay,
  type AddCardEvent,
  type BuyEvent,
} from "@moonpay/platform-sdk-react-native";

export function AddCardThenBuy() {
  const { client } = useMoonPay();

  const start = async () => {
    const addCardResult = await client.setupAddCard({
      onEvent: async (event: AddCardEvent) => {
        if (event.kind !== "complete") return;

        const cardId = event.payload.card.id;

        // Tear down the Add Card frame — we have the card we need.
        addCardResult.value.dispose();

        // Request a quote for the new card.
        const quoteResult = await client.getQuote({
          source: { asset: { code: "USD" }, amount: "100.00" },
          destination: { asset: { code: "ETH" } },
          wallet: { address: "0x1234567890abcdef1234567890abcdef12345678" },
          paymentMethod: { type: "card", id: cardId },
        });

        if (!quoteResult.ok) {
          // Handle error
          return;
        }

        // Execute the transaction with the headless buy frame.
        const buyResult = await client.setupBuy({
          quote: quoteResult.value.data.signature,
          onEvent: (event: BuyEvent) => {
            // Handle buy events (see the Pay with card guide).
          },
        });

        if (!buyResult.ok) {
          // Handle error
          return;
        }
      },
    });

    if (!addCardResult.ok) {
      // Handle error
      return;
    }
  };

  // ...
}
types.ts
type AddCardFrame = {
  dispose: () => void;
};

type CardResponse = {
  id: string;
  /** Payment method type, typically "card". */
  type: string;
  /** Card sub-type, for example "credit" or "debit". */
  cardType: string;
  /** Card network brand, for example "visa" or "mastercard". */
  brand: string;
  last4: string;
  /** Two-digit month string, for example "04". */
  expirationMonth: string;
  /** Four-digit year string, for example "2030". */
  expirationYear: string;
  availability: { active: true };
};

type AddCardEvent =
  | {
      kind: "ready";
    }
  | {
      kind: "complete";
      payload: {
        card: CardResponse;
      };
    }
  | {
      kind: "error";
      payload: AddCardEventError;
    };

type AddCardEventError = {
  code: "configurationError" | "generic";
  /** A developer-facing error message. Not intended to be rendered in UI. */
  message: string;
};

type SetupAddCardError = {
  kind: "configurationError" | "genericError";
  message: string;
};