Apple Pay Button Integration

A guide on integrating the Apple Pay button on a webpage.

This guide is not intended as a comprehensive tutorial for integrating with Apple Pay but it will discuss the steps to take to complete a basic implementation of an Apple Pay button on a web page.

More documentation can be found through Apple here:

Loading the SDK

To begin, the Apple Pay SDK must be loaded on your page. One way to accomplish this is by loading it through a script tag in your HTML using Apple's CDN.

<script
  src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"
  crossorigin="anonymous">
</script>

SDK Source and Compatibility

This URL receives backward-compatible features and fixes. Merchants who require controlled updates may instead pin a specific semantic version and use its matching SRI hash, with a process for reviewing and deploying updates.


As far as the dynamic loading, it is required to wait for the script load event before checking Apple Pay SDK availability or initializing the button. Handle loading failures separately from unsupported devices.
For more information and examples, refer to the official Apple SDK-loading Documentation.

First, make sure that the button is explicitly hidden initially so that it is only revealed after successful initialization.

📘

Note

Apple recommends hiding the Apple Pay button until after making sure the service can be used. For more information, you can review Apple's documentation on Acceptable Use Guidelines for Apple Pay on the Web.

#apple-pay-button {
  display: none;
  --apple-pay-button-width: 240px;
  --apple-pay-button-height: 44px;
  --apple-pay-button-border-radius: 8px;
  --apple-pay-button-padding: 0;
}

To statically render the button on your page the element can be added to your HTML document.

This is the recommended way to render your Apple Pay button element for cross-browser rendering. See Apple's button example.

<body>
  <apple-pay-button id="apple-pay-button" onclick="doApplePay();" buttonstyle="black" type="plain" locale="en"></apple-pay-button>
  
  <p id="apple-pay-unsupported" class="text-secondary mb-0" style="display: none;">
  Apple Pay is not available in this browser/context. Use HTTPS on a registered domain. Safari works natively; Chrome/Edge/Firefox need the official JS SDK button and (for desktop) an iPhone/iPad on iOS&nbsp;18+ to complete payment.
  </p>
</body>

For the full static HTML page, refer to checkout.html.

Browser Support

Apple Pay is available in Safari and supported third-party browsers, including browsers on Mac and PC. In supported third-party desktop browsers, customers scan an Apple Pay code and complete payment using a compatible iPhone or iPad running iOS 18 or iPadOS 18 or later. Availability depends on the browser, device, and market.

It is best to load the Apple Pay JS SDK on every page offering Apple Pay. Third-party browser support requires SDK version 1.2.0 or later; use Apple's recommended auto-updating URL unless your integration requires a pinned version. Determine availability through Apple's APIs instead of restricting the button by browser name or operating system.

Apple documents this flow and its requirements at Apple Pay in Third-Party Browsers.


Setup and Testing

🚧

Identifying and Testing CORS issues

When testing the integration, it is important to host the application on a domain that is not associated with PayConex or Bluefin. This ensures that any potential CORS (Cross-Origin Resource Sharing) issues are properly identified and resolved during testing.

Hosting the test environment under a separate domain allows you to verify that API requests, redirects, and embedded components function correctly in a production-like setup, without relying on the same-origin privileges that may mask CORS configuration problems.

The following code block initially will create variables for storing the Apple Pay version supported by the client browser and another for saving the URL used to load the page.

Then it will check if the window.ApplePaySession and ApplePaySession.canMakePayments() methods from the Apple Pay SDK return true.

If the required conditions are met then the ApplePaySession.supportsVersion() method is called in a for loop.

This loop iterates over the most recent Apple Pay number versions to find the most recent that the browser in use supports and saves that value to the applePayVersion variable. This point in the flow would be a good time to actually display the Apple Pay button on the page.

If either of the required conditions does not evaluate to true, then the Apple Pay button is not supported for our page.

// Create a variable to store the Apple Pay Version supported by the browser.
let applePayVersion = null;
// Create a string to store the referrer URL (will be used in Apple Pay functions)
let referringUrl = location.host


if (window.ApplePaySession && ApplePaySession.canMakePayments()) {
    // Button is displayed/revealed only after successful initialization. 
    document.getElementById('apple-pay-button').style.display = "inline-block";
    for (let i = 15; i > 0; i--) {
        if (ApplePaySession.supportsVersion(i)){
            applePayVersion = i;
            break;
        }
    }
}
else {
    document.getElementById('apple-pay-unsupported').style.display = "block";
}

If the conditions are met, then at that point the Apple Pay button can be rendered.

Furthermore, both window.ApplePaySession and ApplePaySession.canMakePayments(), as the basic availability checks, are required to be performed after the SDK has loaded. canMakePayments() checks payment capability; it does not confirm that a card is already provisioned.

We have best demonstrated these availability checks by putting them into one utility function for our sample application under checkApplePayAvailability and pickApplePayVersion.


SDK version vs. ApplePaySession version

SDK releases such as 1.2.0 and the integer passed to ApplePaySession.supportsVersion() are separate version systems.

The SDK version (for example 1.2.0) identifies the Apple Pay JS SDK loaded on the page.
The integer version passed to ApplePaySession.supportsVersion() identifies the Apple Pay JS API available in the browser ApplePaySession).

It is important to note that these numbers do not correspond. A merchant can load a current SDK and still fail eligibility or init if they request an ApplePaySession API version the browser does not support—or the reverse. Diagnose them independently: confirm the SDK script loaded and initialized, then confirm the requested session API version is supported in that browser.


ApplePaySession.applePayCapabilities vs ApplePaySession.canMakePayments


If the ApplePay SDK supports ApplePaySession.applePayCapabilities , it is recommended using it over ApplePaySession.canMakePayments .

Our availability checks then become the following with the proper fallback logic in place:

For the complete checkApplePayAvailability function, check it out at our GitHub Repository.

const merchantId = (publicConfig && publicConfig.apple_merchant_id) || '';

// The following is the usage of applePayCapabilities()
if (typeof ApplePaySession.applePayCapabilities === 'function' && merchantId) {
  try {
    const capabilities = await ApplePaySession.applePayCapabilities(merchantId);
    const status = capabilities && capabilities.paymentCredentialStatus;
    if (status === 'applePayUnsupported') {
      return { available: false, detail: 'This device does not support Apple Pay.' };
    }
    return { available: true, detail: 'Apple Pay is ready.' };
  } catch (err) {
    console.warn('applePayCapabilities failed; falling back to canMakePayments()', err);
  }
}

if (typeof ApplePaySession.canMakePayments === 'function' && ApplePaySession.canMakePayments()) {
  return { available: true, detail: 'Apple Pay is ready.' };
}

Note that status == "paymentCredentialStatusUnknown" is a supported state in which the button should be shown; it must not be treated as unsupported. Apple also requires Apple Pay to be the primary payment option when this check identifies an available payment credential. For more details and all possible return statuses, refer to Apple's availability guidance.




The next step is to create a function to trigger when the Apple Pay button is clicked. In this example page, the function is named doApplePay().

🚧

Note

The following code blocks are all part of the doApplePay() function. This section just steps through the different steps it takes to build the function.

function doApplePay(){
    // The data needed to set up the session
    // See https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest
    const payData = {
        countryCode: 'US',
        currencyCode: 'USD',
        supportedNetworks: [
            "visa",
            "masterCard",
            "amex",
            "discover"
        ],
        merchantCapabilities:  [
            "supports3DS"
        ],
        requiredBillingContactFields: [
            "postalAddress",
            "name"
        ],
        ApplePayContactField: [
            "email",
            "name",
            "phone",
            "postalAddress",
        ],
        total: {label: 'my merchant name', amount: 10}
    }

With the payData variable defined the ApplePaySession() method can be used to create a new Apple Pay session. It requires the version number we save to the applePayVersion variable when the page was loaded, and the payData object defined in the last block.

// Create the session
let session = new ApplePaySession(applePayVersion, payData);

Then a validation callback function needs to be attached to the Apple Pay session that was just created.

When the session is started at a later stage Apple Pay will attempt to validate the merchant and the Apple Pay SDK will call this function.

// Attach a validation callback
    session.onvalidatemerchant = event => {
        const myMerchantId = "180000000742";
        const myApplePayParams = {
            display_name: 'Test Display Name',
            referrer: referringUrl
        };
        
        console.debug(JSON.stringify(myApplePayParams))

        // Call the PayConex API to fetch a payment session object
        fetch(`https://api-cert.payconex.net/api/v4/accounts/${myMerchantId}/applePay/session`, {
            method: 'POST',
            headers: new Headers({'content-type': 'application/json'}),
            body: JSON.stringify(myApplePayParams)
        })
            .then(res => res.json()) // Parse response as JSON.
            .then(merchantSession => {
                //Log the session data to the console.
                console.debug(merchantSession)

                // Pass the session object to Apple
                session.completeMerchantValidation(merchantSession);
            })
            .catch(err => {
                console.error("Error fetching merchant session", err);
            });
    };

The final step before starting the session is to attach a callback function that will be called after the completeMerchantValidation() method is called by the Apple Pay SDK.

When the Apple Pay SDK makes it to this point in the process it will deliver the tokenized data for use in a payment. In this example, the token is just logged to the console for testing.

// Attach a payment authorized callback
    // This is the function that will be called after the user authenticates
    session.onpaymentauthorized = applePayment => {
        //For the example the token will be printed to the console in this case.
        console.debug(JSON.stringify(applePayment.payment.token))

        /*
        // In this step you should send the token data to your own server.
        // which should submit a payment request to QSAPI to complete the payment with the token.
        fetch(`https://yourserver.com/your_path/`, {
            method: 'POST',
            body: JSON.stringify({'token': applePayment.payment.token})
        }).then(transactionResponse => {
            // Tell Apple about the result of the transaction
            if(transactionResponse.approved) {
                session.completePayment({status: ApplePaySession.STATUS_SUCCESS});
            } else {
                session.completePayment({status: ApplePaySession.STATUS_FAILURE});
            }
        })*/
    }

The last step in the function is to begin the session.

// Actually begin the session
session.begin();


Troubleshooting Button Visibility

For a missing button, ask merchants to verify the following:

  • The SDK request succeeds and initialization runs after loading.
  • Console errors, CSP restrictions, or SRI failures are not blocking initialization.
  • Eligibility checks pass and CSS does not hide the button.
  • The page is served over HTTPS.

Before release, test Safari and supported third-party browsers on macOS, plus a supported browser on Windows. Confirm both that the button is visible and that the code-scanning payment flow completes—not just that the button renders. Apple specifically recommends cross-browser testing of eligibility, payment events, and error handling. For more, refer to Apple's testing guidance.

These checks replace outdated guidance and make missing-button issues easier for merchants to diagnose.




What’s Next

Use the token result from the Apple Pay button to process a transaction.

Did this page help you?