My issue is similar to this question. The answers are however failing for me.
When a new user subscribes to the website, the automatic code fails, as can be read on this log:
Screenshot of log from woocommerce
Specifically, i think the code failing is:
// Get an instance of the customer WP_User Object
$user = $order->get_user();
This is the whole code block:
// Custom function for your settings - Variation id per user role
function variation_id_per_user_role_settings(){
// Settings: set the variation ID as key with the related user role as value
return array(
//Free subscriptions
'1023' => 'free',
//Favorit subscriptions
'995' => 'favorit',
'996' => 'favorit',
'1811' => 'favorit',
'1812' => 'favorit',
'1814' => 'favorit',
'1815' => 'favorit',
'1816' => 'favorit',
'1818' => 'favorit',
//Unlimited subscriptions
'1013' => 'unlimited',
'1014' => 'unlimited',
//Partner subscriptions
'1016' => 'partner',
'1017' => 'partner',
);
}
// Custom function that check item and change user role based on variation ID
function check_order_item_and_change_user_role( $item, $user, $settings ){
$product = $item->get_product(); // The product object
// Only for variation subscriptions
if( $product->is_type('subscription_variation') ) {
$variation_id = $item->get_variation_id(); // the variation ID
$user_role = $settings[$variation_id]; // The right user role for the current variation ID
// If current user role doesn't match with the right role
if( ! in_array( $user_role, $user->roles) ) {
// Remove "subscriber" user role (if it is set)
if( in_array('subscriber', $user->roles) ) {
$user->remove_role( 'subscriber' );
}
// Remove other user roles (if they are set)
foreach ( $settings as $key_id => $value_role ) {
if( in_array($value_role, $user->roles) && $user_role !== $value_role ) {
$user->remove_role( $value_role );
}
}
// Set the right user role (if it is not set yet)
$user->set_role( $user_role );
}
}
}
// On first purchase (if needed)
add_action( 'woocommerce_subscription_status_updated', 'active_subscription_change_user_role', 100, 3 );
function active_subscription_change_user_role( $subscription, $new_status, $old_status ) {
// When subscrition status is updated to "active"
if ( $new_status === 'active' ) {
// Get the WC_Order Object from subscription
$order = wc_get_order( $subscription->get_parent_id() );
// Get an instance of the customer WP_User Object
$user = $order->get_user();
// Check that it's not a guest customer
if( is_a( $user, 'WP_User' ) && $user->ID > 0 ) {
// Load settings
$settings = variation_id_per_user_role_settings();
// Loop through order items
foreach ( $subscription->get_items() as $item ) {
check_order_item_and_change_user_role( $item, $user, $settings );
}
}
}
}
// On switched purchased subscription
add_action( 'woocommerce_order_status_changed', 'switched_subscription_change_user_role_on_order_status_change', 100, 4 );
function switched_subscription_change_user_role_on_order_status_change( $order_id, $old_status, $new_status, $order ) {
// When order status is updated to 'processing' or 'completed' status
if ( in_array( $new_status, array('processing','completed') ) ) {
// Get an instance of the customer WP_User Object
$user = $order->get_user();
// Check that it's not a guest customer
if( is_a( $user, 'WP_User' ) && $user->ID > 0 ) {
// Load settings
$settings = variation_id_per_user_role_settings();
// Loop through order items
foreach ( $order->get_items() as $item ) {
check_order_item_and_change_user_role( $item, $user, $settings );
}
}
}
}
What i'm trying to do is:
check if the order has a subscription, change the role from "subscriber" to a role fitting the subscription.
change role when subscription ends or is changed.
Related
I am looking for a way, to limit coupon usage and to display an error message if a customer have used related coupons in previous orders before in WooCommerce.
By related coupons, I mean: couponcodes that appear in a predefined array along with the currently inserted coupon on the cart/checkout page. On that basis, compare.
My code attempt:
add_filter( 'woocommerce_coupon_is_valid', 'specific_coupons_valid', 10,
3 );
function specific_coupons_valid( $is_valid, $coupon, $discount ){
$coupon_codes = array( 'free10', 'free20', 'free30' );
$args = array(
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'asc',
'post_type' => 'shop_coupon',
'post_status' => 'publish',
);
$coupons = get_posts( $args );
if( in_array( $coupons, $coupon_codes ) ) {
$is_valid = false;
}
return $is_valid;
}
Any help is appreciated
To limit coupon usage if a customer have used related coupons in previous orders in WooCommerce before, you can use:
// Compare & return true OR false
function compare_related_coupon_codes( $current_coupon_code ) {
// Add multiple coupon codes to compare, seperated by a comma
$compare_coupon_codes = array( 'coupon1', 'coupon2', 'coupon3' );
// Default
$valid = true;
// When the current coupon code has to be compared with other coupon codes
if ( in_array( $current_coupon_code, $compare_coupon_codes ) ) {
// Get user ID
$user_id = get_current_user_id();
// Get the WC_Customer instance Object
$customer = New WC_Customer( $user_id );
// Billing email
$email = $customer->get_billing_email();
// Loop through
foreach ( $compare_coupon_codes as $coupon_code ) {
// Get the WC_Coupon instance Object
$coupon_obj = New WC_Coupon( $coupon_code );
// If one of the coupons has already been used by the customer
if ( array_intersect( array( $user_id, $email ), $coupon_obj->get_used_by() ) ) {
$valid = false;
break;
}
}
}
// Return
return $valid;
}
// Valid
function filter_woocommerce_coupon_is_valid( $valid, $coupon, $discount ) {
if ( ! is_user_logged_in() ) return $valid;
// Get current applied coupon code
$current_coupon_code = strtolower( $coupon->get_code() );
// Call function, true OR false
$valid = compare_related_coupon_codes( $current_coupon_code );
// NOT valid
if ( ! $valid ) {
throw new Exception( __( 'My error', 'woocommerce' ), 109 );
}
return $valid;
}
add_filter( 'woocommerce_coupon_is_valid', 'filter_woocommerce_coupon_is_valid', 10, 3 );
Explanation via comment tags added in my answer
Related: Coupon daily time range in WooCommerce
I have a problem in wordpress.
So at the end of a payment in woocommerce, I would like to make a query and insert in a table in the database the name of the user, the type of subscription and the days required.
How do you think I could do that?
So far, I've written this
function wpglorify_change_role_on_purchase( $order_id ) {
global $wpdb;
$order = new WC_Order( $order_id );
$items = $order->get_items();
$product_id = 193; // 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( 'Author' );
// Add new role
$user->add_role( 'Subscriber' );
$query = $wpdb->insert('wp_pms_member_subscriptions', array(
'id' => 1,
'user_id' => $user->id,
'subscribtion_plan_id' => 233,
'status' => 'active'
));
$wpdb->query($query);
}
}
}
But it doesn't really do what I want and I would like to make a query in this table and insert the member directly in the subscription table
And the name of table is
wp_pms_member_subscriptions
I have two scripts that are interfering with each other. What I need for this website is that: all successful purchases should go to page https://aefcoaching.com/gracias-por-tu-compra, except successful purchase of Product ID 1813, which should go to https://aefcoaching.com/gracias-gimnasio-mental/
/* # Redirect to the Thank You page after successful payment in WooCommerce */
if( !function_exists('sc_custom_redirect_after_purchase') ):
function sc_custom_redirect_after_purchase() {
global $wp;
if ( is_checkout() && !empty($wp->query_vars['order-received']) ) :
$order_id = absint($wp->query_vars['order-received']);
$order_key = wc_clean($_GET['key']);
$th_page_url = 'https://aefcoaching.com/gracias-por-tu-compra';
$redirect = add_query_arg(
array(
'order' => $order_id,
'key' => $order_key,
), $th_page_url);
wp_safe_redirect($redirect);
exit;
endif;
}
add_action('template_redirect', 'sc_custom_redirect_after_purchase');
endif;
/* # Redirect Gimnasio Mental purchases to a specific Thank You Page */
function action_woocommerce_thankyou( $order_id ) {
if( ! $order_id ) {
return;
}
// Instannce of the WC_Order Object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// False
$redirection = false;
// Loop through order items
foreach ( $order->get_items() as $item_key => $item ) {
// Product ID(s)
$product_ids = array( $item->get_product_id(), $item->get_variation_id() );
// Product ID in array
if ( in_array( 1813, $product_ids ) ) {
$redirection = true;
}
}
}
// Make the custom redirection when a targeted product has been found in the order
if ( $redirection ) {
wp_safe_redirect( home_url( '/gracias-gimnasio-mental/' ) );
exit;
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
The first piece works great. The second doesn't because of the first one. I'm not sure how to integrate these two rather than have 2 separate codes. Can you help me?
Please merge the two logics into one , by changing your sc_custom_redirect_after_purchase function into the following:
function sc_custom_redirect_after_purchase() {
global $wp;
if ( is_checkout() && !empty($wp->query_vars['order-received']) ) :
$order_id = absint($wp->query_vars['order-received']);
$order_key = wc_clean($_GET['key']);
$th_page_url = 'https://aefcoaching.com/gracias-por-tu-compra';
//////////////////////
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $item_key => $item ) {
// Product ID(s)
$product_ids = array( $item->get_product_id(), $item->get_variation_id() );
// Product ID in array
if ( in_array( 1813, $product_ids ) ) {
// $redirection = true;
$th_page_url = 'https://aefcoaching.com/gracias-gimnasio-mental/';
}
}
/////////////////////
$redirect = add_query_arg(
array(
'order' => $order_id,
'key' => $order_key,
), $th_page_url);
wp_safe_redirect($redirect);
exit;
endif;
}
I have several products in the store, however one product (flammable) can only be shipped via a specific shipping company; Unfortunately that company can't reach the whole country. So in case the customer buys the flammable product and it's outside the coverage of the only company that can ship the product, he should not see any other shipping option except local pickup.
So far I have this code (courtesy of different StackOverFlow answers):
function filter_woocommerce_package_rates( $rates, $package ) {
// Shipping zone
//echo 'entrando';
$shipping_zone = wc_get_shipping_zone( $package );
$product_ids = array( 2267 ); // HERE set the product IDs in the array
$method_id = 'weight_based_shipping:38'; // HERE set the shipping method ID that I want to hide
$found = false;
// Get zone ID
$zone_id = $shipping_zone->get_id();
//echo $shipping_zone;
//echo $zone_id;
// NOT equal
if ( $zone_id != 8 ) {
// Unset a single rate/method for a specific product
foreach( $package['contents'] as $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids ) ){
$found = true;
break;
}
}
if ( $found )
unset( $rates[$method_id] );
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
however I don't know why is not working. Even the 'echo' is not working.
Updated - Try following instead (code is commented):
// SETTINGS BELOW: Custom function that handle your settings
function custom_shipping_settings() {
return array(
'product_ids' => array(2267), // Define the products that need to be in a separated shipping package (local pickup)
'allowed_zones_ids' => array(8), // Define the allowed zones IDs
);
}
// Splitting cart items into 2 shipping packages
add_filter( 'woocommerce_cart_shipping_packages', 'split_shipping_packages' );
function split_shipping_packages( $packages ) {
extract(custom_shipping_settings()); // Load and extract settings
$customer = WC()->customer;
$destination = array(
'country' => $customer->get_shipping_country(),
'state' => $customer->get_shipping_state(),
'postcode' => $customer->get_shipping_postcode(),
'city' => $customer->get_shipping_city(),
'address' => $customer->get_shipping_address(),
'address_2' => $customer->get_shipping_address_2()
);
$package_dest = array( 'destination' => $destination );
$zone = wc_get_shipping_zone( $package_dest );
if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
// Reset packages and initialize variables
$packages = $splitted_cart_items = array();
// Loop through cart items
foreach ( WC()->cart->get_cart() as $item_key => $item ) {
if ( is_a($item['data'], 'WC_Product') && $item['data']->needs_shipping() ) {
if ( ! array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
$splitted_cart_items[0][$item_key] = $item; // Regular items
} else {
$splitted_cart_items[1][$item_key] = $item; // Special separated items
}
}
}
if ( count($splitted_cart_items) < 2 )
return $packages;
// Loop through spitted cart items
foreach ( $splitted_cart_items as $key => $items ) {
// Set each cart items group in a shipping package
$packages[$key] = array(
'contents' => $items,
'contents_cost' => array_sum( wp_list_pluck( $items, 'line_total' ) ),
'applied_coupons' => WC()->cart->get_applied_coupons(),
'user' => array( 'ID' => get_current_user_id() ),
'destination' => $destination,
);
}
}
return $packages;
}
// Force local pickup for specific splitted shipping package (for the defined products)
add_filter( 'woocommerce_package_rates', 'filter_wc_package_rates', 10, 2 );
function filter_wc_package_rates( $rates, $package ) {
extract(custom_shipping_settings()); // Load and extract settings
$zone = wc_get_shipping_zone( $package ); // Get current shipping zone
$found = false;
// For all others shipping zones than allowed zones
if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
// Loop through cart items for current shipping package
foreach( $package['contents'] as $item ) {
// Look for defined specific product Ids
if ( array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
$found = true; // Flag as found
break; // Stop the loop
}
}
// If any defined product is in cart
if ( $found ) {
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ) {
// Hide all shipping methods keeping "Local pickup"
if ( 'local_pickup' !== $rate->method_id ) {
unset( $rates[$rate_key] );
}
}
}
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Refresh the shipping caches:
This code is already saved on your functions.php file.
In a shipping zone settings, disable / save any shipping method, then enable back / save.
You are done and you can test it.
I'm trying to remove a product/products completely from Users that are not logged in and if user is not specific role (e.g Verified Buyer).
I have been able to create a new role called Verified Buyer using the code below;
add_role(
'verified_buyer',
__( 'Verified Buyer', 'text-domain' ),
array(
'read' => true,
'edit_posts' => false,
)
);
//This Role is same role capability as the WooCommerce Customer role
and i have as well added a checkbox to the WooCommerce Add New Product page using the code below
function hide_product_from_unathorised_users() {
$args = array(
'id' => '_hide_from_unauthorize_users',
'label' => 'Hide Product from unauthorized users',
'desc_tip' => true,
'description' => __('Check this to hide this product from unauthorized users', 'text-domain')
);
woocommerce_wp_checkbox( $args );
}
add_action( 'woocommerce_product_options_advanced', 'hide_product_from_unathorised_users' );
// Save Fields
function product_custom_fields_save($post_id){
// Custom Product Text Field
$hide_product_unathorised_users = isset( $_POST['_hide_from_unauthorize_users'] ) ? 'yes' : 'no';
update_post_meta($post_id, '_hide_from_unauthorize_users', esc_attr( $hide_product_unathorised_users ));
}
add_action('woocommerce_process_product_meta', 'product_custom_fields_save');
Now that i have this two options (user role and a checkbox to know which product to hide) ... I want to Hide such product if the following conditions are met;
HIDE PRODUCT COMPLETELY (Even from search Queries) IF;
1. Checkbox is checked on product & User is not logged in
2. Checkbox is checked on product & User logged in & not Verified Buyer or admin Role
Like this
function hide_product_completely_conditionally() {
global $post;
$hide_product_checkbox = get_post_meta( $post->ID, '_hide_from_unauthorize_users', true )
$user = wp_get_current_user();
$authorized_user_role = in_array( 'verified_buyer', (array) $user->roles );
$admin_role = in_array( 'administrator', (array) $user->roles );
if ( ($hide_product_checkbox == 'yes' && !is_user_loggedin()) || ($hide_product_checkbox == 'yes' && is_user_loggedin() && (!$authorized_user_role || !$admin_role) ) ) {
//(...) HIDE SUCH PRODUCT COMPLETELY CODE THAT I'M NOT SURE HOW TO WRITE
}
}
Thanks for your help in advance.
The following will code will filter products based on a your custom product field when users are not allowed (and will redirect them to shop page if they try to access manually to a protected product).
// Conditional function checking for authorized users
function is_authorized_user(){
if ( is_user_logged_in() ) {
$user = wp_get_current_user();
$caps = $user->allcaps;
if ( ( isset($caps['edit_product']) && $caps['edit_product'] )
|| in_array( 'verified_buyer', $user->roles ) ) return true;
}
else return false;
}
// Filter product query (and search) from unauthorized users
add_filter( 'woocommerce_product_query_meta_query', 'only_authorized_users_meta_query', 10, 2 );
function only_authorized_users_meta_query( $meta_query, $query ) {
// Hide specific products from unauthorized users
if( ! is_authorized_user() && ! is_admin() ) {
$meta_query['relation'] = 'OR';
$meta_query[] = array(
'key' => '_hide_from_unauthorize_users',
'value' => 'no',
'compare' => '='
);
$meta_query[] = array(
'key' => '_hide_from_unauthorize_users',
'compare' => 'NOT EXISTS'
);
}
return $meta_query;
}
// Security: Redirect unauthorized users if accessing prodtected products
add_action( 'template_redirect', 'only_authorized_users_redirect' );
function only_authorized_users_redirect() {
// Specific products redirect for unauthorized users (to be sure)
if( is_product() && ! is_authorized_user()
&& get_post_meta( get_the_id(), '_hide_from_unauthorize_users', true ) === 'yes' ) {
wp_safe_redirect( get_permalink( wc_get_page_id( 'shop' ) ) );
exit;
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Instead of using an additional user role, you can:
1) Use use the WC_Customer is_paying_customer boolean property like:
if( WC()->customer->get_is_paying_customer() ) {
// Is a playing customer
} else {
// NOT a playing customer
}
2) Or adding custom user meta as:
update_user_meta( get_current_user_id(), 'allowed_customer', '1' );
Then you will check using:
if( get_user_meta( get_current_user_id(), 'allowed_customer', true ) ) {
// Allowed customer
} else {
// NOT Allowed customer
}
When you say "Remove" I'm assuming you are actually trying to hide the product.
So, using pre_get_posts action hook is your way to go.
The code below will hide any product which has its field _hide_from_unauthorize_users set to yes from users that are not logged in and from users that are logged in but are not a verified_buyer nor an administrator.
Put the snippet below in your functions.php file and pay attention to the comments:
<?php
/**
* #param WP_Query $query
*/
function _hide_products_from_certain_users( $query ) {
if ( is_admin() ) {
return;
}
/**
* Create the query which will make sure only products that are allowed to bee seen will show up.
*/
$meta_query[] = array(
'key' => '_hide_from_unauthorize_users',
'value' => 'yes',
'compare' => '!=',
);
$user = wp_get_current_user();
// If user is not logged in.
if ( ! is_user_logged_in() ) {
$query->set( 'meta_query', $meta_query );
} else {
$authorized_user_role = in_array( 'verified_buyer', (array) $user->roles );
$admin_role = in_array( 'administrator', (array) $user->roles );
// If the current user is not a verified_buyer nor an administrator.
if ( ! $authorized_user_role && ! $admin_role ) {
$query->set( 'meta_query', $meta_query );
}
}
}
add_action( 'pre_get_posts', '_hide_products_from_certain_users' );
Btw, you had a few syntax mistakes in your code, I fixed them.