Selling User Role with WooCommerce, Membership Sites, Digital Products

This code solves your simple paid membership site needs.

If you are selling one-time content and hiding some of the site sections with user role check. This code is for you.

I wrote this code for my upcoming WordPress Development Course site and this code is enough for my case.

I could probably make this work with a subscription membership payment method too but maybe another time 🙂


  1. Adding Custom Role “student” to WordPress:
  2. Adding a Special User Roles Section for Products:
  3. Custom User Role for Each Product:
  4. Giving Roles to Buyers When They Purchase The Role with the Product:
  5. Updating Roles When Users Log In (just incase if the role not assigned in first place. maybe payment failed manual confirmation needed…etc):
    • Every time a user logs into the website, it checks what products they have bought in the past. If they bought something tagged for a special role, like “student”, and they don’t already have that role, the code will auto-assign it.

 * Function to add a new user role named "Student"
function add_student_user_role() {
    // Add a new user role with the name 'Student'
    // The 'read' capability is a minimal capability that can be assigned
    add_role('student', 'student', array('read' => true));

// Hook the function to WordPress 'init'
add_action('init', 'add_student_user_role');

// Add the custom tab
add_filter('woocommerce_product_data_tabs', 'add_user_role_custom_tab');
function add_user_role_custom_tab($tabs) {
    $tabs['user_role_tab'] = array(
        'label'    => __('User Role', 'woocommerce'),
        'target'   => 'user_role_options',
        'class'    => array('show_if_simple', 'show_if_variable'),
    return $tabs;

// Create the tab content
add_action('woocommerce_product_data_panels', 'user_role_options_product_tab_content');
function user_role_options_product_tab_content() {
    global $post;

    ?><div id='user_role_options' class='panel woocommerce_options_panel'><?php
        ?><div class='options_group'><?php
            // Dropdown for user roles
            $roles = wp_roles()->get_names();
                'id'      => 'custom_user_role',
                'label'   => __('Select User Role', 'woocommerce'),
                'options' => array('' => __('Select a role', 'woocommerce')) + $roles

// Save the custom field
add_action('woocommerce_process_product_meta', 'save_user_role_option_fields');
function save_user_role_option_fields($post_id) {
    $custom_user_role = isset($_POST['custom_user_role']) ? $_POST['custom_user_role'] : '';
    update_post_meta($post_id, 'custom_user_role', sanitize_text_field($custom_user_role));

add_action('woocommerce_order_status_completed', 'assign_custom_user_role_on_purchase_optimized');

function assign_custom_user_role_on_purchase_optimized($order_id) {
    $order = wc_get_order($order_id);
    $user = $order->get_user();

    if (!$user) {
        // No user associated with the order

    // Fetch all roles once instead of checking in every iteration
    $available_roles = array_keys(wp_roles()->get_names());

    // Collect all product IDs from the order
    $product_ids = array_map(function ($item) {
        return $item->get_product_id();
    }, $order->get_items());

    // Remove duplicate product IDs to minimize redundant queries
    $unique_product_ids = array_unique($product_ids);

    // Loop through unique product IDs
    foreach ($unique_product_ids as $product_id) {
        $custom_role = get_post_meta($product_id, 'custom_user_role', true);

        if (!empty($custom_role) && in_array($custom_role, $available_roles)) {
            // Assign the custom role to the user
            // Break the loop as we've found a valid role and assigned it

// Hook Function to Login
add_action('wp_login', 'assign_role_based_on_order_history');

function assign_role_based_on_order_history($user_login) {
    $user = get_user_by('login', $user_login);
    if (!$user) {

    $user_id = $user->ID;

    // Fetch all roles once
    $available_roles = array_keys(wp_roles()->get_names());

    // Get all completed orders for the user
    $orders = wc_get_orders(array(
        'status' => 'completed',
        'customer_id' => $user_id,

    foreach ($orders as $order) {
        foreach ($order->get_items() as $item) {
            $product_id = $item->get_product_id();
            $custom_role = get_post_meta($product_id, 'custom_user_role', true);

            if (!empty($custom_role) && in_array($custom_role, $available_roles) && !$user->has_cap('custom_role')) {
                // Add the custom role to the user without removing existing roles
                // Consider breaking the loop if only one role should be assigned
                // break 2; // Uncomment to break out of both loops

Recommended Plugins with this code:

Blocks visibility Plugin: if you need to show or hide your some of your sections or posts depending on users status or roles you can use this free plugin.

User Role Editor Plugin: You can use this plugin if you want to add and sell other roles.

Leave the first comment