Update 5/2/14: People are reporting in the comments that this method breaks in the latest version of WooCommerce. It looks like there’s a woocommerce_shipping_{id}_is_available
filter that can accomplish this in a much cleaner fashion. New code is available below.
Update 4/6/14: Since writing this post I’ve stumbled upon this plugin by Patrick Rauland that looks like it does the job cleaner than my original solution. I haven’t yet tested Patrick’s plugin, but it may be worth trying.
Right now at work I’m working on moving a site from WP eCommerce to WooCommerce and encountered an interesting request: the site offers free shipping but only to the lower 48 United States. That means no free shipping for Alaska, Hawaii, Puerto Rico, etc.
Out of the box WooCommerce supports country-based filtering (e.g. allow free shipping to the United States but not Canada) but to get into more specific restrictions you’d have to start messing with shipping tables or buying the Advanced Shipping Rates plugin which, although I’ve heard good things about it, will set you back $200.
Updated work-around for the latest versions of WooCommerce
As mentioned above, versions of WooCommerce released after this post was originally published caused the work-around to break. Fortunately, we now have a much easier way to block a shipping method that doesn’t meet our requirements as the availability of a shipping method is now controlled by the woocommerce_shipping_{method_name}_is_available
filter. The following code should do the trick:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * Limit the availability of this shipping method based * on the destination state. * * Restricted locations include Alaska, American Samoa, * Guam, Hawaii, North Mariana Islands, Puerto Rico, * US Minor Outlying Islands, and the US Virgin Islands. * * @param bool $is_available Is this shipping method available? * @return bool */ function grunwell_restrict_free_shipping( $is_available ) { $restricted = array( 'AK', 'AS', 'GU', 'HI', 'MP', 'PR', 'UM', 'VI' ); foreach ( WC()->cart->get_shipping_packages() as $package ) { if ( in_array( $package['destination']['state'], $restricted ) ) { return false; } } return $is_available; } add_filter( 'woocommerce_shipping_free_shipping_is_available', 'grunwell_restrict_free_shipping' ); |
Original work-around (which apparently will *not* work with newer versions of WooCommerce)
Fortunately I was able to put together a code snippet that will remove a shipping method (in this case, free shipping) for restricted states. It consists of two parts: a class that extends the WooCommerce core shipping class (WC_Shipping_Free_Shipping
for this example) and a filter that tells WooCommerce to use our class rather than the core shipping class it extends.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class Grunwell_Shipping_Free_Shipping extends WC_Shipping_Free_Shipping { /** * Limit the availability of this shipping method based * on the destination state. * * Restricted locations include Alaska, American Samoa, * Guam, Hawaii, North Mariana Islands, Puerto Rico, * US Minor Outlying Islands, and the US Virgin Islands. * * @param array $package * @return bool */ public function is_available( $package ) { // The states/territories not permitted for this method $restricted = array( 'AK', 'AS', 'GU', 'HI', 'MP', 'PR', 'UM', 'VI' ); if ( in_array( $package['destination']['state'], $restricted ) ) { return false; } return parent::is_available( $package ); } } /** * Make WooCommerce load Grunwell_Shipping_Free_Shipping * instead of WC_Shipping_Free_Shipping. * * @param array $classes An array of classes assembled * by Woocommerce::core_shipping(). * @return array Our filtered $classes */ function grunwell_override_free_shipping_class( $classes ) { if ( $key = array_search( 'WC_Shipping_Free_Shipping', $classes ) ) { $classes[ $key ] = 'Grunwell_Shipping_Free_Shipping'; } return $classes; } add_filter( 'woocommerce_shipping_methods', 'grunwell_override_free_shipping_class', 11 ); |
Bishal Adhikary
I am the kind of guy that rarely comments. This really saved me. The code provided by woocomerce for the same purpose threw an error and i was trying to fix it but to noavail, thanks a lot man.
Steve
Happy to help, Bishal!
Jim
Will this work to help restrict certain states from placing an order. I have a wind sale site that cannopt ship to certain states.
Thank you for this tip.
BTW-where will this code go?
Steve
It won’t necessarily prevent them from placing the order, but it will prevent the order from being shipped to the restricted states. Simply add/remove states from the
$restricted
array as necessary. This code can all go into your theme’s functions.php or into a custom plugin, whichever you prefer.Jim
That’s a “Wine” sale site.
Jim
Thank you Steve. Most importantly, I would like to stop the order from continuing through payment process. Perhaps I could just eliminate the States from the list so it can’t be selected. Do you know of a good plugin for that? I’ll poke around a bit more. Thanks again.
Steve
I don’t know of any good plugins for it off-hand but I haven’t done a ton with WooCommerce. The snippet in this article will prevent there from being shipping methods available for that state though, effectively preventing orders from being placed.
Jim
Thank you again Steve, I will give it a try. Have good weekend.
Mark
Can you Restrict a shipping option. We use the UPS module and a client wants to allow ground in California and 2nd Day Air everywhere else.
Steve
Different classes would be involved but I don’t see any reason you wouldn’t be able to use a similar approach to restrict the methods. The trick is making it obvious to any other developer what you’re trying to accomplish and not editing the WooCommerce core (which will be overwritten with each upgrade). Find your way into the class (like the way this post overrides the
WC_Free_Shipping
class) and change what you need to.Vladislav Vladimirov
Hello Steve,
Thank you very much fopr your modification! I was wondering if this mod works with the latest WooCommerce release, and if it totally overrides the original free shipping function, because I see there are many more checks within the is_available function in the original package:
function is_available( $package ) {
…
}
It checks for conditions like coupons, minimum amount, or both – does this get stripped in your version?
WPShowCase
Hello Vladislav,
Steve’s plugin returns parent::is_available( $package ) – so it still does all the checks because parent is the free shipping method.
WPShowCase
Ryan
thank you thank you thank you!
WORDPRESS98
:)) your snippet is good !
you are good too !
so thanks
Katie
Hello – I found your snippet and was wondering if you think I could use some version of this to do what I need to do.
My customer wants to limit shipping to the contiguous US, but we’re using two shipping classes.
A Flat Rate Class of $100 per Bass Cabinet and a Free Class for two available add-ons.
Could this snippet be modified to make that work?
Thanks so much for any help! I’m not great at code.
Steve
The same sort of logic would apply. I’ve thrown together a quick gist that, though untested, should at least point you in the right direction. Be sure to include other (paid) shipping methods if you’re selling to these restricted areas – you don’t want your customers to be unable to order your gear.
By the way, I checked out the Xsonics demos on your site. Those are some sweet sounding cabs!
Katie
Thanks, Steve! I will pass along the compliment on the cabinets. I placed the code in my themes functions.php and it doesn’t seem to have worked. The restricted states still show up in the dropdowns in the cart. Thanks for the try. I need to find a way to pay my bills while I hide away and learn code for myself.
Gabriel Reguly
Nice tip, thanks for sharing.
Currently I am building a filter to remove shipping and prevent order placing if the delivery state is not allowed and was nice to see a different approach from mine.
Alberto
Very interesting aproach to solve it.
One question, I have a similar issue , and I would like to know where I have to put this code to override the woocommerce code?
In my functions.php theme?
Thanks
Blake Imeson
Does this work with WooCommerce 2.1+ ?
I tried pasting the code in functions.php and it throws an error.
Steve
This post was written during or before WooCommerce 2.0.20, the site it was originally developed for hasn’t been upgraded in the last month or two. What kind of errors are you getting?
Blake Imeson
Not sure what happened but when I copied it over the the functions file this time it worked!
Many thanks for a great fix.
Amie
Thanks a bunch. That’s fantastic!
Evan Thorpe
We’re hoping to restrict shipping of a certain category of products to only one state (NY). We manage a site for an online wine and liquor store, and hard liquor can only be shipped within in New York state. Is there a way to make this work so that it only restricts shipping to one state for a single category of products?
Thanks!
Steve
I’m sure it’s not the most elegant solution, but you could perform another check in our overridden
is_available()
method. I put together a quick gist on how you might go about it, but my example uses shipping classes rather than categories. Hope it helps!Steve
I also came across the WooCommerce Restrict Shipping plugin which might fit the bill, though I have no experience with that plugin or developer.
Evan Thorpe
Steve,
Thanks for this. I’m a coder, but not great with PHP. I took a look at the code you attached on GitHub, and I’m wondering how it might be modified or used to apply to one particular category of products (Spirits). Do you know how I could modify it?
Thanks!
Chris Johnston
Awesome post! This works great for restricting the free shipping method. However I’m also trying to restrict the flat rate shipping method. I replaced the WC_Shipping_Free_Shipping class with the WC_Shipping_Flat_Rate class, but it doesn’t seem to work. Any ideas?
Steve
I threw together a gist for Katie in the comments above a couple months ago that might put you on the right track. It’s untested, but it might cover something you may have missed. Please report back if it works for you!
david
had to upgrade woocommerce and now get this error:
Fatal error: Class ‘WC_Shipping_Free_Shipping’ not found in /*****/techzone/wp-content/themes/enfold/config-woocommerce/woocommerce-mod-css-dynamic.php on line 20
Steve
Apparently there were some big changes under the hood with a recent version of WooCommerce. I’ve posted new code above, please let me know if that works for you!
Belinda
This was working wonderfully for me until version 2.1.8 of Woocommerce. Like david said above, it seems to have something to do with using the WC_Shipping_Free_Shipping class. The woocommerce help person said not to use the class but to use a filter. I don’t know what that means, but maybe it will help you?
return apply_filters( ‘woocommerce_shipping_’ . $this->id . ‘_is_available’, $is_available );
Steve
Thanks for letting me know about the filter, that actually makes the whole thing much easier :). I’ve added a section of updated code above that should work with the latest WooCommerce. Please let me know if that works for you!
Tomas Laverty
Hi,
Could you point me in the right direction. The updated code doesn’t seem to have any effect when I place it in functions.php.
thanks.
Belinda
You’re awesome! Thanks so much!
Gen
Thanks Steve! This code worked perfectly for me (I changed it to flat_rate). I’ve got shipping limited to lower 48 now and non-shippable (virtual) items going everywhere internationally which is exactly what the client wanted. Much appreciated :)
Steve
Awesome, glad I could help and thank you for the feedback!
Juan Martinez
Where does this code get pasted into?
Steve
Hey Juan, you should be safe to just add this to you active theme’s functions.php file.
Susan
Hi, thanks for the snippet I am trying to use 3 different methods of shipping depending on the product, can this be altered to do that? Meaning some products go USPS some go FEDEX and some are using a table rate? I can’t seem to figure out what to change to get it all to work without taking down the site. Any help/advice would be appreciated.
John
Hi,
I use 2 methods – flat rate and local pick up. Can I use this to have a product only allow local pick up?
Thanks
megan
I am trying to limit shipping options based on class. For example I have one product that can only be shipped domestically, and everything else i sell can be shipped internationally how do i set up these capabilities.
Derek Dirks
Is there a way to add a popup comment if they try to select a state that isn’t allowed? We are only allowed to ship our products to 4 states. The only thing displayed now is ‘Invalid shipping method’ which doesn’t make much sense.
Matt
I have a really similar problem and I know *nothing* about PHP. I can see that it’s going to be readable as I study it more but I’m stuck with a problem.
I’ve got a stack of postage methods and I need to force WooCommerce to filter out some when one in particular is activated.
We offer free postage to certain parts of the UK, but there’s about 30 or so postcodes that cost too much to post to. With that in mind I’ve installed the excellent APG shipping plugin which defines weight and postcodes for these particular locations.
Problem in WooCommerce is offering free postage additionally due to the fact that the user is in the UK. When APG is activated I would like to hide any other shipping methods. I’ve seen code snippets that suggest I can do it but once I’ve made (what I think are) the required changes I just end up breaking the whole thing.
WP 3.8.4, WooCommerce 2.1.12
Any chance someone could take time out of their day to help me here? I’m in over my head.
John Griswald
This is a great fix for the Contiguous States shipping issue. I have been setting up a very complex affiliate program for a company running woocommerce using this other affiliate program script, http://mbsy.co/8VRzH
It’s expensive and as much as I wish there was a way to do all of these things without it, it sure does make it easy.
Cheers, J
hammer
Where does this code go? And does it still work with the current woocommerce?
Steve
The (updated version of the) code should be pasted in your theme’s functions.php file and should still work with current versions of WooCommerce (unless something major has changed in the last few months).
hammer
Thank you. Does it matter where I paste it? at the very end or beginning? At the very end after the “}”?
Steve
You want to make sure it’s not being posted in the middle of another function, but other than that no, it doesn’t matter where in your functions.php file it’s posted as long as it’s after the opening “
Tim Mathews
Love the code, Steve. Thank you very much.
I would also like to restrict “Flat Rate Shipping”.
Can you tell me if I can add another line in your code above to restrict free shipping AND flat rate?
The method is called flat_rate in woo commerce.
Love the code, just hope I can make it limit both, free and flat rate.
Thank you for your time!
Tim
Tim Mathews
I tried adding this in, so it would override for free and flat rate shipping, but it isn’t working:
function grunwell_restrict_free_shipping( $is_available ) {
$restricted = array( ‘AK’, ‘AS’, ‘GU’, ‘HI’, ‘MP’, ‘PR’, ‘UM’, ‘VI’ );
foreach ( WC()->cart->get_shipping_packages() as $package ) {
if ( in_array( $package[‘destination’][‘state’], $restricted ) ) {
return false;
}
}
return $is_available;
}
add_filter( ‘woocommerce_shipping_free_shipping_is_available’, ‘woocommerce_shipping_flat_rate_is_available’, ‘grunwell_restrict_free_shipping’ );
Tim Mathews
Any ideas? Basically, we use flat rate shipping for all orders. This flat rate is just $2.99. For all orders over $50.00, we give free shipping. Note: this is for continental USA. We can make the function work for free, or flat rate, but we can’t seem to make it work for both.
Please, help? Thank you.
Tim
Tim Mathews
Here’s the code I put into functions.php.
This code works, kinda. It prevents free and flat rate shipping to Alaska and Hawaii, but also prevents ALL OTHER shipping options. I would love to be able to offer to ship there, but not letting them get shipping is better than them getting flat rate or free shipping at this time. Help?
=== begin code ===
/**
* Limit the availability of this shipping method based on the destination state
*
* Restricted locations include Alaska, American Samoa, Guam, Hawaii, North Mariana Islands, Puerto Rico,
* US Minor Outlying Islands, and the US Virgin Islands
*
* @param bool $is_available Is this shipping method available?
* @return bool
*/
function grunwell_restrict_free_shipping( $is_available ) {
$restricted = array( ‘AK’, ‘AS’, ‘GU’, ‘HI’, ‘MP’, ‘PR’, ‘UM’, ‘VI’ );
foreach ( WC()->cart->get_shipping_packages() as $package ) {
if ( in_array( $package[‘destination’][‘state’], $restricted ) ) {
return false;
}
}
return $is_available;
}
add_filter( ‘woocommerce_shipping_free_shipping_is_available’, ‘grunwell_restrict_free_shipping’ );
/**
* Limit the availability of this shipping method based on the destination state
*
* Restricted locations include Alaska, American Samoa, Guam, Hawaii, North Mariana Islands, Puerto Rico,
* US Minor Outlying Islands, and the US Virgin Islands
*
* @param bool $is_available Is this shipping method available?
* @return bool
*/
function grunwell_restrict_flat_rate( $is_available ) {
$restricted = array( ‘AK’, ‘AS’, ‘GU’, ‘HI’, ‘MP’, ‘PR’, ‘UM’, ‘VI’ );
foreach ( WC()->cart->get_shipping_packages() as $package ) {
if ( in_array( $package[‘destination’][‘state’], $restricted ) ) {
return false;
}
}
return $is_available;
}
add_filter( ‘woocommerce_shipping_flat_rate_is_available’, ‘grunwell_restrict_flat_rate’ );
=== end code ===
Steve
For the sake of maintenance, you’ll want to simplify it – maybe change the method name to
laserpegs_restrict_to_continental_us()
, then apply it to both the Flat Rate and Free Shipping methods by callingadd_filter()
for both of them:add_filter( ‘woocommerce_shipping_free_shipping_is_available’, ‘laserpegs_restrict_to_continental_us’ );
add_filter( ‘woocommerce_shipping_flat_rate_is_available’, ‘laserpegs_restrict_to_continental_us’ );
As for the other shipping methods being disabled, these should only be applied to the free_shipping and flat_rate shipping methods, per the filters. Do you have other shipping methods enabled that WooCommerce can fall back to if both of these prove invalid for the given state/territory?
Tim Mathews
Yes, they can choose all fedex options if I have only one of the filters above added in.
With both added, the cart says there is no shipping options available.
===
With reference to what you are describing above, would you be so kind to help me “formulate” the code? Should I add what you put above instead of what we have there now? Should it go in addition to it?
I’ll happily PP you a few bucks for your time.
I’m pretty sure both similar filters are causing the issue with woo not showing any available shipping options.
Please, any help would me much appreciated. You can email me directly at [email protected]
Thank you for your help.
Tim
Tim Mathews
Any help?
Interact
Not working with WooCommerce 2.2.11
Raphael
Hello,
Is there any way to work with location based outside the US or Canada My client is Sao Paulo – Brazil and we are not finding way to run the application properly.
Tks for help!
Steve
Are you trying to limit what countries it can ship to or restrict shipping methods to a certain region within Brazil? The former should be built into WooCommerce’s shipping settings, but the latter would be more in line with this post (though I don’t know much about Brazilian addresses).
agsfw
I can confirm that this is not working with the latest version of Woocommerce (2.3.10). Any chance of an update? It worked really well when it did!
Thanks!
Steve
Well, shoot. I’ll try to get an updated version up when things cool off a bit, but I welcome anyone who’s already worked this out for the latest WooCommerce to post it here (or shoot me a link to your own blog post about it)!
Gabriel Reguly
Here there is a version for WC 2.4.11 https://gist.github.com/BFTrick/7805588#gistcomment-1643954
davekessler
Worked great… until WooCommerce 2.6
Mark Gason
Free pickup is an option we only want to allow in 1 province of Canada. Rather than listing everything to exclude can you simple include only 1.