Post

Replies

Boosts

Views

Activity

Apple Pay on the Web sheet fails with PKPaymentAuthorizationStateFatalError after successful merchant validation (PROD trust policy)
Environment macOS 26.5.1 (Build 25F80) Safari 26.5 Apple Pay JS API version 8 Using https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js Reproduces on the staging website (Does not reproduce on the production website with non sandbox account) Signed into a Sandbox Tester Apple ID, with a test credit card registered exactly per Apple Pay Sandbox Testing — this is the only card in Wallet on the laptop Merchant validation is performed by our staging backend against our real (non-sandbox) merchant identity/certificate — i.e. a sandbox tester card being evaluated against a staging merchant session, which per the sandbox testing documentation is the expected/supported setup for testing on a live site without a separate merchant sandbox environment Summary After completeMerchantValidation() succeeds and the merchant session is accepted, the payment sheet still terminates with PKPaymentAuthorizationStateFatalError a few hundred milliseconds later, right after the native side logs a second "Evaluating merchant session using PROD trust policy." for the same session. The user sees "Payment failed" and the sheet closes. This happens on the very first attempt, with no user interaction beyond tapping the Apple Pay button — no shipping address is even selected before the failure. Since the sandbox tester card + production merchant session combination is the setup the sandbox testing guide describes, we'd expect the sheet to proceed to onpaymentauthorized (Apple's sandbox docs describe payments as being processed as $0 test transactions in this mode) rather than fail natively before that stage is ever reached. Steps to reproduce Visit an item page with an Apple Pay button. Tap the Apple Pay button to open the payment sheet. The sheet presents normally; no interaction with shipping address or payment method is required for the failure to occur. Within ~1 second, the sheet dismisses and displays a "Payment failed" error state. Minimal reproduction of the JS side const session = new ApplePaySession(8, { countryCode: 'US', currencyCode: 'USD', supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'], merchantCapabilities: ['supports3DS'], total: { label: 'Merchant', amount: '10.00' }, }); session.onvalidatemerchant = async () => { const merchantSession = await validateMerchantOnServer(); // succeeds session.completeMerchantValidation(merchantSession); // accepted — see log below }; session.onpaymentmethodselected = () => { session.completePaymentMethodSelection({ newTotal: { label: 'Merchant', amount: '10.00' }, }); }; session.oncancel = event => console.log('cancelled', event); session.begin(); We reproduced this with both our full checkout implementation and this stripped-down request object (no shipping contact required, no line items) — same failure either way, which rules out anything about our order/line-item data. What we expected The sheet to proceed to the biometric authorization step and dispatch onpaymentauthorized once the user confirms. What actually happens The sheet fails immediately with a PKPaymentAuthorizationStateFatalError transition, even though every JS-side completion call (completeMerchantValidation, the payment-method selection) succeeded and returned PKPaymentAuthorizationStatusSuccess. System log (captured via log stream --info --debug --predicate 'process == "Safari" OR process == "passd" OR process == "com.apple.PassKit.PaymentAuthorizationUIExtension"') 21:50:41.544 PaymentCoordinator::beginPaymentSession() -> 1 21:50:41.570 Received prepareWithPaymentRequest: <private> 21:50:42.297 PaymentCoordinator::validateMerchant() 21:50:42.563 PaymentCoordinator::completeMerchantValidation() 21:50:42.564 Received merchant session update with status:PKPaymentAuthorizationStatusSuccess session:<private> 21:50:42.564 Evaluating merchant session using PROD trust policy. <- 1st occurrence, passes 21:50:42.572 PaymentCoordinator::didSelectPaymentMethod() 21:50:42.572 PaymentCoordinator::completePaymentMethodSelection() 21:50:42.653 Evaluating merchant session using PROD trust policy. <- 2nd occurrence 21:50:42.656 State machine change state from ClientCallback to PrepareTransactionDetails with param: <private> 21:50:43.391 Task Completed: <private> 21:50:43.393 State machine change state from PrepareTransactionDetails to PKPaymentAuthorizationStateFatalError with param: <private> 21:50:43.393 Error Payment failed with fatal error <private> 21:50:45.130 PaymentCoordinator::didCancelPaymentSession() The first "Evaluating merchant session using PROD trust policy." line passes (the flow continues to didSelectPaymentMethod). The second occurrence is immediately followed by Task Completed and then the FatalError transition, which suggests PassKit re-validates the merchant session's trust a second time right before PrepareTransactionDetails, and that second check is what's rejecting the session — even though the identical session object was accepted moments earlier at completeMerchantValidation(). What we've tried / ruled out Confirmed every JS completion method (completeMerchantValidation, completePaymentMethodSelection) is called and returns success — no 30-second timeout, no missing completion call, no thrown exception on our side. Reproduced with a minimal, hardcoded ApplePayPaymentRequest (fixed $10.00 total, no shipping fields) — rules out anything about our real order/line-item/sales-tax data. Reproduced on the main branch and independently on our staging deployment running unmodified code — rules out anything specific to our recent frontend changes. The <private> redaction in the system log prevents us from seeing what specifically fails during the second trust evaluation. Confirmed the sandbox tester card and its registration follow the sandbox testing guide exactly — it's the only card on the device, and no other Wallet cards are involved. Question What does PassKit's second "Evaluating merchant session using PROD trust policy" check (immediately before PrepareTransactionDetails) actually validate, beyond what's already checked at completeMerchantValidation() time? Is a sandbox tester card being evaluated against a production merchant session expected to pass this second check, or does sandbox testing per the linked guide require something additional on the merchant/session side (e.g. a specific field on the merchant session, or a specific environment value) that our backend isn't setting? Is there a way to get an unredacted reason for the rejection — e.g. via a private-data-enabled log profile or another diagnostic — since the redacted system log alone doesn't surface one?
0
0
19
5h
Apple Pay on the Web sheet fails with PKPaymentAuthorizationStateFatalError after successful merchant validation (PROD trust policy)
Environment macOS 26.5.1 (Build 25F80) Safari 26.5 Apple Pay JS API version 8 Using https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js Reproduces on the staging website (Does not reproduce on the production website with non sandbox account) Signed into a Sandbox Tester Apple ID, with a test credit card registered exactly per Apple Pay Sandbox Testing — this is the only card in Wallet on the laptop Merchant validation is performed by our staging backend against our real (non-sandbox) merchant identity/certificate — i.e. a sandbox tester card being evaluated against a staging merchant session, which per the sandbox testing documentation is the expected/supported setup for testing on a live site without a separate merchant sandbox environment Summary After completeMerchantValidation() succeeds and the merchant session is accepted, the payment sheet still terminates with PKPaymentAuthorizationStateFatalError a few hundred milliseconds later, right after the native side logs a second "Evaluating merchant session using PROD trust policy." for the same session. The user sees "Payment failed" and the sheet closes. This happens on the very first attempt, with no user interaction beyond tapping the Apple Pay button — no shipping address is even selected before the failure. Since the sandbox tester card + production merchant session combination is the setup the sandbox testing guide describes, we'd expect the sheet to proceed to onpaymentauthorized (Apple's sandbox docs describe payments as being processed as $0 test transactions in this mode) rather than fail natively before that stage is ever reached. Steps to reproduce Visit an item page with an Apple Pay button. Tap the Apple Pay button to open the payment sheet. The sheet presents normally; no interaction with shipping address or payment method is required for the failure to occur. Within ~1 second, the sheet dismisses and displays a "Payment failed" error state. Minimal reproduction of the JS side const session = new ApplePaySession(8, { countryCode: 'US', currencyCode: 'USD', supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'], merchantCapabilities: ['supports3DS'], total: { label: 'Merchant', amount: '10.00' }, }); session.onvalidatemerchant = async () => { const merchantSession = await validateMerchantOnServer(); // succeeds session.completeMerchantValidation(merchantSession); // accepted — see log below }; session.onpaymentmethodselected = () => { session.completePaymentMethodSelection({ newTotal: { label: 'Merchant', amount: '10.00' }, }); }; session.oncancel = event => console.log('cancelled', event); session.begin(); We reproduced this with both our full checkout implementation and this stripped-down request object (no shipping contact required, no line items) — same failure either way, which rules out anything about our order/line-item data. What we expected The sheet to proceed to the biometric authorization step and dispatch onpaymentauthorized once the user confirms. What actually happens The sheet fails immediately with a PKPaymentAuthorizationStateFatalError transition, even though every JS-side completion call (completeMerchantValidation, the payment-method selection) succeeded and returned PKPaymentAuthorizationStatusSuccess. System log (captured via log stream --info --debug --predicate 'process == "Safari" OR process == "passd" OR process == "com.apple.PassKit.PaymentAuthorizationUIExtension"') 21:50:41.544 PaymentCoordinator::beginPaymentSession() -> 1 21:50:41.570 Received prepareWithPaymentRequest: <private> 21:50:42.297 PaymentCoordinator::validateMerchant() 21:50:42.563 PaymentCoordinator::completeMerchantValidation() 21:50:42.564 Received merchant session update with status:PKPaymentAuthorizationStatusSuccess session:<private> 21:50:42.564 Evaluating merchant session using PROD trust policy. <- 1st occurrence, passes 21:50:42.572 PaymentCoordinator::didSelectPaymentMethod() 21:50:42.572 PaymentCoordinator::completePaymentMethodSelection() 21:50:42.653 Evaluating merchant session using PROD trust policy. <- 2nd occurrence 21:50:42.656 State machine change state from ClientCallback to PrepareTransactionDetails with param: <private> 21:50:43.391 Task Completed: <private> 21:50:43.393 State machine change state from PrepareTransactionDetails to PKPaymentAuthorizationStateFatalError with param: <private> 21:50:43.393 Error Payment failed with fatal error <private> 21:50:45.130 PaymentCoordinator::didCancelPaymentSession() The first "Evaluating merchant session using PROD trust policy." line passes (the flow continues to didSelectPaymentMethod). The second occurrence is immediately followed by Task Completed and then the FatalError transition, which suggests PassKit re-validates the merchant session's trust a second time right before PrepareTransactionDetails, and that second check is what's rejecting the session — even though the identical session object was accepted moments earlier at completeMerchantValidation(). What we've tried / ruled out Confirmed every JS completion method (completeMerchantValidation, completePaymentMethodSelection) is called and returns success — no 30-second timeout, no missing completion call, no thrown exception on our side. Reproduced with a minimal, hardcoded ApplePayPaymentRequest (fixed $10.00 total, no shipping fields) — rules out anything about our real order/line-item/sales-tax data. Reproduced on the main branch and independently on our staging deployment running unmodified code — rules out anything specific to our recent frontend changes. The <private> redaction in the system log prevents us from seeing what specifically fails during the second trust evaluation. Confirmed the sandbox tester card and its registration follow the sandbox testing guide exactly — it's the only card on the device, and no other Wallet cards are involved. Question What does PassKit's second "Evaluating merchant session using PROD trust policy" check (immediately before PrepareTransactionDetails) actually validate, beyond what's already checked at completeMerchantValidation() time? Is a sandbox tester card being evaluated against a production merchant session expected to pass this second check, or does sandbox testing per the linked guide require something additional on the merchant/session side (e.g. a specific field on the merchant session, or a specific environment value) that our backend isn't setting? Is there a way to get an unredacted reason for the rejection — e.g. via a private-data-enabled log profile or another diagnostic — since the redacted system log alone doesn't surface one?
Replies
0
Boosts
0
Views
19
Activity
5h