Just over a year ago I wrote a post which shows how to disable free shipping on a per product basis. That's really useful for stores that like offering free shipping on just about everything everything and want to disable it one or two items. But what about people in the opposite situation? You might have one or two really tiny items like bookmarks don't want to charge people $5 for flat rate shipping.
I figured it was time to revisit this and write a snippet that helps these people out. We can use the shipping classes built into WooCommerce combined with just a couple lines of code to make it really easy to mark any number of products eligible for free shipping.
Create the Shipping Class
The first thing we have to do is create a shipping class. Go to Products > Shipping Classes. Add the name Free Shipping
, leave the rest of the fields blank, and press Add New Shipping Class
.
Now we're going to to make sure the taxonomy slug was created correctly. Don't worry – you don't need to know what that is just that it was created correctly. It's what we'll use in our code to identify the correct shipping class.
Take a look at the existing shipping classes and make sure your class has the slug free-shipping
. If it doesn't that's OK just keep it in mind when we add the code. You'll have to change one line.
Add the Shipping Class to Your Product
Before we jump into the code make sure you add the shipping class to your product. You can do this on the edit product page in the Product data panel under the shipping tab.
Add the Magic Code
We've created a shipping class that marks a product available to use Free Shipping and we've added that to a product. Now we need to write the code that does all the heavy lifting.
You can download this plugin, unzip it, and upload it to your site.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/ | |
* Plugin Name: WooCommerce Enable Free Shipping on a Per Product Basis | |
* Plugin URI: https://gist.github.com/BFTrick/d4a21524a8f7b25ec296 | |
* Description: Enable free shipping for certain products | |
* Author: Patrick Rauland & eugenf | |
* Author URI: http://speakinginbytes.com/ | |
* Version: 1.0.2 | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
* | |
*/ | |
if ( ! class_exists( 'WC_Enable_Free_Shipping' ) ) : | |
class WC_Enable_Free_Shipping { | |
protected static $instance = null; | |
/ | |
* Initialize the plugin. | |
* | |
* @since 1.0 | |
*/ | |
private function __construct() { | |
// add our check | |
add_filter( 'woocommerce_shipping_free_shipping_is_available', array( $this, 'patricks_enable_free_shipping' ), 20 ); | |
} | |
/ | |
* Enable free shipping for orders with products that have the free-shipping shipping class slug | |
* | |
* @param bool $is_available | |
* @return bool | |
* @since 1.0 | |
*/ | |
public function patricks_enable_free_shipping( $is_available ) { | |
global $woocommerce; | |
// set the shipping classes that are eligible | |
$eligible = array( 'free-shipping' ); | |
// get cart contents | |
$cart_items = $woocommerce->cart->get_cart(); | |
// loop through the items checking to make sure they all have the right class | |
foreach ( $cart_items as $key => $item ) { | |
if ( ! in_array( $item['data']->get_shipping_class(), $eligible ) ) { | |
// this item doesn't have the right class. return default availability | |
return $is_available; | |
} | |
} | |
// nothing out of the ordinary return true | |
return true; | |
} | |
/ | |
* Return an instance of this class. | |
* | |
* @return object A single instance of this class. | |
* @since 1.0 | |
*/ | |
public static function get_instance() { | |
// If the single instance hasn't been set, set it now. | |
if ( null == self::$instance ) { | |
self::$instance = new self; | |
} | |
return self::$instance; | |
} | |
} | |
add_action( 'init', array( 'WC_Enable_Free_Shipping', 'get_instance' ), 0 ); | |
endif; |
If your taxonomy slug isn't free-shipping
and is free-shipping-2
or something like that you'll want to replace the default slug with your slug on the $eligible = array( ‘free-shipping' );
line.
Now anytime you have an order filled with products with this shipping class you should see the Free Shipping method. This snippet doesn't affect your other free shipping settings. Ex. if you have a minimum order amount you'll still need to meet that amount.
Happy shipping!
Image credit: Giphy
[…] But what about free shipping based on shipping classes? Patrick just wrote a post explaining that! Check it out. […]
Thank you very much!!!
I cant get this to work, it doesnt seem to do anything, any thoughts?
Are you sure you created the shipping class, set the shipping class on the product page, and activated the plugin?
Hi, I’ve done the above steps too but can’t get it to work either (WooCommerce 2.3.7). Would be a great addition if I could get it to work.
Thanks in advance for any help. 🙂
It’s not working for me. Maybe it’s related to the WooCommerce update?
Hello everyone. I just updated the code snippet so that it works with the latest version of WooCommerce. Make sure you have version 1.0.1 otherwise you won’t see free shipping available.
Hello,
I am having trouble installing the plugin. Above, under “Add the Magic Code” area I clicked on the “download” link to the plugin. On the next page, I downloaded the file ( gistd4a21524a8f7b25ec296-23aad163e05f455a92140d2e398504918eb6f509.tar.gz ). In WordPress I went to install this file and it failed. Do I need to unzip it and only upload the .php file inside (and if so, do I need to compress that php file)? I am trying to launch a campaign tomorrow to make a product have free shipping..
Thanks!
Hey John,
Unfortunately WordPress can’t upload those .tar.gz files. You can either download that file, extract it, rezip it up, and upload it or you can download the file, extract it, and upload it directly to your site via FTP.
I hope that helps! 🙂
Thanks for this tool. But my problem is that I activate it and follow all directions – but I get:
There are no shipping methods available…
hi, this doesnt work!
followed as above, but not working!!
Not working for me either
It’s not working for me either. Followed all the instructions. Shipping is still applied to my free products. Thanks anyway.
Not working
This works great for me! Thank you for sharing this!
For those having trouble, make that sure you have “Free Shipping” enabled in the WooCommerce shipping settings.
For my purposes, I also added a user-role check so that only logged in users with a certain role can access the free shipping.
Patrick, the only issue I’m having (and this is just more forward thinking than an actual issue at the moment) is if my client wanted to offer a coupon for free shipping. If you enable “requires a valid coupon” in the free shipping settings, it overrides this code, and will not offer free shipping without the coupon. I realize that this is a rare usage scenario.
Is there a way around this where a coupon could be used along with offering up free shipping via class?
I’m sure there is a way around that. I would hook into the free shipping method and modify how the coupon works. If you need help with this I would use Codeable.
Followed the steps but can’t seem to get it to work either? Any advice?
Where should the file be uploaded? What folder?
It’s a plugin so it should be uploaded to your plugins directory. 🙂
Hi Patrick,
Great Plugin, works like a charm. Cant thank you enough.
I am having a small issue, however.
When I add any other product with no shipping class along with the one with the “free shipping” class, it disables the Free Shipping. Is there a way we can enable free shipping if any one of the products with “free shipping” class is added to the cart?
Arpan, see my comments to this. I modified the plugin for that very reason. Code is in my reply dated November 18 (today).
-Cameron
Plugin works great. On the Checkout page, in addition to the “Free Shipping” option, the customer also sees “Standard Shipping”. Is there a way to prevent the “Standard Shipping” option from appearing on the checkout page for these products? Normally this would not be an issue, but in our case it could cause some confusion since we are not offering delivery for these particular products, only Pick Up.
I got this to work and wanted to add a few thoughts:
1. Should go without saying…but Free Shipping must be enabled…WooCommerce->Settings->Shipping->Free Shipping “Enable Free Shipping”
2. Only works standalone if the WooCommerce->Settings->Shipping->Free Shipping “Free Shipping Requires” option does NOT include a coupon setting (as Rob pointed out previously).
3. As-is, If ANY of the items in the cart belong to a shipping class OTHER than the ones defined in the plugin, you will NOT see the free shipping option. All items in cart must be linked to the shipping class defined.
I modified the plugin to allow some different scenarios. For example, I wanted to have the option that if a particular item (of a particular class) was in the cart then the ENTIRE CART would ship free.
Insert this snippet to replace Patrick’s function. Make sure you define the slugs properly (as Patrick mentions):
public function patricks_enable_free_shipping( $is_available ) {
global $woocommerce;
$IsEligibleONE = False;
$IsEligibleALL = False;
$eligibleCount = 0;
$Count = 0;
// set the shipping classes that are eligible
$eligibleALL = array( ‘shipfreeall’ );
$eligibleONE = array( ‘shipfree’ );
// get cart contents
$cart_items = $woocommerce->cart->get_cart();
// loop through the items checking to make sure they all have the right class
foreach ( $cart_items as $key => $item ) {
$Count = $Count + 1;
//echo “[Loop ” . $count;
if ( ! in_array( $item[‘data’]->get_shipping_class(), $eligibleONE ) ) {
//echo “Not Eligible ONE -“;
if ( ! in_array( $item[‘data’]->get_shipping_class(), $eligibleALL ) ) {
// echo “Not Eligible ALL]”;
}
else {
$IsEligibleALL = True;
$eligibleCount = $eligibleCount + 1;
// echo “Eligible ALL: Count ” . $eligibleCount . “]”;
}
}
//Is eligible for single-item Free Shipping
else {
$eligibleCount = $eligibleCount + 1;
$IsEligibleONE = True;
// echo “Eligible ONE: Count ” . $eligibleCount . “]”;
}
}
if ($eligibleCount == 0 ) {
//echo “ENTIRE CART NOT ELIGIBLE–“;
//echo “Eligible Count:” . $eligibleCount ;
return false;
}
If ($IsEligibleALL == True) {
//echo “END ALL–Eligible for ALL Count:” . $eligibleCount ;
return $is_available;
}
elseIf ($IsEligibleONE == True && $Count == 1) {
//echo “END ONE–Eligible for Single Count:” . $eligibleCount ;
return $is_available;
}
else {
//echo “END ONE–Single Not Eligible Since Count > 1 : Count” . $Count ;
return false;
}
}
Hi Cameron,
Thanks for this. I tried to replace the function but it constantly gives me an error at the end of the snippet. Which part do I need to replace?
Thanks again
Arpan
Replace lines 50-70 of Patrick’s code (what you should replace is below) with the code from my post above…
public function patricks_enable_free_shipping( $is_available ) {
global $woocommerce;
// set the shipping classes that are eligible
$eligible = array( ‘free-shipping’ );
// get cart contents
$cart_items = $woocommerce->cart->get_cart();
// loop through the items checking to make sure they all have the right class
foreach ( $cart_items as $key => $item ) {
if ( ! in_array( $item[‘data’]->get_shipping_class(), $eligible ) ) {
// this item doesn’t have the right class. return false
return false;
}
}
// nothing out of the ordinary return the default value
return $is_available;
}
Thanks Cameron,
It looked better this time but on the cart page I get a null error for—-: if ( ! in_array( $item[‘data’]->get_shipping_class(), $eligibleONE ) ) {
I think I am doing something wrong here.
Thanks
Arpan
Paste the entire code for the plugin so I can be sure you have it all correct.
Also, make sure you have defined BOTH shipping classes.
In order for my code to work properly, you need to have at least two shipping classes:
-One with slug “shipfreeall” (which you will select as your shipping class for items that give free shipping to the whole cart )
-Another with slug “shipfree” (which would be for items that only get free shipping if they are the only item in the cart)
Cameron I’m trying to override the code so I can sell a specific item for free shipping but still be able to charge shipping for other products- are you saying the code youve posted above will allow that?
Yes, if I understand you correctly.
With the code I included, you can specify which items can be shipped free and which ones can’t (you do this by creating the shipping classes, assigning them to the products, and modifying the code).
With the shipfreeall class, I allow the entire shipment to be free if a product having that class is included in the cart. With the shipfree class, only the items with that class can be in the cart.
I am not sure how your shipping works, but this is an all-or-nothing (meaning either free shipping is enabled for the cart or it isn’t) solution. If you charge shipping by the item and then aggregate costs, this won’t work for you–because this code simply decides whether or not to show the FREE SHIPPING option during checkout based on items in the cart and their shipping class.
Cameron – thanks for the code snipit. Trying to apply your methodology to my current set up, but it doesn’t seem to be working.
http://pastebin.com/hTcV3wZe
I’d actually like to see the ‘echo’ debug statements. I am a bit new to wordpress – where are these outputted? Onto the screen at the View Cart screen?
Thanks for everyone’s help
Edwin,
If you uncomment the “echo” statements by removing the // in front of them, you WILL see the echoes on the view cart screen, if I recall correctly.
You wouldn’t want to uncomment these in a production environment.
In order for this to work, you MUST define the shipping classes:
free-shipping-all
free-shipping
Set these up in your wordpress shipping classes (the “slug” must match the above exactly) under [WOOCOMMERCE->Products->Shipping Classes]. Once these classes are set up, you must add one or the other to the products themselves under the [WOOCOMMERCE -> Product Data-> Shipping] option.
The problem is the UPS Shipping plug-in still wants to add the product to it’s calculation. It will break if you don’t give the item a weight and dimensions. The work around I found is giving the Free Shipping Item really small weight and dimensions so that it doesn’t affect the rate if non-free shipping items are in the cart. I wish the UPS Shipping plug-in allowed you to ignore certain shipping categories.
Thank you so much for this !!! It solved the problem perfectly. I wanted to make one product free shipping, boom, done. You are awesome.
Hi, great stuff thanks!
how can i change “plus shipping” into “free shipping” on the product page?
Where does it say plus shipping on the product page? I don’t believe that’s a default setting.
Hey there… any updates on this, now that the Shipping Zones feature has been released?
I have a site that has been updated to the latest WooCommerce code. Now my one Subscription product that needs Free Shipping is giving me problems and I was hoping that your plug-in would solve it. I’m guessing that it isn’t working because the previous Free Shipping setting is gone now and has to be set up with Shipping Zones. Please, please advise as to how to use your plugin with the new Shipping Zones.
Does this code work with the new Woocommerce Shipping Zones? I see that someone else has asked but no answer yet. I would love to use this code.
I tried this code, looked very promising, but I don’t get anything new on my checkout page. Only the regular UPS/USPS options.
Using 4.5.3 – would love to hear if anyone got this working as is.
Thanks!
It appears this plugin no longer works once WooCommerce moved away from the dropdown field for shipping options?
Help!
With WooCommerce 2.6, none of this appears to work at all. Maybe you should change your post to reflect this?
I haven’t had the time to look into this. Would anyone be able to dig in and update the snippet so it works? I’d be happy to merge in any changes to the original snippet so everyone can benefit from this.
I’ll see what I can do Patrick. Your site is VERY much appreciated.
Chris,
You are my hero.
I had to use something else entirely.. this is a modified version of “Free Shipping on a Per Product Basis in the Same Cart” http://pastebin.com/xVKzAFet
First in woocommerce I added a shipping class of “Free Shipping ” and assigned that to my product. Then I setup a Shipping Zone, name it whatever you want and the regions I used was everywhere by leaving it blank. Then I added a “Flat Rate” to it and saved. Then Click on “Flat Rate” in your new zone. Change the title to what you want your customers to see, I used Free Shipping. Then under “Shipping Class Costs” look for – “Free Shipping” Shipping Class Cost. Make the cost 0 (read.. ZERO). Save your changes.
Add the snippet at this pastebin to your functions.php – http://pastebin.com/xVKzAFet and change the following lines:
—-
Change ‘free-shipping’ to whatever your free shipping class is:
if ( $item[‘data’]->get_shipping_class() == ‘free-shipping’ ) {
—–
The following section uses whatever Shipping Method(s) you add to the shipping zone in ‘ship_via’ as your “Free Shipping” method. My zone uses ‘flat_rate’, no idea how to make it use any other option but that for zones…
if ( $bulky_items ) {
$packages[] = array(
‘ship_via’ => array( ‘flat_rate’ ),
—-
In the next section I force the rest of my shipping to use my ups shipping by adding the ‘ship_via’ line that the original code does not have but my pastebin does. (If you use a different plugin than WooThemes “WooCommerce UPS Shipping” you may have to try different names to figure out what the correct keyword is.)
if ( $regular_items ) {
$packages[] = array(
‘ship_via’ => array( ‘ups’ ),
—
Hope this helps someone. Luckily for me I only need 2 shipping methods, Free and UPS so this is all I need.
With WooCommerce 2.6 you need to return true instead of the $is_available parameter.
https://gist.github.com/eugenf/90df5500f0116fa1bca117584d615b1b
Line 68
Thanks so much! I updated the plugin (without testing so make sure you test) and I added you as an author. Thanks for the help. 🙂
This does not seem to be working on Woocommerce 2.6.4? We have created a shipping class for this and added it to a product. When we add this product to the cart the standard flat rate is still applied?
I have FedEx plugin and this doesn’t seem to work. Do you think that the FedEx plugin conflicts with this?
I am also using the FedEx plugin on the site where I need to be able to set free shipping on one subscription product. The support person at Woo threw up her hands when she couldn’t figure it out and said “FedEx hasn’t updated their plugin yet.” However, I suspect there is more to it than that. Woo hadn’t planned on letting people designate free shipping on a per product basis. Only by geographic location. ???
Would love an update on this. Thanks!
Kyle and Kay,
I don’t have time to work on this right now but looking over Robert and Eugen’s awesome posts it looks like all that is left for us to do is find the name of FedEx shipping and replace on this line:
‘ship_via’ => array( ‘ups’ ),
Is that how you understand it?
This is great.
Like in earlier versions, I need to be able to enable free shipping if one specific product is placed in the cart. If that product is in the cart, it enables free shipping regardless of what else is in there.
How can we do that with the code Eugen posted similar to the code Cameron gave back on November 18, 2015 at 4:15 pm?
Kelley
Kel, it requires a simple change. On the line 64 where we return the $is_available variable you need to return true instead. That means that if you have a product with free shipping then use it for all the cart. Everything else stays the same
Here’s the updated gist for you https://gist.github.com/eugenf/90df5500f0116fa1bca117584d615b1b
Ok I have added your code and followed the instructions. I enter product A which has been assigned to free shipping class. It shows the free shipping option in the cart and if I choose that, no shipping is charged.
I now add product B which should be charged shipping and is not assigned to the free shipping class, but it gives free shipping to both items.
Where I am not sure how your product works is that I am using the UPS shipping plugin by WC and it shows the live rates in the cart, so I have a Next Day Option and Next Day Saver Option and then the Free Shipping option. If I have to select the option and I have items that are free and not free how is this supposed to work? I was under impression, I guess wrong, that an item that is setup as free shipping class would not require any selection and would just be ignored by the shipping calculator.
Plus one for this, I”m having the same issue and tried a bunch of different ways. Even Roberts method which seemed promising, until I realized a custom is only in 1 shipping zone, so it didn’t work. I can’t seem to just get 1 product free shipping, and calculate the rest…
Thank for your your time putting this together! Unfortunately, I am not able to get this working. I have uploaded the plugin to my plugins folder and activated it. I have created a Shipping Zone of Free Shipping with a Shipping Method of Flat Rate with 0 assigned to the cost of the Free Shipping Class. I also have a shipping class of Free Shipping with the slug of free-shipping. I have assigned this Free Shipping Class to one item, my virtual gift cards, which I’d like to be purchased without shipping. When I add only this item to my cart, it is still applying the flat rate shipping charge to the item ($10) and free shipping. Do you have any ideas what I’m doing wrong? Thanks!
Hi Patrick,
Nice article shared.
Is that possible through this code, we can set different price for specific product like $3 or some product shipping charged $10 due to product size. Some of product light weight or some of heavy weight so it will charged higher then normal shipping price.
Thank you in advance and hope I will receive your reply soon
So, that’s the current verdict: not possible with the latest Woo and FedEx shipping both installed?
Patrick you are the best! Always have solutions.
…I have one last question (sorry… not very tech-y over here)
Where would you paste the snippet above? (my guess is theme functions.php but dont want to mess things up. Thanks again for all the super helpful info!
HI Patrick. Thanks for this. Do you know if there is a way to set shipping that if only a single product is in the cart, it will charge one flat rate, or if 2 or more products in the cart, then a different flat rate is applied?
Our shipping for one item is £3.50 (flat fee)
Shipping for 2-25 items is £5.00 (flat fee)
I’d look into Table Rate Shipping (affiliate link).
I have just found this blog. I am wanting to add free shipping for Wear A Hat Day products in support of brain tumour research. Just wanted to say thank you so much 🙂 It’s awesome that there are people like you so willing to help out in the big www. Thanks again x
I want to make a “Free Delivery” option when someone buys more than 1 product… How can I do that?
There seems to be no answer until 2017!
What I did to solve the problem is i set the product into VIRTUAL…
DONE!
I’m trying to make a product bundle be free shipping, then any other product in the cart have shipping. A Woocommerce employee gave me this link, but this code isn’t working for me.
It works if I do as Ellahworks.com says, but I feel like that is a given since any virtual product will have free shipping?
This plugin still enables free shipping when a product with a Free Shipping class is added to the cart, the only issue is free shipping is still an option when a product without a free shipping class is added also.
This is what I’m using:
https://www.pastiebin.com/5a26751e8ef1c
Hello,
For some reason it looks as if this plugin no longer functions or is ignored in WC 3.3.3 on WP 4.9.4; does anyone have any suggestion on how to fix?
Thank you,
Oli Rosenbladt
Hello –
It’s seems like the most recent WC update has disabled this plugin somehow; are there any options to fix/update this?
Thank you,
Oli Rosenbladt
Hi Patrick …
This code is not working for me …
Woorcommerce Version: 3.4.4