Apple Pay Js in Not working (while getting all data from serverside for validation)

Subject: Apple Pay JS - "Payment Was Cancelled by the User" Issue (Braintree + Server-Side Validation)


Issue

I am implementing Apple Pay using Braintree with server-side validation. However, when initiating the payment process, I receive the following error:

[Log] Payment was cancelled by the user: (apple-pay-test-ucxp.onrender.com, line 286)

Additionally, the console logs an ApplePayCancelEvent with:

sessionError: {code: "unknown", info: {}}

Despite successfully fetching merchant session validation data from the backend and completing merchant validation, the payment process does not proceed.


Setup Details

  • Payment Processor: Braintree (Apple Pay integration)
  • Backend API: Fetching merchant session validation via createPaymentSessionGet
  • Payment Processing: Using Braintree nonce tokenization

Client-Side Code (Key Sections)

session.onvalidatemerchant = async (event) => {
  try {
    const merchantSession = await fetch(
      "https://api.paybito.com:9443/ApplePay/api/apple-pay/createPaymentSessionGet",
      {
        method: "GET",
        headers: { "Content-Type": "application/json" },
      }
    ).then((res) => res.json());
    session.completeMerchantValidation(merchantSession);
  } catch (err) {
    console.error("Merchant validation failed:", err);
    session.abort();
  }
};

session.onpaymentauthorized = async (event) => {
  try {
    const payload = await applePayInstance.tokenize({
      token: event.payment.token,
    });
    const response = await fetch(
      "https://api.paybito.com:9443/ApplePay/api/braintree/process-payment",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ nonce: payload.nonce }),
      }
    ).then((res) => res.json());
    
    if (response.success) {
      session.completePayment(ApplePaySession.STATUS_SUCCESS);
    } else {
      session.completePayment(ApplePaySession.STATUS_FAILURE);
    }
  } catch (err) {
    console.error("Payment authorization failed:", err);
    session.completePayment(ApplePaySession.STATUS_FAILURE);
  }
};

Observations

  1. Merchant validation completes successfully.
  2. The error occurs before onpaymentauthorized executes.
  3. Session error code is unknown, making debugging difficult.

Questions

  • Has anyone encountered this issue before?
  • Could this be related to how the validation session is fetched from the backend?
  • Is there a way to obtain more meaningful debug information from Apple Pay?

Any insights would be greatly appreciated!

Where is the code that actually logs "sessionError"? Sounds like your onvalidatemerchant completes without error and your onpaymentauthorized never starts, so what catches and logs that error?

(No, I don't know anything about Braintree, and not a whole lot about Apple Pay. I've just implemented it once myself and have hit some cases where I haven't been able to get help either. So I'm empathetic to others struggling and trying to help out any way I can.)

Here is the All Code

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Apple Pay Button Example</title>
  <style>
    .apple-pay-button {
      -webkit-appearance: -apple-pay-button;
      -apple-pay-button-type: buy;
      height: 50px;
      width: 200px;
      border-radius: 5px;
    }
  </style>
 
    <script src="https://js.braintreegateway.com/web/3.92.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.92.0/js/apple-pay.min.js"></script>
<!-- includes the Braintree JS client SDK --> 
<script src="https://js.braintreegateway.com/web/dropin/1.44.0/js/dropin.min.js"></script> 
<script crossorigin 
      src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"> 
    </script>
<!-- includes jQuery -->
<script src="http://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>

  </script>
</head>

<body>

  <h2>Pay with Apple Pay</h2>
  <button id="apple-pay-button" class="apple-pay-button"></button>




  <script>
    let applePayInstance; // Keep it available for later use
    let authorization = ""; // Store the client token
  
    // Preload Braintree client token and Apple Pay instance
    async function initializeBraintree() {
      try {
        const response = await fetch("https://api.paybito.com:9443/ApplePay/api/braintree/client-token");
        const data = await response.json();
        authorization = data?.btToken;
  
        const clientInstance = await braintree.client.create({
          authorization,
        });
  
        applePayInstance = await braintree.applePay.create({
          client: clientInstance,
        });
  
        console.log("Braintree Apple Pay initialized");
      } catch (error) {
        console.error("Error initializing Braintree:", error);
      }
    }
  
    // Start Apple Pay session on button click
    function startApplePaySession() {
      if (!applePayInstance) {
        alert("Apple Pay is not ready. Please try again.");
        return;
      }
  
      const paymentRequest = applePayInstance.createPaymentRequest({
        total: {
          label: "Apple Pay Example",
          amount: "8.99",
        },
        currencyCode: "USD",
        countryCode: "US",
        supportedNetworks: ["visa", "masterCard", "amex", "discover"],
        merchantCapabilities: ["supports3DS"],
      });
  
      const session = new ApplePaySession(3, paymentRequest);
  
  session.onvalidatemerchant = async (event) => {
  try {
    const merchantSession = await fetch(
      "https://api.paybito.com:9443/ApplePay/api/apple-pay/createPaymentSessionGet",
      {
        method: "GET",
        headers: { "Content-Type": "application/json" },
        // body: JSON.stringify({ validationURL: event.validationURL }),
      }
    ).then((res) => res.json()).then((data)=>{
      console.log("data",data)
      session.completeMerchantValidation(data);
    })
 
    // console.log("Merchant Session:", );
   
    
  } catch (err) {
    console.error("Merchant validation failed:", err);
    session.abort();
  }
};

session.onpaymentmethodselected = event => {
  // No updates or errors are needed, pass an empty object.
  const update = {};
  session.completePaymentMethodSelection(update);
};

session.onshippingmethodselected = event => {
  // No updates or errors are needed, pass an empty object.
  const update = {};
  session.completeShippingMethodSelection(update);
};
session.onshippingcontactselected = event => {
  const update = {};
  session.completeShippingContactSelection(update);
};
      session.onpaymentauthorized = async (event) => {
       
        const payment = event.payment;
        console.log(event,"event",payment)

        try {
          const payload = await applePayInstance.tokenize({
            token: event.payment.token,
          });

          console.log(payload,"payload")
  
          const response = await fetch(
            "https://api.paybito.com:9443/ApplePay/api/braintree/process-payment",
            {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ nonce: payload.nonce}),
            }
          ).then((res) => res.json());
  
          if (response.success) {
            session.completePayment(ApplePaySession.STATUS_SUCCESS);
            alert("Payment successful!");
          } else {
            session.completePayment(ApplePaySession.STATUS_FAILURE);
            alert("Payment failed.");
          }
        } catch (err) {
          console.error("Payment authorization failed:", err);
          session.completePayment(ApplePaySession.STATUS_FAILURE);
        }
      };
  
      session.oncancel = (event) => {
        console.log("Payment was cancelled by the user:", event);
        const reason = event.sessionError?.code || "User closed the payment modal.";
        console.error("Cancellation reason:", event?.sessionError?.info || "Unknown reason");
  console.error("Cancellation reason:", reason);
      };
  
      session.begin();
    }
  
    // Initialize Braintree on page load
    document.addEventListener("DOMContentLoaded", () => {
      initializeBraintree();
  
      // Attach click event to the Apple Pay button
      if (window.ApplePaySession && ApplePaySession.canMakePayments()) {
        console.log("Apple Pay is available!");
        document
          .getElementById("apple-pay-button")
          .addEventListener("click", startApplePaySession);
      } else {
        console.error("Apple Pay is not supported on this device.");
        document.getElementById("apple-pay-button").style.display = "none";
      }
    });
  </script>

  
</body>

</html>

Could you please review the code and help me identify the error? Thank you!

Apple Pay Js in Not working (while getting all data from serverside for validation)
 
 
Q