How to: Accept cards with Apple Pay

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

In addition to being a fast and easy way to accept payments, 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.

Prerequisites

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

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

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.

Apple Pay section in Settings view of Moov Dashboard

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.

Apple Pay add a domain modal in Moov Dashboard

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

List of domains added to Apple Pay in Moov Dashboard

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.

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

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

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 transfer
  • displayName - Merchant name for display in the Apple Pay payment sheet, for example, “Whole Body Fitness”
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) will be displayed with the same logic as other card payments.

Link an Apple Pay token to the payer’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 of the payer as well as the opaque paymentResponse from Apple.

moov.accounts.applePay.tokens.create({ 
  accountID: 'payers-moov-account-id', 
  applePayToken: paymentResponse.details
});

Create a transfer & dismiss payment sheet

The response payload from linking a token includes a paymentMethodID that is used as the source for the transfer in Moov’s create transfer POST endpoint. No Moov.js method exists for creating a transfer.

Besides the apple-pay source payment method type, Apple Pay transfers are created the same as other card payments. 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: paymentMethodID })
});

Use the X-Wait-For header on the transfer request to receive a synchronous response from the card network to close out the payment sheet.

let status;
if (moovTransferResponse.source.cardDetails.status === 'confirmed') {
  status = 'success';
} else {
  status = 'fail';
}
// Completes the payment request process and closes the payment sheet
try {
  paymentResponse.complete(status);
  // Payment sheet is dismissed
} catch (error) {
  console.error(error);
}

Seeing it altogether

Here’s a sample that synthesizes the steps above all in one place:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// 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
};

// 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 = (event) => {
      // Note: token with scopes to include this merchant account for use with Moov.js or any client-side integration
      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();
    /* Note: token with scopes should include this payer account for use with Moov.js or any client-side integration
      moov.accounts.applePay.tokens.create() will return a promise of the token, including. the paymentMethodID
    */
    const { paymentMethodID } = await moov.accounts.applePay.tokens.create({ accountID: 'payer-uuid', applePayToken: paymentResponse.details });

    // 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: 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);
  }
};

For more information, refer to Apple’s documentation.

Summary Beta