I would like to make a members only section for WooCommerce customers who have made completed purchases, by assigning them a new role as the default for all registered users are "customer".
I've stumbled across some code which can solve this, however with over 200 products in store, it would be a hassle to list all the products individually.
Is there anyway to give a customer who makes ANY purchase a new role?
This is the original code:
add_action( 'woocommerce_order_status_completed', 'wpglorify_change_role_on_purchase' );
function wpglorify_change_role_on_purchase( $order_id ) {
// get order object and items
$order = new WC_Order( $order_id );
$items = $order->get_items();
$product_id = 56; // that's a specific product ID
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
// Remove old role
$user->remove_role( 'customer' );
// Add new role
$user->add_role( 'editor' );
}
}
}
Updated
You can use the following instead, which will change the user role when a customer make a successful purchase (paid order):
add_action( 'woocommerce_order_status_processing', 'change_role_on_purchase', 10, 2 );
add_action( 'woocommerce_order_status_completed', 'change_role_on_purchase', 10, 2 );
function change_role_on_purchase( $order_id, $order ) {
$user = $order->get_user(); // Get the WP_User Object
// Check for "customer" user roles only
if( is_a( $user, 'WP_User' ) && in_array( 'customer', (array) $user->roles ) ) {
// Remove WooCommerce "customer" role
$user->remove_role( 'customer' );
// Add new role
$user->add_role( 'editor' );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Note: The WC_Order Object Is already an existing argument for that hooked function (missing from your code).
Addition: To remove all previous roles and assign a new role
This is possible using just the WP_User set_role() method, so in the code, replace:
// Remove WooCommerce "customer" role
$user->remove_role( 'customer' );
// Add new role
$user->add_role( 'editor' );
by:
// Reset user roles and set a new one
$user->set_role( 'editor' );
Related
I am using the code below. I want to add an extra user role instead of the default 'customer' role right after an order is placed by a customer.
Unfortunately, the user role is not added by this code after an order is placed. Where did I miss?
add_action( 'woocommerce_order_status_completed', 'change_role_on_purchase' );
function change_role_on_purchase( $order_id ) {
$order = new WC_Order( $order_id );
$items = $order->get_items();
$product_id = 73; // that's my product ID
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
// Remove role
$user->remove_role( 'customer' );
// Add role
$user->add_role( 'premium' );
}
}
}
With your current code, or rather using the woocommerce_order_status_completed hook the user role will only be modified when an order contains the status 'complete'. However, this is rarely the case immediately right after an order is placed by a customer, the order status will be much more likely to be 'pending' or 'on-hold'
If you want to add a user role for existing users, immediately after an order is placed, you can do this via the woocommerce_thankyou hook
So you get:
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get user
$user = $order->get_user();
if ( is_a( $user, 'WP_User' ) ) {
// Add role
$user->add_role( 'premium' );
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
OR
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get the WP_User Object
$user = $order->get_user();
// Check for "customer" user roles only
if ( is_a( $user, 'WP_User' ) && in_array( 'customer', (array) $user->roles ) ) {
// Remove WooCommerce "customer" role (Optional)
$user->remove_role( 'customer' );
// Add role
$user->add_role( 'premium' );
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
Where you don't apply the code for every user, but only for a user with a certain user role
I get several orders where a customer selects "Direct Bank Transfer" and then they change their mind and want to pay by Credit Card. This is quite annoying because I have to manually change the order from "On Hold" to "Pending Payment" so they can pay by card via the "order-pay" endpoint which is found in "My Account" under "Orders".
I've been using the WooCommerce change order status BACS processing to automatically change the order status from "On Hold" to "Pending Payment".
// WooCommerce Change Order Status BACS Pending
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
function bacs_order_payment_pending_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) && ('on-hold' == $order->status ) ) {
$order->update_status('pending');
} else {
return;
}
}
But since I have several user profiles (I sell B2B as well), this is not practical for my shop. I'm trying to expand this snippet to also check for the user role. I've used the following in my other snippets. Is it possible to add the below logic to the snippet above?
$user = wp_get_current_user();
$roles = (array) $user->roles;
$roles_to_check = array('administrator', 'customer', 'shop_manager');
$compare = array_diff($roles, $roles_to_check);
if (empty($compare)){
This is my attempt.
// WooCommerce Change Order Status BACS Pending
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
function bacs_order_payment_pending_order_status( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order object
$order = new WC_Order( $order_id );
$user = wp_get_current_user();
$roles = (array) $user->roles;
$roles_to_check = array('administrator', 'customer', 'shop_manager');
$compare = array_diff($roles, $roles_to_check);
if (empty($compare)){
if ( ( get_post_meta($order->id, '_payment_method', true) == 'bacs' ) && ('on-hold' == $order->status ) ) {
$order->update_status('pending');
} else {
return;
}
}
You can use this as follows, comment with explanation added in the code
function bacs_order_payment_pending_order_status( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get user
$user = $order->get_user();
// Roles
$roles = (array) $user->roles;
// Roles to check
$roles_to_check = array( 'administrator', 'customer', 'shop_manager' );
// Compare
$compare = array_diff( $roles, $roles_to_check );
// Result is empty
if ( empty ( $compare ) ) {
if ( $order->get_payment_method() == 'bacs' && $order->has_status( 'on-hold' ) ) {
$order->update_status( 'pending' );
}
}
}
}
add_action( 'woocommerce_thankyou', 'bacs_order_payment_pending_order_status', 10, 1 );
Might Come in handy: WooCommerce: Get Order Info (total, items, etc) From $order Object
Woocommerce version 3.4.0 has introduced a much better hook that allows to change the default status for BACS payment gateway which is set to "on-hold".
Using this hook will:
Lighten your code,
Avoid "on-hold" notification to the customer when a BACS order is placed.
Here is that code:
add_filter( 'woocommerce_bacs_process_payment_order_status','filter_process_payment_order_status_callback', 10, 2 );
function filter_process_payment_order_status_callback( $status, $order ) {
// Here set the user roles to check
$roles_to_check = array( 'administrator', 'customer', 'shop_manager' );
$user = $order->get_user(); // Get the WP_User Object
$compare = array_diff( $user->roles, $roles_to_check ); // compare
if ( empty ( $compare ) ) {
return 'pending';
}
return $status;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Since WooCommerce 5+: Allow re-sending New Order Notification in WooCommerce 5+
Enabling New Order email notification (sent to the admin) for BACS payments:
As pending Orders doesn't send email notifications, you can enable that with the following
add_action( 'woocommerce_checkout_order_processed', 'pending_new_order_notification', 20, 1 );
function pending_new_order_notification( $order_id ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Only for "pending" order status and BACS payments
if( $order->has_status( 'pending' ) && $order->get_payment_method() === 'bacs' )
{
// Send "New Email" notification (to admin)
WC()->mailer()->get_emails()['WC_Email_New_Order']->trigger( $order_id );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Related: Send an Email notification to the admin for pending order status in WooCommerce
Useful: How to get WooCommerce order details
Related: Change default WooCommerce order status to processing for cheque and bacs payments
Freshly updated answer thread: WooCommerce change order status BACS processing
Here’s the situation: I initially set users that once they have completed a form, their user role is set to "Interviewed".
The problem arises when the subscription payment gets renewed (user role goes back to "Subscriber") or there's some temporary payment renewal issue (user role is set to "Customer").
I need the User's role to stay on "Interviewed" if the Order status is Completed.
So this is what I came up with:
add_action( 'woocommerce_order_status_completed', 'keep_role_interviewed' );
function keep_role_interviewed( $order_id ) {
$order = wc_get_order( $order_id );
if ( $order->user_id > 0 ) {
$user = new WP_User( $order->user_id );
$user_data = array( 'ID' => $user->ID,'role' => 'Interviewed' );
wp_update_user( $user_data );
}
}
But it doesn’t work.
Here I finally made it work.
add_action( 'woocommerce_subscription_status_updated', 'keep_interviewed_role' );
function keep_interviewed_role( $subscription) {
// Get the WC_Order Object from subscription
$order = wc_get_order( $subscription->get_parent_id() );
$order_status = $order->get_status();
// Get an instance of the customer WP_User Object
$user = $order->get_user();
if( is_a( $user, 'WP_User' ) && !in_array('interviewed', $user->roles)
&& $order_status == ('completed')); {
$user->set_role( 'interviewed' );
}
}
I have a need to build two plans (paid) on my site. If the user buys Gold Plan it should create a user (role) Gold and give him 20% discount on travel packages. If user buys platinum wp should create 'Platinum' user role for that customer. Now I have found the code online but it does not work:
add_action( 'woocommerce_order_status_completed',
'wpglorify_change_role_on_purchase' );
function wpglorify_change_role_on_purchase( $order_id ) {
// get order object and items
$order = new WC_Order( $order_id );
$items = $order->get_items();
$product_id = 85; // that's a specific product ID
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
// Remove role
$user->remove_role( 'customer' );
// Add role
$user->add_role( 'gold' );
}
}
$product_id = 86; // that's a specific product ID
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
// Remove role
$user->remove_role( 'customer' );
// Add role
$user->add_role( 'platinum' );
}
}
Now I have put this code in function.php file of a current active (child) theme but when I test it and buy the product wordpress keeps on creating 'customer' user.
Is something wrong with my code?
Updated
Your code is outdated and with some mistakes. Try the following, that will change the user role based on purchased product when order is completed ("completed" status):
add_action( 'woocommerce_order_status_completed', 'wpglorify_change_role_on_purchase', 10, 2 );
function wpglorify_change_role_on_purchase( $order_id, $order ) {
$gold_product_id = 85; // specific product ID for "gold" user role
$platinium_product_id = 86; // specific product ID for "platinium" user role
if( $user_id = $order->get_customer_id() ) {
// Get the WP_User Object
$wp_user = new WP_User( $user_id );
foreach ( $order->get_items() as $item ) {
// For "gold" user role
if ( $gold_product_id == $item->get_product_id() && $order->get_user_id() > 0 ) {
$user->remove_role( 'customer' ); // Remove 'customer' user role
$user->add_role( 'gold' ); // Add 'gold' user role
}
// For "platinum" user role
elseif ( $platinium_product_id == $item->get_product_id() && $order->get_user_id() > 0 ) {
$user->remove_role( 'customer' ); // Remove 'customer' user role
$user->add_role( 'platinum' ); // Add 'platinum' user role
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). It should work now.
Update: As you are using the following code to autocomplete orders:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$order->update_status( 'completed' );
}
You can include in it the user role change based on specific products. So try the following code will replace your existing function:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order' );
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) {
return;
}
// Get an instance of the WC_Order Object
$order = wc_get_order( $order_id );
// Only for logged in "customer" user role
if ( current_user_can( 'customer' ) ) {
$gold_product_id = 85; // specific product ID for "gold" user role
$platinium_product_id = 86; // specific product ID for "platinium" user role
$user_id = $order->get_customer_id(); // The user Id
// Get the WP_User Object
$wp_user = new WP_User( $user_id );
foreach ( $order->get_items() as $item ) {
// For "gold" user role
if ( $gold_product_id == $item->get_product_id() && $order->get_user_id() > 0 ) {
$user->remove_role( 'customer' ); // Remove 'customer' user role
$user->add_role( 'gold' ); // Add 'gold' user role
}
// For "platinum" user role
elseif ( $platinium_product_id == $item->get_product_id() && $order->get_user_id() > 0 ) {
$user->remove_role( 'customer' ); // Remove 'customer' user role
$user->add_role( 'platinum' ); // Add 'platinum' user role
}
}
}
$order->update_status( 'completed' );
}
Code goes in function.php file of your active child theme (or active theme). This should also work, merging both functions in one.
In Woocommerce, I am using YITH WooCommerce Subscription plugin and I want to change user role when a subscription get expired.
For that I know that I have to use this hook:
add_action( 'subscription_expired', 'my_function', 10, 2 );
function my_function( $user_id, $subscription_key ) {
$sub= wcs_get_subscription_from_key( $subscription_key );
// do something
}
Now I have two base roles, agencia and talento, how can I make it so when a subscription expires, it changes the user role from agencia_pro or agencia_pro_plus back to agencia and talento_pro or talento_pro_plus to talento?
How can I make a function that checks the user roles and change it to either talento or agencia based on its current user role?
Thanks.
YITH Woocommerce subscription free plugin seems to be closed and don't allow customizations…
For Official Woocommerce Subscriptions plugin use the following:
add_action( 'woocommerce_subscription_status_expired', 'change_user_role_on_subscription_expired', 10, 1 );
function change_user_role_on_subscription_expired( $subscription ) {
// Get WP_User Object from subscription
$user = new WP_User($subscription->get_user_id());
if ( in_array('agencia_pro', $user->roles) ) {
$user->remove_role( 'agencia_pro' );
$user->add_role( 'agencia' );
}
elseif ( in_array('agencia_pro_plus', $user->roles) ) {
$user->remove_role( 'agencia_pro_plus' );
$user->add_role( 'agencia' );
}
elseif ( in_array('talento_pro', $user->roles) ) {
$user->remove_role( 'talento_pro' );
$user->add_role( 'talento' );
}
elseif ( in_array('talento_pro_plus', $user->roles) ) {
$user->remove_role( 'talento_pro_plus' );
$user->add_role( 'talento' );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested, it should works.