If you have ever wondered how to enable product-based automatic free shipping for WooCommerce (that is, when a customer places adds a product to cart and that product is flagged for free shipping, then automatically also flag the cart for free shipping), there are in fact at least two ways.
The hard way
The hard way and probably the more interesting way for tech-savvy users or, hopefully, tech savvy developers would be to write the code yourself. We will be looking at the basics of how this can be achieved in the following paragraphs.
One solution would be to use a shipping class as a marker for products that trigger free shipping. Assuming you want to go down that route, there are a couple of things you need to do:
- first, setup a shipping tax class;
- second, setup special coupon that grants free shipping to the entire cart (obviously, this means you need to enable coupon usage on your store);
- third, make sure that coupon is only added to the cart by your plugin when there’s a product with the special shipping class, in order to disallow illegal coupon code usage.
Assuming you have setup your assets (shipping tax class and the special coupon – we will be using sample01-free-ship-coupon for the coupon code and sample01-free-ship-class for the shipping tax class slug in this example), the first thing you want to do is ensure that the coupon is synced with the contents of the cart, in such a way that:
- if the cart contains a product marked with the special tax class, then the coupon is added to the cart;
- otherwise, the coupon should be removed.
You would implement this step using the woocommerce_before_cart_table action hook:
function sample01_ensure_coupon_synced() {
if (wc_coupons_enabled()) {
$couponCode = 'sample01-free-ship-coupon';
$hasCoupon = in_array($couponCode, WC()->cart->get_applied_coupons());
$shouldHaveCoupon = sample01_is_free_shipping_valid_for_cart();
if ($shouldHaveCoupon) {
if (!$hasCoupon) {
WC()->cart->apply_coupon($couponCode);
}
} else {
if ($hasCoupon) {
WC()->cart->remove_coupon($couponCode);
}
}
}
}
function sample01_is_free_shipping_valid_for_cart() {
$shouldHaveCoupon = false;
$cartContents = WC()->cart->get_cart();
foreach ($cartContents as $cartItem) {
$productId = $cartItem['product_id'];
if (sample01_is_product_eligible_for_free_shipping_coupon($productId)) {
$shouldHaveCoupon = true;
}
}
return $shouldHaveCoupon;
}
function sample01_is_product_eligible_for_free_shipping_coupon($productId) {
$shouldHaveCoupon = false;
$product = wc_get_product($productId);
if (!empty($product)) {
$shipClassSlug = $product->get_shipping_class();
if ($shipClassSlug == 'sample01-free-ship-class') {
$shouldHaveCoupon = true;
}
}
return $shouldHaveCoupon;
}
add_action('woocommerce_before_cart_table', 'sample01_ensure_coupon_synced');
Once this is accomplished, the next thing on your list should be to intercept coupon validation in order to prevent users manually adding your coupon code to their cart.
You would implement this step using the woocommerce_coupon_is_valid filter hook:
function sample01_is_free_shipping_coupon_valid($valid, WC_Coupon $coupon, WC_Discounts $discounts) {
//Only override validity if it's our coupon code
$override = sample01_is_free_ship_coupon($coupon);
if ($valid && $override) {
//function defined above
$valid = sample01_is_free_shipping_valid_for_cart();
}
return $valid;
}
function sample01_is_free_ship_coupon(WC_Coupon $coupon) {
$couponCode = 'sample01-free-ship-coupon';
return $coupon->get_code() === $couponCode;
}
add_filter('woocommerce_coupon_is_valid', 'sample01_is_free_shipping_coupon_valid', 10, 3);
This will leave you with one last small problem: you wouldn’t really want to display any message neither when the coupon is added or removed, neither when our validation routine disallows the coupon as invalid.
The first part you would solve by using the woocommerce_coupon_message filter hook:
function sample01_filter_coupon_message($message, $messageCode, WC_Coupon $coupon) {
if (sample01_is_free_ship_coupon($coupon)) {
$message = null;
}
return $message;
}
add_filter('woocommerce_coupon_message', 'sample01_filter_coupon_message', 10, 3);
The second part, by using the woocommerce_coupon_error filter hook:
function sample01_filter_coupon_error_message($error, $errorCode, \WC_Coupon $coupon) {
if (sample01_is_free_ship_coupon($coupon)) {
$error = null;
}
return $error;
}
add_filter('woocommerce_coupon_error', 'sample01_filter_coupon_error_message', 10, 3);
The easy way
The easy and more economical way would be to buy our plugin, install it, and configure it. It will take care of everything for you: creating the required assets, the core logic described above and some sugar on top.