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:

/**
 * 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.

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

64 comments on "Quick Tip: Restrict a WooCommerce Shipping Method to the Contiguous United States"

  1. Bishal Adhikary 3 years ago

    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 3 years ago

    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 3 years ago

      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 3 years ago

    That’s a “Wine” sale site.

  4. Jim 3 years ago

    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 3 years ago

      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 3 years ago

        Thank you again Steve, I will give it a try. Have good weekend.

  5. Mark 3 years ago

    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 3 years ago

      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. Vladislav Vladimirov 3 years ago

    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 3 years ago

      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. Ryan 3 years ago

    thank you thank you thank you!

  8. WORDPRESS98 3 years ago

    :)) your snippet is good !

    you are good too !

    so thanks

  9. Katie 3 years ago

    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 3 years ago

      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 3 years ago

        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. Gabriel Reguly 3 years ago

    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 3 years ago

    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. Blake Imeson 3 years ago

    Does this work with WooCommerce 2.1+ ?

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

    • Steve 3 years ago

      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 3 years ago

        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 3 years ago

    Thanks a bunch. That’s fantastic!

  14. Evan Thorpe 3 years ago

    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 3 years ago

      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 3 years ago

        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 3 years ago

          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. Chris Johnston 3 years ago

    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 3 years ago

      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. david 3 years ago

    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 3 years ago

      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. Belinda 3 years ago

    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 3 years ago

      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 3 years ago

        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 3 years ago

        You’re awesome! Thanks so much!

  18. Gen 3 years ago

    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 3 years ago

      Awesome, glad I could help and thank you for the feedback!

  19. Juan Martinez 3 years ago

    Where does this code get pasted into?

    • Steve 3 years ago

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

  20. Susan 3 years ago

    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. John 3 years ago

    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. megan 3 years ago

    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 2 years ago

    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 2 years ago

    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. John Griswald 2 years ago

    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 2 years ago

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

    • Steve 2 years ago

      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 2 years ago

        Thank you. Does it matter where I paste it? at the very end or beginning? At the very end after the “}”?

        • Steve 2 years ago

          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. Tim Mathews 2 years ago

    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. Tim Mathews 2 years ago

    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 2 years ago

      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. Tim Mathews 2 years ago

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

  30. Tim Mathews 2 years ago

    Any help?

  31. Interact 2 years ago

    Not working with WooCommerce 2.2.11

  32. Raphael 2 years ago

    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 2 years ago

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

  33. agsfw 2 years ago

    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 2 years ago

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

  34. Gabriel Reguly 1 year ago

    Here there is a version for WC 2.4.11 https://gist.github.com/BFTrick/7805588#gistcomment-1643954

  35. davekessler 9 months ago

    Worked great… until WooCommerce 2.6

  36. Mark Gason 6 months ago

    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.

Leave a Reply