Skip to main content

Full Integration Example

A copy-paste-ready end-to-end Apple Pay Express Checkout integration. The button gates rendering behind isAvailable(), collects the shipping address and shipping method inside the Apple Pay sheet via payload, and recomputes totals through onShippingContactSelected / onShippingMethodSelected.

Standard Apple Pay flow

If your checkout already has the shipping address when the customer clicks pay, use the standard Apple Pay component instead: it skips the in-sheet shipping flow and is simpler to wire up.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src='https://test-pay.dnapayments.com/components/apple-pay/apple-pay-component.js'></script>
<script>
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'
}]
};

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' }
]
};

async function renderApplePay() {
if (!await window.DNAPayments.ApplePayComponent.isAvailable()) {
// Device cannot make Apple Pay payments, leave the button hidden.
return;
}

const events = {
onClick: () => {
console.log('Apple Pay button clicked');
},

onShippingContactSelected: async (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' }
]
});
},

onShippingMethodSelected: async (resolve, reject, event) => {
const cost = parseFloat(event.shippingMethod.amount);
resolve({
newTotal: {
label: 'My Store',
amount: String(parseFloat(paymentData.amount) + cost),
type: 'final'
}
});
},

onPaymentSuccess: (result) => {
console.log('Payment processed', result);
window.location.href = '/success';
},
onCancel: () => {
console.log('Payment cancelled');
},
onError: (error) => {
console.error('Apple Pay error:', error);
}
};

window.DNAPayments.ApplePayComponent.init({
containerElement: document.getElementById('apple-pay-btn-container'),
paymentData,
payload,
events,
token: 'YOUR_ACCESS_TOKEN'
});
}

window.addEventListener('load', renderApplePay);
</script>
</head>
<body>
<div id='apple-pay-btn-container' style='width: 100%; height: 46px;'></div>
</body>
</html>
Switch to production

Replace the script source with the production CDN before going live:

<script src='https://pay.dnapayments.com/components/apple-pay/apple-pay-component.js'></script>

See Deployment for the full go-live checklist.