How can I set certain products to be shippable to only certain countries in Woocommerce?
For example, during checkout, if Country
is not USA
, I want to show the message "We cannot deliver to your country."
Advertisement
Answer
This can be done mainly with a custom function hooked in woocommerce_package_rates
filter hook.
The code below, will allow you to disable shipping, for specific products IDs or specific product categories (you can choose) based on defined country codes.
You will have to add your settings in the first function.
The code:
// HERE your settings - Utility function function your_country_shipping_settings(){ $results = array(); // Can be based on "Product IDs" (or "Product categories" ==> false) $results['type'] = true; // or false // Allowed countries (Only compatible country codes - 2 digits) $results['countries'] = array( 'US', 'CA' ); if( $results['type'] ){ // Restricted product IDs $results['matching'] = array( 37, 38 ); } else { // Restricted product categories (IDs, Slugs or Names) $results['matching'] = array('t-shirts', 'sweet-shirts' ); } // Message $results['message'] = __( "can not be delivered to your country.", "woocommerce" ); return $results; } // Utility function that check cart items function get_items_names( $matching, $package, $type ){ $product_names = array(); // Search in cart items foreach( $package['contents'] as $item ){ if( $type ){ if( in_array( $item['data']->get_id(), $matching ) ) $product_names[] = $item['data']->get_name(); } else { if( has_term( $matching, 'product_cat', $item['product_id'] ) ) $product_names[] = $item['data']->get_name(); } } return $product_names; } // Conditionally disabling shipping methods add_filter('woocommerce_package_rates','custom_country_shipping_rules', 10, 2 ); function custom_country_shipping_rules( $rates, $package ){ if( isset($package['destination']['country']) && isset($package['contents']) ){ // Load your settings $data = your_country_shipping_settings(); // If matching allowed countries ==> We Exit if( in_array( $package['destination']['country'], $data['countries'] ) ) return $rates; // Exit $product_names = get_items_names( $data['matching'], $package, $data['type'] ); // When product match we Remove all shipping methods if( count($product_names) > 0 ){ // Removing all shipping methods foreach( $rates as $rate_id => $rate ) unset( $rates[$rate_id] ); } } return $rates; } // Conditionally displaying a shipping message add_filter('woocommerce_cart_no_shipping_available_html','custom_country_shipping_notice', 10, 1 ); add_filter('woocommerce_no_shipping_available_html','custom_country_shipping_notice', 10, 1 ); function custom_country_shipping_notice( $html ){ $package = WC()->shipping->get_packages()[0]; if( isset($package['destination']['country']) && isset($package['contents']) ){ // Load your settings $data = your_country_shipping_settings(); // If matching allowed countries ==> We Exit if( in_array( $package['destination']['country'], $data['countries'] ) ) return $html; // Exit $product_names = get_items_names( $data['matching'], $package, $data['type'] ); if( count($product_names) > 0 ){ $text = '"' . implode( '", "', $product_names ) . '" ' . $data['message']; $html = wpautop( $text ); } } return $html; }
Code goes in function.php file of your active child theme (active theme).
Tested and works.
When a cart item match with your settings and if customer is located in another country that the ones defined he will get something like:
On cart page:
On checkout page: