Superseded by #1012 - #1009
Closed
claude[bot] wants to merge 2 commits into
Closed
Conversation
A second ApplePayButton on the same page found the first instance's script tag before Apple's SDK had executed, resolved its script wait immediately, and cached the resulting "unsupported" for the life of the instance, so the button never mounted. Move script loading to a single module-level promise and stop memoizing an "unsupported" availability result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HHDw5Q6brV8iTrctYMJJr
🦋 Changeset detectedLatest commit: 9e56bbb The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
claude
Bot
requested review from
ana-maksimovskikh and
Max Harrison (maxharrison)
September 7, 2026 15:38
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014HHDw5Q6brV8iTrctYMJJr
Alexander (alexander-rw)
deleted the
claude/applepay-availability-two-instance-fix
branch
September 8, 2026 09:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by oisín, Alexander Reyes-Wainwright · Slack thread
Why
Before: A page that constructs two
ui.applePay()instances gets"unsupported"from the second one, permanently. Each instance loaded the Apple Pay SDK itself, and an instance that found a<script>tag already in the DOM marked the script loaded without checking whether it had run — so the second instance sailed past the load wait while Apple's script was still in flight, found noApplePaySession, and returned"unsupported".availability()cached that answer for the life of the instance and cleared it only on a thrown probe, so no later call and no remount recovered. The express-checkout button never appeared. This only shows up whereApplePaySessioncomes from Apple's injected SDK rather than the browser: Chrome and Edge on macOS and iOS, in-app browsers, older Safari. Native Safari 17+ definesapplePayCapabilitiesitself, so it never reproduced.After: Every
ApplePayButtonon the page shares one SDK load, and the second instance waits for the same script the first one injected, so both resolve"available"and the express-checkout button mounts. A"unsupported"result is no longer cached, so a call made before the SDK is ready no longer poisons every call after it. A tag the merchant injected themselves — the workaround the reporting customer is running — is now handled whether or not it finished loading before the SDK was constructed. Browsers that genuinely have no Apple Pay still get"unsupported", not an error.How
Script loading moves from per-instance
#injectScript()to a single module-levelloadApplePaySDK()promise: it resolves immediately when a tag exists andApplePaySession.applePayCapabilitiesis already a function, otherwise listens forloadon the one tag and applies the existing 10s timeout — rejecting for a tag we injected, resolving for one we did not, since a tag that already ran will never fireloadagain.availability()keeps memoizing"available"and"unavailable"and drops"unsupported"so the next call re-probes.Where it came from
Introduced by
e6891313a42fee08ebf86894b257d4190989a334; shipped in@evervault/browser@2.62.1(Aug 10) but only reached prod when 2.63.0 deployed 2026-09-02, which is why it surfaced in September.Two calls worth a reviewer's attention
The resolve-immediately fast path is gated on a
<script>tag existing rather than onapplePayCapabilitiesalone, because the script-loading suite'sbeforeEachstubs a readyApplePaySessionwith no tag in the DOM and an unconditional fast path breaks the existing onload and timeout tests.For a tag we did not inject, the 10s timeout resolves rather than rejects, so a merchant-self-injected tag on a browser without Apple Pay waits up to 10s before answering
"unsupported".Testing
The new regression test fails on master with
expected 'unsupported' to be 'available'and passes with this change.applePay.test.tsis 75/75;tsc --noEmitand eslint are clean.resetApplePaySDKLoader()exists for test isolation and must be called from the top-levelbeforeEach.