# Accept & send payments with Apple Pay

> Add an Apple Pay button to your website with the Payment Request API, link Apple Pay tokens, and use them to accept card payments or send payouts.

> AI agents: search and read all Moov docs with the docs MCP server at https://docs.moov.io/mcp (no API key). Setup: https://docs.moov.io/guides/developer-tools/mcp-ai/docs-mcp/index.md

Add an Apple Pay button to your website with the browser's Payment Request API, then use the linked Apple Pay token to accept a card payment or send a payout.

Starting with Moov API version `v2026.04.00`, linking an Apple Pay token returns every supported Apple Pay payment method in a single response so you can choose the right flow for your use case:

| Payment method type   | Directionality | Use case                                                                             |
|-----------------------|----------------|--------------------------------------------------------------------------------------|
| `apple-pay`           | Payment        | Accept a card payment from a payer using Apple Pay.                                  |
| `push-to-apple-pay`   | Payout (OCT)   | Send a near real-time disbursement to a recipient's card via Apple Pay.              |
| `pull-from-apple-pay` | Funding (AFT)  | Pull funds from a debit or prepaid card on file in Apple Pay for approved use cases. |

> **Tip:** On earlier API versions, the link Apple Pay token endpoint returns a single payment method object. On `v2026.04.00` and later, it returns an array so you can filter to the `paymentMethodType` that matches your flow (`apple-pay`, `push-to-apple-pay`, or `pull-from-apple-pay`).

## Prerequisites

- **Capabilities:** to accept payments, the merchant account needs `collect-funds.card-payments` and `wallet.balance`. To send payouts, the source account needs `send-funds.push-to-card` and `wallet.balance`. See [capabilities](/guides/accounts/capabilities/index.md).
- **Registered domain:** each merchant account (the transfer destination) must register and verify every domain that shows the Apple Pay button (step 1 below).
- **Access token scopes:** one Moov.js token with `/accounts/{merchantAccountID}/apple-pay.write` and `/accounts/{payerOrRecipientAccountID}/cards.write`. The payer or recipient account must exist before you request the token.
- **API version:** `v2026.04.00` or later to get the `push-to-apple-pay` and `pull-from-apple-pay` payment methods.
- **Website and server:** your site follows Apple's guidelines and your server meets Apple's requirements:
  
  - [Apple Pay website guidelines](https://developer.apple.com/apple-pay/acceptable-use-guidelines-for-websites/)
  - [Apple Pay server requirements](https://developer.apple.com/documentation/apple_pay_on_the_web/setting_up_your_server): every page with Apple Pay is served over HTTPS with a valid SSL certificate, and your server supports TLS 1.2 or later with one of the listed [cipher suites](https://support.apple.com/guide/security/tls-security-sec100a75d12/web)

> **Note:** Apple Pay is only available in production mode. Moov doesn't support Apple Pay in test mode.

## Register your domains

Any domains accepting payments must first be registered and verified with Apple. You can do this through the Moov Dashboard. Domains must be registered for each individual merchant account, which is the destination of the Moov transfer.

Within an account, navigate to the **Settings** tab and click the **Add a domain** button to add domains to Apple Pay.

[*Video: apple pay*](https://docs.moov.io/guides/shared-images/apple-pay.mp4)

To verify your domain, download the file and host it at the path shown for each domain you are registering. Ensure the file is in place before clicking **Add domain**. The file must be served exactly as it was downloaded from Apple. Any transformation will cause the verification to fail.

For example, the domain verification file should not be decoded or served via a redirect - both are common causes of failure. If you encounter any issues, refer to this Apple Pay [troubleshooting guide](https://developer.apple.com/documentation/technotes/tn3173-troubleshooting-issues-with-your-apple-pay-merchant-id-configuration) for guidance on common causes and resolutions.

You will now see a list of the domains you’ve added. You can come back to this page to remove or add domains.

## Add Apple Pay button

Using the browser-native [Payment Request API](https://developer.apple.com/documentation/apple_pay_on_the_web/payment_request_api), no additional libraries are needed to add an Apple branded button, since everything is supported within Safari. Alternatively, you can also use the [Apple Pay JS API](https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api).

> **Tip:** Use [Apple Pay JS API](https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api) to allow for support in non-Safari browsers.

Add a `button` element to your webpage and use the [Apple Pay documentation](https://developer.apple.com/documentation/apple_pay_on_the_web/displaying_apple_pay_buttons_using_css) to set the button type, and to see best practices on using CSS. For disbursements, we recommend using the `continue` button type from Apple.

Here’s sample code for adding a black **Buy with Apple Pay** button, using the default size and corner radius:

```html
<button class="apple-pay-button"></button>
<style>
  .apple-pay-button {
    -webkit-appearance: -apple-pay-button;
    border-radius: .5rem;
    height: 2.75rem;
    cursor: pointer;
  }
</style>
```

You can use the [canMakePayment()](https://developer.mozilla.org/en-US/docs/Web/API/PaymentRequest/canMakePayment) method to check if the device supports Apple Pay:

```javascript
const supportsApplePay = new PaymentRequest(
  [{ supportedMethods: "https://apple.com/apple-pay" }],
  details
).canMakePayment();

// Supports Apple Pay?
if (await supportsApplePay) {
  // show Apple Pay logo, for instance
  return;
}
```

## Set up the Payment Request API for Apple Pay

After adding the button, you can set up methods, details, and options in the Payment Request API.

### Methods

In **Methods**, indicate Apple Pay as a payment method by using:

- `"https://apple.com/apple-pay"`
- Apple Pay certified Moov account ID
- Capabilities
- Supported networks

For more details, read the [W3 documentation on Methods](https://www.w3.org/TR/payment-request/#paymentmethoddata-dictionary).

### Details

In **Details**, you can specify transaction details. For example, the transaction’s amount, shipping options, display items, and other modifiers. For Apple Pay transactions, Moov has a platform limit of $100,000.00 per transaction. If you need a higher limit, contact [Moov support](https://support.moov.io/).

For disbursements, use the `modifiers` array to pass an Apple Pay [disbursement request](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaydisbursementrequest), which surfaces a "Send" confirmation in the Apple Pay sheet and lets you collect recipient contact fields.

For more details, read the [W3 documentation on Details](https://www.w3.org/TR/payment-request/#paymentdetailsinit-dictionary).

### Options

In **Options**, you can include any specific customer details or shipping information.

For more details, read the [W3 documentation on Options](https://www.w3.org/TR/payment-request/#paymentoptions-dictionary).

**Accept payment**

For pay-ins, include `supportsCredit` and `supportsDebit` in `merchantCapabilities`, and list all card networks you intend to accept.

```javascript
// Sets up methods, details, and options for Payment Request API
const methods = [
  {
    supportedMethods: "https://apple.com/apple-pay",
    data: {
      version: 3,
      merchantIdentifier: "merchants-moov-account-id",
      merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"],
      supportedNetworks: ["amex", "discover", "masterCard", "visa"],
      countryCode: "US",
      requiredBillingContactFields: ["postalAddress"]
    },
  }
];

// Details about the purchase
const details = {
  total: {
    label: "Typically the merchant name",
    amount: { value: "1.00", currency: "USD" },
  }
};

const options = {
  requestPayerName: true,
  requestPayerEmail: true,
  requestPayerPhone: false
};
```

> **Tip:** Failing to include the `requiredBillingContactFields` with `postalAddress` may result in declines or interchange downgrades.

**Send payout**

For disbursements (OCT), include `supportsInstantFundsOut` in `merchantCapabilities` so Apple Pay only surfaces cards eligible for instant funds out, and restrict `supportedNetworks` to networks that support push to card (`visa`, `masterCard`). Pass an `ApplePayDisbursementRequest` via `modifiers` with `additionalLineItems` that include a `disbursement` line item (and optionally an `instantFundsOutFee` line item).

```javascript
const APPLE_PAY_METHOD_URL = "https://apple.com/apple-pay";

// Fields collected for the recipient receiving the funds
const requiredRecipientContactFields = ["name", "email", "postalAddress"];

const methods = [
  {
    supportedMethods: APPLE_PAY_METHOD_URL,
    data: {
      version: 14,
      merchantIdentifier: "merchants-moov-account-id",
      merchantCapabilities: ["supports3DS", "supportsDebit", "supportsInstantFundsOut"],
      supportedNetworks: ["visa", "masterCard"],
      countryCode: "US",
      currencyCode: "USD"
    }
  }
];

// Total the recipient will receive
const details = {
  total: {
    label: "Typically the merchant name",
    amount: { value: "1.00", currency: "USD" }
  },
  modifiers: [
    {
      supportedMethods: APPLE_PAY_METHOD_URL,
      data: {
        disbursementRequest: {
          requiredRecipientContactFields
        },
        additionalLineItems: [
          { label: "Payout", amount: "1.00" },
          {
            label: "Instant Funds Out Fee",
            amount: "0.00",
            disbursementLineItemType: "instantFundsOutFee"
          },
          {
            label: "Typically the merchant name",
            amount: "1.00",
            disbursementLineItemType: "disbursement"
          }
        ]
      }
    }
  ]
};

const options = {};
```

> **Tip:** Payment Request API version `14` or higher is required to use `disbursementRequest`. Apple Pay will only display cards that support instant funds out when `supportsInstantFundsOut` is set.

## Detect if Apple Pay is available

The Apple Pay button should only be displayed if your user is using a supported device. You can check if the browser will support ApplePaySession with the call below:

```javascript
if (window.PaymentRequest) {
  // If available, show Apple Pay button
}
```

Another option is to use Apple’s JS library [canMakePaymentsWithActiveCard](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778000-canmakepaymentswithactivecard) method to check if the device supports Apple Pay, and the user has an active card in their wallet. In the request below, use the merchant’s Moov `accountID` as the `merchantIdentifier`.

```javascript
if (window.ApplePaySession) {
  let merchantIdentifier = 'merchants-moov-account-id';
  let promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier);
  promise.then((canMakePayments) => {
    if (canMakePayments) {
        // Display Apple Pay button here.
    }
  });
}
```

Now that you have verified eligibility and added the button, you are ready to create a payment request with the previously defined options when the button is clicked:

```javascript
const paymentRequest = new PaymentRequest(methods, details, options);
```

## Create & validate a merchant session

Once the payment request has been created, it’s time to create and validate an Apple Pay merchant session using the Moov API’s create Apple Pay session `POST` [endpoint](/api/sources/apple-pay/session/index.md), or Moov.js method shown below:

```javascript
moov.accounts.applePay.sessions.create({ 
  accountID: 'merchants-moov-account-id', 
  applePaySessionConfig: { displayName: 'string' }
});
```

This request accepts the account ID and display name of the merchant that will be accepting the payment. The response is a promise that includes the opaque `merchantSession` response from Apple to be used to resolve the `onmerchantvalidation` step.

- `accountID` - Moov account ID of the merchant, which is the destination of the transfer
- `displayName` - Merchant name for display in the Apple Pay payment sheet, for example, “Whole Body Fitness”

> **Note:**
> 
> `moov` must be initialized with an access token generated by your backend. This token must include the `/accounts/{merchantAccountID}/apple-pay.write` scope, where `merchantAccountID` is the Moov account ID of the merchant accepting the payment.

> **Tip:** The `displayName` from the merchant session may be displayed as the merchant name in Apple’s native Wallet app. On the actual card statement, the `statementDescriptor` (or `dynamicDescriptor` for [approved accounts](/guides/accept-payments/cards/statement-descriptors/index.md#set-a-transfer-level-statement-descriptor)) will be displayed with the same logic as other card payments.

## Link an Apple Pay token

Link an Apple Pay token to the payer or recipient's Moov account using the Moov API's link Apple Pay token `POST` [endpoint](/api/sources/apple-pay/token/index.md), or Moov.js method shown below. The request accepts the account ID as well as the opaque `paymentResponse` from Apple.

On API `v2026.04.00` and later, the response is an **array** of payment methods — one per supported Apple Pay flow that could be created for the linked token. Filter the array by `paymentMethodType` to pick the `paymentMethodID` you need for your transfer.

**Accept payment**

For pay-ins, use the `apple-pay` payment method as the transfer source.

```javascript
const paymentMethods = await moov.accounts.applePay.tokens.create({
  accountID: 'payers-moov-account-id',
  applePayToken: paymentResponse.details
});

const paymentMethodID = paymentMethods.find(
  (pm) => pm.paymentMethodType === "apple-pay"
)?.paymentMethodID;
```

**Send payout**

For disbursements, use the `push-to-apple-pay` payment method (OCT) as the transfer destination. For approved AFT use cases, `pull-from-apple-pay` will also be present in the response.

```javascript
const paymentMethods = await moov.accounts.applePay.tokens.create({
  accountID: 'recipients-moov-account-id',
  applePayToken: paymentResponse.details
});

const paymentMethodID = paymentMethods.find(
  (pm) => pm.paymentMethodType === "push-to-apple-pay"
)?.paymentMethodID;
```

> **Note:** This call requires the access token to also include the `/accounts/{accountID}/cards.write` scope, where `accountID` is the Moov account ID of the payer (for pay-ins) or the recipient (for payouts). Since the two scopes reference different account IDs — the merchant for `sessions.create` and the payer or recipient for `tokens.create` — your backend must generate a single token with both scopes before initializing `moov`. This means the payer or recipient account must be created before the token is requested.

> **Tip:** On Moov API versions earlier than `v2026.04.00`, this endpoint returns a single payment method object (with `paymentMethodType: "apple-pay"`) rather than an array. If you're on an older version, upgrade to `v2026.04.00` to access `push-to-apple-pay` and `pull-from-apple-pay`.

## Create a transfer & dismiss payment sheet

Use the `paymentMethodID` you filtered from the link-token response as the transfer source (for pay-ins) or destination (for payouts) when calling Moov's create transfer `POST` [endpoint](/api/money-movement/transfers/create/index.md). No Moov.js method exists for creating a transfer.

Use the `X-Wait-For` [header](/guides/money-movement/events-and-statuses/index.md#create-transfer-responses) on the transfer request to receive a synchronous response so you can close out the payment sheet.

**Accept payment**

Apple Pay pay-ins are created the same as other card payments — the `apple-pay` payment method is the transfer `source`. To learn more about card payments with Moov, read our [Accept card payments](/guides/accept-payments/cards/online/index.md) use case guide.

```javascript
// Pseudo-code: instruct your server to make the transfer
const moovTransferResponse = await fetch(`/pay`, {
  method: 'post',
  body: JSON.stringify({ paymentMethodID })
});

let status;
if (moovTransferResponse.source.cardDetails.status === 'confirmed') {
  status = 'success';
} else {
  status = 'fail';
}

try {
  paymentResponse.complete(status);
  // Payment sheet is dismissed
} catch (error) {
  console.error(error);
}
```

**Send payout**

Use the `push-to-apple-pay` payment method as the transfer `destination`. Otherwise, Apple Pay payouts follow the same source and capability requirements as any [push to card](/guides/send-payments/push-to-card/index.md) transfer.

```javascript
// Pseudo-code: instruct your server to make the payout
const moovTransferResponse = await fetch(`/payout`, {
  method: 'post',
  body: JSON.stringify({ paymentMethodID })
});

let status;
if (moovTransferResponse.destination.cardDetails.status === 'completed') {
  status = 'success';
} else {
  status = 'fail';
}

try {
  paymentResponse.complete(status);
  // Payment sheet is dismissed
} catch (error) {
  console.error(error);
}
```

> **Tip:** Moov has a transfer limit of $50,000 per `push-to-apple-pay` transaction. Network velocity limits also apply — see the [push to card guide](/guides/send-payments/push-to-card/index.md#velocity-limits) for details.

A pay-in is authorized when `source.cardDetails.status` is `confirmed`; a payout is delivered when `destination.cardDetails.status` is `completed`. Subscribe to the `transfer.updated` webhook to track the transfer after the sheet closes. See [card transfer statuses](/guides/accept-payments/cards/timing/index.md#card-transfer-statuses) and, to undo a payment, [reversals](/guides/accept-payments/cards/reversals/index.md).

## Full example

The tabs below combine the steps into one Apple Pay sample, for accepting a payment or sending a payout.

**Accept payment**

```javascript
// Sets up methods, details, and options for Payment Request API
const methods = [
  {
    supportedMethods: "https://apple.com/apple-pay",
    data: {
      version: 3,
      merchantIdentifier: "merchants-moov-account-id",
      merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"],
      supportedNetworks: ["amex", "discover", "masterCard", "visa"],
      countryCode: "US",
    },
  }
];

const details = {
  total: {
    label: "Typically the merchant name",
    amount: { value: "1.00", currency: "USD" },
  }
};

const options = {
  requestPayerName: true,
  requestPayerEmail: true,
  requestPayerPhone: false
};

// Your backend generates a single access token with both required scopes:
//   /accounts/{merchantAccountID}/apple-pay.write
//   /accounts/{payerAccountID}/cards.write
// The payer account must already exist before this token is requested.
const { access_token } = await fetch('/token').then(r => r.json());
const moov = Moov(access_token);

// Assumes a button click handler
async function handleApplePayClick(event) {
  event.preventDefault();
  try {
    // Uses native Payment Request API and passes the options we configured above
    const paymentRequest = new PaymentRequest(methods, details, options);

    // Event is fired as soon as the payment sheet is opened
    paymentRequest.onmerchantvalidation = async (event) => {
      const merchantSession = await moov.accounts.applePay.sessions.create({ accountID: 'merchant-uuid', applePaySessionConfig: { displayName: 'Merchant name' }});
      event.complete(merchantSession);
    };

    // Display the payment sheet via Payment Request API
    const paymentResponse = await paymentRequest.show();

    // On v2026.04.00+, tokens.create() returns an array of payment methods.
    // Filter by `apple-pay` to get the pay-in paymentMethodID.
    const paymentMethods = await moov.accounts.applePay.tokens.create({
      accountID: 'payer-uuid',
      applePayToken: paymentResponse.details
    });
    const paymentMethodID = paymentMethods.find(
      (pm) => pm.paymentMethodType === "apple-pay"
    )?.paymentMethodID;

    // Pseudo-code: instruct your server to make the transfer. X-Wait-For header is needed to get response.
    const moovTransferResponse = await fetch(`/pay`, {
      method: 'post',
      body: JSON.stringify({ paymentMethodID })
    });

    let status;
    if (moovTransferResponse.source.cardDetails.status === 'confirmed') {
      status = 'success';
    } else {
      status = 'fail';
    }

    // Completes the payment request process and closes the payment sheet
    paymentResponse.complete(status);
    // Payment sheet is dismissed
  } catch (error) {
    console.error(error);
  }
};
```

**Send payout**

```javascript
const APPLE_PAY_METHOD_URL = "https://apple.com/apple-pay";

// Fields collected for the recipient receiving the funds
const requiredRecipientContactFields = ["name", "email", "postalAddress"];

// Sets up methods, details, and options for Payment Request API
const methods = [
  {
    supportedMethods: APPLE_PAY_METHOD_URL,
    data: {
      version: 14,
      merchantIdentifier: "merchants-moov-account-id",
      merchantCapabilities: ["supports3DS", "supportsDebit", "supportsInstantFundsOut"],
      supportedNetworks: ["visa", "masterCard"],
      countryCode: "US",
      currencyCode: "USD"
    }
  }
];

const details = {
  total: {
    label: "Typically the merchant name",
    amount: { value: "1.00", currency: "USD" }
  },
  modifiers: [
    {
      supportedMethods: APPLE_PAY_METHOD_URL,
      data: {
        disbursementRequest: {
          requiredRecipientContactFields
        },
        additionalLineItems: [
          { label: "Payout", amount: "1.00" },
          {
            label: "Instant Funds Out Fee",
            amount: "0.00",
            disbursementLineItemType: "instantFundsOutFee"
          },
          {
            label: "Typically the merchant name",
            amount: "1.00",
            disbursementLineItemType: "disbursement"
          }
        ]
      }
    }
  ]
};

const options = {};

// Your backend generates a single access token with both required scopes:
//   /accounts/{merchantAccountID}/apple-pay.write
//   /accounts/{recipientAccountID}/cards.write
// The recipient account must already exist before this token is requested.
const { access_token } = await fetch('/token').then(r => r.json());
const moov = Moov(access_token);

// Assumes a button click handler
async function handleApplePayDisbursementClick(event) {
  event.preventDefault();
  try {
    const paymentRequest = new PaymentRequest(methods, details, options);

    paymentRequest.onmerchantvalidation = async (event) => {
      const merchantSession = await moov.accounts.applePay.sessions.create({
        accountID: 'merchant-uuid',
        applePaySessionConfig: { displayName: 'Merchant name' }
      });
      event.complete(merchantSession);
    };

    const paymentResponse = await paymentRequest.show();

    // Returns all supported Apple Pay payment methods for this token.
    // Filter by `push-to-apple-pay` to get the OCT destination paymentMethodID.
    const paymentMethods = await moov.accounts.applePay.tokens.create({
      accountID: 'recipient-uuid',
      applePayToken: paymentResponse.details.token
    });
    const paymentMethodID = paymentMethods.find(
      (pm) => pm.paymentMethodType === "push-to-apple-pay"
    )?.paymentMethodID;

    // Pseudo-code: instruct your server to make the payout. X-Wait-For header is needed to get response.
    const moovTransferResponse = await fetch(`/payout`, {
      method: 'post',
      body: JSON.stringify({ paymentMethodID })
    });

    let status;
    if (moovTransferResponse.destination.cardDetails.status === 'completed') {
      status = 'success';
    } else {
      status = 'fail';
    }

    paymentResponse.complete(status);
    // Payment sheet is dismissed
  } catch (error) {
    console.error(error);
  }
};
```

For more information, refer to [Apple’s documentation](https://developer.apple.com/documentation/apple_pay_on_the_web/payment_request_api/setting_up_the_payment_request_api_to_accept_apple_pay/) and the [Apple Pay disbursement request reference](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaydisbursementrequest).

