I've developed a catalogue using woocommerce, however I need to be able to hide product prices from users who are visiting the site from outside of the UK due to reasons outside of my control.
I've found plugins that allow me to change product prices based on visitor location, but nothing allows me to hide the prices.
Is there any plugins that I've missed or anything I can add in to the woocommerce files to achieve this?
There are various web APIs that will help you. For example http://ipinfo.io
ip = $_SERVER['REMOTE_ADDR'];
$details = json_decode(file_get_contents("http://ipinfo.io/{$ip}"));
echo $details->country; // -> "US"
If you have to do many checks a local database is better. MaxMind offer a free database that you can use with various PHP libraries, including GeoIP.
The following will hide prices outside United kingdom based on customer geolocated country:
add_filter( 'woocommerce_get_price_html', 'country_geolocated_based_hide_price', 10, 2 );
function country_geolocated_based_hide_price( $price, $product ) {
// Get an instance of the WC_Geolocation object class
$geo_instance = new WC_Geolocation();
// Get geolocated user geo data.
$user_geodata = $geo_instance->geolocate_ip();
// Get current user GeoIP Country
$country = $user_geodata['country'];
return $country !== 'GB' ? '' : $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
If you want to enable that geolocated feature only for unlogged customers, use the following:
add_filter( 'woocommerce_get_price_html', 'country_geolocated_based_hide_price', 10, 2 );
function country_geolocated_based_hide_price( $price, $product ) {
if( get_current_user_id() > 0 ) {
$country = WC()->customer->get_billing_country();
} else {
// Get an instance of the WC_Geolocation object class
$geo_instance = new WC_Geolocation();
// Get geolocated user geo data.
$user_geodata = $geo_instance->geolocate_ip();
// Get current user GeoIP Country
$country = $user_geodata['country'];
}
return $country !== 'GB' ? '' : $price;
}
An updated version of this code is available on this answer avoiding a backend bug.
I have added in the function on start:
if ( is admin() ) return $price;
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related
I want to enable or disable Shipping Methods, according to user role.
I have tested various different codes and approaches, none of which have worked so far.
The current code: (source: stacklink )
add_filter( 'woocommerce_package_rates', 'hide_specific_shipping_method_based_on_user_role', 30, 2 );
function hide_shipping_method_based_on_user_role( $rates, $package ) {
$shipping_id = 'shipmondo:3';
foreach( $rates as $rate_key => $rate ){
if( $rate->method_id === $shipping_id ){
if( current_user_can( 'b2b' ) || ! is_user_logged_in()){
unset($rates[$rate_key]);
break;
}
}
}
return $rates;
}
(Have tested the original snippet as well, without changes, accept for the user role )
Does not work for me. I tested it with 'local_pickup' as well, It does work sometimes, but seems to be very sensitive to browser cache and session. Also what I need, is 3 methods which are called under the same name, but with a child number separating them: shipmondo:3, shipmondo:4, etc. (found in browser inspection under "Value") There is also something called ID, which looks like: 'shipping_method_0_shipmondo3' Dont know if I could use that instead, but it is kind of hard to figure out, when the code isn't updating correctly. The snippet is from 2018, so it could be an outdated thing, but the newer snippets I have found, are based on the same principale, and does not look to be doing it much different.
Also, why would the "|| ! is_user_logged_in()" be necessary? I only need to disable 2 out of 3 methods for a wholesale user, need not to effect any other roles, nor guests. Been fighting with this for days now.
Also, any advice on forcing Wordpress and Woocommerce to update, and not swim around in cache?
Thanks in advance.
You are confusing shipping method "Method Id" and shipping method "Rate Id". Also your code can be simplified. Try the following instead:
add_filter( 'woocommerce_package_rates', 'hide_specific_shipping_method_based_on_user_role', 100, 2 );
function hide_specific_shipping_method_based_on_user_role( $rates, $package ) {
// Here define the shipping rate ID to hide
$targeted_rate_id = 'shipmondo:3'; // The shipping rate ID to hide
$targeted_user_roles = array('b2b'); // The user roles to target (array)
$current_user = wp_get_current_user();
$matched_roles = array_intersect($targeted_user_roles, $current_user->roles);
if( ! empty($matched_roles) && isset($rates[$targeted_rate_id]) ) {
unset($rates[$targeted_rate_id]);
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should work.
Don't forget to empty your cart, to clear shipping cached data.
I would like the customers to be able to set their post code before adding a product to cart.
This post code is then saved and used to define available delivery methods.
I've made the following functions but it's not always working and I'm not sure about which Woocommerce methods should be used and what's the difference between them:
WC()->customer->set_shipping_postcode(...) and WC()->customer->get_shipping_postcode()
WC()->session->set('shipping_postcode', ...) and WC()->session->get('shipping_postcode')
update_user_meta(get_current_user_id(), 'shipping_postcode', ...) and get_user_meta(get_current_user_id(), 'shipping_postcode', true)
Also, I'm saving post code for billing and shipping cause I don't know if user has previously made an order and chosen to delivered it to a shipping address different than billing address.
function getDeliveryZipcode()
{
$shipping_postcode = WC()->customer->get_shipping_postcode();
$billing_postcode = WC()->customer->get_billing_postcode();
return $shipping_postcode ? $shipping_postcode : $billing_postcode;
}
function setDeliveryZipcode()
{
$zipcode = $_GET['zipcode'];
// ...
WC()->customer->set_shipping_postcode(wc_clean($zipcode));
WC()->customer->set_billing_postcode(wc_clean($zipcode));
}
Thanks
Your code is mostly correct, but there is something missing, to avoid any issues:
// Important: Early enable customer WC_Session
add_action( 'init', 'wc_session_enabler' );
function wc_session_enabler() {
if ( ! is_admin() && ! WC()->session->has_session() ) {
WC()->session->set_customer_session_cookie( true );
}
}
function getDeliveryZipcode()
{
$shipping_postcode = WC()->customer->get_shipping_postcode();
$billing_postcode = WC()->customer->get_billing_postcode();
return ! empty($shipping_postcode) ? $shipping_postcode : $billing_postcode;
}
function setDeliveryZipcode()
{
if ( isset($_GET['zipcode']) ) {
WC()->customer->set_shipping_postcode(wc_clean($_GET['zipcode']));
WC()->customer->set_billing_postcode(wc_clean($_GET['zipcode']));
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Here are the differences explained between WC_Session, WC_Customer and WordPress related to user data:
WC()->customer is the WC_Customer Object that access the registered user-data from a defined logged in user (so the data that is stored on database wp_users and wp_usermeta tables) or it will read the session data for guests.
WC()->session is the data stored in WooCommerce session for any customers or guests, linked to a browser cookie and to database through wp_woocommerce_sessions table. But note that "customer" WC session is enabled on the first add to cart.
The WordPress functions get_user_meta(), set_user_meta() and update_user_meta() allow to read / write / update user metadata from wp_usermeta table for a registered user.
Note: The following doesn't exist in WooCommerce:
$postcode = WC()->session->get('shipping_postcode');
WC()->session->set('shipping_postcode', $postcode);
The customer session data can be read using:
// Get an array of the current customer data stored in WC session
$customer_data = (array) WC()->session->get('customer');
// Get the billing postcode
if ( isset( $customer_data['postcode'] ) )
$postcode = $customer_data['postcode'];
// Get the shipping postcode
if ( isset( $customer_data['shipping_postcode'] ) )
$postcode = $customer_data['shipping_postcode'];
The customer session data can be set for example using:
// Get an array of the current customer data stored in WC session
$customer_data = (array) WC()->session->get('customer');
// Change the billing postcode
$customer_data['postcode'] = '10670';
// Change the shipping postcode
$customer_data['shipping_postcode'] = '10670';
// Save the array of customer WC session data
WC()->session->set('customer', $customer_data);
For WC()->customer, you can use any of the WC_Customer available getter and setter methods, but some few methods will not work for guests.
For some reason, I need to show the list of private products on a single page on WooCommerce for guest users (non-logged in users). How can this be done with (or without) programming?
You can use a normal woocommerce shortcode on a specific page where you want to display the private products, like for example:
[products limit="12" columns="4" paginate="true"]
You will set shortcode arguments as you wish (like number of columns, number of items per page, enable pagination and so on)…
Then to query all private products, use following (replacing below 102 by the page ID where you are using the shortcode):
add_filter( 'woocommerce_shortcode_products_query', 'display_private_product_list', 10, 3 );
function display_private_product_list( $query_args, $atts, $loop_name ){
if( get_the_id() == 102 ){
if( ! is_user_logged_in() ){
$query_args['post_status'] = 'private';
} else {
$query_args['post_type'] = 'nothing'; // Display nothing for logged users
}
}
return $query_args;
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.
It will display all private products for non logged users.
In my Woocommerce shop I set up the geolocation system, when geolocation identifies any country other than IT I would like to disable payment methods
If it is IT (geop-ip), show payment methods
If all other country (geo-ip), disable all payment methods.
Woocommerce has already a geolocation Ip feature through WC_Geolocation class, so you don't need any additional plugin.
Here is the way to disable payment gateways for all countries except "IT" (Italy) country code, based on costumer geolocated IP country:
// Disabling payment gateways except for the defined country codes based on user IP geolocation country
add_filter( 'woocommerce_available_payment_gateways', 'geo_country_based_available_payment_gateways', 90, 1 );
function geo_country_based_available_payment_gateways( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
// ==> HERE define your country codes
$allowed_country_codes = array('IT');
// Get an instance of the WC_Geolocation object class
$geolocation_instance = new WC_Geolocation();
// Get user IP
$user_ip_address = $geolocation_instance->get_ip_address();
// Get geolocated user IP country code.
$user_geolocation = $geolocation_instance->geolocate_ip( $user_ip_address );
// Disable payment gateways for all countries except the allowed defined coutries
if ( ! in_array( $user_geolocation['country'], $allowed_country_codes ) )
$available_gateways = array();
return $available_gateways;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Related:
Disable WooCommerce payment gateway for guests and specific user roles
Enabling Payment method based on the customers location
Hide specific payment method based on total weight in Woocommerce
Hide payment method based on product type in WooCommerce
Here is a variation from #LoicTheAztec answer that disable just a specific payment method instead of all:
add_filter( 'woocommerce_available_payment_gateways', 'geo_country_based_available_payment_gateways', 90, 1 );
function geo_country_based_available_payment_gateways( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
// ==> HERE define your country codes
$allowed_country_codes = array('DE','AT');
// Get an instance of the WC_Geolocation object class
$geolocation_instance = new WC_Geolocation();
// Get user IP
$user_ip_address = $geolocation_instance->get_ip_address();
// Get geolocated user IP country code.
$user_geolocation = $geolocation_instance->geolocate_ip( $user_ip_address );
// Disable payment gateways for all countries except the allowed defined coutries
if ( ! in_array( $user_geolocation['country'], $allowed_country_codes ) ) {
unset( $available_gateways['stripe_sofort'] );
}
return $available_gateways;
}
I know Istack, as well as maxmind etc ..
I thought something simpler like this function, which is based on the blling_country and not on the geo-ip country:
function payment_gateway_disable_country( $available_gateways ) {
global $woocommerce;
if ( is_admin() ) return;
if ( isset( $available_gateways['authorize'] ) && $woocommerce->customer->get_billing_country() <> 'US' ) {
unset( $available_gateways['authorize'] );
} else if ( isset( $available_gateways['paypal'] ) && $woocommerce->customer->get_billing_country() == 'US' ) {
unset( $available_gateways['paypal'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateway_disable_country' );
In order to find out the user's country you can use a tool like FreeGeoIp, now renamed to Ipstack. You provide the service an IP address and it will tell you the country address the user is likely in (among other information).
There are two options
1. Using their hosted API (free for 10,000 requests and paid for more than that)
2. Downloading a release from the GitHub link and hosting it yourself
When you need to know the user's country you can send a HTTP request with the user's IP address to the API and then use that information to enable or disable the payment method.
I don't know if it's possible, but, we would need to add some different payment methods for Barcelona.
So, our idea is that if the customer lives in Barcelona area (Catalunya), he will see a credit card payment method and a bank transfer account different than the rest of Spain.
Is that possible with WooCommerce?
Thanks.
If you want to enable this kind of feature in WooCommerce, Customers need to be registered and logged on first, as it's the only way to get they town location before checkout process.
Also you will have to change some settings in WooCommerce allowing only registerers users to checkout.
Then you will have to add some mandatory fields in the registration process as the town, zip code and country.
Once that done, it's going to be easy to enable / disable payment gateways based on this registered customer fields.
1) For customizing the registration fields:
How to add custom fields in user registration on the “My Account” page
2) For payment gateways/methods based on this Customer information, You can use a custom hooked function in woocommerce_available_payment_gateways filter hook:
add_filter( 'woocommerce_available_payment_gateways', 'custom_payment_gateways_process' );
function custom_payment_gateways_process( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
if ( is_admin() || !is_user_logged_in() )
return $available_gateways;
$current_user_id = get_current_user_id();
$user_meta = get_user_meta( $current_user_id );
// User City, Postcode, State and Country code
$user_city = $user_meta['billing_city'];
$user_postcode = $user_meta['shipping_postcode'];
$user_State = $user_meta['shipping_state'];
$user_country = $user_meta['shipping_country'];
// Disable Cash on delivery ('cod') method example for customer out of spain:
if ( isset( $available_gateways['cod'] ) && $user_country != 'ES' ) {
unset( $available_gateways['cod'] );
}
// You can set many conditions based on the user data
return $available_gateways;
}
This code is just an example, and you will have to set inside the right conditions for the targeted payment methods/gateways…
Code goes in functions.php file of your active child theme (or theme) or also in any plugin file.