Conversation
WordPress.com answers region, city, device and UTM stats requests on unsupported plans with an "unauthorized" error, which the card treated as a generic permission problem - telling the user they lack permission and offering WP Admin, neither of which is actionable. Detect the plan gate and show the same upgrade prompt iOS does instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
Generated by 🚫 Danger |
Contributor
|
|
Contributor
|
|
Contributor
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
The gate was detected from the WordPress.com `unauthorized` error code alone, but that code is also what every stats endpoint returns from its permission check, which runs before the plan check. A user whose role doesn't allow viewing stats - the Jetpack default is administrators only - was told to upgrade, which no purchase would have fixed. Only the plan check names the plan, so match the message too and let everything else fall back to the regular permission error. PLAN_GATED also carried the upsell copy as its message, and only the Locations, Devices and UTM cards show the upsell UI. Any other card rendered "Upgrade your plan..." as red error text with a Retry button that could never succeed. Give the type a message that stands on its own as an error; the cards that show the upsell supply its copy themselves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
adalpari
marked this pull request as ready for review
October 2, 2026 13:13
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## trunk #23389 +/- ##
==========================================
- Coverage 38.44% 38.42% -0.02%
==========================================
Files 2298 2299 +1
Lines 127260 127362 +102
Branches 17990 18016 +26
==========================================
+ Hits 48920 48941 +21
- Misses 74285 74365 +80
- Partials 4055 4056 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The gated stats endpoints name the site by its numeric blog id, not by a phrase: WordPress.com builds the body with `sprintf( 'The plan for %d does not allow fetching Device stats', $blog_id )`. The KDoc and every positive test fixture used "The plan for this site ..." instead, a body the API never sends. Detection was unaffected, because PLAN_GATE_MESSAGE_FRAGMENT starts after the blog id. The risk was the next edit: the fragment reads as narrower than the sentence the doc quotes, so tightening it to match that sentence is a natural change, and it would have silently dropped the upsell on every gated card while the fixtures kept passing. Correct both, and say why the fragment has to stay short. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.


TL;DR
This Pr is showing a plan upsell for stats that are not available to the user. Previously it was a plan message and a wp-admin CTA.
Description
Fixes CMM-2341.
Regions, Cities, Devices and UTM are gated behind a paid plan. WordPress.com
answers those requests on an unsupported plan with
403and a body of{"error":"unauthorized","message":"The plan for this site does not allow fetching Device stats"}.StatsDataSourceImpl.classifyErroronly looked at theHTTP status, so the gate collapsed into
AUTH_ERRORand the card told the user"You don't have permission to view these stats" and offered a WP Admin button
— neither of which is actionable. iOS already handles this
(
Modules/Sources/JetpackStats/Services/StatsFeatureGateError.swift+FeatureGateBannerView); this is a port of that behaviour.Detection.
StatsApiError.ktgainsisStatsGatedByPlan()next to theexisting
isStatsUnavailableForSite(), reusing the same regex parse of theWordPress.com
errorfield. A newStatsErrorType.PLAN_GATEDis checked inclassifyErrorahead of the status-code fallback, in both theWpErrorandUnknownErrorbranches.Plumbing.
isPlanGatedtravels alongside the existingisAuthErrorthroughthe Country/Region/City, Devices and UTM repository results into the Locations,
Devices and UTM card UI states.
UI.
StatsCardErrorContenttakes an optionalonExplorePlans; when it isset, a lock, the upgrade message and an "Explore Plans" button replace the red
error text and Retry button — there is nothing to retry until the plan changes.
The card's type selector stays visible, so you can switch back to an ungated
breakdown.
Upgrade URL. New
SiteModel.statsUpgradeUrl()returnswordpress.com/pricing/for WordPress.com and Atomic sites andcloud.jetpack.com/pricingfor Jetpack sites, matching iOS'smakeUpgradeURL.It opens in a Custom Tab and fires a new
stats_feature_gate_explore_plans_tappedevent with afeatureproperty.Notes for the reviewer
unauthorizedfor a user who genuinely cannot view asite's stats, so that case now reads "upgrade your plan" too. iOS makes the
same trade-off: the plan gate is by far the common cause, and the permission
message this replaces was no more accurate for gated sites. Documented in a
comment on
isStatsGatedByPlan.it is the same endpoint gate, and iOS's
StatsFeatureGateError.featureNameenumerates it alongside devices and locations.
reachable through "Show All", which a gated card never renders.
Testing instructions
Because the gate depends on the site's plan, the quickest way to exercise it is
to force the gated result. In
StatsRepository, makefetchDevicesDataandfetchRegionViewsreturn theirError(..., isPlanGated = true)early, then:an Explore Plans button appear, and that the
Countries/Regions/Cities selector is still usable.