Resolved — the root cause was on our side: we were discarding event.validationURL.
Thank you, the two-stage description was what made this findable. Posting the
resolution in case it helps someone who lands here with sessionError.code: "unknown".
What was wrong
Our onvalidatemerchant handler ignored the event argument entirely and asked our
own backend for a merchant session using only the domain name:
session.onvalidatemerchant = async () => {
const merchantSession = await validateMerchant(); // event.validationURL dropped
session.completeMerchantValidation(merchantSession);
};
The backend then requested that session from a hardcoded production host:
POST https://apple-pay-gateway.apple.com/paymentservices/paymentSession
Signed in with a Sandbox Tester account, Safari was handing us a different URL —
https://apple-pay-gateway-cert.apple.com/paymentservices/startSession. By
overriding it with the production host, we were requesting a production-trust
session for a sandbox DAN.
The fix
Use the URL Apple supplies, per session:
session.onvalidatemerchant = async event => {
const merchantSession = await validateMerchant(event.validationURL);
session.completeMerchantValidation(merchantSession);
};
Notably, no separate sandbox merchant enrollment was needed. The merchant
identifier, both certificates and the registered domain were all correct already,
which is what made this so hard to see — every checklist item was green.
Note: The production Apple Pay gateway, hardcoded for our usage, had successfully worked even with sandbox account at least until some time in 2026, but suddenly has not been working.
One documentation request
Is the set of gateway hosts, and the per-environment paths, published anywhere
authoritative? apple-pay-gateway-cert.apple.com does not appear on the Apple Pay
Sandbox Testing page, and the only reference I could find to the host varying at all
was an unanswered 2018 forum post.
Topic:
Safari & Web
SubTopic:
General
Tags: