Accept & send payments with Apple Pay
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. |
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
Before getting started, make sure your website follows Apple’s guidelines, and that your server is set up accordingly.
- Apple Pay website guidelines
- Apple Pay server requirements
- 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 listed
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 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, 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.
Add a button element to your webpage and use the Apple Pay documentation 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:
<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() method to check if the device supports Apple Pay:
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.
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.
For disbursements, use the modifiers array to pass an Apple Pay disbursement request, 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.
Options
In Options, you can include any specific customer details or shipping information.
For more details, read the W3 documentation on Options.
For pay-ins, include supportsCredit and supportsDebit in merchantCapabilities, and list all card networks you intend to accept.
// 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
};
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).
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 = {};
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:
if (window.PaymentRequest) {
// If available, show Apple Pay button
}
Another option is to use Apple’s JS library 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.
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:
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, or Moov.js method shown below:
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 transferdisplayName- 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.
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) 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, 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.
For pay-ins, use the apple-pay payment method as the transfer source.
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.
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;
/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.
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. No Moov.js method exists for creating a transfer.
Use the X-Wait-For header on the transfer request to receive a synchronous response so you can close out the payment sheet.
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 case guide.
// 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 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 enabled for funds disbursement use cases.
// 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);
}
push-to-apple-pay transaction. Network velocity limits also apply — see the push to card guide for details.
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.
// 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);
}
};
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 and the Apple Pay disbursement request reference.