PaidMembershipsBridge
Benecaster\Bridge\Bridges\PaidMembershipsBridge
The built-in bridge for Paid Memberships Pro. PMP routes all membership lifecycle transitions — activations, cancellations, and renewals — through a single hook (pmpro_after_change_membership_level). The bridge distinguishes event types by inspecting the argument values on that hook rather than hooking into separate actions.
Slug: paid-memberships
Detection: defined('PMPRO_VERSION') — true when Paid Memberships Pro is active.
Minimum version: PMP 2.0 or higher is required. PMP 2.0 introduced pmpro_getMembershipLevelsForUser(), which this bridge uses for tier resolution. Sites running PMP 1.x will not see this bridge in the subscription plugin picker.
Tier Resolution
get_user_tier( int $user_id, int $show_id ) works as follows:
- Calls
pmpro_getMembershipLevelsForUser( $user_id )to retrieve all active membership level objects for the user. - For each active level, reads its level ID.
- Looks up a matching row in
benecaster_tier_mapwhereplugin_slug = 'paid-memberships'andexternal_tier_idmatches the level ID. - Filters to rows where
show_idmatches the given$show_id. - Returns the
internal_tier_slugof the first matching row, ornullif none matches.
If pmpro_getMembershipLevelsForUser is unavailable (PMP < 2.0), get_user_tier() returns null unconditionally.
Required setup: At least one row must exist in benecaster_tier_map with plugin_slug = 'paid-memberships' and external_tier_id matching a PMP membership level ID. Without a mapped row, get_user_tier() always returns null. See Mapping Your Membership Tiers.
Tier Listing
get_all_tiers( int $show_id ) returns all site-wide membership levels via pmpro_getAllLevels(). The $show_id parameter is accepted but not used. Returns [] if pmpro_getAllLevels is unavailable. Each entry is array{id: int, name: string, price: float}, where id is the level ID as an integer, name is the level name, and price is the level’s billing_amount. A billing_amount of 0.00 marks the tier as free — it will not count toward the Benecaster license tier limit.
Tier Save Events
on_tier_saved() hooks into pmpro_save_membership_level, which fires after a PMPro membership level is created or updated. The same hook covers both create and update events. Fires the callback with array{id: int, name: string, price: float} where price is read from the level’s billing_amount. Verify hook name and available arguments against PMPro docs before implementation.
PMP Hooks
Paid Memberships Pro centralises all membership lifecycle events in a single hook. PaidMembershipsBridge hooks into that hook once and routes to the appropriate bridge event based on the argument values:
| Condition | Bridge event | Notes |
|---|---|---|
pmpro_after_change_membership_level: level_id > 0, cancel_level_id ≠ level_id |
on_subscription_activated |
New level granted or level changed. cancel_level_id == 0 → source 'new' (first activation); cancel_level_id > 0 → source 'resubscribe' (level change or reactivation). |
pmpro_after_change_membership_level: level_id == 0, cancel_level_id > 0 |
on_subscription_cancelled |
Membership removed. cancel_level_id identifies the removed level. |
pmpro_after_change_membership_level: level_id > 0, cancel_level_id == level_id |
on_subscription_renewed |
Same level re-granted via pmpro_changeMembershipLevel() — interpreted as an explicit renewal. |
pmpro_subscription_payment_failed |
on_payment_failed |
Recurring payment failed. See gateway coverage limitation below. |
Because the single-hook dispatch covers activation, cancellation, and renewal, on_subscription_changed() is a documented no-op — level changes arrive as a cancellation of the old level followed by a new activation (cancel_level_id > 0, level_id > 0), each routing through the dispatch above.
Known Limitations
Gateway-dependent payment failure coverage. on_payment_failed fires when pmpro_subscription_payment_failed fires. This hook is confirmed to fire for PMP’s Stripe and PayPal Express gateway integrations. Other gateways may not call this hook on payment failure — if a gateway doesn’t fire it, Benecaster takes no immediate action on payment failure. Feed access continues until PMP expires or cancels the membership through its own retry logic.
Autorenewal detection gap. Some PMP gateway integrations renew subscriptions by extending the membership expiry date directly in the database, bypassing pmpro_changeMembershipLevel(). In those cases, the renewal condition above (level_id == cancel_level_id) is never met and on_subscription_renewed does not fire. benecaster_subscription_renewed is therefore not fired for those renewals. Feed access is unaffected — the membership level stays active — but any code listening to benecaster_subscription_renewed will not run.
Setup Notes
No custom code is needed to use PaidMembershipsBridge. It is configured, connected, and tested entirely through the Benecaster admin interface.
See Paid Memberships Pro Setup for the complete setup walkthrough, including version requirements, how to connect the bridge, map levels to tiers, and test the connection.