Integration
This guide walks you through embedding the Apple Pay Express Checkout button on your page. The flow is:
- Load the component script.
- Add a container element.
- Define event handlers (including the Express-Checkout-specific shipping events).
- Build the in-sheet shipping
payloadand thepaymentDataobject. - Call
init(options).
For a copy-paste-ready end-to-end snippet, jump to the Full Example.
Step 1: Add the Apple Pay Component script
Include the component bundle in the <head> of your HTML page.
Sandbox / Test:
<script src='https://test-pay.dnapayments.com/components/apple-pay/apple-pay-component.js'></script>
Production:
<script src='https://pay.dnapayments.com/components/apple-pay/apple-pay-component.js'></script>
The script exposes window.DNAPayments.ApplePayComponent.
Step 2: Create the container element
<div id='apple-pay-btn-container' style='width: 100%; height: 46px;'></div>
Apply the desired width and height to the container: the button takes the container's dimensions.
Step 3: Gate the render with isAvailable()
DNAPayments.ApplePayComponent.isAvailable() returns a Promise<boolean> that resolves to true only when the device supports Apple Pay and can make payments. Always gate init() behind it so non-supported devices (Android, unsupported browsers) never see a broken button:
async function renderApplePay() {
if (!await window.DNAPayments.ApplePayComponent.isAvailable()) {
return; // device cannot make Apple Pay payments
}
// ...continue with init() below
}
Step 4: Build the in-sheet shipping payload
Express Checkout collects the shipping address and delivery method inside the Apple Pay sheet. Tell the sheet which contact fields to ask for and which shipping methods to offer up-front via the payload option:
const payload = {
requiredShippingContactFields: ['postalAddress', 'name', 'email', 'phone'],
requiredBillingContactFields: ['postalAddress'],
shippingMethods: [
{ label: 'Free Shipping', detail: 'Arrives in 5–7 days', identifier: 'FreeShip', amount: '0.00' },
{ label: 'Fast Shipping', detail: 'Arrives in 2 days', identifier: 'FastShip', amount: '5.00' },
{ label: 'Collect yourself', detail: 'Collect today', identifier: 'Collect', amount: '0.00' }
]
};
See payload reference for the full field list.
Step 5: Define event handlers
Express Checkout adds two shipping events on top of the standard set. The full list is in the Events section.
async function onShippingContactSelected(resolve, reject, event) {
const { countryCode } = event.shippingContact;
if (countryCode !== 'GB') {
// Reject delivery to unsupported countries
resolve({
newTotal: { label: 'My Store', amount: paymentData.amount, type: 'final' },
errors: [new ApplePayError('shippingContactInvalid', 'countryCode',
'Cannot ship to the selected country')]
});
return;
}
// Recompute available shipping methods for this address
resolve({
newTotal: { label: 'My Store', amount: paymentData.amount, type: 'final' },
newShippingMethods: [
{ label: 'Fast Shipping', detail: 'Arrives in 2 days', identifier: 'FastShip', amount: '5.00' },
{ label: 'Collect yourself', detail: 'Collect today', identifier: 'Collect', amount: '0.00' }
]
});
}
async function onShippingMethodSelected(resolve, reject, event) {
const cost = parseFloat(event.shippingMethod.amount);
resolve({
newTotal: {
label: 'My Store',
amount: String(parseFloat(paymentData.amount) + cost),
type: 'final'
}
});
}
const events = {
onClick: () => { /* ... */ },
onShippingContactSelected,
onShippingMethodSelected,
onBeforeProcessPayment: (payment) => { /* ... */ },
onPaymentSuccess: (result) => { /* ... */ },
onCancel: () => { /* ... */ },
onError: (error) => { /* ... */ }
};
Step 6: Build paymentData
paymentData carries the order details. Top-level fields and nested structures are described in paymentData reference.
const paymentData = {
amount: 24,
currency: 'GBP',
invoiceId: 'YOUR_INVOICE_ID',
description: 'Shoes',
paymentSettings: {
terminalId: 'YOUR_TERMINAL_ID',
callbackUrl: 'https://example.com/callback',
failureCallbackUrl: 'https://example.com/failure-callback'
},
customerDetails: {
accountDetails: { accountId: 'uuid000001' },
billingAddress: { country: 'GB' }
},
orderLines: [{
name: 'Running shoe',
quantity: 1,
unitPrice: 24,
taxRate: 20,
totalAmount: 24,
totalTaxAmount: 4,
imageUrl: 'https://www.example.com/logo.png',
productUrl: 'https://www.example.com/AD6654412.html'
}]
};
Step 7: Initialize the button
Pass the container, paymentData, payload, events and your access token to init():
async function renderApplePay() {
if (!await window.DNAPayments.ApplePayComponent.isAvailable()) return;
window.DNAPayments.ApplePayComponent.init({
containerElement: document.getElementById('apple-pay-btn-container'),
paymentData,
payload,
events,
token: 'YOUR_ACCESS_TOKEN',
});
}
window.addEventListener('load', renderApplePay);
The OAuth token endpoint returns an object (access_token, expires_in, token_type, ...). Pass only the access_token string to the component. See Authentication for the token flow and the scopes required for the seamless component.
API reference
The component exposes the following functions on window.DNAPayments.ApplePayComponent:
| Method | Description |
|---|---|
init(options) | Render the button and return the component instance. |
isAvailable() | Promise<boolean>: true if the device can make Apple Pay payments. |
create(...) | ⚠️ Deprecated: use init() instead. |
isAvailable()
const ok = await window.DNAPayments.ApplePayComponent.isAvailable();
Resolves to true if Apple Pay is supported and the device can make payments.
init(options)
const component = window.DNAPayments.ApplePayComponent.init(options);
Renders the Apple Pay Express Checkout button into containerElement and returns the initialized component instance.
options fields
| Property | Type | Required | Description |
|---|---|---|---|
containerElement | HTMLElement | ✅ | DOM node where the button is rendered. |
events | object | ✅ | Event handler map: see Events. |
paymentData | object | ✅ | Payment request: see paymentData reference. Must include at least amount and currency so the Apple Pay sheet can display the total. |
token | string | ✅ | OAuth2 access token (the access_token string returned by Authentication). |
payload | object | ✅ | In-sheet shipping configuration: see payload reference. Required for Express Checkout flow. |
environment | string | - | Either 'sandbox' or 'production'. Optional, defaults to the environment baked into the bundle. |
cardBrands | string[] | - | Restrict accepted card networks. Defaults to ['VISA', 'MASTERCARD']. Allowed values: 'VISA', 'MASTERCARD', 'AMEX', 'DISCOVER', 'INTERAC', 'JCB'. |
locale | string | - | Locale applied to the Apple Pay button (e.g. 'en-GB', 'de-DE'). Defaults to 'en-GB'. Does not affect the Apple Pay sheet: the sheet always follows the user's device language. |
domainName | string | - | Override for the domain used for merchant validation. Pass this only if the SDK's auto-detected host does not match the domain registered in the portal. The provided host must be registered in the relevant portal (see Prerequisites). |
create(...) (deprecated)
create() is kept for backwards compatibility and will be removed in a future version. Use init(options) instead.
Migration:
// Before: positional arguments (5 parameters for Express Checkout)
window.DNAPayments.ApplePayComponent.create(
container, paymentData, events, token, payload
);
// After: single options object
window.DNAPayments.ApplePayComponent.init({
containerElement: container,
paymentData, events, token, payload
});
Events
The component emits the following events. All handlers are attached via the events map passed to init().
| Event | Signature | Fires when |
|---|---|---|
onClick | () => void | The user taps the Apple Pay button, before the sheet is shown. |
onLoad | () => void | Fires after the button has been rendered and mounted into containerElement. Does not fire on devices where Apple Pay is unavailable: in that case init() simply renders nothing. Use isAvailable() to check support before relying on this event. |
onShippingContactSelected | async (resolve, reject, event) => void | The user selected or changed a shipping address inside the sheet. Call resolve(update) with new totals / line items / shipping methods, or resolve(update) with errors for unsupported addresses. Call reject(error) only on unexpected failures of your own infrastructure (e.g. tax/shipping API is down). See the note below. |
onShippingMethodSelected | async (resolve, reject, event) => void | The user selected a shipping method inside the sheet. Call resolve(update) with the new total. Call reject(error) only on unexpected failures of your own infrastructure. See the note below. |
onBeforeProcessPayment | async function | Fires after the user authorizes the payment but before the component executes it on the backend. The payment argument contains the full Apple Pay token plus the billing/shipping contacts the customer entered in the sheet. |
onPaymentSuccess | (result) => void | The payment has been processed successfully. See onPaymentSuccess payload. |
onError | (error) => void | Any error during initialization or processing. See onError payload and Handling Error Codes. |
onCancel | () => void | The user dismisses the Apple Pay sheet. |
onShippingContactSelected and onShippingMethodSelected: update object
resolve(update) accepts an object with the following fields:
| Field | Type | Description |
|---|---|---|
newTotal | LineItem | The new total to show in the sheet. |
newLineItems | LineItem[] | Optional, replaces the line items shown in the sheet. |
newShippingMethods | ShippingMethod[] | Optional, replaces the shipping methods offered in the sheet (only applicable in onShippingContactSelected). |
errors | ApplePayError[] | Optional, surface validation errors against specific fields (e.g. unsupported country). |
LineItem
| Field | Type | Required | Description |
|---|---|---|---|
label | string | ✅ | Text shown to the customer (e.g. 'Total', 'Shoes'). |
amount | string | ✅ | Decimal amount as a string (e.g. '24.00'). Apple Pay rejects numeric values here. |
type | 'final', 'pending' | - | 'final' for known amounts, 'pending' if the value is still being calculated. Defaults to 'final'. |
ShippingMethod
| Field | Type | Required | Description |
|---|---|---|---|
label | string | ✅ | Short name shown in the sheet (e.g. 'Fast Shipping'). |
detail | string | ✅ | Longer description (e.g. 'Arrives in 2 days'). |
amount | string | ✅ | Cost as a decimal string ('5.00'). Use '0.00' for free shipping. |
identifier | string | ✅ | Stable ID you receive back in event.shippingMethod.identifier inside onShippingMethodSelected. |
ApplePayError
ApplePayError is Apple's global class, available on every Apple Pay-capable device: no import needed.
new ApplePayError(errorCode, contactField?, message?)
Common usage in the shipping flow:
new ApplePayError('shippingContactInvalid', 'countryCode', 'Cannot ship to the selected country');
new ApplePayError('addressUnserviceable', undefined, 'No couriers cover this postcode');
For the full list of allowed values see Apple's reference: ApplePayErrorCode, ApplePayErrorContactField.
resolve with errors, not rejectValidation errors (unsupported country, invalid postal code, no shipping methods for the address) must be returned via resolve({ errors: [...] }) so Apple Pay can surface a field-level message inside the open sheet and let the customer correct it.
reject(...) is reserved for unexpected failures of your own infrastructure (e.g. tax/shipping API is down). It does not display your error message to the customer, so always prefer resolve({ errors: [...] }) for anything you want the customer to see and act on.
onPaymentSuccess payload
{
id: string,
reference: string,
success: boolean
}
The object may include additional fields when applicable.
onError payload
{
code: number,
message: string,
additionalInfo?: any
}
additionalInfo carries the underlying cause when available (the original exception, or the response body from the DNA Payments backend), useful for logging and debugging. See Handling Error Codes for the full list of code values.
paymentData reference
The most commonly used fields of paymentData:
| Field | Required | Description |
|---|---|---|
amount | ✅ | Total transaction amount as a decimal number. The customer can adjust this via the shipping flow inside the sheet. |
currency | - | ISO 4217 currency code (e.g. 'GBP'). Defaults to 'GBP'. |
invoiceId | - | Your internal order / invoice identifier. |
description | - | Human-readable description shown on receipts. |
paymentSettings.terminalId | ✅ | Your DNA Payments terminal ID. |
paymentSettings.callbackUrl | - | Server-to-server success callback. |
paymentSettings.failureCallbackUrl | - | Server-to-server failure callback. |
customerDetails.accountDetails.accountId | - | Stable customer ID on your side. |
customerDetails.email | - | Customer email (the SDK populates this from the Apple Pay sheet contacts at authorization). |
customerDetails.mobilePhone | - | Customer phone (same, populated from the sheet). |
customerDetails.billingAddress.country | - | ISO 3166-1 alpha-2 country code shown in the Apple Pay sheet (e.g. 'GB'). Defaults to 'GB'. |
orderLines | - | Array of line items (name, quantity, unitPrice, taxRate, totalAmount, totalTaxAmount, imageUrl, productUrl). |
payload reference
payload is required in Express Checkout: it tells the Apple Pay sheet which contact fields to collect and which shipping methods to offer.
| Field | Type | Description |
|---|---|---|
requiredBillingContactFields | string[] | Contact fields to collect for billing, e.g. ['postalAddress']. |
requiredShippingContactFields | string[] | Contact fields to collect for shipping, e.g. ['postalAddress', 'name', 'email', 'phone']. |
shippingMethods | object[] | Initial list of shipping methods shown in the sheet. Each item: { label, detail, identifier, amount }. |
const payload = {
requiredShippingContactFields: ['postalAddress', 'name', 'email', 'phone'],
requiredBillingContactFields: ['postalAddress'],
shippingMethods: [
{ label: 'Standard', detail: '5–7 business days', identifier: 'standard', amount: '5.00' },
{ label: 'Express', detail: '1–2 business days', identifier: 'express', amount: '12.00' }
]
};
Once everything is wired up, continue to Full Example, Testing, and Handling Error Codes.