Everything we have shipped, with dates. Current release 2.0.0.
74New
40Improved
42Fixed
6Security
2.0.0
Latest Major releaseNew8
- Gift article links — subscribers can share a single article with someone who doesn't subscribe. One link, one reader, once: the first person to open it claims it, it stops working for anyone else, and it expires. Enable per level with a monthly allowance, adjustable per subscriber; gift activity is reported on the Dashboard and Analytics, with a CSV export of individual links, senders and recipients.
- Per-subscriber RSS feed — active subscribers get a personal, authenticated feed link from My Account that carries full article content, listing only the posts their own plan grants. The link can be regenerated at any time, which immediately invalidates the old one. Off by default — enable it under Settings → Advanced.
- Analytics page replaces Insights, with the Dashboard's reports over a date range you choose. The old
ppaccess-insightsURL redirects toppaccess-analyticswith the query string intact. - Warm leads and missed payments worklists — who to chase before they subscribe, and who to chase before they churn. Missed payments shows the amount, the failure reason, and how long Stripe will keep retrying; both export to CSV.
- Going quiet — paying subscribers who have stopped showing up, so you can reach them before they cancel rather than after.
- Reading history — a dated record of the articles each subscriber opened, built for chargeback evidence when a card issuer asks a publisher to show the subscription was actually used. Off by default under Settings → Advanced, kept for 180 days, exportable per subscriber or across a date range.
- Failed payments are now recorded rather than only reflected in a subscriber's live status, so missed revenue can be reported on after the fact.
- Admins can change a subscriber's email address from the subscriber detail page, and the linked Stripe customer is updated with it.
Improved5
- The paywall display report now says why the paywall appeared and names the free-registration prompt separately from a subscribe prompt, which on a site leading with free registration was previously almost every row.
- Paywalls rendered on the server are now counted. Only paywalls drawn by the browser were recorded before, which meant reports undercounted signed-in readers specifically.
- Dashboard and Analytics charts and cards restyled — muted palette, revenue and signups drawn in one frame on two scales, and reports laid out in two columns.
- Reader links throughout the reports now open the subscriber record rather than the WordPress user editor.
- A subscription status re-asserted as the same value no longer appends a history entry, which previously grew a subscriber's meta on every Stripe webhook.
Fixed1
- "Resume" is no longer offered to a subscriber whose Stripe subscription has already finished cancelling, where it always failed; they are offered "Start new subscription" instead.
Security3
- Search-engine crawler verification no longer blocks a PHP worker. Confirming a crawler needs a DNS lookup, and the previous lookup could not be timed out — measured at 16 seconds against an unresponsive resolver. Since anyone can claim to be Googlebot, a burst of forged requests could exhaust the worker pool. Lookups are now bounded and cached per address.
- Values in CSV exports that begin with
=,+,-or@are neutralised, so a subscriber cannot get a formula to run in a spreadsheet by putting one in a name or email field. - Reading history is registered with WordPress's personal-data export and erase tools, so a subject-access or erasure request can be answered completely.
1.4.5
New1
- New
ppaccess_captcha_bypassfilter to bypass the captcha on specific requests, e.g. the PP Mobile app.
Fixed2
- Subscription cancellations now void any leftover payable invoices for that subscription, so a customer can't pay an old invoice after their access has already ended.
- Duplicate subscription-payment transactions caused by invoice and PaymentIntent both recording the same payment.
1.4.4
New5
- Configurable multi-day renewal reminders — set multiple "days before expiry" milestones (e.g. 30, 14, 1) on the renewal_reminder email template, with a new
{days_remaining}merge tag; each subscriber gets one reminder per milestone as it's crossed, and reminders reset on renewal. - Attach a subscription to an existing WordPress account from Create Subscriber — an email that already belongs to a plain WP user with no paywall metadata can now be turned into a subscriber instead of hard-erroring.
- Level, status, and expiry-range filters on the Tools subscriber CSV export, in addition to the existing level preset.
group.member_access_changedwebhook — fires per member when a group owner's status changes or a member is removed, so downstream automations (Pabbly, newsletter lists) can react to lost or regained access.- Remove saved webhooks from the Webhooks settings tab, with an undoable "Remove webhook" action before saving.
Improved4
- Group members now get correct access on capped (metered) levels and on paywalls restricted to specific levels — previously they were blocked or given no group access at all.
- A free-level member's personal access is cleared when they join a group, so group-level access applies instead of the free tier.
- The self-hosted updater now passes the plugin icon through to WordPress so it shows correctly on the Updates screen instead of the default icon.
- The Leaky Paywall importer on the Tools page is now hidden by default, with a new "Leaky Paywall migrator" toggle under Settings → Advanced to show it while migrating.
Fixed1
- The lead-in fade's pull-up margin no longer gets zeroed by block-theme global styles, so the paywall card consistently tucks into the fade.
1.4.3
Fixed1
- The paywall card's top border is no longer hidden by the lead-in fade gradient, and block themes (e.g. Blocksy) no longer add a gap between the fade and the card.
1.4.2
New3
- Blocked email domains for free registration — list domains under Paywall → Free registration to stop those addresses from completing a free sign-up; blocked readers can still subscribe to a paid plan, and subdomains of a blocked domain are covered too.
- Saved level-filter presets — save the Subscribers list level selection as a named preset shared with all admins, apply or delete presets from the level dropdown, and export just a preset's subscribers from the Tools CSV download.
Email me a sign-in linkon free registration — returning readers who forgot their password can request a passwordless sign-in link (30-minute, single-use) from the sign-in step, and it works even when requested on one device and opened on another.
Improved1
- The lead-in fade now also appears on server-rendered paywalls — logged-in metered or capped readers see the same fade-out into the upgrade box as anonymous readers — and the paywall card tucks into the fade without a gap.
Fixed1
- Stripe webhooks are no longer lost during a database outage — if a query fails while handling an event, the plugin now returns a retryable error so Stripe redelivers it once the database recovers, instead of silently acknowledging it.
1.4.1
New3
- Change plan from the account page — subscribers on an active recurring paid plan get a "Change plan" action on
[ppaccess_account], and the subscribe page grid now marks their current plan. - Per-category paywall copy — override the heading and subheading of the free-registration and subscribe boxes for specific categories, with add/remove rows on the Paywall settings page and a category preview dropdown; when a post has several categories the primary category wins.
- Free window — optionally keep rule-restricted posts free for a set number of days after publish (Restrictions → Lead-in and bypass), then gate them normally; posts explicitly set to require access still gate immediately, and no metered view is spent during the window.
Improved1
- The Subscribers list level filter is now a checkbox dropdown so you can filter by several levels at once — for example all print tiers together; old single-level filter links keep working.
1.4.0
New4
- Subscriber-only commenting — a new comment gate restricts who can post comments to active subscribers, optionally limited to specific levels, with configurable logged-out and upgrade messages. Enable it under Restrictions → Comments; comment moderators can always post.
- Lapsed-subscriber access — optionally let past-due, canceled, or expired members who were on a paid plan keep the access of a chosen free level instead of being blocked. It only grants access, leaves their status and level untouched, and is fully reversible.
- Passwordless sign-in recovery at checkout — a returning subscriber who forgot their password can request an emailed sign-in link that returns them straight to the checkout they were on, logged in at the payment step.
- Gift redemption now collects a shipping address when the gifted level ships something — pre-filled for signed-in recipients, marked required when the level requires it, and validated before the recipient account is created.
Improved5
- Direct free sign-up forms now honor the level's configured fields (name, phone, company, billing, shipping) instead of only first/last/email, so free print and mailing tiers can collect a shipping address; the flow no longer redirects back to the subscribe page and saves profile fields before activation.
- Custom email templates now accept full HTML — paste a complete responsive email with
<!DOCTYPE>,<head>, and<style>(media queries,!important) and it is stored intact instead of being stripped and dumped out as visible CSS. Applies to the global and per-level templates for editors with the unfiltered-HTML capability. - Metered articles now render inline for logged-in readers, so embeds, maps, and inline scripts inside gated content load correctly — no more stuck loading skeletons or "x is not defined" errors, and no flash of hidden content before the paywall.
- The free-registration slide-up prompt has been restyled NYT-style — the article fades to white into the form panel instead of a dark scrim, with a subscription-options callout, an "or" divider, larger inputs, full style-preset support, and a fix for the panel getting stuck when dragged down on mobile.
- Accepting a group invitation now cancels the member's own personal subscription so they aren't billed twice, and fires the
subscriber.createdwebhook so invited members reach your integrations.
Fixed6
- Subscription payments no longer record a duplicate transaction row — Stripe's 2025 API moved the payment-intent field the dedup relied on, and the webhook now reads it from the new location.
- Recurring-level subscribers with no Stripe subscription — manually created, imported, check-payment, or complimentary — now expire on schedule instead of staying active forever.
- A canceled subscriber is no longer reverted to past_due by Stripe webhooks.
- Custom transactional email templates no longer send the welcome email twice.
- Checkout no longer fails with an opaque "502 Could not create Stripe customer" when a shipping address is entered without a name — first/last name is now required alongside shipping, and Stripe's real error message is surfaced.
- Form inputs no longer trigger iOS Safari's zoom-on-focus on mobile.
Security1
- Hardened group-invitation acceptance against emailed-link replay (each link is now single-use) and against a member cancelling or orphaning a group they already own.
1.3.0
New4
- Pay-what-you-want levels — a new "Pay what you want" payment type lets buyers enter their own amount at checkout, with configurable minimum, maximum, and suggested defaults, billed either one-time (a donation) or on a recurring interval. Works for new signups and plan changes, with the server always re-clamping the entered amount authoritatively.
- Admin email notifications for three new conditions — payment succeeded, subscription canceled, and subscription expired — each an individually toggleable, editable template under PaywallProject → Emails. The expired notice can be scheduled a set number of days before or after the expiry date.
- Search subscribers by name — the Subscribers list search now matches first, last, and display name (not just email and IDs), auto-submits as you type, and a new quick-jump autocomplete on the subscriber detail page links straight to any other subscriber.
- Per-level CTA button label — override the subscribe grid button text for each level, defaulting to "Subscribe" when left blank.
Improved5
- Customer merge tags (
{phone},{shipping_address},{billing_address}) now work in every email template, not just admin notifications, and are filled in automatically from the subscriber. - The email template picker is grouped into labelled sections — Subscriber emails, Admin notifications, Gift emails, and Group accounts — for easier scanning.
- The
{admin_url}email merge tag has been renamed to{subscriber_url}for clarity;{admin_url}keeps working as a backward-compatible alias. - The default renewal reminder email copy has been rewritten for manual (non-recurring) renewals — an accurate "this plan doesn't renew automatically" message with a manage-account link and a proper greeting.
- Pay-what-you-want level cards now show a "From" price (or "Pay what you want" when no suggested amount is set) instead of $0.00, and the checkout amount field snaps to the enforced minimum or maximum on entry.
Fixed2
- Pay-what-you-want (donation) levels are now exempt from Stripe Tax by default — a donation has no fixed taxable price and was blocking checkout with a tax-address error; a new
ppaccess_level_taxablefilter lets publishers re-enable tax per level. - The cancel-and-recheckout notice no longer always reads "trial" — it now reflects whether the current subscription is a trial or an active plan.
Security1
- Group-member invite keys are now generated with a cryptographically secure random generator instead of a predictable value.
1.2.0
New10
- New
[ppaccess_login]shortcode — a standalone login form styled like the account page, with a smart post-login redirect back to where the visitor came from. - New
[ppaccess_members_only]shortcode — wraps content so it is visible only to active subscribers. - New
[ppaccess_guests_only]shortcode — hides the wrapped content from logged-in users. - Account page now offers a self-service password change in the profile form and a full forgot-password reset flow.
- Account shortcode now renders nested shortcodes inside its content.
- Admin transaction detail page — inspect a single transaction with a paid/free status split and the subscriber's billing address.
- Per-level welcome email — each subscription level can define its own welcome email subject and body, falling back to the global template.
- Tag-based paywall bypass — posts carrying selected tags always bypass restrictions, configured with a searchable tag picker in the Restrictions admin (mirrors the existing bypass-by-category).
- Subscribers are now logged in automatically after completing Stripe hosted checkout.
- Plan descriptions are parsed into paragraphs and bulleted feature lists for richer plan display.
Improved8
- The inline paywall now renders server-side when the access decision is final, removing the brief client-side flash; the free-registration slide-up continues to work on the JavaScript path.
- Free, Subscribe, and Upgrade paywall box copy now allows HTML, and the heading/subhead render on both card templates with newlines converted to line breaks.
- Paywall lead-in length is now measured by word count instead of HTML element count, for more consistent truncation.
- Leaky Paywall migrator now batches transactions, migrates group accounts, and retries on failure for more reliable large imports.
- Slack notifications now show the subscription level name instead of the numeric level ID.
- The paywall "Log in here" link now points at the configured
[ppaccess_login]page. - Levels admin table now displays each level's ID for use in shortcode
levelattributes. - Inline checkout is limited to card payments for more reliable processing, and the "send a magic link" fallback was removed from the paywall OTP sign-in flow.
Fixed1
- Renewal reminder emails are now sent only to non-recurring subscribers — recurring subscriptions renew automatically and were receiving reminders they did not need.
1.1.0
Improved1
- Stripe Tax invalid-address error now returns a clear "check your ZIP/postal code" message at checkout instead of the generic subscription failure; card postal code is forwarded server-side and used as a tax location fallback for card-only levels.
Fixed6
- Free registration box settings (heading, subhead, CTA label/URL, show login) not persisting on save — fields were never registered in SettingsDescriptors so values were silently discarded.
- Expiration cron wrongly expiring never-expire accounts — empty/0 expiry values were cast to 0 by MySQL and matched the
expires < nowquery; a lower-bound guard and a PHP safety check now skip those accounts. - Subscriber bulk delete silently doing nothing for non-Stripe subscribers; per-row Delete action added to the subscribers table.
- Subscriber delete now removes all
_ppaccess_*meta from the WP user instead of deleting the WP account; delete actions are blocked for subscribers with an active Stripe subscription. - Incorrect proration amount shown when switching across billing intervals (e.g. yearly → monthly) with a coupon — prorated charge is now read from Stripe's invoice-preview line items instead of being recalculated from the old plan's period.
- Surcharge price cache key omitted billing interval, causing stale surcharge prices after an interval change and Stripe rejections on plan updates.
1.0.0
Major releaseNew36
- Stripe Payment Element as an optional alternative to the classic card element.
- Stripe Tax support for recurring and one-time subscriptions.
- Stripe Address Element for tax estimation at checkout.
- One-click Stripe webhook setup from Settings → Stripe tab.
- Group Accounts — owners can invite members, manage seats, and switch plans with automatic group resizing.
- Gift subscriptions — purchase and redeem gift access codes.
- Coupon support with
ppaccess_couponURL param, lifecycle fixes, and proration accuracy improvements. - Plan-change preview showing prorated amount due before confirming a switch.
- Terms of Service banner and checkbox on checkout, configurable per level.
- Per-level shipping surcharge based on subscriber postal code.
- Stripe Tax applied to shipping surcharges.
- Bypass paywall by post category, with category filter UI in Restrictions admin.
- Per-post lead-in override — set the number of visible paragraphs before the paywall on individual posts.
- Slide-up free registration form with server-side content truncation.
- IP exception management with delete functionality.
- Option to hide a level from the subscription/levels page.
- Drag-to-reorder levels with visual level cards.
- Option to disable the direct sign-up link per level.
- Custom color controls for paywall boxes.
- Self-hosted plugin updater — receives updates from paywallproject.com without the WordPress.org directory.
- Leaky Paywall migrator — import subscribers, levels, and shipping metadata with per-user dry-run details.
- Subscriber CSV export and import, including phone number field.
- Phone number display and editing on the admin subscriber detail page.
- Admin can create a new Stripe subscription from the subscriber detail page.
- Admin Stripe sync — syncs active/canceled status, expiry date, and shipping address to Stripe.
- Admin email notifications on plan switch for all subscription types.
- Support for multiple admin notification email addresses.
{shipping_address}merge tag for admin new-subscriber email notifications.- Renewal reminder emails with deduplication via cron.
- Subscriber webhooks include
first_name,last_name, andlevel_idin all payloads. ppaccess_subscriber_createdwebhook fires on free registration.charge.refundedincluded in webhook setup event list.- Daily database pruner for logs and tightened Insights date range.
- Interactive admin charts with tooltips, legends, and improved accessibility.
- Cancel subscription confirmation modal on My Account page.
- Loading spinner on checkout buttons to prevent double-submit.
Improved10
- My Account page redesigned with card-based layout.
- Levels admin page redesigned with sectioned layout.
- Checkout and level admin pages use consistent
ppaccess-form-rowlayout with live checkout preview. - Group Accounts admin page uses
ppaccess-cardstyling; Subscribers table columns reordered (email before name). - Restrictions admin page gains category filter input and grid layout for bypass sections.
- Mobile styling improvements for admin area.
- PaywallProject menu moved to the bottom of the WP admin sidebar.
pending_cancelsubscriptions no longer show a cancel link; a status note is shown instead.- Plan-change banner skipped when
amount_dueis zero or negative. - Same-plan checkout skips proration and applies coupon without re-billing.
Fixed21
- Payment Element blank fields in stepped checkout.
- Payment Element blank for logged-in users.
setup_intent_already_succeededblocking Payment Element checkout.- Potential double-charge for one-time levels with Payment Element.
- Tax estimate falling back to shipping address correctly with Payment Element.
- Plan-change proration preview coupon accuracy.
- Plan-change preview returning 502 for discounted subscriptions.
fresh_checkoutreturned (200) instead of 400 for subscribers without a Stripe subscription on plan-change preview.- Double
public/in nonce URL from restriction resolver. - Checkout token consumed by coupon check before subscribe submit.
- Cron jobs not firing and renewal reminder deduplication.
push_updateTypeError when transient is not an object.- Group membership ended message shown correctly even when a subscriber record exists.
- Fatal error in
add_memberwhen first name is empty and user lookup fails. - Blank account page for group owners without a subscriber record.
- Blank page on Add Member — redirect always runs after nonce validates.
- Preserve subscription status when expiration date is unchanged in admin save.
- Level ID synced via webhook metadata and
update_subscription_price. - UTF-8 mangling in lead-in content and Leaky Paywall migrator gateway detection.
- Lead-in override of 0 now correctly shows no content before the paywall.
- Leaky Paywall migration no longer overwrites PaywallProject paywall messaging and settings.
Security1
- Fail-closed webhook secret validation — unsigned events are now rejected instead of processed.
0.1.0
Initial development
