Skip to content

Implementation

Learn how to use the Apple Pay component for Web in your project.

Overview

Every component follows the same basic three-step lifecycle:

  1. Create the component and optionally add your own configuration.
  2. Mount the component. This is what makes the component visible and interactive.
  3. Unmount the component. This is a clean-up step that clears up resources.

Before you start

To use the Apple Pay component, you first need to:

Browser and device compatibility

Apple Pay for Web has specific requirements for optimal functionality.

Supported browsers

  • Safari 11.1+ on macOS 10.13.4+.
  • Safari on iOS 11.2+.
  • Other WebKit-based browsers with Apple Pay support.

Device requirements

  • iOS devices: iPhone 6 or later, iPad Pro, iPad (5th generation) or later, iPad Air 2, iPad mini 3 or later.
  • macOS devices: MacBook Pro with Touch Bar, MacBook Air (2018 or later), iMac Pro, Mac Pro (2019 or later), or any Mac with Touch ID.

Configuration requirements

  • The customer must have a supported payment method in their Wallet.
  • The device must have Touch ID, Face ID, or passcode enabled.

Step 1: Initialise the SDK

To get started, initialise the Checkout SDK with Apple Pay support.

import { PxpCheckout } from 'pxp-checkout-sdk';

const pxpSdk = PxpCheckout.initialize({
  environment: 'test', // or 'live' for production
  session: {
    sessionId: 'your-session-id',
    allowedFundingTypes: {
      wallets: {
        applePay: {
          merchantId: 'merchant.com.yourcompany.yourapp'
        }
      }
    }
  },
  ownerId: 'your-owner-id',
  ownerType: 'Merchant',
  transactionData: {
    currency: 'USD',
    amount: 25.00,
    merchantTransactionId: 'txn-' + Date.now(),
    merchantTransactionDate: () => new Date().toISOString(),
    entryType: 'Ecom',
    intent: {
      card: 'Purchase'
    }
  },
  onGetShopper: () => {
    // Return current shopper data dynamically
    return Promise.resolve({
      id: 'shopper-' + Date.now(),
      email: 'customer@example.com',
      firstName: 'John',
      lastName: 'Doe'
    });
  }
});
PropertyDescription
environment
string
required
The environment type.

Possible values:
  • test: For sandbox.
  • live: For production.
session
sessionData
required
Details about the checkout session.
session.sessionId
string
required
The unique session identifier.
session.allowedFundingTypes.wallets.applePay.merchantId
string
required
Your Apple Pay merchant identifier.
ownerId
string
required
The identifier of the owner related to the ownerType.
ownerType
string
required
The type of owner.

Possible values:
  • MerchantGroup
  • Merchant Coming soon
  • Site Coming soon
onGetShopper
function
Callback function to provide shopper data dynamically. Returns a Promise with shopper information including ID, email, name, and contact details.
transactionData
object
required
Details about the transaction.
transactionData.currency
string (3 characters)
required
The currency code associated with the transaction, in ISO 4217 format.
transactionData.amount
number
required
The transaction amount.
transactionData.entryType
string
required
The entry type.

Possible values:
  • Ecom: E-commerce transactions.
  • MOTO: Mail order/telephone order transactions.
transactionData.intent
string
required
The transaction intent.

Possible values:
  • Create: Create a payment.
  • Confirm: Confirm a payment.
  • Capture: Capture an authorised payment.
  • Void: Void an authorised payment.
  • Refund: Refund a captured payment.
transactionData.merchantTransactionId
string
required
A unique identifier for this transaction.
transactionData.merchantTransactionDate
function
required
A function that returns the date and time of the transaction, in ISO 8601 format.

Step 2: Create the component configuration

Next, you're going to create the Apple Pay component configuration.

const config = {
  merchantDisplayName: 'Your Store',
  paymentDescription: 'Purchase from Your Store',
  usingCss: false, // Use official Apple Pay SDK (recommended)
  style: {
    type: 'buy',
    buttonstyle: 'black',
    height: '44px',
    borderRadius: '4px'
  },
  paymentRequest: {
    countryCode: 'US',
    currencyCode: 'USD',
    merchantCapabilities: ['supports3DS', 'supportsEMV'],
    supportedNetworks: ['visa', 'masterCard', 'amex'],
    total: {
      label: 'Total',
      amount: '25.00'
    }
  },
  onError: (error) => console.error('Apple Pay Error:', error),
  onPostAuthorisation: async (data) => {
    const result = await getAuthorisationResultFromGateway(
      data.merchantTransactionId,
      data.systemTransactionId
    );
    if (result.status === 'Authorised') {
      console.log('Payment successful!');
      // Handle successful payment
    }
  }
};
ParameterDescription
merchantDisplayName
string (≤ 64 characters)
required
The name of your store as it appears to customers during payment.
paymentDescription
string (≤ 128 characters)
required
A description of the payment that appears to customers.
usingCss
boolean
Whether to use custom CSS rendering instead of the official Apple Pay SDK. Defaults to false.
style
object
Button styling configuration.
style.type
string
required
The button type.

Possible values:
  • buy: Purchase button
  • pay: Payment button
  • donate: Donation button
  • check-out: Checkout button
  • book: Booking button
  • subscribe: Subscription button
style.buttonstyle
string
required
The button style.

Possible values:
  • black: Black background with white text
  • white: White background with black text
  • white-outline: White background with black border
style.height
string
The button height (e.g., '44px', '50px').
style.borderRadius
string
The button border radius (e.g., '4px', '8px').
paymentRequest
object
required
Apple Pay payment request configuration.
paymentRequest.countryCode
string (2 characters)
required
The country code, in ISO 3166-1 alpha-2 format.
paymentRequest.currencyCode
string (3 characters)
required
The currency code, in ISO 4217 format.
paymentRequest.merchantCapabilities
array of strings
required
Payment processing capabilities.

Possible values:
  • supports3DS: 3D Secure support
  • supportsEMV: EMV support
  • supportsCredit: Credit card support
  • supportsDebit: Debit card support
paymentRequest.supportedNetworks
array of strings
required
Supported card networks.

Possible values:
  • visa
  • masterCard
  • amex
  • discover
  • jcb
  • chinaUnionPay
paymentRequest.total
object
required
The total payment amount.
paymentRequest.total.label
string
required
A short description of the total.
paymentRequest.total.amount
string
required
The total amount as a string with decimal precision.
paymentRequest.total.type
string
The total type.

Possible values:
  • final: Final amount (default)
  • pending: Pending amount
paymentRequest.requiredBillingContactFields
array of strings
The required billing contact fields.

Possible values:
  • postalAddress
  • name
  • email
  • phone
paymentRequest.requiredShippingContactFields
array of strings
The required shipping contact fields.

Possible values:
  • postalAddress
  • name
  • email
  • phone
paymentRequest.shippingMethods
array of objects
The available shipping methods.
paymentRequest.shippingMethods.identifier
string
required
Unique identifier for the shipping method.
paymentRequest.shippingMethods.label
string
required
The display name for the shipping method.
paymentRequest.shippingMethods.detail
string
Additional details about the shipping method.
paymentRequest.shippingMethods.amount
string
required
The shipping cost as a string.
paymentRequest.lineItems
array of objects
Individual line items for the payment.
paymentRequest.lineItems.label
string
required
The display name for the line item.
paymentRequest.lineItems.amount
string
required
The line item amount as a string.
paymentRequest.lineItems.type
string
The line item type.

Possible values:
  • final: Final amount (default)
  • pending: Pending amount

Step 3: Setup the HTML container

Create an HTML container where the Apple Pay button will be mounted.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Apple Pay Checkout</title>
</head>
<body>
    <div class="checkout-container">
        <h2>Complete your purchase</h2>
        <div class="payment-summary">
            <div class="item">
                <span>Product</span>
                <span>$20.00</span>
            </div>
            <div class="item">
                <span>Tax</span>
                <span>$3.00</span>
            </div>
            <div class="item">
                <span>Shipping</span>
                <span>$2.00</span>
            </div>
            <hr>
            <div class="total">
                <span>Total</span>
                <span>$25.00</span>
            </div>
        </div>
        
        <!-- Apple Pay button container -->
        <div id="apple-pay-container" style="margin: 20px 0;"></div>
        
        <div class="alternative-payments">
            <p>Or pay with card</p>
            <!-- Other payment methods -->
        </div>
    </div>
</body>
</html>

Step 4: Create and mount the component

For the component to be visible and functional, you need to mount it. Make sure to supply both the component configuration and the container ID.

const applePayComponent = pxpSdk.create('apple-pay-button', config);
applePayComponent.mount('apple-pay-container');

At this point, your Apple Pay button is ready to use and will appear in the specified container.

Step 5: Handle payment results

The component will automatically handle the payment flow. You can listen for payment results through the callback functions:

const config = {
  // ... other configuration
  onPreAuthorisation: async () => {
    // Called before payment authorisation
    // Return transaction initialisation data
    console.log('Pre-authorisation started');
    return {
      addressVerification: true,
      riskScreeningData: {
        // Kount risk screening data
      }
    };
  },
  onPostAuthorisation: async (data) => {
    console.log('Payment completed');
    console.log('Merchant Transaction ID:', data.merchantTransactionId);
    console.log('System Transaction ID:', data.systemTransactionId);
    
    // Retrieve authorization result from backend
    const result = await getAuthorisationResultFromGateway(
      data.merchantTransactionId,
      data.systemTransactionId
    );
    
    if (result.status === 'Authorised') {
      // Payment was successful
      console.log('Payment authorised successfully');
      console.log('Transaction ID:', result.transactionId);
      
      // Redirect to success page or show success message
      window.location.href = '/payment-success';
    } else {
      // Payment was declined or error occurred
      console.log('Payment failed:', result.errorReason);
      // Show error message to user
    }
  },
  onError: (error) => {
    console.error('Apple Pay error:', error);
    // Handle and display error to user
  },
  onCancel: (error) => {
    console.log('Payment cancelled by user');
    // Handle cancellation
  }
};

Step 6: Unmount the component

Lastly, remember to unmount the component when it's no longer needed to free up resources.

applePayComponent.unmount();

What's next?

Customise the look and feel

You can configure the appearance and behaviour of the Apple Pay component to fit your brand. We've documented all configurable parameters in the Customisation page.

const customConfig = {
  // ... other configuration
  usingCss: true, // Enable custom CSS
  style: {
    type: 'buy',
    buttonstyle: 'black',
    template: '<button class="custom-apple-pay-btn">Pay with Apple Pay</button>',
    templateCSS: `
      .custom-apple-pay-btn {
        background: linear-gradient(135deg, #000 0%, #333 100%);
        color: white;
        border: none;
        border-radius: 8px;
        height: 50px;
        width: 100%;
        font-size: 16px;
        cursor: pointer;
        transition: all 0.3s ease;
      }
      .custom-apple-pay-btn:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
      }
    `
  }
};

Add more event handling

The Apple Pay component emits events based on user interaction, shipping changes, and payment method updates. You can implement callback functions to handle these events and perform actions such as updating totals or validating addresses. For more information about all the available events, see the Events page.

const eventConfig = {
  // ... other configuration
  onShippingContactSelected: async (contact) => {
    // Update totals based on shipping address
    const shipping = calculateShipping(contact.postalAddress);
    return {
      newShippingMethods: [
        {
          identifier: 'standard',
          label: 'Standard Shipping',
          amount: shipping.standard.toString(),
          detail: '5-7 business days'
        }
      ],
      newTotal: {
        label: 'Total',
        amount: (25.00 + shipping.standard).toString()
      }
    };
  },
  onPaymentMethodSelected: async (paymentMethod) => {
    // Handle payment method changes
    console.log('Selected payment method:', paymentMethod);
    return {
      newTotal: {
        label: 'Total',
        amount: '25.00'
      }
    };
  }
};

Add error handling

Error handling is crucial for payment components because they deal with sensitive financial data and complex validation rules. Errors can happen due to issues with configuration, validation, authentication, or connectivity. For more details about error handling, see the Data Validation page.

const errorConfig = {
  // ... other configuration
  onError: (error) => {
    if (error instanceof ApplePayValidationException) {
      // Handle validation errors
      console.error('Validation error:', error.message);
      showUserError('Please check your payment information and try again.');
    } else if (error instanceof ApplePayHttpRequestFailedException) {
      // Handle network errors
      console.error('Network error:', error.message);
      showUserError('Connection error. Please check your internet and try again.');
    } else {
      // Handle other errors
      console.error('Unexpected error:', error);
      showUserError('An unexpected error occurred. Please try again.');
    }
  }
};

function showUserError(message) {
  const errorDiv = document.getElementById('error-message');
  errorDiv.textContent = message;
  errorDiv.style.display = 'block';
}

Complete example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Apple Pay Checkout Example</title>
    <style>
        .checkout-container {
            max-width: 400px;
            margin: 50px auto;
            padding: 20px;
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
        }
        
        .payment-summary {
            background: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
            margin-bottom: 20px;
        }
        
        .item, .total {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
        }
        
        .total {
            font-weight: 600;
            font-size: 18px;
        }
        
        hr {
            border: none;
            border-top: 1px solid #ddd;
            margin: 15px 0;
        }
        
        #apple-pay-container {
            margin: 20px 0;
        }
        
        .error-message {
            background: #ffebee;
            color: #c62828;
            padding: 12px;
            border-radius: 4px;
            margin: 10px 0;
            display: none;
        }
        
        .success-message {
            background: #e8f5e8;
            color: #2e7d32;
            padding: 12px;
            border-radius: 4px;
            margin: 10px 0;
            display: none;
        }
    </style>
</head>
<body>
    <div class="checkout-container">
        <h2>Complete your purchase</h2>
        
        <div class="payment-summary">
            <div class="item">
                <span>Premium T-Shirt</span>
                <span>$20.00</span>
            </div>
            <div class="item">
                <span>Sales Tax</span>
                <span>$3.00</span>
            </div>
            <div class="item">
                <span>Shipping</span>
                <span>$2.00</span>
            </div>
            <hr>
            <div class="total">
                <span>Total</span>
                <span>$25.00</span>
            </div>
        </div>
        
        <div id="error-message" class="error-message"></div>
        <div id="success-message" class="success-message"></div>
        
        <!-- Apple Pay button will be mounted here -->
        <div id="apple-pay-container"></div>
        
        <div class="alternative-payments">
            <p style="text-align: center; color: #666; margin: 20px 0;">
                Or pay with credit card
            </p>
            <!-- Other payment method buttons would go here -->
        </div>
    </div>

    <script type="module">
        import { PxpCheckout, AuthorisedSubmitResult, DeclinedSubmitResult, ExceptionSubmitResult } from 'pxp-checkout-sdk';

        // Initialise the SDK
        const pxpSdk = PxpCheckout.initialize({
            environment: 'test',
            session: {
                sessionId: 'your-session-id-from-backend',
                allowedFundingTypes: {
                    wallets: {
                        applePay: {
                            merchantId: 'merchant.com.yourcompany.yourapp'
                        }
                    }
                }
            },
            ownerId: 'your-owner-id',
            ownerType: 'Merchant',
            transactionData: {
                currency: 'USD',
                amount: 25.00,
                merchantTransactionId: 'txn-' + Date.now(),
                merchantTransactionDate: () => new Date().toISOString(),
                entryType: 'Ecom',
                intent: {
                    card: 'Purchase'
                }
            },
            onGetShopper: () => {
                return Promise.resolve({
                    id: 'shopper-' + Date.now(),
                    email: 'customer@example.com',
                    firstName: 'John',
                    lastName: 'Doe'
                });
            },
            onGetShippingAddress: () => {
                return Promise.resolve({
                    address: '123 Main Street',
                    city: 'San Francisco',
                    state: 'CA',
                    postalCode: '94103',
                    countryCode: 'US'
                });
            }
        });

        // Apple Pay component configuration
        const config = {
            merchantDisplayName: 'Your Store Name',
            paymentDescription: 'Premium T-Shirt Purchase',
            usingCss: false, // Use official Apple Pay SDK
            style: {
                type: 'buy',
                buttonstyle: 'black',
                height: '50px',
                borderRadius: '8px'
            },
            paymentRequest: {
                countryCode: 'US',
                currencyCode: 'USD',
                merchantCapabilities: ['supports3DS', 'supportsEMV'],
                supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
                total: {
                    label: 'Your Store Name',
                    amount: '25.00',
                    type: 'final'
                },
                lineItems: [
                    {
                        label: 'Premium T-Shirt',
                        amount: '20.00'
                    },
                    {
                        label: 'Sales Tax',
                        amount: '3.00'
                    },
                    {
                        label: 'Shipping',
                        amount: '2.00'
                    }
                ],
                requiredBillingContactFields: ['postalAddress', 'name', 'email'],
                requiredShippingContactFields: ['postalAddress', 'name', 'phone'],
                shippingMethods: [
                    {
                        identifier: 'standard',
                        label: 'Standard Shipping',
                        detail: '5-7 business days',
                        amount: '2.00'
                    },
                    {
                        identifier: 'express',
                        label: 'Express Shipping',
                        detail: '2-3 business days',
                        amount: '5.00'
                    }
                ]
            },
            
            // Event handlers
            onPreAuthorisation: async () => {
                console.log('Pre-authorisation started');
                showMessage('Processing payment...', 'info');
                
                return {
                    addressVerification: true,
                    riskScreeningData: {
                        // Kount integration data would go here
                    }
                };
            },
            
            onPostAuthorisation: async (data) => {
                console.log('Payment completed');
                console.log('Merchant Transaction ID:', data.merchantTransactionId);
                
                // Retrieve authorization result from backend
                const result = await getAuthorisationResultFromGateway(
                    data.merchantTransactionId,
                    data.systemTransactionId
                );
                
                if (result.status === 'Authorised') {
                    showMessage('Payment successful! Redirecting...', 'success');
                    console.log('Transaction ID:', result.transactionId);
                    
                    // Redirect to success page after a short delay
                    setTimeout(() => {
                        window.location.href = '/payment-success?txn=' + data.merchantTransactionId;
                    }, 2000);
                    
                } else {
                    showMessage('Payment failed: ' + (result.errorReason || 'Please try again'), 'error');
                }
            },
            
            onShippingContactSelected: async (contact) => {
                console.log('Shipping contact selected:', contact);
                
                // Calculate shipping based on address
                const shippingCost = calculateShippingCost(contact.postalAddress);
                const tax = calculateTax(contact.postalAddress);
                const newTotal = 20.00 + shippingCost + tax;
                
                return {
                    newShippingMethods: [
                        {
                            identifier: 'standard',
                            label: 'Standard Shipping',
                            detail: '5-7 business days',
                            amount: shippingCost.toString()
                        }
                    ],
                    newTotal: {
                        label: 'Your Store Name',
                        amount: newTotal.toFixed(2)
                    },
                    newLineItems: [
                        {
                            label: 'Premium T-Shirt',
                            amount: '20.00'
                        },
                        {
                            label: 'Sales Tax',
                            amount: tax.toFixed(2)
                        },
                        {
                            label: 'Shipping',
                            amount: shippingCost.toFixed(2)
                        }
                    ]
                };
            },
            
            onShippingMethodSelected: async (method) => {
                console.log('Shipping method selected:', method);
                
                const baseAmount = 20.00;
                const tax = 3.00;
                const shippingCost = parseFloat(method.amount);
                const newTotal = baseAmount + tax + shippingCost;
                
                return {
                    newTotal: {
                        label: 'Your Store Name',
                        amount: newTotal.toFixed(2)
                    },
                    newLineItems: [
                        {
                            label: 'Premium T-Shirt',
                            amount: '20.00'
                        },
                        {
                            label: 'Sales Tax',
                            amount: '3.00'
                        },
                        {
                            label: 'Shipping',
                            amount: method.amount
                        }
                    ]
                };
            },
            
            onError: (error) => {
                console.error('Apple Pay error:', error);
                showMessage('Payment error: ' + error.message, 'error');
            },
            
            onCancel: (error) => {
                console.log('Payment cancelled by user');
                showMessage('Payment was cancelled', 'info');
            }
        };

        // Helper functions
        function showMessage(message, type) {
            const errorDiv = document.getElementById('error-message');
            const successDiv = document.getElementById('success-message');
            
            // Hide all messages first
            errorDiv.style.display = 'none';
            successDiv.style.display = 'none';
            
            if (type === 'error') {
                errorDiv.textContent = message;
                errorDiv.style.display = 'block';
            } else if (type === 'success') {
                successDiv.textContent = message;
                successDiv.style.display = 'block';
            } else {
                // For info messages, use success styling but different color
                successDiv.textContent = message;
                successDiv.style.display = 'block';
                successDiv.style.background = '#e3f2fd';
                successDiv.style.color = '#1565c0';
            }
        }
        
        function calculateShippingCost(address) {
            // Simple shipping calculation based on state/country
            if (address.countryCode === 'US') {
                return address.administrativeArea === 'CA' ? 5.00 : 2.00;
            }
            return 10.00; // International shipping
        }
        
        function calculateTax(address) {
            // Simple tax calculation
            const taxRates = {
                'CA': 0.0875, // California
                'NY': 0.08,   // New York
                'TX': 0.0625  // Texas
            };
            
            const rate = taxRates[address.administrativeArea] || 0.06;
            return 20.00 * rate;
        }

        // Create and mount the Apple Pay component
        try {
            const applePayComponent = pxpSdk.create('apple-pay-button', config);
            applePayComponent.mount('apple-pay-container');
            
            console.log('Apple Pay component mounted successfully');
            
        } catch (error) {
            console.error('Failed to initialise Apple Pay:', error);
            showMessage('Apple Pay is not available on this device/browser', 'error');
        }

        // Cleanup on page unload
        window.addEventListener('beforeunload', () => {
            if (window.applePayComponent) {
                window.applePayComponent.unmount();
            }
        });
    </script>
</body>
</html>