Skip to content

Forms: refresh cached plan on editor return after plan purchase - #50324

Merged
kraftbj merged 7 commits into
trunkfrom
forms-712-forms-upsell-doesnt-go-away-after-plan-purchase
Aug 10, 2026
Merged

Forms: refresh cached plan on editor return after plan purchase#50324
kraftbj merged 7 commits into
trunkfrom
forms-712-forms-upsell-doesnt-go-away-after-plan-purchase

Conversation

@kraftbj

@kraftbj kraftbj commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Fixes FORMS-712 (Linear).

Proposed changes

The Forms file-upload field (and any other plan_check-gated editor block) kept showing its upgrade nudge after a plan purchase. Block availability (Jetpack_Editor_Initial_State.available_blocks) is computed server-side at editor render from Current_Plan::supports(), which reads the cached jetpack_active_plan option. That option is only refreshed by the daily jetpack_v2_heartbeat cron, and nothing refreshed it when the user returned from checkout — so the editor rendered the upsell from a pre-purchase plan while the "Congratulations" banner (driven by the ?plan_upgraded URL param) showed success. Simple/WordPress.com sites gate features live via wpcom_site_has_feature(), so they don't hit this; it's a self-hosted Jetpack / Atomic issue.

  • Server (class.jetpack-gutenberg.php): in enqueue_block_editor_assets(), when the editor loads with ?plan_upgraded, refresh the plan from WordPress.com before block availability is computed, so the first post-checkout render is correct. Guarded to connected non-WPCOM sites, throttled to once per minute via a transient (a repeated/bookmarked ?plan_upgraded URL can't trigger a blocking request every load), and time-boxed to 5s so a slow origin can't hang the editor.
  • Client (extensions/shared/plan-upgrade-notification.js): a one-shot reload fallback for the narrow race where WordPress.com finishes provisioning between the server refresh and the banner's own site fetch. It reloads once when the freshly fetched plan slug differs from the slug the page rendered with. The guard is keyed on the rendered plan slug and stored in sessionStorage through throw-safe helpers, and the reload only fires if the guard can be persisted first — so browsers with storage blocked/full (Safari private mode, blocked site data) can never enter a reload loop.
  • Extracted shouldReloadAfterPlanUpgrade() as a pure function and added unit tests for it plus the IIFE orchestration (reload guard, error path, storage-throw, normal navigation).

Reviewed with a multi-persona code review + an independent adversarial pass; the two P2s they agreed on (unthrottled blocking refresh; sessionStorage throwing past optional chaining) are addressed above.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No. The server refresh only re-fetches the site's own plan from WordPress.com (authenticated as the blog) into the existing jetpack_active_plan cache; no new data is collected or sent.

Testing instructions

Reproduces on a self-hosted Jetpack site connected to a WordPress.com account (e.g. a Jurassic Ninja site); it does not reproduce on Simple sites.

Baseline (before this change / on trunk):

  • On a free Jetpack site, add a Form with a File upload field, save, and click Upgrade.
  • Purchase a plan that includes file uploads (Jetpack Complete), and get redirected back to the editor.
  • Note the "Congratulations" banner appears but the File upload field still shows the upgrade nudge until a manual reload / the next daily heartbeat.

With this change:

  • Repeat the flow. On return from checkout the File upload field should no longer show the upsell on the first render (the server refreshes the plan before computing availability).
  • If WordPress.com provisioning lags, the editor performs a single automatic reload and then shows the field unlocked. It must never reload more than once.

Quick mechanism check without a real purchase (WP-CLI on the connected site):

  • wp option get jetpack_active_plan on a free site → the field-file block reports available:false, missing_plan.
  • Set the plan to jetpack_complete and reload the editor → field-file reports available:true and the nudge is gone. (This is the exact state the server refresh produces on the ?plan_upgraded return.)

Paid blocks (e.g. the Forms file-upload field) kept showing their upgrade
nudge after a plan purchase because block availability is baked from the
cached jetpack_active_plan option, which only refreshes on the daily
heartbeat. Refresh the plan from WordPress.com when the editor loads with
?plan_upgraded (connected non-WPCOM sites, throttled and time-boxed), and
add a client-side one-shot reload fallback for the provisioning race.

See FORMS-712.
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the forms-712-forms-upsell-doesnt-go-away-after-plan-purchase branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack forms-712-forms-upsell-doesnt-go-away-after-plan-purchase

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions github-actions Bot added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] In Progress [Tests] Includes Tests labels Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Jul 8, 2026
@jp-launch-control

jp-launch-control Bot commented Jul 8, 2026

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 2 files.

File Coverage Δ% Δ Uncovered
projects/plugins/jetpack/class.jetpack-gutenberg.php 268/553 (48.46%) -0.98% 11 💔
projects/plugins/jetpack/extensions/shared/plan-upgrade-notification.js 35/41 (85.37%) 85.37% -15 💚

Full summary · PHP report · JS report

Coverage check overridden by Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR .

kraftbj added 3 commits July 20, 2026 12:51
jest.spyOn on window.location.reload throws 'Cannot assign to read only
property' in this jsdom config, so the IIFE test suite failed to run on CI.
Replace window.location and window.sessionStorage via Object.defineProperty
using a Map-backed sessionStorage double whose methods can be overridden to
simulate storage that throws.
window.location is non-configurable and reload is read-only in this jsdom, and
a real reload() logs to console (which jest-console fails on). Drive the query
string via history.pushState and install the reload mock on whichever of the
location instance or its prototype accepts a redefine.
jsdom's Location is unforgeable so reload() cannot be mocked; a real reload()
logs a 'Not implemented: navigation' console error that jest-console fails on.
Assert the reload path via the guard write + consumed console error, and the
no-reload paths via expect(console).not.toHaveErrored().
@kraftbj kraftbj added the Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR label Jul 20, 2026
@kraftbj

kraftbj commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@enejb — I dug into FORMS-712 and have a fix up here (draft for now, so we can test it on JN and via the beta plugin). Wanted to sanity-check the approach with you before taking it out of draft.

Root cause: on a self-hosted Jetpack (or Atomic) site, the editor decides whether a paid block like the Forms file-upload field shows its upsell from available_blocks, which is computed server-side at page render from the cached jetpack_active_plan option. That option only refreshes on the daily heartbeat cron — nothing refreshes it when you come back from checkout. So the "Congratulations" banner shows (it's driven purely by the plan_upgraded URL param), but the upsell is still drawn from the pre-purchase plan. That's why it's inconsistent — a reload, or the next day's heartbeat, clears it. Simple sites don't hit this because they gate features live via wpcom_site_has_feature() on every request, so the return-to-editor render already sees the purchase. That's the "different upgrade flow" you spotted.

The fix is two parts. Server: when the editor loads with plan_upgraded present, refresh the plan from WP.com before availability is computed, so the first render after checkout is correct (guarded to connected non-WPCOM sites, throttled to once a minute, and time-boxed so a slow WP.com response can't hang the editor). Client: a one-shot reload fallback for the narrow race where WP.com finishes provisioning between the server refresh and the page's own site fetch — if the freshly-fetched plan differs from what the page rendered with, it reloads once (guarded so it can never loop). Because it lives in the editor's availability layer, it covers every plan_check-gated block, not just Forms.

The honest caveat: if WP.com hasn't actually recorded the purchase yet when you land back in the editor (the "delay in sync" theory in the ticket), even a forced refresh gets the old plan and you'd still see the nudge until the next load. The reload catches a slightly-late provision, but a truly lagging WP.com is beyond what the site can fix locally. So this closes the structural gap and the common case; it's strictly better than today, but not a 100% guarantee in that pathological race.

One thing I didn't touch: the activity log dashboard you mentioned. That's a separate surface with its own caching, so it needs its own look — happy to investigate that separately if it's still happening.

Does this direction seem reasonable? If so, I'll double-check my work and take it out of draft for a real review.

@kraftbj
kraftbj marked this pull request as ready for review August 5, 2026 21:59
@kraftbj
kraftbj requested a review from enejb August 5, 2026 22:00
@kraftbj kraftbj added [Status] Needs Review This PR is ready for review. and removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Status] In Progress labels Aug 5, 2026
@enejb

enejb commented Aug 10, 2026

Copy link
Copy Markdown
Member

A couple of things I noticed while testing this PR.
This Pr worked as expected for me.

They don't have to be fixed in this PR.

  1. On the front end. The notice doesn't look as expected for me.
Screenshot 2026-08-10 at 12 41 50 PM
  1. When I click on the View Plan in the notification that gets created on the Jetpack side I am taken to the following page.
Screenshot 2026-08-10 at 12 35 02 PM I end up here. Screenshot 2026-08-10 at 12 42 51 PM

This page is kind of hidden already from the regular Jetpack UI. It isn't accessible via the My Jetpack for example.

alreadyReloaded,
} ) {
return (
! isSimple &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this really hard to read.

@enejb

enejb commented Aug 10, 2026

Copy link
Copy Markdown
Member

The code makes sense to me. I think we can ship this since it does improve things.

@kraftbj

kraftbj commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for testing this, @enejb! On the two things you flagged:

  1. The collapsed front-end nudge is already fixed in Upgrade nudge: fix collapsed banner outside the block editor #50873.
  2. The "View my plan" link landing on the hidden plan page is now tracked separately as FORMS-755.

Neither blocks this one — appreciate the review!

@kraftbj
kraftbj merged commit a1e8ee7 into trunk Aug 10, 2026
84 checks passed
@kraftbj
kraftbj deleted the forms-712-forms-upsell-doesnt-go-away-after-plan-purchase branch August 10, 2026 21:00
@github-actions github-actions Bot removed the [Status] Needs Review This PR is ready for review. label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants