> ## 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.

# Pay with Apple Pay

> Allow customers to buy crypto headlessly with Apple Pay.

Use this guide to execute a transaction with Apple Pay after you have a [connected customer](/platform/guides/connect-a-customer). To let new customers buy with Apple Pay before they have a MoonPay account, see [Guest checkout](/platform/guides/guest-checkout).

See the [Going Live](/platform/overview/going-live) section for details on the requirements you must meet before you can take this integration to production.

## Prerequisites

* A connected customer (via `client.getConnection()` or `client.connect()`).
* A UI surface where you can render the [Apple Pay frame](/platform/frames/apple-pay).

<Tip>
  You can test the full Apple Pay flow without a real Apple Pay account by using
  [test mode](/platform/overview/test-mode#apple-pay). The frame renders a mock
  Apple Pay button that simulates successful and failed transactions.
</Tip>

## Flow overview

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor C as Customer
    participant FE as Your frontend
    participant API as MoonPay API
    participant APF as Apple Pay frame
    participant CF as Challenge frame

    Note over C,CF: Prerequisite: customer is connected

    FE->>API: GET /platform/v1/payment-methods
    API-->>FE: [{ type: "apple_pay", availability }]

    C->>FE: Enters amount
    FE->>API: POST /platform/v1/quotes/buy
    API-->>FE: { quote with signature }

    FE->>APF: Render Apple Pay frame (signature)
    APF-->>FE: ready
    FE->>C: Reveals Apple Pay button
    C->>APF: Taps button, approves in native sheet

    alt Happy path
        APF-->>FE: complete({ transaction: { id, status } })
    else Verification required
        APF-->>FE: challenge({ url })
        FE->>CF: Render Challenge frame at URL
        CF-->>FE: complete({ transaction: { id, status } })
    end

    FE->>API: GET /platform/v1/transactions/{id}
    Note over FE: Poll for final status
```

## Device and browser support

Apple Pay is available in Safari on macOS and in every iOS browser. The customer also needs a card set up in Apple Pay.

In other browsers, the frame reports Apple Pay as unavailable and renders nothing. It does not offer Apple's cross-device flow, where the page shows a QR code that the customer scans with their iPhone to approve the payment. The [widget](/platform/guides/pay-with-widget) does support the cross-device flow.

In a native iOS app, you can embed the [Apple Pay frame](/platform/frames/apple-pay) in a `WKWebView`. By default, `WKWebView` silently dismisses the JavaScript dialogs the frame relies on, so you must handle them through `WKUIDelegate`. See the [frame requirements](/platform/frames/apple-pay#wkwebview) and the [iOS manual integration guide](/platform/guides/manual-integration/ios#apple-pay-frame).

When Apple Pay isn't available in the customer's environment, the frame emits an `unsupported` event. Use it to build the experience that fits your product — for example, hide the Apple Pay option up front, or route customers who select Apple Pay to the widget with your `apple_pay` quote. The code sample in [Execute the transaction](#execute-the-transaction) handles this event.

## Display payment methods

Use the SDK or API to fetch and display the payment methods that are available for the customer right now.

<CodeGroup>
  ```ts List payment methods theme={null}
  // After connecting, list available payment methods
  const paymentMethodsResult = await client.getPaymentMethods();

  if (!paymentMethodsResult.ok) {
    // Handle error
  }

  console.log(paymentMethodsResult.value);
  ```

  ```ts Result theme={null}
  [
    {
      type: "apple_pay",
      capabilities: {
        supportedCurrencies: ["USD", "EUR", "GBP"],
        supportedTransactionTypes: ["buy"],
      },
      availability: {
        active: true,
      },
    },
  ];
  ```
</CodeGroup>

## Get quotes

Quotes provide real-time prices and fees for transactions. Show the contextual
quote summary above the Apple Pay button. The Apple Pay sheet
presents fees, so you do not need to render a fee breakdown. See
[Going Live](/platform/overview/going-live#quote-presentation) for the required
presentation and [fee behavior](/platform/overview/core-concepts#fee-behavior)
for how fees relate to the source amount.

Only quotes with `executable: true` can be used to execute a transaction. See the [quotes API reference](/api-reference/platform/endpoints/quotes/get) for the fields required to receive `executable: true`.

<CodeGroup>
  ```ts Get quote theme={null}
  const quoteResult = await client.getQuote({
    source: { asset: { code: "USD" }, amount: "100.00" }, // The fiat currency and amount to pay
    destination: { asset: { code: "ETH" } }, // The crypto the customer will receive
    wallet: { address: "0x1234..." }, // The destination wallet address
    paymentMethod: { type: "apple_pay" }, // The payment method type
  });

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

  console.log(quoteResult.value);
  ```

  ```ts Result theme={null}
  {
    source: {
      amount: "100.00",
      asset: {
        code: "USD",
        name: "US Dollar",
        precision: 2
      }
    },
    destination: {
      amount: "0.025",
      asset: {
        code: "ETH",
        name: "Ethereum",
        precision: 18
      }
    },
    fees: {
      network: {
        amount: "2.50",
        currencyCode: "USD"
      },
      moonpay: {
        amount: "3.99",
        currencyCode: "USD"
      }
    },
    wallet: {
      address: "0x1234..."
    },
    paymentMethod: {
      type: "apple_pay"
    },
    expiresAt: "2026-01-12T14:45:00Z",
    executable: true,
    signature: "eyJhbGciOiJFUzI1NiIs..."
  }
  ```
</CodeGroup>

## Execute the transaction

To execute a transaction, set up the payment flow based on the quote. Different payment methods have different requirements—you can configure each as needed to control your experience. For the frame URL, size, permissions, and events, see the [Apple Pay frame](/platform/frames/apple-pay) reference.

<Tip>
  Some transactions require a challenge — the Apple Pay frame emits a
  `challenge` event when extra verification is needed. See [Handle
  challenges](/platform/guides/handling-challenges) for the full flow.
</Tip>

```ts Apple Pay theme={null}
import type { ApplePayEvent } from "@moonpay/platform-sdk-web";

// Render the Apple Pay frame into your UI and handle callbacks
const applePayResult = await client.setupApplePay({
  quote: quoteResult.value.signature, // The quote signature from getQuote

  container: document.querySelector("#applePayContainer"), // DOM element to render the button

  onEvent: (event: ApplePayEvent) => {
    switch (event.kind) {
      case "ready":
        // The frame is ready. Use this event to reveal the button if needed.
        break;

      case "complete": {
        const txn = event.payload.transaction;

        if (txn.status === "failed") {
          // The transaction failed. Branch on failureCode for programmatic handling.
          switch (txn.failureCode) {
            case "authorizationDeclined":
              // Prompt the customer to try a different card.
              break;

            case "serviceUnavailable":
            case "applePayMerchantUnavailable":
              // Retry after a short delay.
              break;

            default:
              // Show txn.failureReason to the customer.
              break;
          }
          break;
        }

        // The transaction is executing. Track the final status via polling and/or webhooks.
        console.log(txn);
        // { id: "txn_01", status: "pending" }
        break;
      }

      case "challenge":
        // Verification required. Render the challenge frame at the provided URL.
        // See: /platform/guides/handling-challenges
        console.log(event.payload.url);
        break;

      case "quoteExpired":
        // Fetch a new quote, then pass its signature into the frame
        // const newQuote = await client.getQuote({...});
        // event.payload.setQuote(newQuote.value.signature);
        break;

      case "error":
        // Depending on the error, you can have the customer pick a different payment method or retry.
        console.error(event.payload.message);
        break;

      case "unsupported":
        // Apple Pay isn't supported in the current environment.
        break;
    }
  },
});

if (!applePayResult.ok) {
  // Handle error setting up Apple Pay
}

// You can update the quote or dispose the frame later
// applePayResult.value.setQuote(newQuoteSignature);
// applePayResult.value.dispose();
```

## 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.
