Steve Grunwell

Open-source contributor, speaker, and electronics tinkerer

Quick Tip: Restrict a WooCommerce Shipping Method to the Contiguous United States

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:

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.

Previous

New speaking section

Next

Growing a Mustache for Men’s Health

64 Comments

  1. 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.

  2. 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?

    • 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.

  3. Jim

    That’s a “Wine” sale site.

  4. 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.

    • 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.

  5. 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.

    • 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.

  6. 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?

    • 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

  7. thank you thank you thank you!

  8. :)) your snippet is good !

    you are good too !

    so thanks

  9. 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.

    • 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!

      • 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.

  10. 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.

  11. 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

  12. Does this work with WooCommerce 2.1+ ?

    I tried pasting the code in functions.php and it throws an error.

    • 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?

      • Not sure what happened but when I copied it over the the functions file this time it worked!

        Many thanks for a great fix.

  13. Amie

    Thanks a bunch. That’s fantastic!

  14. 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!

    • 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!

      • I also came across the WooCommerce Restrict Shipping plugin which might fit the bill, though I have no experience with that plugin or developer.

        • 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!

  15. 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?

    • 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!

  16. 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

    • 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!

  17. 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 );

    • 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!

      • 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.

      • You’re awesome! Thanks so much!

  18. 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 :)

  19. Juan Martinez

    Where does this code get pasted into?

    • Hey Juan, you should be safe to just add this to you active theme’s functions.php file.

  20. 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.

  21. 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

  22. 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.

  23. 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.

  24. 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.

  25. 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

  26. hammer

    Where does this code go? And does it still work with the current woocommerce?

    • 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 “}”?

        • 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 “

  27. 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

  28. 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’ );

    • 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

  29. 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 ===

    • 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 calling add_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?

      • 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

  30. Interact

    Not working with WooCommerce 2.2.11

  31. 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!

    • 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).

  32. 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!

    • 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)!

  33. Worked great… until WooCommerce 2.6

  34. 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.

Comments are closed.

Be excellent to each other.