Apple Pay Integration Guide


How to Integrate Apple Pay with : Full Technical Walkthrough


Integrating Apple Pay on the merchant side requires more than just enabling a payment method — it involves hosting the Apple Pay button, handling H2H (host-to-host) API flows, and managing token decryption. The setup defines where the button is rendered, how validation and payment requests are exchanged, and whether decryption is performed by the or directly on the merchantʼs side. This guide provides a step-by-step overview of Apple Pay connectivity and security requirements for handling payment tokens.

  1. General Information
  2. H2H flow with Decryption on side. Apple Pay button on Merchants side
    1. Interaction Key Participants
    2. High-Level Interaction Flow
    3. Apple pay button integration and Merchant validation
    4. API documentation
  3. H2H flow with Decryption on Merchants side. Apple Pay button on Merchants side
    1. General info
    2. Apple Pay Button Integration, Merchant Validation and Decryption
    3. API documentation
  4. Merchant Website Apple Pay Button Integration (Redirect)
    1. Apple Pay Redirect Button Integration
    2. API documentation
  5. Hosted Payment Page with Apple Pay Button
    1. General info
    2. API documentation

1. General Information

The platform allows the use of the Apple Pay payment method, given that it is also supported by the Payment Provider.

  1. Hosted payment page with Apple Pay Button
  2. Apple Pay button can be hosted on end (Redirect flow)
  3. H2H flow with Decryption on side
  4. H2H flow with Decryption on Merchant side

Who can host Apple Pay button in case of the H2H flow ?

The platform supports two options:

  1. Apple Pay button can be hosted on end (Hosted Payment Page / Redirect Button)
  2. Apple Pay button can be hosted on Merchantʼs end
Please note, if the Apple Pay button is hosted on Merchantʼs end, the API integration to our platform should be implemented.

2. H2H flow with Decryption on side. Apple Pay button on Merchants side

H2H Apple Pay allows the Merchant to place the Apple Pay Payment Button on their side (technically), without embedding the platform's page into the user's payment flow. Token decryption in this case (described below) is done by .

Pre-requisites

  1. Merchant should host domain verification file received during registration with Apple Pay
  2. Merchant should have merchant validation certificate received during registration with Apple Pay
  3. platform should have private key generated while registering with Apple Pay

2.1 Interaction Key Participants

User The user making the payment.
Merchant Website (Frontend) The frontend of the merchant's web application.
Apple Pay Widget The user interface of Apple Pay.
Merchant Website (Backend)
  • The backend of the merchant's web application.
  • It performs the server side validation with the Apple Validation Server.
system Decrypt tokens, and interacts with the final acquirer.
Apple Pay Validation Server A server that validates the merchant and the host site where the payment (Apple Pay button placement) takes place.

2.2 High-Level Interaction Flow

Actions within the Merchantʼs system may vary in presence, sequence, etc.

  1. User – initiates an "order."
  2. Merchant Website (Backend) – creates a transaction and opens the Checkout page (where payment will takes place).
  3. User – clicks the Apple Pay button. Merchant Website (Frontend) – initiates interaction with the Apple Pay Widget. Apple Pay Widget returns a URL for merchant and host validation.
  4. Merchant Website (Backend) – initiates Merchant and Host Validation. and performs the validation via Apple Pay Validation Server.
  5. User – confirms the payment with the selected card. Apple Pay Widget provides the Encrypted token.
  6. Merchant Website (Backend) – initiates the payment with the received encrypted token.
  7. Platform – decrypt the encrypted token and executes the payment through the Acquirer (Bank).
  8. Merchant Website (Backend) – opens the Payment Status page.

2.3 Apple Pay Button Integration and Merchant Validation

Apple Pay Button:

Example Code for Displaying the Button:


<script src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
<apple-pay-button buttonstyle="black" type="plain" locale="en-US"></apple-pay-button>
                    

Example Code for Styling the Button:

apple-pay-button { 
     --apple-pay-button-width: 150px; 
    --apple-pay-button-height: 30px; 
    --apple-pay-button-border-radius: 3px; 
    --apple-pay-button-padding: 0px 0px; 
    --apple-pay-box-sizing: border-box; 
}
                    

For more information, please visit below documentation

Displaying Apple Pay Buttons Using JavaScript | Apple Developer Documentation

Sample code for JavaScript
                        

    if (window.ApplePaySession && ApplePaySession.canMakePayments()) {

        document.getElementById("apple-pay-button").classList.remove("hidden");

    } else {
        alert("Apple Pay is not supported on this device/browser");
    }
    document.getElementById("apple-pay-button").addEventListener("click", startApplePay);

    function buildApplePayDetails(payment) {
        var token   = payment.token || {};
        var pm      = token.paymentMethod || {};
        var billing = payment.billingContact || {};
        var contact = payment.shippingContact || {};
        var lines   = billing.addressLines || [];

        return {
    
            apay_card_network: pm.network || "",     
            apay_card_type: pm.type || "",         
            apay_card_display_name: pm.displayName || "",   

            apay_billing_first_name: billing.givenName || "",
            apay_billing_last_name:  billing.familyName || "",
            apay_billing_address1:   lines[0] || "",
            apay_billing_address2:   lines[1] || "",
            apay_billing_address3:   lines.slice(2).join(", "),
            apay_billing_sublocality: billing.subLocality || "",
            apay_billing_city:       billing.locality || "",
            apay_billing_state:      billing.administrativeArea || "",
            apay_billing_zip:        billing.postalCode || "",
            apay_billing_country:    billing.countryCode || "",

            apay_email: contact.emailAddress || "",
            apay_phone: contact.phoneNumber || ""
        };
    }

    function startApplePay() {

        const paymentRequest = {

            countryCode: "<%=country%>",

            currencyCode: "<%=currency%>",

            supportedNetworks: ["AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA"],

            merchantCapabilities: ["supports3DS"],
            requiredBillingContactFields: ["postalAddress", "name"],

            requiredShippingContactFields: ["email", "phone"],
         
            total: {
                label: "<%=apay_merchantName%>",
                amount: "<%=amount%>"
            }
        };

        const session = new ApplePaySession(3, paymentRequest);

        session.onvalidatemerchant = function(event) {

            fetch("/transaction/APPMerchantValidationServlet", {

                method: "POST",

                headers: {
                    "Content-Type": "application/json"
                },

                body: JSON.stringify({
                    accountId:"<%=accountid%>",
                    host:"<%=HostUrl%>",
                    validationURL: event.validationURL
                })

            })
                    .then(function(response) {
                        return response.json();
                    })
                    .then(function(merchantSession) {
                        session.completeMerchantValidation(merchantSession);
                    })
                    .catch(function(error) {
                        session.abort();
                    });

        };

        session.onpaymentauthorized = function(event) {

            var payment = event.payment;
            var details = buildApplePayDetails(payment);

            var payload = JSON.parse(JSON.stringify(payment));
            Object.assign(payload, details, {
                trackingId: "<%=trackingid%>",
                accid: "<%=accountid%>"
            });
         
            fetch("/transaction/AppPaymentProcessServlet", {

                method: "POST",

                headers: {
                    "Content-Type": "application/json"
                },

                body: JSON.stringify(payload)

            })
                    .then(function(response) {
                        return response.text();
                    })
                    .then(function(result) {

                        var jsonResult = JSON.parse(result);

                        if (jsonResult.status === "SUCCESS") {

                            session.completePayment(ApplePaySession.STATUS_SUCCESS);
                            
                            alert("Payment Success:::");

                        } else {
                            session.completePayment(ApplePaySession.STATUS_FAILURE);
                            alert("Payment Fail::"+jsonResult.remark);
                        }

                    
                        setTimeout(function() {

                            var form = document.createElement("form");

                            form.method = "POST";
                            form.action = "/transaction/ConfirmationCheckoutServlet";

                            var input = document.createElement("input");

                            input.type = "hidden";
                            input.name = "ctoken";
                            input.value = jsonResult.ctoken;
                            form.appendChild(input);

                            var input1 = document.createElement("input");
                            input1.type = "hidden";
                            input1.name = "trackingid";
                            input1.value = jsonResult.tid;
                            form.appendChild(input1);

                            var input2 = document.createElement("input");
                            input2.type = "hidden";
                            input2.name = "status";
                            
                           
                            input2.value = (jsonResult.status === "SUCCESS") ? "success" : jsonResult.status;
                            form.appendChild(input2);

                            var input3 = document.createElement("input");
                            input3.type = "hidden";
                            input3.name = "mid";
                            input3.value = jsonResult.mid;
                            form.appendChild(input3);

                            document.body.appendChild(form);

                            form.submit();

                        }, 500);

                    })
                    .catch(function(error) {

                        session.completePayment(ApplePaySession.STATUS_FAILURE);

                        alert("Payment Processing Error:::");

                    });

        };

        
        session.oncancel = function(event) {

            console.log("Apple Pay Cancelled");

        };

        session.begin();
    }


                    

Merchant Validation :

Merchant has to verify his identity with Apple Pay.

Sample java code for merchant validation
                        
KeyStore keyStore = KeyStore.getInstance("PKCS12");

                FileInputStream fis = new FileInputStream(validationPath);  //applepay_merchant_identity.p12 file path

                keyStore.load(fis, validationPass.toCharArray());

                Enumeration<String> aliases1 = keyStore.aliases();

                while (aliases1.hasMoreElements()) {
                    String alias = aliases1.nextElement();
                }

                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

                kmf.init(keyStore, validationPass.toCharArray());

                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

                tmf.init((KeyStore) null);

                X509KeyManager km = (X509KeyManager) kmf.getKeyManagers()[0];


                String[] aliases = km.getClientAliases("RSA", null);

                if (aliases != null) {
                    for (String a : aliases)
                 }

                SSLContext sslContext = SSLContext.getInstance("TLS");

                sslContext.init(
                        kmf.getKeyManagers(),
                        tmf.getTrustManagers(),
                        new SecureRandom()
                );

                JSONObject payload = new JSONObject();

                payload.put("merchantIdentifier",merchantIdentifier);

                payload.put("displayName",displayName);

                payload.put("initiative","web");

                payload.put("initiativeContext",host);

                transactionlogger.error("payload.toString() : " + payload.toString());
                
                SSLConnectionSocketFactory socketFactory =
                        new SSLConnectionSocketFactory(
                                sslContext,
                                new String[]{"TLSv1.2"},
                                null,
                                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

                client = HttpClients.custom()
                        .setSSLSocketFactory(socketFactory)
                        .setConnectionReuseStrategy(
                                NoConnectionReuseStrategy.INSTANCE)
                        .disableConnectionState()
                        .disableCookieManagement()
                        .build();

                X509Certificate cert = (X509Certificate) keyStore.getCertificate("1");

                HttpPost post = new HttpPost(validationURL);
                post.setHeader("Content-Type","application/json");
                post.setHeader("Connection", "close");
                post.setEntity(new StringEntity(payload.toString(),ContentType.APPLICATION_JSON));

                appleResponse = client.execute(post);

                int statusCode = appleResponse.getStatusLine().getStatusCode();

                transactionlogger.error("Apple Status Code : "+ statusCode);

                String responseBody = EntityUtils.toString(appleResponse.getEntity(),"UTF-8");
                    

For more information, please visit below documentation

Requesting an Apple Pay payment session | Apple Developer Documentation

2.4 API Integration

Merchant needs to pass encrypted token details to ’s Rest Api.

Refer below link for API sample request and response

Apple Pay Workflow

For Auth token, refer

Transaction AuthToken

Sample code to pass encrypted/token details to

paymentMode=APAY

paymentBrand=APPLEPAY

tokenType=applepayEncrypted

Sample Request
                        
{
  "authentication": {
    "memberId": "11344",
    "checksum": "efac101d0b30bc51771567f9957cec58",
    "terminalId": "1106"
  },
  "merchantTransactionId": "01234",
  "paymentBrand": "APPLEPAY",
  "paymentMode": "APAY",
  "tokenType": "applepayEncrypted",
  "paymentType": "DB",
  "amount": "50.00",
  "currency": "USD",
  "orderDescriptor": "Test Transaction",
  "shipping": {
    "country": "UK",
    "city": "Aston",
    "state": "NA",
    "postcode": "CH5 3LJ",
    "street1": "19 Scrimshire Lane"
  },
  "customer": {
    "telnocc": "+44",
    "phone": "07730432996",
    "email": "john.doe@xyz.com",
    "givenName": "John",
    "surname": "Doe",
    "ip": "192.168.0.1",
    "birthDate": "19890202",
    "customerId": "123455"
  },
  "card": {
    "number": "5101082187256500",
    "expiryMonth": "12",
    "expiryYear": "2018",
    "cvv": "123"
  },
  "applepayEncryptedpayload": {
    "encryptedToken": "{\"token\":{\"paymentData\":{\"data\":\"WSu6ZyexdOvwLwm+TcgsnNa1tJmXA9I2nyVYq5vUpB7nDIR9RHBLoVXISfz9BAONSrhPFooN9aej32wvZSsy6PeMweWwavD20PpSZcp4hC9zqpaEnF0rLb1TEy5jB2+DqTUtYKHgByTAJcNLsCy+K76bajdAoHvBHtP636Csd6B8MCMPeKA3AMxBdifXNJ4N7fDTYSf+g9/JUyoNblwiN34nv9PRTD6kKPv8oc+Qnr0O2Xf7IQs3f3vXFAVMBHFad8aR1BYWHGsbriqE9wBRM7D1Z/wDZtH1xmuHIwnJsvMYKPXuAWE4y7k6ZhIMj07D3aEilRTe3Nat61kax6On5kuLMdDuUg73uKk7d+uCjndZI4+2v0UUyp9JkiQWMv/wzvhdfm9PFoD+m1XK2A==\",\"signature\":\"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\",\"header\":{\"publicKeyHash\":\"lMnacfWgaaeHmxaVndDlynbaQzg8I3ujXZq4060uJEs=\",\"ephemeralPublicKey\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPTrkOI54678ilkjhgfdcvbnmrtyu0wYHVQkTJbBWs+4xEt5U5kVkbik0i5v2i0nxV1nkCg==\",\"transactionId\":\"977868e4949181a8f61aa231ba9da3a4dd6de9c8dd9096b26a381d520b1ab667\"},\"version\":\"EC_v1\"},\"paymentMethod\":{\"displayName\":\"MasterCard 5223\",\"network\":\"MasterCard\",\"type\":\"credit\"},\"transactionIdentifier\":\"977868e4949181a8f61aa231ba9da3a4dd6de9c8dd9096b26a381d520b1ab667\"}"
  },
  "merchantRedirectUrl": "https://www.merchantRedirectUrl.com",
  "notificationUrl": "www.merchantNotificationUrl.com",
  "tmpl_amount": "50.00",
  "tmpl_currency": "USD",
  "merchant_website_url": "https://www.abc.com"
}
                    

The transaction status can be verified using standard methods:

  • Fetching transaction via Inquiry API request.
  • Receiving a status update via Callback.


3. H2H flow with Decryption on Merchant's side. Apple Pay button on Merchants side

3.1 General info

H2H Apple Pay allows the Merchant to place the Apple Pay Payment Button on their side (technically), without embedding the platform's page into the user's payment flow. Token decryption in this case (described below) is done by the Merchant.

3.2 Apple Pay Button Integration, Merchant Validation and Decryption

Apple Pay Button:

Example Code for Displaying the Button:


<script src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
<apple-pay-button buttonstyle="black" type="plain" locale="en-US"></apple-pay-button>
                    

Example Code for Styling the Button:

                        
apple-pay-button { 
   display: inline-block;
  width: 100%;
  max-width: 320px;
  --apple-pay-button-width: 100%;
  --apple-pay-button-height: 44px;
  --apple-pay-button-border-radius: 6px;
  --apple-pay-button-padding: 0px 0px;
  --apple-pay-button-box-sizing: border-box; 
} 
                    

For more information, please visit below documentation

Displaying Apple Pay Buttons Using JavaScript | Apple Developer Documentation

Sample code for JavaScript
                        

  var applePayInProgress = false;

  function onApplePayLoaded() {

    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", onApplePayLoaded);
      return;
    }

    var buttonHolder = document.getElementById("applePayButton");
    var unsupported  = document.getElementById("applePayUnsupported");
    var payButton    = document.getElementById("apple-pay-button");

    if (!buttonHolder || !unsupported || !payButton) {
      return;
    }

    if (window.ApplePaySession && ApplePaySession.canMakePayments()) {

      buttonHolder.style.display = "block";
      unsupported.style.display  = "none";

      payButton.addEventListener("click", startApplePay);

    } else {

      buttonHolder.style.display = "none";
      unsupported.style.display  = "block";
    }
  }
  function showApplePayLoader() {
    var loader = document.getElementById("apayProcessingLoader");
    var applePayButton = document.getElementById("applePayButton");

    applePayInProgress = true;

    if (loader) {
      loader.style.display = "block";
    }

    if (applePayButton) {
      applePayButton.style.display = "none";
    }
  }

  function hideApplePayLoader() {
    var loader = document.getElementById("apayProcessingLoader");
    var applePayButton = document.getElementById("applePayButton");

    applePayInProgress = false;

    if (loader) {
      loader.style.display = "none";
    }

    if (applePayButton) {
      applePayButton.style.display = "block";
    }
  }
  function buildApplePayDetails(payment) {
    var token   = payment.token || {};
    var pm      = token.paymentMethod || {};
    var billing = payment.billingContact || {};
    var contact = payment.shippingContact || {};
    var lines   = billing.addressLines || [];

    return {

      apay_card_network:      pm.network || "",
      apay_card_type:         pm.type || "",
      apay_card_display_name: pm.displayName || "",  

      apay_billing_first_name: billing.givenName || "",
      apay_billing_last_name:  billing.familyName || "",
      apay_billing_address1:   lines[0] || "",
      apay_billing_address2:   lines[1] || "",
      apay_billing_address3:   lines.slice(2).join(", "),
      apay_billing_sublocality: billing.subLocality || "",
      apay_billing_city:       billing.locality || "",
      apay_billing_state:      billing.administrativeArea || "",
      apay_billing_zip:        billing.postalCode || "",
      apay_billing_country:    billing.countryCode || "",

      apay_email: contact.emailAddress || "",
      apay_phone: contact.phoneNumber || ""
    };
  }

  function startApplePay() {

    if (applePayInProgress) {
      return;
    }

    showApplePayLoader();
    const paymentRequest = {

      countryCode: "<%=country%>",

      currencyCode: "<%=currency%>",

      supportedNetworks: ["AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA"],

      merchantCapabilities: ["supports3DS"],

      requiredBillingContactFields: ["postalAddress", "name"],

      requiredShippingContactFields: ["email", "phone"],

      total: {
        label: "<%=apay_merchantName%>",
        amount: "<%=amount%>"
      }
    };

    let session;

    try {

      session = new ApplePaySession(3, paymentRequest);

    } catch (e) {

      hideApplePayLoader();

      alert("Unable to start Apple Pay: " + e.message);

      return;
    }

  
    session.onvalidatemerchant = function(event) {

      fetch("/transaction/APPMerchantValidationServlet", {

        method: "POST",

        headers: {
          "Content-Type": "application/json"
        },

        body: JSON.stringify({
          accountId:"<%=accid%>",
          host:"<%=merchantDetailsVO.getHostUrl()%>",
          validationURL: event.validationURL
        })

      })
              .then(function(response) {

                return response.json();

              })
              .then(function(merchantSession) {

                session.completeMerchantValidation(
                        merchantSession
                );

              })
              .catch(function(error) {

                session.abort();

                hideApplePayLoader();

                alert("Merchant validation failed");

              });

    };


    session.onpaymentauthorized = function(event) {

      var payment = event.payment;
      var details = buildApplePayDetails(payment);

    
      var payload = JSON.parse(JSON.stringify(payment));
      Object.assign(payload, details, {
        trackingId: "<%=trackingId%>",
        accid: "<%=accid%>",
        fromRedirect: "<%=fromRedirect%>"
      });

      fetch("/transaction/AppPaymentProcessServlet", {

        method: "POST",

        headers: {
          "Content-Type": "application/json"
        },

        body: JSON.stringify(payload)

      })
              .then(function(response) {

                return response.text();

              })
              .then(function(result) {

                var jsonResult = JSON.parse(result);

                if (jsonResult.status === "SUCCESS") {

                  session.completePayment(ApplePaySession.STATUS_SUCCESS);

                } else {

                  session.completePayment(ApplePaySession.STATUS_FAILURE);
                  alert("Payment Fail::"+jsonResult.remark);
                }

                setTimeout(function() {

                  var form = document.createElement("form");

                  form.method = "POST";
                  form.action = "/transaction/ConfirmationCheckoutServlet";

                  var input = document.createElement("input");

                  input.type = "hidden";
                  input.name = "ctoken";
                  input.value = jsonResult.ctoken;
                  form.appendChild(input);

                  var input1 = document.createElement("input");
                  input1.type = "hidden";
                  input1.name = "trackingid";
                  input1.value = jsonResult.tid;
                  form.appendChild(input1);

                  var input2 = document.createElement("input");
                  input2.type = "hidden";
                  input2.name = "status";
                  input2.value = (jsonResult.status === "SUCCESS") ? "success" : jsonResult.status;
                  form.appendChild(input2);

                  var input3 = document.createElement("input");
                  input3.type = "hidden";
                  input3.name = "mid";
                  input3.value = jsonResult.mid;
                  form.appendChild(input3);

                  document.body.appendChild(form);

                  form.submit();

                }, 500);

              })
              .catch(function(error) {

                session.completePayment(ApplePaySession.STATUS_FAILURE);

                hideApplePayLoader();

                alert("Payment Processing Error:::");

              });

    };

    session.oncancel = function(event) {

      hideApplePayLoader();

    };

    session.begin();
  }


                    

Merchant Validation :

Merchant has to verify his identity with Apple Pay.

Sample java code for merchant validation
                        
KeyStore keyStore = KeyStore.getInstance("PKCS12");

                FileInputStream fis = new FileInputStream(validationPath);  //applepay_merchant_identity.p12 file path

                keyStore.load(fis, validationPass.toCharArray());

                Enumeration <String> aliases1 = keyStore.aliases();

                while (aliases1.hasMoreElements()) {
                    String alias = aliases1.nextElement();

                }

                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

                kmf.init(keyStore, validationPass.toCharArray());

                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

                tmf.init((KeyStore) null);

                X509KeyManager km = (X509KeyManager) kmf.getKeyManagers()[0];


                String[] aliases = km.getClientAliases("RSA", null);

                if (aliases != null) {
                    for (String a : aliases)
                 }

                SSLContext sslContext = SSLContext.getInstance("TLS");

                sslContext.init(
                        kmf.getKeyManagers(),
                        tmf.getTrustManagers(),
                        new SecureRandom()
                );

                JSONObject payload = new JSONObject();

                payload.put("merchantIdentifier",merchantIdentifier);

                payload.put("displayName",displayName);

                payload.put("initiative","web");

                payload.put("initiativeContext",host);

                transactionlogger.error("payload.toString() : " + payload.toString());
                
                SSLConnectionSocketFactory socketFactory =
                        new SSLConnectionSocketFactory(
                                sslContext,
                                new String[]{"TLSv1.2"},
                                null,
                                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

                client = HttpClients.custom()
                        .setSSLSocketFactory(socketFactory)
                        .setConnectionReuseStrategy(
                                NoConnectionReuseStrategy.INSTANCE)
                        .disableConnectionState()
                        .disableCookieManagement()
                        .build();

                X509Certificate cert = (X509Certificate) keyStore.getCertificate("1");

                

                HttpPost post = new HttpPost(validationURL);
                post.setHeader("Content-Type","application/json");
                post.setHeader("Connection", "close");
                post.setEntity(new StringEntity(payload.toString(),ContentType.APPLICATION_JSON));

                appleResponse = client.execute(post);

                int statusCode = appleResponse.getStatusLine().getStatusCode();

                transactionlogger.error("Apple Status Code : "+ statusCode);

                String responseBody = EntityUtils.toString(appleResponse.getEntity(),"UTF-8");
                    

For more information, please visit below documentation

Requesting an Apple Pay payment session | Apple Developer Documentation

Payment token decryption :

Merchant has to decrypt the token on merchant side and pass the decrypted data in the request

Sample java code for token decryption
                        
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public static String decrypt(String base64Data,
                                  String base64EphemeralKey, String pKeyPath, String mid)
            throws Exception {

        Security.addProvider(
                new org.bouncycastle.jce.provider.BouncyCastleProvider());

        // ----------------------------------------------------
        // 1. Decode Apple Pay values
        // ----------------------------------------------------
        // Decode inputs
        byte[] encryptedData = Base64.getDecoder().decode(base64Data);

        byte[] ephemeralKeyBytes = Base64.getDecoder().decode(base64EphemeralKey);

        transactionLogger.error(
                "Encrypted data length = " +
                        encryptedData.length
        );

        transactionLogger.error(
                "Ephemeral key length = " +
                        ephemeralKeyBytes.length
        );

        // ----------------------------------------------------
        // 2. Apple ephemeral public key
        // ----------------------------------------------------

        // Build ephemeral public key
        KeyFactory kf = KeyFactory.getInstance("EC");

        PublicKey ephemeralPublicKey =
                kf.generatePublic(
                        new X509EncodedKeySpec(ephemeralKeyBytes));
        transactionLogger.error(
                "Ephemeral public key loaded"
        );

        // ----------------------------------------------------
        // 3. Load merchant private key
        // ----------------------------------------------------
        // Load payment processing private key
        PrivateKey privateKey = loadECPrivateKey(pKeyPath);
        //PrivateKey privateKey = loadECPrivateKey2(pKeyPath);
        transactionLogger.error(
                "Private key loaded. Algorithm = " +
                        privateKey.getAlgorithm()
        );

        // ----------------------------------------------------
        // 4. ECDH
        // ----------------------------------------------------
        // ECDH
        KeyAgreement ka = KeyAgreement.getInstance("ECDH");

        ka.init(privateKey);
        ka.doPhase(ephemeralPublicKey, true);

        byte[] sharedSecret = ka.generateSecret();

        transactionLogger.error("Shared secret length : "+ sharedSecret.length);


        // ----------------------------------------------------
        // 5. Merchant ID Hash
        // ----------------------------------------------------



        String merchantId = mid;

        byte[] merchantIdHash =
                MessageDigest.getInstance("SHA-256")
                        .digest(
                                merchantId.getBytes(
                                        StandardCharsets.UTF_8
                                )
                        );

        transactionLogger.error(
                "Merchant ID = " +
                        merchantId);


        // ----------------------------------------------------
        // 6. Apple Pay KDF
        // ----------------------------------------------------

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // Counter = 00000001
        baos.write(new byte[] {
                0x00, 0x00, 0x00, 0x01
        });

        // Z = ECDH shared secret
        baos.write(sharedSecret);

        // Algorithm ID length = 13
        baos.write(0x0D);

        // Algorithm ID
        baos.write("id-aes256-GCM".getBytes(StandardCharsets.US_ASCII));

        // Party U Info
        baos.write("Apple".getBytes(StandardCharsets.US_ASCII));

        // Party V Info
        // SHA-256(merchant ID)
        baos.write(merchantIdHash);

        byte[] kdfInput = baos.toByteArray();

        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");

        byte[] aesKey = sha256.digest(kdfInput);

        transactionLogger.error(
                "AES Key Length = " + aesKey.length
        );


        transactionLogger.error(
                "Merchant ID = [" + merchantId + "]"
        );


        transactionLogger.error(
                "KDF Input Length = " + kdfInput.length
        );


        // ----------------------------------------------------
        // 7. AES-256-GCM
        // ----------------------------------------------------
        // AES-GCM
        SecretKeySpec key = new SecretKeySpec(aesKey,"AES");

        byte[] iv = new byte[16];

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding","BC");

        GCMParameterSpec spec = new GCMParameterSpec(128,iv);

        cipher.init(Cipher.DECRYPT_MODE,key,spec);

        byte[] decrypted = cipher.doFinal(encryptedData);

        String json = new String(decrypted,StandardCharsets.UTF_8);

        transactionLogger.error(
                "Decrypted JSON : "
                        + json);

        return json;

    }

    public static PrivateKey loadECPrivateKey(String keyPath)
    {
        String key = null;
        PrivateKey privateKey = null;
        try
        {
            key = new String(
                    Files.readAllBytes(Paths.get(keyPath))
            );


            key = key
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            byte[] decoded = Base64.getDecoder().decode(key);

            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);

            KeyFactory kf = KeyFactory.getInstance("EC");

            privateKey = kf.generatePrivate(spec);
        }
        catch (IOException e)
        {
            transactionLogger.error("IOException---",e);
            e.printStackTrace();
        }
        catch (NoSuchAlgorithmException e)
        {
            transactionLogger.error("NoSuchAlgorithmException---",e);
            e.printStackTrace();
        }
        catch (InvalidKeySpecException e)
        {
            transactionLogger.error("InvalidKeySpecException---",e);
            e.printStackTrace();
        }
        return privateKey;
    }
                    

For more information, please visit below documentation

https://developer.apple.com/documentation/passkit/payment-token-format-reference

3.3 API Integration

Merchant needs to pass decrypted token details to ’s Rest Api.

Refer below link for API sample request and response

Apple Pay Workflow

For Auth token, refer

Transaction AuthToken

Sample code to pass encrypted/token details to

paymentMode=APAY

paymentBrand=APPLEPAY

tokenType=applepayToken

Sample Request
                        
{
  "authentication": {
    "memberId": "11344",
    "checksum": "efac101d0b30bc51771567f9957cec58",
    "terminalId": "1106"
  },
  "merchantTransactionId": "01234",
  "paymentBrand": "APPLEPAY",
  "paymentMode": "APAY",
  "tokenType": "applepayToken",
  "paymentType": "DB",
  "amount": "50.00",
  "currency": "USD",
  "orderDescriptor": "Test Transaction",
  "shipping": {
    "country": "UK",
    "city": "Aston",
    "state": "NA",
    "postcode": "CH5 3LJ",
    "street1": "19 Scrimshire Lane"
  },
  "customer": {
    "telnocc": "+44",
    "phone": "07730432996",
    "email": "john.doe@xyz.com",
    "givenName": "John",
    "surname": "Doe",
    "ip": "192.168.0.1",
    "birthDate": "19890202",
    "customerId": "12345"
  },
  "applepayTokenPayload": {
    "tokenPaymentData": {
      "applicationPrimaryAccountNumber": "4111111111111111",
      "applicationExpirationDate": "2812",
      "currencyCode": "USD",
      "transactionAmount": "100.00",
      "deviceManufacturerIdentifier": "000100010000",
      "paymentDataType": {
        "onlinePaymentCryptogram": "MOLynj+8bwD66fFGi5AoABFIA=",
        "eci": "05"
      }
    }
  },
  "merchantRedirectUrl": "https://www.merchantRedirectUrl.com",
  "notificationUrl": "www.merchantNotificationUrl.com",
  "tmpl_amount": "50.00",
  "tmpl_currency": "USD",
  "merchant_website_url": "https://www.abc.com"
}
                    

4. Merchant Website Apple Pay Button Integration (Redirect)

This section describes the flow where Apple Pay payment button will be hosted on ’s side which can be opened on merchant frontend in an iframe.

In this case tokenType=applepayRedirect

In order to get it enabled, kindly contact our Support Team.

4.1 API Integration

Refer below link for API sample request and response

Apple Pay Workflow

Sample code to pass encrypted/token details to

paymentMode=APAY

paymentBrand=APPLEPAY

tokenType=applepayRedirect

Sample Request
                        
{

"authentication": {
    "memberId": "11344",
    "checksum": "efac101d0b30bc51771567f9957cec58",
    "terminalId": "1106"
  },
  "merchantTransactionId": "01234",
  "paymentBrand": "APPLEPAY",
  "paymentMode": "APAY",
  "tokenType": "applepayRedirect",
  "paymentType": "DB",
  "amount": "50.00",
  "currency": "USD",
  "orderDescriptor": "Test Transaction",
  "shipping": {
    "country": "UK",
    "city": "Aston",
    "state": "NA",
    "postcode": "CH5 3LJ",
    "street1": "19 Scrimshire Lane"
  },
  "customer": {
    "telnocc": "+44",
    "phone": "07730432996",
    "email": "john.doe@xyz.com",
    "givenName": "John",
    "surname": "Doe",
    "ip": "192.168.0.1",
    "birthDate": "19890202",
    "customerId": "12345"
  },
  "merchantRedirectUrl": "https://www.merchantRedirectUrl.com",
  "notificationUrl": "www.merchantNotificationUrl.com",
  "tmpl_amount": "50.00",
  "tmpl_currency": "USD",
  "merchant_website_url": "https://www.abc.com"
}
                    
Sample Response
                    
{
 "paymentId": "54279",
 "paymentType": "DB",
 "paymentBrand": "APPLEPAY",
 "paymentMode": "APAY",
 "amount": "50.00",
 "currency": "USD",
 "result": {
  "code": "10004",
  "description": "Transaction is pending"
 },
 "timestamp": "2026-09-05 16:03:53",
 "transactionStatus": "P",
 "redirect": {
  "url": "https://document.twpayz.com/transaction/ApplePayRedirect.jsp?trackingId=54279"
 }
}

                    

5. Hosted Payment Page with Apple Pay Button

5.1 General info

In this integration flow, the merchant integrates the Hosted Payment Page into their payment process. When a customer chooses to pay using Apple Pay, the customer is redirected to the -hosted payment page, where the Apple Pay button is displayed.

The customer completes the payment using Apple Pay directly on the -hosted payment page. handles the Apple Pay payment flow, including the interaction with Apple Pay, processing of the encrypted Apple Pay payment token, and forwarding the payment for further processing.

5.2 API Integration

Refer below link for API sample request and response

Standard checkout flow