# Accept & send payments with Apple Pay

Moov allows you to both accept card payments and send payouts with Apple Pay. This step by step guide covers how you can start using Apple Pay on the web using the open standard Payment Request API.

In addition to being a fast and easy way to move money, Apple Pay offers strong privacy and security. In this guide, we’ll direct you to Apple’s guidelines, and bring you through a step by step process of adding a functional Apple Pay button for either accepting a payment or sending 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. |

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](#prerequisites)

Before getting started, make sure your website follows Apple’s guidelines, and that your server is set up accordingly.

- [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)
  
  - All pages that include Apple Pay must be served over HTTPS
  - Your domain must have a valid SSL certificate
  - Your server must support the Transport Layer Security (TLS) protocol version 1.2 or later, and one of the [cipher suites](https://support.apple.com/guide/security/tls-security-sec100a75d12/web) listed

Apple Pay is only available in production mode. Moov does not offer Apple Pay in test mode at this time.

## [Register your domains](#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.

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**. Note that serving the domain verification file via a redirect is a common cause 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](#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).

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](#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](#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](#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 for your use-case, please 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](#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](#tab-865472931-0-0) [Send payout](#tab-865472931-0-1)

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
};
```

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

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 = {};
```

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](#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 &amp; validate a merchant session](#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/), 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”

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

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/money-movement/accept-payments/card-acceptance/statement-descriptors/#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)

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/), 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](#tab-284736951-0-0) [Send payout](#tab-284736951-0-1)

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;
```

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;
```

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.

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 &amp; dismiss payment sheet](#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/). No Moov.js method exists for creating a transfer.

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

[Accept payment](#tab-578416923-0-0) [Send payout](#tab-578416923-0-1)

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](/use-cases/card-acceptance/) 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);
}
```

Apple Pay payouts are created the same as other [push to card](/guides/money-movement/send-payments/push-to-card/) transfers — the `push-to-apple-pay` payment method is the transfer `destination`, and the source is a Moov wallet with sufficient funds. The source account must have the `send-funds.push-to-card` [capability](/guides/accounts/capabilities/enablement/) enabled for funds disbursement use cases.

```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);
}
```

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/money-movement/send-payments/push-to-card/#transfer--velocity-limits) for details.

## [Seeing it altogether](#seeing-it-altogether)

Here’s a sample that synthesizes the steps above all in one place. Switch tabs to see the full flow for either accepting a payment or sending a payout.

[Accept payment](#tab-378916245-5-0) [Send payout](#tab-378916245-5-1)

```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);
  }
};
```

```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).
