Skip to main content

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:

  1. Calls pmpro_getMembershipLevelsForUser( $user_id ) to retrieve all active membership level objects for the user.
  2. For each active level, reads its level ID.
  3. Looks up a matching row in benecaster_tier_map where plugin_slug = 'paid-memberships' and external_tier_id matches the level ID.
  4. Filters to rows where show_id matches the given $show_id.
  5. Returns the internal_tier_slug of the first matching row, or null if 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.