WooCommerce: How to Setup Tiered Shipping Rates by Order Amount

The new shipping zone management that was introduced with Woo 2.6 gives us the chance to add flat rate, free shipping and local pick-up methods by default.

But what if a client requires 3Β different rates depending on the order amount (tiered shipping)? For example:Β “For orders up to $100, shipping = $5; for orders up to $250, shipping = $2; for orders above $500, shipping = free”.

Is this possible without using a plugin? Well, the answer, as usual, is absolutely yes!

1. Tiered Shipping – Shipping Zone Setup

Go to WooCommerce > Settings > Shipping and create your shipping zone. In the example, I will target US customers and add 3 shipping methods to it: Flat Rate, Flat Rate and Free Shipping.

Shipping Zone Setup for Tiered Shipping
Shipping Zone Setup for Tiered Shipping

2. Tiered Shipping – Shipping Methods Setup

Open each one of the shipping methods previously added to the zone and rename them / set them up like this:

  1. Flat Rate #1 > rename to “Orders Below $100” and assign cost = $5
  2. Flat Rate #2 > rename to “Orders Below $250” and assign cost = $2
  3. Free Shipping > select “Requires a minimum order amount” = $500

Here’s one of the method’s setup:

Shipping Method Setup for Tiered Shipping
Shipping Method Setup for Tiered Shipping

3. Tiered Shipping –Β PHP Snippet

Now we need to “tell” WooCommerce that, based on the order amount, a Flat Rate should be used instead of the other. Only in this way we can show the correct shipping method to the end user.

First, take a note of the unique ID of the two flat rates. They should look look something like “flat_rate:9“. For more info on how to find it, check here: https://businessbloomer.com/woocommerce-disable-free-shipping-if-cart-has-shipping-class/#woocommerce-how-to-find-shipping-method-id-woo-2-6-after-the-introduction-of-the-new-shipping-zones

Second, let’s code! We’ll need to “unset” flat rate #2 if we are under $100, otherwise we’ll require to “unset” flat rate #1.


/**
* @snippet       Tiered Shipping Rates | WooCommerce
* @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
* @sourcecode    https://businessbloomer.com/?p=21425
* @author        Rodolfo Melogli
* @testedwith    WooCommerce 3.5.2
* @donate $9     https://businessbloomer.com/bloomer-armada/
*/

add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
  
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
  
	$threshold = 100;
	
	if ( WC()->cart->subtotal < $threshold ) {
   		unset( $rates['flat_rate:1'] );
	} else {
		unset( $rates['flat_rate:2'] );
	}
  
	return $rates;
  
}

Where to add this code?

You can place PHP snippets at the bottom of your child theme functions.php file (before "?>" if you have it). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such delicate files - if you need more guidance, please take a look at my free WooCommerce Customization video tutorial.

Does this snippet still work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7.

If you think this code saved you time & money, please join other Business Bloomer supporters and avail of 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce?

Check out these free videos, tutorials and tips!

  • how-to-edit-woocommerce-with-php-snippets
  • woocommerce-hooks-add_action-list-visual
  • woocommerce-customize-single-product-page-PHP
  • woocommerce-customize-shop-page-PHP
  • woocommerce-advanced-customization
  • how-to-edit-woocommerce-cart
  • woocommerce-customize-checkout-page-PHP
  • woocommerce-email-customization
  • woocommerce-conditional-logic

Rodolfo Melogli

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as a WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza.

54 thoughts on “WooCommerce: How to Setup Tiered Shipping Rates by Order Amount

  1. Hi this snippet works perfectly on the cart page but for some reason not calculating correctly on the checkout page, any ideas?

    1. Hello Jon, thanks so much for your comment! I just retested this on the latest version of WooCommerce and it still works. Unfortunately this looks like custom troubleshooting work and I cannot help here via the blog comments. Thanks a lot for your understanding! ~

  2. Great article. I tried this out but it still shows all the choices instead of just the one for the correct range. Does this work with other extensions also installed like the UPS and USPS ones from WooCommerce?

        $threshold1 = 24.99;
        $threshold2 = 34.99;
        $threshold3 = 49.99;
        $threshold4 = 79.99;
         
        if ( WC()->cart->subtotal <= $threshold1 ) {
            unset( $rates['flat_rate:19'], $rates['flat_rate:20'], $rates['flat_rate:21'], $rates['flat_rate:22'] );
        } elseif ( WC()->cart->subtotal <= $threshold2 ) {
            unset( $rates['flat_rate:18'], $rates['flat_rate:20'], $rates['flat_rate:21'], $rates['flat_rate:22'] );
        } elseif ( WC()->cart->subtotal <= $threshold3 ) {
            unset( $rates['flat_rate:18'], $rates['flat_rate:19'], $rates['flat_rate:21'], $rates['flat_rate:22'] );
        } elseif ( WC()->cart->subtotal <= $threshold4 ) {
            unset( $rates['flat_rate:18'], $rates['flat_rate:19'], $rates['flat_rate:20'], $rates['flat_rate:22'] );
        } else {
            unset( $rates['flat_rate:18'], $rates['flat_rate:19'], $rates['flat_rate:20'], $rates['flat_rate:21'] );		
        }
       
        return $rates;
    
    1. Hey Patrick! Did you empty the Cart before re-testing? Also, it depends on how UPS / USPS are coded, I don’t think they’re shipping rates are called “flat_rate:18” – they’re possibly something else.

    2. I have similar code with the same problem – but only on the 3rd shipping rate.

      function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
        $threshold = 50;
        $threshold2 = 150;
        $threshold3 = 250;
      	
      	if ( WC()->cart->subtotal <= $threshold ) { 
              if ( isset( $rates['flat_rate:2'] ) ) unset( $rates['flat_rate:3'], $rates['flat_rate:2'] );
          }
          elseif ( WC()->cart->subtotal <= $threshold2  ){
              if ( isset( $rates['flat_rate:2'] ) ) unset( $rates['flat_rate:3'], $rates['flat_rate:1'] );
          }
          elseif ( WC()->cart->subtotal <= $threshold3  ){
              if ( isset( $rates['flat_rate:3'] ) ) unset( $rates['flat_rate:1'], $rates['flat_rate:2'] );
          } else {
              if ( isset( $rates['free_shipping:4'] ) ) unset( $rates['flat_rate:1'], $rates['flat_rate:2'] );   
          }
        return $rates;
      }
      
    3. Hello Jessica, thanks for your comment! I think you have an issue in the first IF πŸ™‚

  3. How Can I Write this in code? I tried but it show wrongly.

    I have THREE
    $65 Above – FREE SHIPPING
    33 Above – $8
    0 to 32 – $15

    1. Hello Vir, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  4. I’m struggling with shipping fees right now. I already have states and cities enabled, the problem now is: how to add cities into the calculation. I mean, if a customer is buying from city X (state A) fee is going to be $5, but if he purchases from city Y ( State A), fee would be $9. Do you have any idea on how to do it?

    1. Hello Yara, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  5. Here is the code I added for three different shipping options based on quantity in the cart:

    
    // Change shipping based on quantity purchased.
    add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
    function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
    $threshold1 = 2; 
    $threshold2 = 3;
    if ( WC()->cart->get_cart_contents_count() < $threshold1 ) {
    unset( $rates['flat_rate:6'], $rates['flat_rate:8'] );
    } elseif ( WC()->cart->get_cart_contents_count() < $threshold2 ){
    unset( $rates['flat_rate:1'], $rates['flat_rate:8'] ); 
    } else {
    unset( $rates['flat_rate:1'], $rates['flat_rate:6'] );
    } 
    return $rates; 
    }
    
    

    I made this tutorial: https://www.easywebdesigntutorials.com/adjust-shipping-price-when-quantity-changes/

    1. Excellent, thanks Paal!

  6. Hi there, When I post the php code for this the latest version of WordPress rejects it with multiple ‘Unexpected Token’ errors. Ive double checked all the ‘ and , punctuation and it seems fine.
    Can anyone suggest anything that may be causing this please?

    Here is a screengrab of the errors: https://www.dropbox.com/s/el65759iea14zqh/Unexpected%20Token%20errors.png?dl=0

    Thanks in advance! πŸ™‚

    1. Sorry, I was putting the code into the css, I’ve tried it in the functions and now I get:

      Your PHP code changes were rolled back due to an error on line 28 of file wp-content/themes/uk-cbd-oil/functions.php. Please fix and try saving again.
      syntax error, unexpected ‘add_filter’ (T_STRING)

      Here is a screengrab again πŸ™‚
      https://www.dropbox.com/s/fc5ihk2x9qidugw/string_error.png?dl=0

    2. Hey Brian, that’s the weirdest PHP error I’ve ever seen πŸ™‚ Do you have this at the beginning of the file:

      <?php
      
  7. Rodolfo,
    Very helpful snippet and I almost have it working. I have two levels; less than $100, less than $200 then anything over $200 is free shipping. Shipping costs display properly until I exceed $200 in my cart total then both the Free Shipping option and Less than $200 option appears. How do I get rid of the Less than $200 option?
    Thank you.

  8. I’m trying to do something a bit more advanced because my client has three different flat rate tiers as well as two free shipping tiers. Idea is when you hit $100, the free shipping should trigger but also so should a flat rate tier for expedited shipping and the first two flat rate tiers go away. This isn’t working for me, but am I at least in the right ballpark?

    /**
    * @snippet Tiered Shipping Rates | WooCommerce
    * @how-to Watch tutorial @ https://businessbloomer.com/?p=19055
    * @sourcecode https://businessbloomer.com/?p=21425
    * @author Rodolfo Melogli
    * @testedwith WooCommerce 2.6.8, WordPress 4.7
    */
     
    add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 5, 2 );
       
    function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
      $threshold = 100;
        if ( WC()->cart->subtotal < $threshold ) { 
            if ( isset( $rates['flat_rate:1'] ) ) unset( $rates['flat_rate:3'], $rates['free_shipping:1'], $rates['free_shipping:2'] );  
        } else {
            if ( isset( $rates['flat_rate:3'] ) ) unset( $rates['flat_rate:1'], $rates['flat_rate:2'] );   
        }
      return $rates;
    }
    1. Hey Adam, thanks so much for your comment! I don’t see anything wrong with this, try emptying the Cart before testing again πŸ™‚

  9. Sorry i posted on the wrong post. This code works great on woocomerce 3.2.6 and WordPress 4.9.1. Also in theme-functions.php file. i had to add some custom code of mine also but it works like a charm.
    Thank you guys, i really appreciate

  10. I was wondering if there is a way to charge 2.50 if an order is over $300 for the signature required but have customers be able to waive the signature and 2.50 charge if they don’t want to sign for it. is this possible?

    1. Hello Connor, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  11. Hi is there anyway to simply change the free shipping trigger to occur based on the subtotal (i.e. not effected by he use of coupons) rather than the order total?

    Thanks
    Z

    1. Zed, thanks for your comment! Yes, sure, you can do that. You just need to find the correct order total function: https://docs.woocommerce.com/wc-apidocs/class-WC_Order.html

  12. Is it possible to assign both the country and total suborder based shipping cost in this php code. Please help me

    1. Hey Gopinathan, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  13. Hi Rodolfo, how much would you charge for implementing this for me?

    1. Wesley, thanks so much for your comment! If you’d like to get a quote, feel free to contact me here. Take care πŸ™‚

    2. Done, thank you sir.

  14. Thank you, this was super handy! Worked absolutely perfectly!

    1. Thanks Nicola πŸ™‚

  15. Hi Rodolfo,
    I am new to php and I’m looking to create different “thresholds” to different shipping zones. For example, shipping zone A needs a minimum spend of 100 in order to get free shipping; and shipping zone B needs to spend 200 for free shipping. How would I implement this into the code you provided? Is it even possible lol. Thank you so much!

    1. Hey Thy, thanks for your comment! Yes, of course this is possible, but I’m afraid it’s custom work and I can’t help via the blog comments. Thank you πŸ™‚

  16. Sorry, I didn’t wrap the php

    $threshold1 = 200;
    $threshold2 = 400;
    $threshold3 = 800;
    
     
    if ( WC()->cart->subtotal < $threshold1 ) {
     
    unset( $rates['flat_rate:2'], $rates['flat_rate:6'], $rates['flat_rate:7'], );
     
    } elseif ( WC()->cart->subtotal < $threshold2 ) { 
     
    unset( $rates['flat_rate:1'], $rates['flat_rate:6'], $rates['flat_rate:7'], );
     
    } elseif ( WC()->cart->subtotal < $threshold3 ) { 
     
    unset( $rates['flat_rate:1'], $rates['flat_rate:2'], $rates['flat_rate:7'], );  
     
    }
     
    return $rates;
    
    1. Hey Jamie, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom troubleshooting work and I cannot provide a complementary solution here on the blog. Thanks a lot for your understanding! ~R

    2. I understand. Thank you πŸ™‚

  17. Hello,

    Great article! Really helpful. I’m looking to implement tiered shipping options like this:

    up to Β£200.00 Β£ 10.00
    Β£200.00-Β£400.00 Β£ 20.00
    Β£400.00-Β£800.00 Β£ 30.00
    Β£800.00 and Over Β£ 60.00

    I’ve written the following, but it’s not working. Any glaring errors that you can see?

    $threshold1 = 200;
    $threshold2 = 400;
    $threshold3 = 800;

    if ( WC()->cart->subtotal cart->subtotal cart->subtotal < $threshold3 ) {

    unset( $rates['flat_rate:1'], $rates['flat_rate:2'], $rates['flat_rate:7'], );

    }

    return $rates;

    Thank you,

    Jamie

  18. Hi there,

    This code looks great to use but I was wondering what to do if I only have one flat rate meaning orders up until 250 euros require shipping costs and above it’s free.

    Thanks!

    1. Hey Delana, thanks for your comment πŸ™‚ You need no snippet for that! Just create 2 shipping methods, and set up the Free Shipping with a minimum threshold πŸ™‚

  19. I have 5 rates, ranging from $0 to > $100, do I repeat this block 5 times and just change the amount?
    [php]
    $threshold = 100;

    if ( WC()->cart->subtotal < $threshold ) {

    if ( isset( $rates['flat_rate:1'] ) ) unset( $rates['flat_rate:2'] );

    } else {

    if ( isset( $rates['flat_rate:2'] ) ) unset( $rates['flat_rate:1'] );

    }

    return $rates;
    {/php]

    1. Hey Liz! No, you’d do something like this:

      $threshold1 = 20;
      $threshold2 = 40;
      // etc..
      
      if ( WC()->cart->subtotal < $threshold1 ) {
      
      unset( $rates['flat_rate:2'], $rates['flat_rate:3'], $rates['flat_rate:4'], etc... );
      
      } elseif ( WC()->cart->subtotal < $threshold2 ) { 
      
      unset( $rates['flat_rate:1'], $rates['flat_rate:3'], $rates['flat_rate:4'], etc... );
      
      } elseif ( etc... ) { 
      
      }
      
      return $rates;
      
    2. Hi Rodolfo,
      I didn’t see your response here til today when I got back to working on this. Thank you for the model of what to use!!! How do you indicate a threshold for everything above a dollar amount? Threshold 6 is for everything above $100. The other thresholds (have no idea what happened to #2) all calculate properly in the cart but once I have over $100 of products, it displays all of the price points. Site is still in progress ..

      	$threshold1 = 25;
        	$threshold3 = 50;
      	$threshold4 = 70;
      	$threshold5 = 100;
      	$threshold6 = 10000;
      // etc..
       
      if ( WC()->cart->subtotal < $threshold1 ) {
       
      unset( $rates['flat_rate:3'], $rates['flat_rate:4'], $rates['flat_rate:5'], $rates['flat_rate:6'] );
       
      } elseif ( WC()->cart->subtotal < $threshold3 ) { 
       
      unset( $rates['flat_rate:1'], $rates['flat_rate:4'], $rates['flat_rate:5'], $rates['flat_rate:6'] ); 
       
      } elseif ( WC()->cart->subtotal < $threshold4 ) { 
       
      unset( $rates['flat_rate:1'], $rates['flat_rate:3'], $rates['flat_rate:5'], $rates['flat_rate:6'] ); 
       
      } elseif ( WC()->cart->subtotal < $threshold5 ) { 
       
      unset( $rates['flat_rate:1'], $rates['flat_rate:3'], $rates['flat_rate:4'], $rates['flat_rate:6'] ); 
       
      } elseif ( WC()->cart->subtotal < $threshold6 ) { 
       
      unset( $rates['flat_rate:1'], $rates['flat_rate:3'], $rates['flat_rate:4'], $rates['flat_rate:5'] ); 
       
      }
       
      return $rates;
      
    3. Liz, in that code you have a big problem πŸ™‚

      When you check:

      if ( WC()->cart->subtotal < $threshold1 )
      

      this also triggers when:

      WC()->cart->subtotal < $threshold3
      

      So, you have to work with those ranges and make sure every elseif does not override the previous one.

      Hope this helps

  20. This is exactly the solution I was looking for but in my case I have three flat rates (No free shipping):
    Orders up to $50 in value = $10 for shipping
    Orders from $51-$100 in value = $20 for shipping
    Order above $100 in value = $30 for shipping

    Do I have to define three different threshold variables? Any guidance would be greatly appreciated.

    1. Stella, thanks for your comment and corect, you’ll need to define a second threshold and do some more work with the if>else in PHP. Good luck πŸ™‚

    2. Hi there,

      Currently I’ve got 2 Shipping Zones, one Zone for specific Post Code areas and I’ve followed the tutorial and got it working just fine.

      However, in the second Zone for everywhere else I’ve got the same shipping methods setup (but with different prices) but just not sure how to apply them to the snippet?

    3. Darren, thanks for your comment! I’m afraid it’s not 100% clear what you’re trying to achieve. Can you link to a screenshot of your settings and explain your objective again? Thank you πŸ™‚

  21. Precisely what I’ve been looking for for ages, thanks!

    Expanding on this method I was also able to unset other superfluous delivery options that had free versions of themselves over a certain amount.

    1. Brilliant, thanks for your feedback Andrew!

    2. Hi Rodolfo,

      So sorry I missed your reply back in March.

      So basically we’ve got two Shipping Zones – one for Local – Tiered Shipping which we’ve used the code you provided and that works fine – https://pasteboard.co/g432wor4e.png as you can see the shipping option shows based on price (plus a local collection option which is fine).

      We’ve got a second Shipping Zone – Rest of UK – Tiered Pricing and want to use the same code or adapt the code. At the moment this is what the checkout page looks like – https://pasteboard.co/g3WO1BxAF.png

      So you can see the customer select their shipping price, we would like the shipping to be based on their Subtotal.

      So we’d like to be able to adapt the code so that it can be used for both Shipping Zones – Local – Tiered Shipping and Rest of UK – Tiered Pricing.

      Hope that makes sense? Darren

    3. Darren, thanks so much for your comment! Yes, it makes sense and it is possible – but unfortunately this is custom work and I cannot provide a complementary solution here on the blog. Thanks a lot for your understanding! ~R

Questions? Feedback? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between: [php]code_here[/php]. Failure to complying with this (as well as going off topic) will result in comment deletion. You should expect a reply in about a week - this is a popular blog but I need to get paid work done first. Please consider joining #BloomerArmada to ask me 1-to-1 WooCommerce questions. Thank you :)

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.