Published on

How to restrict coupon by user roles in WooCommerce

Adding a custom field to the WooCommerce coupon usage restriction tab

Custom fields can be added to the WooCommerce coupon edit screen via many different hooks, each can be used to appear in different locations of the screen, to restrict coupon by user roles we add our custom field via the woocommerce_coupon_options_usage_restriction action which is triggered on the usage restriction tab.

The custom field being added needs to know what the previously saved value is for the currently edited coupon, to do this we use a custom function that we have not created yet, but this function returns an array of allowed user roles, combine this list with a full list of wordpress roles allows the population and display of our custom dropdown field.

function jc_rcbr_woocommerce_coupon_options_usage_restriction($id)
{
    $selected_roles = jc_rcbr_get_coupon_user_roles($id);

    $available_roles = get_editable_roles();
    $roles = [];

    foreach ($available_roles as $role_id => $role) {
        $roles[$role_id] = $role['name'];
    }
?>
    <div class="options_group">
        <p class="form-field">
            <label for="customer_roles"><?php _e('Allowed user roles', 'woocommerce'); ?></label>
            <select id="customer_roles" name="customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php esc_attr_e('Any role', 'woocommerce'); ?>">
                <?php
                if ($roles) {
                    foreach ($roles as $role_id => $role) {
                        echo '<option value="' . esc_attr($role_id) . '"' . wc_selected($role_id, $selected_roles) . '>' . esc_html($role) . '</option>';
                    }
                }
                ?>
            </select> <?php echo wc_help_tip(__('List of user roles to check against when an order is placed, leave empty to allow any role.', 'woocommerce')); ?>
        </p>
    </div>
<?php
}

add_action('woocommerce_coupon_options_usage_restriction', 'jc_rcbr_woocommerce_coupon_options_usage_restriction', 10);

Saving WooCommerce coupon custom field data

When WooCommerce saves the default coupon data we hook into the process using the woocommerce_coupon_options_save hook, capture and sanitize out custom field data from the global post variable, storing the data as a comma separated string of selected roles.

function jc_rcbr_woocommerce_coupon_options_save($coupon_id)
{
    // convert list of allowes user roles to csv string
    $customer_roles = isset($_POST['customer_roles']) ? implode(',', wc_clean($_POST['customer_roles'])) : '';
    update_post_meta($coupon_id, 'jc_rcbr_customer_roles', $customer_roles);
}

add_action('woocommerce_coupon_options_save', 'jc_rcbr_woocommerce_coupon_options_save');

Reading list of user roles to restrict coupon usage

We read a coupons user roles restriction list from the post meta table, splitting the comma separated string into an array, using trim to remove any whitespace from entries, combined with array filter to remove any array elements.

function jc_rcbr_get_coupon_user_roles($coupon_id)
{
    $selected_roles = get_post_meta($coupon_id, 'jc_rcbr_customer_roles', true);
    $selected_roles = explode(',', $selected_roles);
    return array_filter(array_map( 'trim', $selected_roles ) );
}

Validate WooCommerce coupon usage against users roles

To intercept when a customer adds a coupon to their card we can use the woocommerce_coupon_is_valid filter, this allows us to return a boolean if its valid or not, we check to see if the coupon has any user roles restrictions, and that the user is logged in, if matched we compare the logged in users roles against the list of allowed roles, if none match then we invalidate the coupon.

function jc_rcbr_coupon_is_valid($is_valid, $coupon)
{
    $allowed_user_roles = jc_rcbr_get_coupon_user_roles($coupon->get_id());
    if ($is_valid && !empty($allowed_user_roles) && is_user_logged_in()) {

        $user = wp_get_current_user();
        $roles = (array) $user->roles;
        foreach ($roles as $role) {
            if (in_array($role, $allowed_user_roles, true)) {
                return true;
            }
        }
        return false;
    }

    return $is_valid;
}

add_filter('woocommerce_coupon_is_valid', 'jc_rcbr_coupon_is_valid', 10, 2);

Conclusion

In this article we showed how to restrict coupon by user roles by extending the default WooCommerce coupon management screen, adding custom custom fields to the usage restriction tab, how to store coupon custom fields in the post meta table and use that to validate if a coupon should be added to the cart.