Skip to main content

Collect Apple Pay

In this guide, we will set up Credova Elements SDKs to capture Apple Pay in a frontend application and securely authorize and store the Apple Pay data while avoiding PCI requirements.

Getting Started

To get started, you will need a Credova Account.

Get your Publishable Key

Next you will need your Publishable Key. Go to your Developers section and click Reveal for your Publishable Key and copy the value.

Save the Publishable Key as it will be used in the next steps of this guide.

Domain Verification

Domain verification is a key step in getting Apple Pay up and running on your site. It is a required step for development and production servers, to confirm that you own the domain where Apple Pay will be implemented, preventing unauthorized use and maintaining the integrity of the payment system.

  1. Serve the domain association file on your server at https://<YOUR_DOMAIN>/.well-known/apple-developer-merchantid-domain-association by proxying that path to the domain association endpoint and returning the response as-is.
  2. Add the public domain to your tenant using the merchant portal or the API

Apple requires the website to be served via HTTPS, which can be tricky during the development phase. Tunneling may represent a good option to preview local projects through a working HTTPS connection. Cloudflare tunnel may be worth checking out.

Retrieve the domain association file

The API serves the Apple-issued domain association file for your account, so you no longer need to download and host a static copy yourself. Instead, have your server fetch it and return it at the well-known path Apple expects:

GET https://api.publicsquare.com/.well-known/apple-developer-merchantid-domain-association

Authentication: Requires an API key with the payment-method:capture permission, sent in the X-API-KEY header. Your Publishable Key already has this permission, so no additional key is needed.

Retrieve the domain association file
curl "https://api.publicsquare.com/.well-known/apple-developer-merchantid-domain-association" \
-H "X-API-KEY: <PUBLISHABLE_API_KEY>"

The returned file is processor-specific: the API automatically returns the domain association file matching whichever processor is currently set as your account's default processor, so you don't need to specify one.

StatusDescription
200Returns the domain association file contents (Content-Type: text/plain) for your account's default processor.
404No domain association file is configured for your account's default processor. Contact support if you believe this is unexpected.

Proxy this request rather than caching a static copy of the file. If your account's default processor ever changes, the file this endpoint returns updates automatically, so proxying keeps the file Apple verifies against always current.

Configuring Credova Elements

Credova Elements are available for the following technologies. Click below for detailed instructions on how to install and configure them.

Javascript
React

Creating an Apple Pay session

To initiate the Apple Pay payment modal, an Apple Pay session must be created.

Configure Apple Pay

First, let's add the Apple Pay button and initiate the Apple Pay session.

index.html
<div ref={buttonContainerRef}></div>
index.js
import { PublicSquare } from "@publicsquare/elements-js";

declare global {
var ApplePaySession: any;
}

let publicsquare;

async function init() {
publicsquare = await new PublicSquare().init('<PUBLISHABLE_API_KEY>');

// Step 1: Render the Apple Pay button
publicsquare.applePay.renderButton(buttonContainerRef.current!, {
id: 'apple-pay-btn',
onClick: onSubmitApplePay
});

function onSubmitApplePay() {
if (!ApplePaySession) {
return;
}

// Step 2: Create an Apple Pay session
const session = createApplePaySession();

// Step 3: Validate the CSR hosted at the your website
session.onvalidatemerchant = async () => {
const merchantSession = await validateMerchant();
session.completeMerchantValidation(merchantSession);
};

session.begin();
}

function createApplePaySession() {
return new ApplePaySession(3, {
countryCode: 'US',
currencyCode: 'USD',
merchantCapabilities: ['supports3DS'],
supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
requiredBillingContactFields: ['postalAddress', 'phone'],
total: {
label: 'Demo (Card is not charged)',
type: 'final',
amount: '1.99',
},
});
}

async function validateMerchant() {
try {
return await publicsquare?.applePay.createSession({
display_name: 'eCommerce Merchant Store',
domain: window.location.host,
});
} catch (error) {
console.error('Error validating merchant:', error);
throw error;
}
}
}

await init();

Adding the code above will render an Apple Pay button on your site with an onClick event which will start an Apple Pay session.

The createApplePaySession function creates a new Apple Pay session with information about the payment.

The validateMerchant function calls Public Square to initiate the Apple Pay session by checking the presence of the Apple Pay CSR configured during the Domain Verification step. This returns an Apple Pay session instance which is valid for 30 seconds.

Billing and Shipping Addresses

This behavior requires Credova Elements SDKs v1.15.2 or later.

Apple Pay should always collect a billing address and phone number from the customer — this is required to create the payment method.

Set requiredBillingContactFields: ['postalAddress', 'phone'] on the Apple Pay payment request passed to ApplePaySession, as shown in the Configure Apple Pay step above.
When the customer authorizes the payment, Apple returns this information as event.payment.billingContact, and is automatically stored as the payment method's billing_details when you create the payment method.

Apple Pay collects the billing address in its FULL format, meaning customers see and enter a complete street address (not just a postal code), along with a billing phone number, and is stored on the payment method.

Shipping address collection is optional and off by default. Enable it with the requiredShippingContactFields option on the same payment request:

OptionTypeDefaultDescription
requiredBillingContactFieldsApplePayContactField[](required)Must include 'postalAddress' and 'phone'. Apple Pay always collects a billing address and phone number. Optionally 'phone', 'name' and/or 'email'.
requiredShippingContactFieldsApplePayContactField[]undefinedWhen set, the Apple Pay sheet also collects a shipping address. Include 'postalAddress' to collect the address, and optionally 'phone', 'name' and/or 'email'.
index.js
function createApplePaySession() {
return new ApplePaySession(3, {
countryCode: 'US',
currencyCode: 'USD',
merchantCapabilities: ['supports3DS'],
supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
requiredBillingContactFields: ['postalAddress', 'phone'],
requiredShippingContactFields: ['postalAddress'],
total: {
label: 'Demo (Card is not charged)',
type: 'final',
amount: '1.99',
},
});
}

When requiredShippingContactFields is set, the shipping address is returned as event.payment.shippingContact. We don't store the shipping address on the payment method, pass it along as shipping_address when you create the payment. The billing address is already stored on the payment method from the earlier tokenization step, so you don't need to read it again here.

index.js
session.onpaymentauthorized = async (event: any) => {
const shippingContact = event.payment.shippingContact;

const shippingAddressPayload = shippingContact && {
address_line_1: shippingContact.addressLines?.[0],
address_line_2: shippingContact.addressLines?.[1],
city: shippingContact.locality,
state: shippingContact.administrativeArea,
postal_code: shippingContact.postalCode,
country: shippingContact.countryCode
};

const paymentMethod = await createApplePayPaymentMethod(event);
session.completePayment(ApplePaySession.STATUS_SUCCESS);

await chargePayment(paymentMethod.id, {
shipping_address: shippingAddressPayload
});
};

Tokenization and Storing Apple Pay

After the customer has authorized the payment, your application will need to call our create method from the SDK with the encrypted Apple Pay token to create an Apple Pay payment method in your account.

To do this, we will handle the session.onpaymentauthorized event to call Public Square, passing the Apple Pay payment data:

index.js
import { PublicSquare } from "@publicsquare/elements-js"

declare global {
var ApplePaySession: any;
}

let publicsquare;

async function init() {
publicsquare = await new PublicSquare().init('<PUBLISHABLE_API_KEY>');

publicsquare.applePay.renderButton(buttonContainerRef.current!, {
id: 'apple-pay-btn',
onClick: onSubmitApplePay
});

function onSubmitApplePay() {
if (!ApplePaySession) {
return;
}

const session = createApplePaySession();

session.onvalidatemerchant = async () =>{ ... };

session.onpaymentauthorized = async (event: any) => {
try {
// Create an Apple Pay payment method from the encrypted payment token
const paymentMethod = await createApplePayPaymentMethod(event);

// Present green check to the user before the timeout (30 seconds)
session.completePayment(ApplePaySession.STATUS_SUCCESS);

// Send the payment method to the backend for payment processing
await chargePayment(paymentMethod.id);
} catch (e) {
console.error(e);
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
};

session.begin();
}

function createApplePaySession() { ... }

async function validateMerchant() { ... }

async function createApplePayPaymentMethod(event: any) {
if (publicsquare) {
try {
const response = await publicsquare.applePay.create({
apple_payment_data: event.payment,
});
if (response) {
return response;
}
} catch (error) {
console.log(error);
}
}
}
}

await init();

The created Apple Pay object contains the non-sensitive information about the Apple Pay:

{
"id": "appl_Aw3U2eESBnq6DoYEFn8qaSMrA",
"account_id": "acc_B518niGwGYKzig6vtrRVZGGGV",
"environment": "test",
"customer_id": "cus_7Ay5mcUXAxwrN6wQEQUVEHBCJ",
"token_type": "dpan",
"last4": "4242",
"exp_month": "12",
"exp_year": "2025",
"brand": "visa",
"billing_details": {
"address_line_1": "111 Colorado Ave.",
"address_line_2": "Apt 403",
"city": "Des Moines",
"state": "IA",
"postal_code": "51111",
"country": "US"
},
"expires_at": "2024-06-31T01:02:29.212Z",
"created_at": "2024-06-30T01:02:29.212Z",
"modified_at": "2024-06-30T01:02:29.212Z"
}

You can safely store the Apple Pay payment method id value in your database to use it to process a payment or associate it with a customer.

You can optionally pass in a customer_id to associate with an existing Created Customer. The billing_details shown above always comes from the Apple Pay sheet (see Billing and Shipping Addresses) — there's no need to pass them yourself, and doing so has no effect.

Set requiredBillingContactFields: ['postalAddress', 'phone'] on the Apple Pay create session is required when creating an Apple Pay payment method. Requests without it will fail validation.

Conclusion

Following this guide, you have successfully collected a customer's Apple Pay. To use this Apple Pay payment method, proceed with the Process Apple Pay Payments guide.