Paid Content: expire subscription access at end-of-day (NL-735)#50460
Paid Content: expire subscription access at end-of-day (NL-735)#50460allilevine wants to merge 1 commit into
Conversation
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
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:
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. |
Code Coverage SummaryCoverage changed in 1 file.
|
fef93f5 to
a38841d
Compare
Paid Content enforced access on a membership subscription's exact end_date timestamp. Memberships store end_date as the precise purchase timestamp, and auto-renewals run via a deferred day-0 billing job that BDSE spreads across a 3-10 hour window — so a subscriber could lose access the instant their timestamp passed, hours before that day's renewal completed, even though the renewal then succeeded. Billing confirmed the deferral is expected and that the wider WordPress.com / Jetpack platform already treats end-of-day as the formal expiration time for subscriptions. Paid Content was the outlier. Grant access through 23:59:59 UTC of the end_date day in both enforcement points (validate_subscriptions and the newsletter-tier gate), matching the platform convention. This covers a same-day auto-renewal regardless of how long BDSE defers it. Cancelled/inactive subscriptions never reach this check (dropped by the active-status filter), so no gating is needed. Tests that used a same-day past end_date to represent an expired subscription (NL-546 refresh-before-deny, and the subscriptions/newsletter access + comp tests) now use a prior-day end_date, so they still read as expired under end-of-day expiry. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_015QcwooRyPv7STjV5KPc1rj
a38841d to
b05c634
Compare
Fixes NL-735
Proposed changes
Paid Content enforced access on a membership subscription's exact
end_datetimestamp. Memberships storeend_dateas the precise purchase timestamp (e.g.2026-07-10 09:34:45), and auto-renewals run via a deferred day-0 billing job that BDSE spreads across a 3–10 hour window. So a subscriber could lose access the instant their timestamp passed, hours before that day's renewal completed — even though the renewal then succeeded.Billing (Shilling) confirmed the ~6h renewal deferral is expected, and that the wider WordPress.com / Jetpack platform already treats end-of-day as the formal expiration time for subscriptions (which is why expiration messaging/scheduling holds until EOD). Paid Content was the outlier, enforcing on the exact timestamp.
This PR aligns Paid Content with that convention: a subscription grants access through
23:59:59UTC of itsend_dateday, in the two enforcement points (validate_subscriptions()non-tier, andmaybe_gate_access_for_user_if_tier()tier). That naturally covers a same-day auto-renewal regardless of how long BDSE defers it.Why no gating is needed: cancelled/inactive subscriptions never reach this check — they're dropped upstream by the
'active' === statusfilter inabbreviate_subscriptions(). So end-of-day only ever extends an already-active subscription to the end of its final day, matching the platform. Noauto_renewflag or grace-period duration is involved.Related product discussion/links
Does this pull request change what data or activity we track or use?
No. It changes only how the existing
end_dateis compared against the current time (rounded up to end-of-day, UTC). No new data, tracking, or token-shape change.Testing instructions
Automated:
jetpack docker phpunit jetpack -- tests/php/extensions/blocks/premium-content/Jetpack_Premium_Content_Test.php— 34 tests / 79 assertions pass, including:end_dateis earlier today still grants access (the incident case), end-to-end throughcurrent_visitor_can_accesswith no refresh call.end_dateday has fully passed is denied.end_datestill grants; string (Y-m-d H:i:s) and integerend_datevalues both handled.end_dateso they still exercise the refresh path under end-of-day expiry.Manual (Simple/Atomic, or a Jetpack Beta JN site): with a Premium Content post gated to a plan and an active subscription, set the subscription's
end_dateto earlier today via theearn_get_user_subscriptions_for_site_idfilter → content stays visible; set it to a prior day → paywall returns.🤖 Generated with Claude Code