WooCommerce Memberships: access and discounts
Use this when an integration needs to decide whether a user can access content/products, alter restriction behavior, adjust drip-content timing, grant access from products, or work with member prices. This is where generic WooCommerce or WordPress assumptions most often produce subtle bugs.
Misconception this skill corrects
"Membership access is a simple active-status check, and member discounts are just a product price filter."
Memberships combines plan rules, user membership status, access start time, delayed/dripped access, product/category restrictions, purchase restrictions, product grants, and price-adjustment filters. Use the public capability/discount APIs and the Memberships filters at the rule layer.
When to use this skill
Trigger when ANY of the following is true:
- The user asks "can this member view/buy/access", "hide protected product", "drip content", "scheduled access", "member discount", "membership price", "grant access product", or "original price before discount".
- Code contains
wc_memberships_user_can(), wc_memberships_is_post_content_restricted(), wc_memberships_is_product_viewing_restricted(), wc_memberships_access_from_time, wc_memberships_user_object_access_start_time, wc_memberships_user_has_member_discount(), wc_memberships_get_member_product_discount(), or wc_memberships_get_discounted_price.
- You are writing WooCommerce price filters in a store that uses Memberships.
Access checks
Prefer wc_memberships_user_can() for final "can this user do this" decisions:
if ( wc_memberships_user_can( $user_id, 'view', array( 'post' => $post_id ) ) ) {
myplugin_show_content();
}
if ( wc_memberships_user_can( $user_id, 'purchase', array( 'product' => $product_id ) ) ) {
myplugin_allow_purchase();
}
Useful public helpers:
| Need |
API |
Notes |
| Post content restricted? |
wc_memberships_is_post_content_restricted( $post_id ) |
Tells whether Memberships restricts the object, not whether a specific user has access. |
| Term restricted? |
wc_memberships_is_term_restricted( $term_id, $taxonomy ) |
Useful for category archives/custom taxonomies. |
| Product viewing restricted? |
wc_memberships_is_product_viewing_restricted( $product_id ) |
Product-level viewing rules. |
| Product purchasing restricted? |
wc_memberships_is_product_purchasing_restricted( $product_id ) |
Use if available in installed version for purchase-only rules. |
| User active member? |
wc_memberships_is_user_active_member( $user_id, $plan ) |
Membership status check, not rule/object access. |
| User active or delayed member? |
wc_memberships_is_user_active_or_delayed_member( $user_id, $plan ) |
Useful for drip UX where membership exists but access is not yet open. |
Do not replace wc_memberships_user_can() with a raw membership status check when the target object matters.
Restriction and drip hooks
| Need |
Hook |
Type |
Args |
Use |
| Change rule access start timestamp |
wc_memberships_access_from_time |
filter |
int $from_time, WC_Memberships_Membership_Plan_Rule $rule, WC_Memberships_User_Membership $membership |
Main drip-content timing extension point. Return a Unix timestamp. |
| Override object access start time |
wc_memberships_user_object_access_start_time |
filter |
int $access_time, array $args |
Override calculated access start time for a target object/rule args. |
| Force content public |
wc_memberships_is_post_public |
filter |
bool $public, int $post_id, string $post_type |
Bypass restrictions for selected content. |
| Public-content cache TTL |
wc_memberships_public_content_cache_expiration |
filter |
int $expiration |
Adjust cache lifetime for public-content lookups. |
| Products granting access |
wc_memberships_products_that_grant_access |
filter |
array $product_ids, int $object_id, string $rule_type, array $args, array $unfiltered |
Change product IDs shown as granting access to protected objects. |
| Products granting access to term query args |
wc_membership_get_products_that_grant_access_to_term_args |
filter |
array $args |
Note singular wc_membership. Adjust lookup query. |
| Feed restriction |
wc_memberships_is_feed_restricted |
filter |
bool $restricted |
Control restricted feed output. |
| Restrictable comment types |
wc_memberships_restrictable_comment_types |
filter |
string[] $types |
Include/exclude comment types from restriction behavior. |
| Rule object IDs |
wc_memberships_rule_object_ids |
filter |
array $ids, WC_Memberships_Membership_Plan_Rule $rule |
Adjust objects a rule applies to. |
| Rule priority |
wc_memberships_rule_priority |
filter |
int $priority, $rule |
Resolve conflicts between rules. |
| Rule access time |
wc_memberships_rule_access_start_time |
filter |
int $access_time, int $from_time, WC_Memberships_Membership_Plan_Rule $rule |
Lower-level rule access timing. |
There is not a single universal wc_memberships_user_can result filter to "just allow access". Change the rule inputs, public-content decision, access start time, or membership status intentionally.
Product grants
| Need |
Hook/API |
Type |
Use |
| Product in order grants plan access |
wc_memberships_access_granting_purchased_product_id |
filter |
Choose parent/variation or multiple product IDs used for the grant. |
| Access granted from purchase |
wc_memberships_grant_membership_access_from_purchase |
action |
React after purchase grants membership. Args include order/product/user/membership context. |
| Order grant metadata |
wc_memberships_get_order_access_granted_memberships(), wc_memberships_has_order_granted_access(), wc_memberships_set_order_access_granted_membership() |
API |
Read or mark which memberships an order granted. |
| Force account at checkout |
wc_memberships_force_checkout_registration |
filter |
Ensure a guest can become a user before access is granted. |
When a variable product grants access, check whether the plan expects the parent product, variation, or both. The grant filter is also used by checkout registration logic, so return IDs that really grant the selected plan.
Member discount APIs
| Need |
API |
Notes |
| Product has any member discount |
wc_memberships_product_has_member_discount( $product ) |
Product-level rule availability. |
| User has discount for product |
wc_memberships_user_has_member_discount( $product, $member ) |
Use for current-user eligibility. $member can be user/membership context. |
| Get discount amount/rule |
wc_memberships_get_member_product_discount( $membership, $product, $formatted = false ) |
For display or external sync. |
| Badge HTML |
wc_memberships_get_member_discount_badge( $product, $variation = false ) |
Use rather than recreating badge markup. |
Member discount hooks
| Need |
Hook |
Type |
Args |
Use |
| Stack discounts |
wc_memberships_allow_cumulative_member_discounts |
filter |
bool $allow, int $user_id, WC_Product $product |
Allow several plans/rules to cumulate. |
| User discount eligibility |
wc_memberships_is_user_eligible_for_member_discounts |
filter |
bool $eligible, int $user_id |
Global user-level discount gate. |
| Exclude product |
wc_memberships_exclude_product_from_member_discounts |
filter |
bool $excluded, WC_Product $product |
Product-level exclusion. |
| Discounted price |
wc_memberships_get_discounted_price |
filter |
$price, $base_price, int $product_id, int $user_id, WC_Product $product |
Final price extension point. Return numeric price-like value. |
| Rounding precision |
wc_memberships_discount_rounding_precision |
filter |
int $precision |
Match store/accounting needs. |
| Price filter priority |
wc_memberships_price_adjustments_filter_priority |
filter |
int $priority |
Coordinate with other dynamic pricing plugins. |
| Product discount rules for user |
wc_memberships_product_purchasing_discount_rules_for_user |
filter |
array $rules, WP_User $user, WC_Product $product |
Add/remove discount rules before price calculation. |
| Use discount format |
wc_memberships_member_prices_use_discount_format |
filter |
bool $use_discount_format |
Control sale/strike-through style. |
| Price HTML |
wc_memberships_get_price_html, wc_memberships_get_discounted_price_html, wc_memberships_get_price_html_before_discount, wc_memberships_get_price_html_after_discount |
filter |
Customize displayed price HTML. |
|
| Sale price display |
wc_memberships_member_prices_display_sale_price |
filter |
bool $display_sale_price |
Control how sale price and member price interact. |
| Product sale state while discounted |
wc_memberships_product_is_on_sale_before_discount_excluded_filters |
filter |
array $filters |
Coordinate sale checks with price filters. |
| Badge HTML |
wc_memberships_member_discount_badge, wc_memberships_variation_member_discount_badge |
filter |
Customize badge text/markup. |
|
Price adjustment recursion safety
Memberships adjusts WooCommerce product prices through filters. If your code needs the original WooCommerce price while Memberships discounts are active, disable Memberships adjustments only around that read and re-enable them immediately.
do_action( 'wc_memberships_discounts_disable_price_adjustments' );
$product = wc_get_product( $product_id );
$regular_price = $product ? $product->get_regular_price() : null;
do_action( 'wc_memberships_discounts_enable_price_adjustments' );
For price HTML only:
do_action( 'wc_memberships_discounts_disable_price_html_adjustments' );
$html = $product->get_price_html();
do_action( 'wc_memberships_discounts_enable_price_html_adjustments' );
Do not leave adjustments disabled across template rendering, cart calculation, REST responses, or async callbacks.
Common mistakes
// WRONG: active membership is not the same as access to this protected object.
if ( wc_memberships_is_user_active_member( $user_id, $plan_id ) ) {
echo get_post_field( 'post_content', $post_id );
}
// RIGHT: ask Memberships for access to the target object.
if ( wc_memberships_user_can( $user_id, 'view', array( 'post' => $post_id ) ) ) {
echo apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) );
}
// WRONG: custom price filter can double-discount or recurse through Memberships.
add_filter( 'woocommerce_product_get_price', function ( $price, $product ) {
return wc_memberships_get_member_product_discount( get_current_user_id(), $product );
}, 10, 2 );
// RIGHT: use Memberships discount filters or APIs at their intended layer.
add_filter( 'wc_memberships_get_discounted_price', function ( $price, $base_price, $product_id, $user_id, $product ) {
if ( myplugin_should_cap_member_price( $product_id, $user_id ) ) {
return min( $price, 99 );
}
return $price;
}, 10, 5 );
// WRONG: drip access by comparing post dates or membership start meta yourself.
$can_view = strtotime( get_post_meta( $membership_id, '_start_date', true ) ) < time();
// RIGHT: alter access start time or call the final access API.
add_filter( 'wc_memberships_access_from_time', function ( $from_time, $rule, $membership ) {
if ( myplugin_rule_uses_external_unlock( $rule ) ) {
return myplugin_get_unlock_timestamp( $membership->get_user_id(), $rule );
}
return $from_time;
}, 10, 3 );
Cross-references
- Use
wcm-membership-hooks for creation, status transitions, profile fields, REST/webhooks, members area, CSV, admin, and Subscriptions-linked membership hooks.
- Use
wc-variations-pricing-filters when combining Memberships discounts with custom variation price logic and cached variation price hashes.
1---2name: wcm-access-discounts3description: WooCommerce Memberships access, restriction, drip-content, product-grant, and member-discount playbook. Use when code checks whether a user can view/purchase content or products, changes scheduled access dates, customizes protected/public content, maps parent or variation products that grant membership access, applies member prices, reads original prices around discounts, or contains wc_memberships_user_can, wc_memberships_access_from_time, wc_memberships_user_has_member_discount, wc_memberships_get_member_product_discount, wc_memberships_get_discounted_price, or member discount badge hooks.4---56# WooCommerce Memberships: access and discounts78Use this when an integration needs to decide whether a user can access content/products, alter restriction behavior, adjust drip-content timing, grant access from products, or work with member prices. This is where generic WooCommerce or WordPress assumptions most often produce subtle bugs.910## Misconception this skill corrects1112> "Membership access is a simple active-status check, and member discounts are just a product price filter."1314Memberships combines plan rules, user membership status, access start time, delayed/dripped access, product/category restrictions, purchase restrictions, product grants, and price-adjustment filters. Use the public capability/discount APIs and the Memberships filters at the rule layer.1516## When to use this skill1718Trigger when ANY of the following is true:1920- The user asks "can this member view/buy/access", "hide protected product", "drip content", "scheduled access", "member discount", "membership price", "grant access product", or "original price before discount".21- Code contains `wc_memberships_user_can()`, `wc_memberships_is_post_content_restricted()`, `wc_memberships_is_product_viewing_restricted()`, `wc_memberships_access_from_time`, `wc_memberships_user_object_access_start_time`, `wc_memberships_user_has_member_discount()`, `wc_memberships_get_member_product_discount()`, or `wc_memberships_get_discounted_price`.22- You are writing WooCommerce price filters in a store that uses Memberships.2324## Access checks2526Prefer `wc_memberships_user_can()` for final "can this user do this" decisions:2728```php29if ( wc_memberships_user_can( $user_id, 'view', array( 'post' => $post_id ) ) ) {30 myplugin_show_content();31}3233if ( wc_memberships_user_can( $user_id, 'purchase', array( 'product' => $product_id ) ) ) {34 myplugin_allow_purchase();35}36```3738Useful public helpers:3940| Need | API | Notes |41|---|---|---|42| Post content restricted? | `wc_memberships_is_post_content_restricted( $post_id )` | Tells whether Memberships restricts the object, not whether a specific user has access. |43| Term restricted? | `wc_memberships_is_term_restricted( $term_id, $taxonomy )` | Useful for category archives/custom taxonomies. |44| Product viewing restricted? | `wc_memberships_is_product_viewing_restricted( $product_id )` | Product-level viewing rules. |45| Product purchasing restricted? | `wc_memberships_is_product_purchasing_restricted( $product_id )` | Use if available in installed version for purchase-only rules. |46| User active member? | `wc_memberships_is_user_active_member( $user_id, $plan )` | Membership status check, not rule/object access. |47| User active or delayed member? | `wc_memberships_is_user_active_or_delayed_member( $user_id, $plan )` | Useful for drip UX where membership exists but access is not yet open. |4849Do not replace `wc_memberships_user_can()` with a raw membership status check when the target object matters.5051## Restriction and drip hooks5253| Need | Hook | Type | Args | Use |54|---|---|---|---|---|55| Change rule access start timestamp | `wc_memberships_access_from_time` | filter | `int $from_time, WC_Memberships_Membership_Plan_Rule $rule, WC_Memberships_User_Membership $membership` | Main drip-content timing extension point. Return a Unix timestamp. |56| Override object access start time | `wc_memberships_user_object_access_start_time` | filter | `int $access_time, array $args` | Override calculated access start time for a target object/rule args. |57| Force content public | `wc_memberships_is_post_public` | filter | `bool $public, int $post_id, string $post_type` | Bypass restrictions for selected content. |58| Public-content cache TTL | `wc_memberships_public_content_cache_expiration` | filter | `int $expiration` | Adjust cache lifetime for public-content lookups. |59| Products granting access | `wc_memberships_products_that_grant_access` | filter | `array $product_ids, int $object_id, string $rule_type, array $args, array $unfiltered` | Change product IDs shown as granting access to protected objects. |60| Products granting access to term query args | `wc_membership_get_products_that_grant_access_to_term_args` | filter | `array $args` | Note singular `wc_membership`. Adjust lookup query. |61| Feed restriction | `wc_memberships_is_feed_restricted` | filter | `bool $restricted` | Control restricted feed output. |62| Restrictable comment types | `wc_memberships_restrictable_comment_types` | filter | `string[] $types` | Include/exclude comment types from restriction behavior. |63| Rule object IDs | `wc_memberships_rule_object_ids` | filter | `array $ids, WC_Memberships_Membership_Plan_Rule $rule` | Adjust objects a rule applies to. |64| Rule priority | `wc_memberships_rule_priority` | filter | `int $priority, $rule` | Resolve conflicts between rules. |65| Rule access time | `wc_memberships_rule_access_start_time` | filter | `int $access_time, int $from_time, WC_Memberships_Membership_Plan_Rule $rule` | Lower-level rule access timing. |6667There is not a single universal `wc_memberships_user_can` result filter to "just allow access". Change the rule inputs, public-content decision, access start time, or membership status intentionally.6869## Product grants7071| Need | Hook/API | Type | Use |72|---|---|---|---|73| Product in order grants plan access | `wc_memberships_access_granting_purchased_product_id` | filter | Choose parent/variation or multiple product IDs used for the grant. |74| Access granted from purchase | `wc_memberships_grant_membership_access_from_purchase` | action | React after purchase grants membership. Args include order/product/user/membership context. |75| Order grant metadata | `wc_memberships_get_order_access_granted_memberships()`, `wc_memberships_has_order_granted_access()`, `wc_memberships_set_order_access_granted_membership()` | API | Read or mark which memberships an order granted. |76| Force account at checkout | `wc_memberships_force_checkout_registration` | filter | Ensure a guest can become a user before access is granted. |7778When a variable product grants access, check whether the plan expects the parent product, variation, or both. The grant filter is also used by checkout registration logic, so return IDs that really grant the selected plan.7980## Member discount APIs8182| Need | API | Notes |83|---|---|---|84| Product has any member discount | `wc_memberships_product_has_member_discount( $product )` | Product-level rule availability. |85| User has discount for product | `wc_memberships_user_has_member_discount( $product, $member )` | Use for current-user eligibility. `$member` can be user/membership context. |86| Get discount amount/rule | `wc_memberships_get_member_product_discount( $membership, $product, $formatted = false )` | For display or external sync. |87| Badge HTML | `wc_memberships_get_member_discount_badge( $product, $variation = false )` | Use rather than recreating badge markup. |8889## Member discount hooks9091| Need | Hook | Type | Args | Use |92|---|---|---|---|---|93| Stack discounts | `wc_memberships_allow_cumulative_member_discounts` | filter | `bool $allow, int $user_id, WC_Product $product` | Allow several plans/rules to cumulate. |94| User discount eligibility | `wc_memberships_is_user_eligible_for_member_discounts` | filter | `bool $eligible, int $user_id` | Global user-level discount gate. |95| Exclude product | `wc_memberships_exclude_product_from_member_discounts` | filter | `bool $excluded, WC_Product $product` | Product-level exclusion. |96| Discounted price | `wc_memberships_get_discounted_price` | filter | `$price, $base_price, int $product_id, int $user_id, WC_Product $product` | Final price extension point. Return numeric price-like value. |97| Rounding precision | `wc_memberships_discount_rounding_precision` | filter | `int $precision` | Match store/accounting needs. |98| Price filter priority | `wc_memberships_price_adjustments_filter_priority` | filter | `int $priority` | Coordinate with other dynamic pricing plugins. |99| Product discount rules for user | `wc_memberships_product_purchasing_discount_rules_for_user` | filter | `array $rules, WP_User $user, WC_Product $product` | Add/remove discount rules before price calculation. |100| Use discount format | `wc_memberships_member_prices_use_discount_format` | filter | `bool $use_discount_format` | Control sale/strike-through style. |101| Price HTML | `wc_memberships_get_price_html`, `wc_memberships_get_discounted_price_html`, `wc_memberships_get_price_html_before_discount`, `wc_memberships_get_price_html_after_discount` | filter | Customize displayed price HTML. |102| Sale price display | `wc_memberships_member_prices_display_sale_price` | filter | `bool $display_sale_price` | Control how sale price and member price interact. |103| Product sale state while discounted | `wc_memberships_product_is_on_sale_before_discount_excluded_filters` | filter | `array $filters` | Coordinate sale checks with price filters. |104| Badge HTML | `wc_memberships_member_discount_badge`, `wc_memberships_variation_member_discount_badge` | filter | Customize badge text/markup. |105106## Price adjustment recursion safety107108Memberships adjusts WooCommerce product prices through filters. If your code needs the original WooCommerce price while Memberships discounts are active, disable Memberships adjustments only around that read and re-enable them immediately.109110```php111do_action( 'wc_memberships_discounts_disable_price_adjustments' );112113$product = wc_get_product( $product_id );114$regular_price = $product ? $product->get_regular_price() : null;115116do_action( 'wc_memberships_discounts_enable_price_adjustments' );117```118119For price HTML only:120121```php122do_action( 'wc_memberships_discounts_disable_price_html_adjustments' );123$html = $product->get_price_html();124do_action( 'wc_memberships_discounts_enable_price_html_adjustments' );125```126127Do not leave adjustments disabled across template rendering, cart calculation, REST responses, or async callbacks.128129## Common mistakes130131```php132// WRONG: active membership is not the same as access to this protected object.133if ( wc_memberships_is_user_active_member( $user_id, $plan_id ) ) {134 echo get_post_field( 'post_content', $post_id );135}136137// RIGHT: ask Memberships for access to the target object.138if ( wc_memberships_user_can( $user_id, 'view', array( 'post' => $post_id ) ) ) {139 echo apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) );140}141142// WRONG: custom price filter can double-discount or recurse through Memberships.143add_filter( 'woocommerce_product_get_price', function ( $price, $product ) {144 return wc_memberships_get_member_product_discount( get_current_user_id(), $product );145}, 10, 2 );146147// RIGHT: use Memberships discount filters or APIs at their intended layer.148add_filter( 'wc_memberships_get_discounted_price', function ( $price, $base_price, $product_id, $user_id, $product ) {149 if ( myplugin_should_cap_member_price( $product_id, $user_id ) ) {150 return min( $price, 99 );151 }152153 return $price;154}, 10, 5 );155156// WRONG: drip access by comparing post dates or membership start meta yourself.157$can_view = strtotime( get_post_meta( $membership_id, '_start_date', true ) ) < time();158159// RIGHT: alter access start time or call the final access API.160add_filter( 'wc_memberships_access_from_time', function ( $from_time, $rule, $membership ) {161 if ( myplugin_rule_uses_external_unlock( $rule ) ) {162 return myplugin_get_unlock_timestamp( $membership->get_user_id(), $rule );163 }164165 return $from_time;166}, 10, 3 );167```168169## Cross-references170171- Use `wcm-membership-hooks` for creation, status transitions, profile fields, REST/webhooks, members area, CSV, admin, and Subscriptions-linked membership hooks.172- Use `wc-variations-pricing-filters` when combining Memberships discounts with custom variation price logic and cached variation price hashes.