Apple Pay - Payment Not Completed Error

I’m attempting to integrate Apple Pay with the existing Web Payments SDK, and I’m facing an issue.

Sandbox Application ID: sandbox-sq0idb-Cy6QuV_eqqgldwD2kwrABQ
Location ID: 733JJPMXQA97X
Apple Merchant ID: merchant.io.applova.square.733JJPMXQA97X

I have added my Sandbox Domain and have verified it.

When attempting to make the payment, the Apple Pay Payment dialog pops up. When biometric authorisation is given, then I see a “Payment not completed” message on the Apple Pay pop-up, and it dismisses automatically.

{
function loadSquareSdkAndInitApplePay(sdkUrl, applicationId, locationId) {
debugLog(‘Loading Square Web Payments SDK from: ’ + sdkUrl, ‘info’);
var script = document.createElement(‘script’);
script.src = sdkUrl;
script.onload = function() {
debugLog(‘Square Web Payments SDK loaded successfully’, ‘info’);
initializeSquareApplePay(applicationId, locationId);
};
script.onerror = function() {
debugLog(‘Failed to load Square Web Payments SDK’, ‘error’);
var wrapper = document.querySelector(’.apple-pay-button-wrapper’);
if (wrapper) { wrapper.style.display = ‘none’; }
};
document.head.appendChild(script);
}

function initializeSquareApplePay(applicationId, locationId) {
    debugLog('Initializing Square Apple Pay - AppId: ' + applicationId + ', LocationId: ' + locationId, 'info');

    var requestId = $('#apple-pay-request-id').val();
    var amount = parseFloat($('#apple-pay-amount').val());
    var currency = $('#apple-pay-currency').val();
    var tipAmount = parseFloat($('#apple-pay-tip').val() || '0');
    var subTotal = parseFloat($('#apple-pay-subtotal').val() || amount.toString());
    var completeUrl = $('#apple-pay-complete-url').val();
    var billingRequiredVal = $('#apple-pay-billing-address-required').val();
    var billingRequired = billingRequiredVal === 'true' || billingRequiredVal === true;

    debugLog('Square Apple Pay params — requestId: ' + requestId + ', amount: ' + amount
        + ', currency: ' + currency + ', tip: ' + tipAmount + ', subTotal: ' + subTotal, 'info');

    if (applicationId && applicationId.indexOf('sandbox-') === 0) {
        debugLog('⚠️ SANDBOX mode detected (appId starts with "sandbox-"). '
            + 'Apple Pay requires Apple Pay sandbox test cards — real cards will show "payment not completed". '
            + 'Add sandbox tester cards via Apple Developer portal to test end-to-end.', 'warn');
    }

    if (!window.Square) {
        debugLog('Square SDK not available after load — hiding Apple Pay button', 'error');
        var wrapper = document.querySelector('.apple-pay-button-wrapper');
        if (wrapper) { wrapper.style.display = 'none'; }
        return;
    }

    var payments;
    try {
        payments = Square.payments(applicationId, locationId);
        debugLog('Square.payments() initialized', 'info');
    } catch (e) {
        debugLog('Failed to initialize Square.payments(): ' + e.message, 'error');
        var wrapper = document.querySelector('.apple-pay-button-wrapper');
        if (wrapper) { wrapper.style.display = 'none'; }
        return;
    }

    var lineItems = [];
    if (subTotal > 0) {
        lineItems.push({ label: 'Subtotal', amount: subTotal.toFixed(2), pending: false });
    }
    if (tipAmount > 0) {
        lineItems.push({ label: 'Tip', amount: tipAmount.toFixed(2), pending: false });
    }

    var paymentRequestConfig = {
        countryCode: 'US',
        currencyCode: currency,
        lineItems: lineItems,
        total: { label: 'Total', amount: amount.toFixed(2), pending: false }
    };
    debugLog('Square paymentRequest config: ' + JSON.stringify(paymentRequestConfig), 'info');

    var squarePaymentRequest = payments.paymentRequest(paymentRequestConfig);

    var squareApplePay = null;
    payments.applePay(squarePaymentRequest).then(function(applePay) {
        squareApplePay = applePay;
        debugLog('Square applePay instance ready — showing button', 'info');

        var wrapper = document.querySelector('.apple-pay-button-wrapper');
        if (wrapper) { wrapper.style.display = 'block'; }

        // Define the global startApplePaySession so the button onclick calls it
        window.startApplePaySession = function() {
            var billingModal = document.getElementById('apple-pay-billing-modal');
            if (billingModal && billingRequired) {
                debugLog('Billing address required — showing modal before Square Apple Pay', 'info');
                pendingApplePayBilling = null;
                $('#apple-pay-billing-address').val('');
                $('#apple-pay-billing-zip').val('');
                $('#apple-pay-billing-address-error').text('');
                $('#apple-pay-billing-zip-error').text('');
                $('#apple-pay-billing-address').removeClass('billing-input-error');
                $('#apple-pay-billing-zip').removeClass('billing-input-error');
                $('#apple-pay-billing-modal').addClass('is-visible').css('display', 'flex');
                return;
            }
            triggerSquareApplePay(squareApplePay, completeUrl, requestId);
        };

        // Wire the billing modal submit button to continue to Square tokenization
        window.beginApplePaySession = function() {
            triggerSquareApplePay(squareApplePay, completeUrl, requestId);
        };

    }).catch(function(e) {
        debugLog('Square applePay() not available: ' + e.message, 'warn');
        var wrapper = document.querySelector('.apple-pay-button-wrapper');
        if (wrapper) { wrapper.style.display = 'none'; }
    });
}

function triggerSquareApplePay(squareApplePay, completeUrl, requestId) {
    debugLog('Triggering Square Apple Pay tokenization...', 'info');
    // NOTE: Do NOT show the processing overlay before tokenize() on mobile.
    // Showing a full-page overlay before the Apple Pay sheet can interfere with
    // the user gesture chain and cause the sheet to be dismissed immediately.
    // The overlay is shown only after the user authorizes (status OK).

    squareApplePay.tokenize().then(function(tokenResult) {
        // Log the full result — this is our primary diagnostic tool
        try {
            debugLog('Square tokenize() full result: ' + JSON.stringify(tokenResult), 'info');
        } catch (jsonErr) {
            debugLog('Square tokenize() status: ' + tokenResult.status + ' (could not serialize full result)', 'info');
        }

        if (tokenResult.status === 'OK') {
            debugLog('Square Apple Pay authorized — showing processing overlay', 'info');
            $('#processing-view').show();
            $('.overlay').show();
            var requestData = { squareNonce: tokenResult.token };
            if (pendingApplePayBilling) {
                requestData.billingAddress = pendingApplePayBilling.address;
                requestData.billingZip = pendingApplePayBilling.zip;
                pendingApplePayBilling = null;
            }
            debugLog('Sending Square nonce to backend: ' + completeUrl, 'info');
            sendSquareApplePayNonce(completeUrl, requestData);

        } else if (tokenResult.status === 'Cancel') {
            // User dismissed the Apple Pay sheet — restore UI without an error message
            debugLog('Apple Pay sheet dismissed (status: Cancel) — restoring UI', 'info');
            $('#processing-view').hide();
            $('.overlay').hide();

        } else {
            // Actual tokenization failure — extract every detail Square provides
            debugLog('Square tokenize() error — status: ' + tokenResult.status, 'error');
            $('#processing-view').hide();
            $('.overlay').hide();
            var errMsg = 'Apple Pay failed. Please try again.';
            if (tokenResult.errors && tokenResult.errors.length > 0) {
                tokenResult.errors.forEach(function(err, idx) {
                    debugLog('  Error[' + idx + '] field: ' + (err.field || 'n/a')
                        + ' | type: ' + (err.type || 'n/a')
                        + ' | message: ' + (err.message || 'n/a'), 'error');
                });
                errMsg = tokenResult.errors[0].message || errMsg;
            } else {
                debugLog('  No error details provided by Square SDK for status: ' + tokenResult.status, 'warn');
            }
            showError(errMsg, true);
        }
    }).catch(function(e) {
        debugLog('Square tokenize() threw an exception: ' + (e.message || e), 'error');
        if (e.stack) {
            debugLog('Exception stack: ' + e.stack, 'error');
        }
        $('#processing-view').hide();
        $('.overlay').hide();
        var msg = (e.message || '').toLowerCase();
        if (msg.indexOf('cancel') !== -1 || msg.indexOf('dismiss') !== -1) {
            debugLog('Apple Pay cancelled (surfaced as exception)', 'info');
        } else {
            showError('Apple Pay failed. Please try again.', true);
        }
    });
}

function sendSquareApplePayNonce(completeUrl, requestData) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', completeUrl, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.timeout = 30000;

    xhr.onload = function() {
        debugLog('Square Apple Pay completion response - HTTP ' + xhr.status, 'info');
        var response = null;
        try { response = JSON.parse(xhr.responseText); } catch (e) { /* non-JSON */ }

        if (xhr.status >= 200 && xhr.status < 300 && response && response.status === 'success') {
            debugLog('Square Apple Pay payment successful — redirecting', 'info');
            $('#processing-view').hide();
            $('.overlay').hide();
            $('#apple-pay-billing-modal').removeClass('is-visible').css('display', 'none');
            window.location.href = response.redirectUrl;
        } else {
            debugLog('Square Apple Pay payment failed', 'error');
            $('#processing-view').hide();
            $('.overlay').hide();
            var msg = (response && response.error) ? response.error : 'Payment failed. Please try again.';
            showError(msg, true);
        }
    };

    xhr.onerror = function() {
        $('#processing-view').hide();
        $('.overlay').hide();
        showError('Network error. Please check your connection and try again.', true);
    };

    xhr.ontimeout = function() {
        $('#processing-view').hide();
        $('.overlay').hide();
        showError('Request timed out. Please try again.', true);
    };

    xhr.send(JSON.stringify(requestData));
}

}

Front End Logs:

[12:06:29 AM] [INFO] Digital Wallet module loaded
[12:06:29 AM] [INFO] Debug log panel found: yes
[12:06:29 AM] [INFO] Debug log content found: yes
[12:06:29 AM] [INFO] Running in regular browser (not WebView)
[12:06:29 AM] [INFO] Platform: iPad
[12:06:29 AM] [INFO] isSimulator() result: false
[12:06:29 AM] [INFO] Checking Apple Pay availability… Provider: squareup
[12:06:29 AM] [INFO] Checking Apple Pay availability…
[12:06:29 AM] [INFO] ApplePaySession API found (Safari)
[12:06:29 AM] [INFO] ApplePaySession.canMakePayments() = true
[12:06:29 AM] [INFO] User Agent: Mozilla/5.0 (iPad; CPU OS 26_3_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/142.0.7444.128 Mobile/15E148 Safari/604.1
[12:06:29 AM] [INFO] isWebView() result: true
[12:06:29 AM] [INFO] ReactNativeWebView available: false
[12:06:29 AM] [INFO] webkit.messageHandlers available: true
[12:06:29 AM] [WARN] Apple Pay: Running in WebView - Apple Pay may be restricted by Apple security policies
[12:06:29 AM] [INFO] Square provider: loading Square Web Payments SDK for Apple Pay
[12:06:29 AM] [INFO] Loading Square Web Payments SDK from: https://sandbox.web.squarecdn.com/v1/square.js
[12:06:29 AM] [INFO] Google Pay initialization starting (non-digital-wallet page)…
[12:06:29 AM] [INFO] Environment: PRODUCTION
[12:06:29 AM] [INFO] User Agent: Mozilla/5.0 (iPad; CPU OS 26_3_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/142.0.7444.128 Mobile/15E148 Safari/604.1
[12:06:29 AM] [INFO] isWebView() result: true
[12:06:29 AM] [INFO] ReactNativeWebView available: false
[12:06:29 AM] [INFO] webkit.messageHandlers available: true
[12:06:29 AM] [INFO] WebView detected: true
[12:06:29 AM] [INFO] Platform: iPad
[12:06:29 AM] [INFO] isSimulator() result: false
[12:06:29 AM] [INFO] Simulator detected: false
[12:06:29 AM] [INFO] :white_check_mark: Google Pay JS API loaded successfully
[12:06:29 AM] [INFO] Testing Google Pay in simulators:
[12:06:29 AM] [INFO] - Android Emulator: Requires Google Play Services and test account
[12:06:29 AM] [INFO] - iOS Simulator: Use Chrome browser, may have limitations
[12:06:29 AM] [INFO] - Best practice: Test on real devices for production
[12:06:29 AM] [INFO] Initializing Google Pay…
[12:06:29 AM] [INFO] Google Pay config - RequestId: 69f1ad01e60a3f0d9d95396e, Amount: 6.36, Currency: USD, Tip: 0, SubTotal: 6.36, Type: GATEWAY, Environment: PRODUCTION
[12:06:29 AM] [INFO] Google Pay Type: GATEWAY - Using payment gateway tokenization
[12:06:29 AM] [WARN] :warning: Google Pay Merchant ID is missing. Google Pay will not be available.
[12:06:29 AM] [INFO] Square Web Payments SDK loaded successfully
[12:06:29 AM] [INFO] Initializing Square Apple Pay - AppId: sandbox-sq0idb-Cy6QuV_eqqgldwD2kwrABQ, LocationId: 733JJPMXQA97X
[12:06:29 AM] [INFO] Square Apple Pay params — requestId: 69f1ad01e60a3f0d9d95396e, amount: 6.36, currency: USD, tip: 0, subTotal: 6.36
[12:06:29 AM] [WARN] :warning: SANDBOX mode detected (appId starts with “sandbox-”). Apple Pay requires Apple Pay sandbox test cards — real cards will show “payment not completed”. Add sandbox tester cards via Apple Developer portal to test end-to-end.
[12:06:29 AM] [INFO] Square.payments() initialized
[12:06:29 AM] [INFO] Square paymentRequest config: {“countryCode”:“US”,“currencyCode”:“USD”,“lineItems”:[{“label”:“Subtotal”,“amount”:“6.36”,“pending”:false}],“total”:{“label”:“Total”,“amount”:“6.36”,“pending”:false}}
[12:06:29 AM] [INFO] Square applePay instance ready — showing button
[12:06:36 AM] [INFO] Triggering Square Apple Pay tokenization…
[12:06:45 AM] [INFO] Square tokenize() full result: {“status”:“Cancel”}
[12:06:45 AM] [INFO] Apple Pay sheet dismissed (status: Cancel) — restoring UI

The device we are using is a Sandbox Account. And we have added Test Cards from Apple.

There’s no other information on why the payment doesn’t go through.

Please assist.

@Bryan-Square Any assistance here would be appreciated,