OMembership v1.0.0
WordPress membership and paywall engine: content access rules, member tiers, subscriptions via Stripe or PayPal, drip content, a customizable member portal, and a REST API. Seven engines in one plugin; no per-member SaaS fee.
What OMembership does
OMembership turns any WordPress site into a paid community or subscription business. Define plans, gate content, take recurring payments, drip lessons, and run the whole thing from a member portal that looks like a product, not an admin add-on.
Getting installed
Requirements
- WordPress 6.0 or newer
- PHP 8.0 or newer
- HTTPS (required for Stripe and for cookie-based session security)
- A Stripe account, a PayPal account, or both (only required for paid plans)
- WooCommerce is optional. If installed, OMembership can hook into Woo products as membership entry points.
From WordPress admin
omembership.zip, click Install Now, then Activate.om_plans, om_subscriptions, om_access_rules, om_drip_schedules, om_courses, om_lessons, om_progress, om_invoices. Schema version is stored in omembership_db_version.Up and running in minutes
Connect Stripe, create one plan, and add a single access rule. About five minutes; less if your Stripe is already keyed up.
/membership/ page. The plan card renders with a Subscribe button that opens Stripe Checkout.Paid checkout in test mode? Use 4242 4242 4242 4242, any future expiry, any CVC. After the redirect back, the user is auto-logged in and the Premium category is fully readable.
Plans & tiers
A plan is the sellable unit. Each plan has a price, interval, currency, optional trial, and zero or more capabilities. Users can hold multiple plans simultaneously.
Plan types
| Type | Price | Interval | Use case |
|---|---|---|---|
free | 0 | n/a | Newsletter signup, lead magnet, read-only tier |
one_time | > 0 | n/a | Lifetime access, ebook, single-payment course |
recurring | > 0 | day / week / month / year | Subscription business; renews automatically |
team | > 0 | day / week / month / year | Seat-based pricing; one owner manages N members |
Capabilities
A capability is a short string attached to a plan. Use them as the unit of access; access rules check capabilities, not plan names. Adding read_premium to two plans means any rule referencing read_premium grants both plans access. This decouples your content rules from your pricing.
PHP// Read capabilities for the current user
$caps = OM_Capabilities::for_user( get_current_user_id() );
// e.g. ['read_premium', 'download_assets', 'forum_pro']
if ( in_array( 'read_premium', $caps, true ) ) {
// user can read premium content
}
// Or as a single check
if ( omembership_user_can( 'read_premium' ) ) { /* ... */ }
Trials & coupons
| Field | Behaviour |
|---|---|
trial_days | Free period before the first billing cycle. Stripe creates the subscription with trial_period_days; PayPal uses a setup fee of 0. |
setup_fee | One-time charge at checkout. Charged in addition to the first cycle. |
| Coupons | Stored in om_coupons. Support fixed or percentage discounts, single-use or multi-use, expiry date, and per-plan restrictions. |
| Proration | Plan upgrades prorate by default (Stripe behaviour). Disable globally under Payments → Behaviour. |
Access rules & drip
An access rule binds a content source (a post, a category, a custom rule) to one or more required capabilities, and defines what happens when a visitor does not meet them.
Rule shape
PHP// Create an access rule programmatically
OM_Access_Rules::save([
'source_type' => 'category', // post | category | tag | cpt | custom
'source_value' => 'premium', // slug, ID, or filter callback name
'required_caps' => [ 'read_premium' ], // any one match grants access
'action' => 'block', // block | teaser | blur | replace | drip
'teaser_words' => 60, // for action=teaser
'replace_post' => 0, // post ID to swap in for action=replace
'cta_text' => 'Subscribe to read',
'cta_url' => '/pricing',
]);
Actions
| Action | Effect |
|---|---|
block | Replace the post body with a paywall CTA. Title and excerpt remain visible. |
teaser | Show the first N words, then a fade gradient and a CTA. |
blur | Render the full post with a CSS blur over the body. SEO-safe; bots see the unblurred version. |
replace | Swap the body with the content of a different post (e.g. a marketing-page version of the lesson). |
drip | Block until the drip schedule says the content is unlocked for this member. |
Drip schedules
PHP// Drip lesson 3 to a course member 14 days after signup
OM_Drip::save([
'plan_id' => 7,
'post_id' => 142, // a lesson inside a course
'mode' => 'relative', // relative | absolute | event
'offset_days' => 14,
'offset_basis' => 'signup', // signup | first_payment | enrollment
]);
// Or: unlock everything on a fixed calendar date
OM_Drip::save([
'plan_id' => 7,
'post_id' => 142,
'mode' => 'absolute',
'unlock_at'=> '2026-08-01 00:00:00',
]);
Evaluation order
- 1Find all rules that match the current post (source_type and source_value).
- 2If the user is an admin or has the
omembership_bypasscapability, grant access immediately. - 3For each matching rule, collect
required_caps. - 4Resolve the user's capabilities via active subscriptions (
OM_Capabilities::for_user()). - 5If the user holds at least one required capability, grant access.
- 6If the rule action is
drip, additionally check that the drip schedule has unlocked. - 7Otherwise apply the rule's action (block / teaser / blur / replace).
Payments: Stripe & PayPal
Both gateways are first-class. You can offer either, both, or default one and fall back to the other. All checkout state lives in om_subscriptions.
Stripe
| Setting | Notes |
|---|---|
| Publishable key | Public; loaded into OMembership.stripeKey on the front end. |
| Secret key | Stored encrypted in wp_options. Never echoed to the client. |
| Webhook secret | Validated on every webhook delivery. Mandatory; without it, OMembership rejects events. |
| Webhook URL | /wp-json/omembership/v1/webhook/stripe |
| Tax | Stripe Tax can be enabled per plan; tax lines flow into om_invoices. |
Supported Stripe events: checkout.session.completed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.payment_succeeded, invoice.payment_failed, charge.refunded.
PayPal
OMembership uses PayPal Subscriptions (REST v2). The flow returns to /membership/return/ with a subscription_id, which OMembership resolves against the PayPal API to confirm the subscription state before granting access.
Supported PayPal events: BILLING.SUBSCRIPTION.CREATED, BILLING.SUBSCRIPTION.ACTIVATED, BILLING.SUBSCRIPTION.UPDATED, BILLING.SUBSCRIPTION.CANCELLED, PAYMENT.SALE.COMPLETED, PAYMENT.SALE.REFUNDED.
Dunning
When a renewal payment fails, OMembership marks the subscription past_due and starts the dunning sequence. By default: retry on day 1, day 3, day 7. After the final attempt, the subscription transitions to canceled and the member loses access. The schedule and email templates are configurable under Payments → Dunning.
PHP// Customise retry schedule
add_filter( 'omembership_dunning_schedule', function( array $days ): array {
return [ 1, 3, 5, 10, 14 ]; // days after the failed charge
} );
Member portal
The portal is the part your members see. It is a single React-rendered app embedded via shortcode, with eight built-in themes and a tab structure you can extend.
Built-in tabs
Shortcodes
HTML[omembership_portal]
[omembership_portal tabs="dashboard,content,billing"]
[omembership_portal theme="midnight"]
[omembership_pricing]
[omembership_login redirect="/membership/"]
[omembership_signup plan_id="3"]
[omembership_gate cap="read_premium"]
Premium-only HTML goes here.
[/omembership_gate]
Custom tabs
PHPadd_filter( 'omembership_portal_tabs', function( array $tabs ): array {
$tabs['my_downloads'] = [
'label' => 'My Downloads',
'icon' => 'download',
'cap' => 'download_assets', // optional capability gate
'render' => function( int $user_id ): string {
ob_start();
include get_template_directory() . '/parts/member-downloads.php';
return ob_get_clean();
},
];
return $tabs;
} );
REST API
All endpoints live under /wp-json/omembership/v1/. Member-facing endpoints require an authenticated WordPress user. Admin endpoints additionally require manage_options. Webhooks are public but secret-verified.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /me | Return current user with active plans and capabilities. |
| GET | /plans | List public plans. |
| POST | /checkout | Start a checkout session; returns a Stripe or PayPal redirect URL. |
| POST | /subscriptions/{id}/cancel | Cancel at period end (default) or immediately. |
| POST | /subscriptions/{id}/swap | Swap plan; prorates by default. |
| GET | /invoices | Current user's invoices. |
| GET | /access/check?post=42 | Return whether the current user can read post ID 42, and the reason if not. |
| POST | /progress/lesson | Mark a lesson as started / completed for LMS tracking. |
| POST | /webhook/stripe | Stripe webhook receiver (signature verified). |
| POST | /webhook/paypal | PayPal webhook receiver (signature verified). |
Check access
cURLcurl -X GET 'https://example.com/wp-json/omembership/v1/access/check?post=142' \
-H 'X-WP-Nonce: <rest-nonce>'
# Response
{
"allowed": false,
"reason": "drip_locked",
"unlock_at": "2026-06-20 00:00:00",
"required_caps":["read_premium"],
"user_caps": ["read_premium"]
}
Start checkout
cURLcurl -X POST https://example.com/wp-json/omembership/v1/checkout \
-H 'Content-Type: application/json' \
-H 'X-WP-Nonce: <rest-nonce>' \
-d '{
"plan_id": 3,
"gateway": "stripe",
"coupon": "LAUNCH20",
"return_url": "https://example.com/membership/"
}'
# Response
{ "redirect_url": "https://checkout.stripe.com/c/pay/cs_..." }
Frequently asked questions
omembership_register_portal_theme().wp_options using SECURE_AUTH_KEY as the salt. They are never echoed to the front end. The publishable / client-id values are public by design and are loaded into the front-end runtime.past_due and the dunning sequence starts (default: retry on days 1, 3, 7). The user retains access during dunning. After the final failed retry, the subscription is canceled and access ends. All retry attempts and email sends are logged in om_subscriptions.meta.uninstall.php, which drops the eight custom tables and removes plugin options. The auto-created pages (portal, pricing, login) are left in place so you can recover them on reinstall. Deactivating alone leaves all data in place.What's changed
- NewSeven engines: Plans, Access, Drip, LMS, Community, Portal, Lifecycle
- NewFour plan types: free, one_time, recurring, team
- NewStripe Checkout and Stripe Billing integration with webhook verification
- NewPayPal Subscriptions (REST v2) with webhook verification
- NewCapabilities model decoupling pricing from access rules
- NewFive access-rule actions: block, teaser, blur, replace, drip
- NewDrip schedules: relative, absolute, event-anchored
- NewLMS-lite: courses, lessons, quizzes, PDF certificates
- NewCommunity spaces with DMs, reactions, moderation
- NewMember portal with 8 themes and 6 built-in tabs
- NewBlock-editor access panel and Gutenberg blocks for every shortcode
- NewLifecycle automations: welcome, dunning, renewal, win-back
- NewCoupons with fixed / percentage discounts and per-plan restrictions
- NewProration on plan swaps; configurable dunning schedule
- NewREST API at
/wp-json/omembership/v1/ - NewAnalytics: MRR, churn, LTV, cohort retention, plan mix
- NewEight custom DB tables with full
uninstall.phpcleanup
Got a question about OMembership?
Reach out directly. Kenneth replies within 24 hours.

