The Bench
WordPress membership site with three subscription tiers, custom paywall card via three filter hooks, and an asset-hygiene mu-plugin worth 25 Lighthouse perf points.

Overview
A paid online community demo built on WordPress + Paid Member Subscriptions. Three membership tiers (Free / Member $12/mo / Founder $24/mo) with category-based content gating. Custom mu-plugin (~70 lines) replaces PMS's default restricted-content message with a styled paywall card via three filter hooks (including the buried pms_restricted_term_message for taxonomy-level restrictions) and dequeues PMS frontend assets on non-account pages.
Stack choice — PMPro → PMS pivot:
The original plan was Paid Memberships Pro. PMPro's wordpress.org listing is currently closed, so wp plugin install paid-memberships-pro fails. Switched to Paid Member Subscriptions (PMS) — comparable feature set, free on wp.org, actively maintained. The lesson worth keeping: the membership pattern is portable across plugins. The per-demo theme + paywall card + asset hygiene all stay identical regardless of which plugin underwrites the auth.
PMS configuration:
- Three subscription plans as
pms-subscriptionCPT entries: Free reader ($0), Member ($12/mo), Founder ($24/mo, adds office hours + critique queue priority) - Four categories (Lessons / Essays / Projects / Office Hours) with term-meta gating via
pms-content-restrict-subscription-plan - Essays = free; Lessons + Projects = Member or Founder; Office Hours = Founder only
- PMS account pages (
/register/,/login/,/account/,/recover/) created with PMS shortcodes
Custom mu-plugin — bench-pms-setup.php (~70 lines):
Two responsibilities. Paywall card — PMS ships with a default "you must be logged in" message that's functional but visually disconnected from the host site. Replaced with a styled paywall card via three filter hooks:
pms_restriction_message_logged_out(anonymous visitors)pms_restriction_message_non_members(logged-in users without the right subscription)pms_restricted_term_message(taxonomy-level restrictions — different code path than post-level, buried infunctions-content-restriction.php)
Asset dequeue — PMS loads its frontend CSS and Stripe.js on every pageload by default. On a marketing home page that costs roughly 25 perf points (51 → 76 in this build). The mu-plugin strips PMS-prefixed enqueues and Stripe.js on non-account pages.
The Challenge
Paid community sites — substacks-with-extras, gated knowledge bases, niche professional groups — are one of the most common questions a freelance developer gets, and the prevailing freelancer portfolio doesn't show WordPress as a credible answer. Substack/Patreon don't let you own your URL or members; Ghost is opinionated about pricing and forms; Memberful requires either a Memberful-hosted site or heavy WP integration.
The Solution
A three-tier paid community on WordPress + Paid Member Subscriptions with a custom paywall card replacing the plugin's default message and an asset-hygiene mu-plugin that strips PMS's default-everywhere CSS + Stripe.js on non-account pages. The per-demo theme + paywall + asset hygiene pattern is portable — PMPro vs PMS swap was ~80% mechanical.
Results
Lighthouse 75 / 92 / 77 / 92 — honest reflection of PMS overhead
Asset-dequeue mu-plugin worth 25 perf points on the home page (51 → 75)
Discovered the buried
pms_restricted_term_messagefilter — not in PMS docs, only in sourceIdempotent seed sets up 3 plans + 4 categories + term-meta gating + 6 posts + 4 PMS account pages
Plugin-portable architecture: per-demo theme + paywall + asset hygiene stayed identical across PMPro → PMS pivot
Six seeded posts across 4 categories (2 free, 3 member, 1 founder) demonstrate every gating path
Gallery


