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.
Related
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.
I use wordpress, woocommerce, and Paypal for a website.
I want to hide/disable the Paypal option from users from within a certain country. Due to Paypal doesn't do business there. http://recordit.co/oNF3aHp1D4
What code can achieve this without interfere with other users from using Paypal to checkout in other countries?
Found the answer to my question. Add this to your function.php
// Hide Paypal from _____ Country
function YOURTHEME_payment_gateway_disable_country( $available_gateways ) {
global $woocommerce;
if ( isset( $available_gateways['paypal'] ) && $woocommerce->customer->get_country() == 'YOUR COUNTRY CODE' ) {
unset( $available_gateways['paypal'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'YOURTHEME_payment_gateway_disable_country' );
In Woocommerce, I would like to hide "paypal" gateway on the "checkout" page before the order is created for the first time and just show "cash on delivery" gateway (labeled as Reserve).
On the other hand, on checkout/order-pay page when the order status is "pending", hide the 'Reserve' gateway and show "paypal". (this happens when we change the status of the order to "pending" manually and send the invoice to the customer with a payment link).
I thought it should be done by checking order status and using the woocommerce_available_payment_gateways filter hook. But I have problems with getting current order status.
Also I'm not sure what's the status of a newly created order which the user is on the checkout page and still the order is not shown in the admin backend.
This is my incomplete code:
function myFunction( $available_gateways ) {
// How to check if the order's status is not pending payment?
// How to pass the id of the current order to wc_get_order()?
$order = wc_get_order($order_id);
if ( isset($available_gateways['cod']) && /* pending order status?? */ ) {
// hide "cod" gateway
} else {
// hide "paypal" gateway
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'myFunction' );
I also tried WC()->query_vars['order'] instead of wc_get_order(); to get the current order and check its status but it didn't work too.
I saw woocommerce_order_items_table action hook but couldn't get the order either.
How could I retrieve the Id and the status of the order on the checkout/order-pay page?
Update 2021
If I have correctly understood, You want to set/unset your available payment gateways, depending on the live generated order which status has to pending to have the "paypal" gateway. Ian all other cases the available gateway is only "reserve" (renamed "cod" payment gateway).
This code retrieve the live order ID using the get_query_var(), this way:
add_filter( 'woocommerce_available_payment_gateways', 'custom_available_payment_gateways' );
function custom_available_payment_gateways( $available_gateways ) {
// Not in backend (admin)
if( is_admin() )
return $available_gateways;
if ( is_wc_endpoint_url( 'order-pay' ) ) {
$order = wc_get_order( absint( get_query_var('order-pay') ) );
if ( is_a( $order, 'WC_Order' ) && $order->has_status('pending') ) {
unset( $available_gateways['cod'] );
} else {
unset( $available_gateways['paypal'] );
}
} else {
unset( $gateways['paypal'] );
}
return $available_gateways;
}
Code goes in functions.php file of your active child theme (or theme) or also in any plugin file.
The code is tested and works.
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.
The following code will allow free shipping for a specific product:
function wcs_my_free_shipping( $is_available ) {
global $woocommerce;
// set the product ids that are eligible
$eligible = array( '360' );
// get cart contents
$cart_items = $woocommerce->cart->get_cart();
// loop through the items looking for one in the eligible array
foreach ( $cart_items as $key => $item ) {
if( in_array( $item['product_id'], $eligible ) ) {
return true;
}
}
// nothing found return the default value
return $is_available;
}
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'wcs_my_free_shipping', 20 );
What I would like to do is allow free shipping not for a product, but for a specific street and zip code combination in the delivery address. I found out how to check this for a logged-in user, but can't seem to find the right variables that have this information at checkout. Any help would be greatly appreciated.
Thanks in advance,
-Ben
There are Shipping zones available in Woocommerce already. For each specific zone you can set the shipping method either "Flat rate" or "Free Shipping". You can check it under Woocommerce->Settings. Find Shipping tab.
Screenshot for shipping tab
Yes you can, there is an extra parameter you can pass into this hook names $package.
for eg.
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'wcs_my_free_shipping', 20,2 );
function wcs_my_free_shipping( $is_available, $package){
//Your code for free shipping for a selected address found in $package
return $is_available
}
$package contains the address you entered so you can use this to apply free shipping for selected zip codes or streets