Enabling Payment method based on the customers location - php

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.

Related

Set shipping postcode early before add to cart in WooCommerce

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.

Disable all payment methods based on user country geo-ip in WooCommerce

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.

Hide Paypal from certain country

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' );

Showing a product to registered users that haven't purchase it yet in WooCommerce

I wanted to show a product only when the user has registered and has not bought this product (new user product).
After they buy this product once, it is no longer available to that user to purchase (because it's a 1-shot deal).
So this means after I bought it, and do things like try to navigate directly to that product's URL, or do a search for all products, etc, this item would not show up.
If I was a new user and didn't buy it, of course, it should show up everywhere.
I have a custom property (metadata) on a user that indicates whether they buy it or not. After they purchase it, I will set this user flag to true indicating it can no longer show.
I had two questions:
Is there a way to hook into the actual successful purchasing of an item, and set this user flag to be true?
How can I instruct Woo to not show this product for a certain user when their flag is true?
Thank you!
Yes this is possible trough 3 functions:
1) A conditional function that will check that customer has bought your specific product:
function has_bought_items( $user_id = 0, $product_id = 0 ) {
// The customer ID
$customer_id = $user_id == 0 || $user_id == '' ? get_current_user_id() : $user_id;
// Retrieve your customer flag '_has_bought_flag' (or replace it by your slug)
if ( get_user_meta( $customer_id, '_has_bought_flag', true ) )
return true;
else
return false;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
2) A custom function hooked in pre_get_posts that will change the WP_Query on shop and archives pages, checking if user is logged in and if he has already bought this specific product:
// Changing the WP_Query loop conditionally
add_action( 'pre_get_posts', 'conditional_product_query', 10, 1 );
function conditional_product_query( $q ) {
// HERE set your product ID
$product_id = 37;
if( ! is_user_logged_in() || has_bought_items( '', $product_id ) )
$q->set( 'post__not_in', array($product_id) );
}
It will remove completely this product everywhere, when the condition match.
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
3) A custom function hooked in woocommerce_order_status_completed that will set the customer flag when order status get "completed" when the product is in that order and when the customer flag doesn't exist yet:
// When Order get the "completed" status (paid) we check and we set the user flag (if necessary)
add_action( 'woocommerce_order_status_completed', 'set_customer_specific_product_flag', 10, 2 );
function set_customer_specific_product_flag( $order_id, $order ) {
// HERE set your product ID
$product_id = 37;
// If customer has already bought the product we exit
if( has_bought_items( $order->get_user_id(), $product_id ) ) return;
// Checking order items (if it match we update user meta data
foreach( $order->get_items() as $product_item ){
if ( $product_item->get_product_id() == $product_id ){
update_user_meta( $order->get_user_id(), '_has_bought_flag', '1' );
break;
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested on Woocommerce 3+ and works (It should work on previous versions too).
Related answer: Checking if customer has already bought something in WooCommerce

Conditionally hiding et showing payment gateways

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.

Categories