WooCommerce: Free Shipping on a Per Product Basis in the Same Cart

This is a common request from WooCommerce users:

I want to ship specific products for free while some others will have shipping costs, but when a product having free shipping is in cart with another product that has shipping costs, free shipping isn’t available at all, what to do?

There’s a solution to that! And you don’t need any plugins to solve this issue. Based on a post by Mike Jolley, I’d like to explain how to use different shipping methods on a per product basis in the same cart. Let’s say you have two products, one that must be shipped for free, and a second one having a flat shipping cost (that would work with rates based on APIs as well,  like UPS, USPS, Fedex, Canada Post, Australia Post etc…). The 1st step is to create 2 shipping classes, in Products > Shipping Classes:

shipping-classes

Create at least two shipping classes

You need to create a “Free” shipping class. You can create as many other shipping classes as ou want.

The next step is to assign classes to each product:

product1

1st Product Settings

product2

2nd Product settings

Now that you assigned classes to products, you need to define flat rates per classes in WooCommerce > Settings > Shipping > Flat Rate:

flat-rates

Shipping classes flat rates

Make sure that you setup a cost equal to zero for the free shipping class.

Finally you need to filter the shipping package using custom code and separate products from one package to two packages having each its own shipping method. Add the following snippet in the file called functions.php in your theme folder:

add_filter( 'woocommerce_cart_shipping_packages', 'bulky_woocommerce_cart_shipping_packages' );

function bulky_woocommerce_cart_shipping_packages( $packages ) {
    // Reset the packages
    $packages = array();

    // Bulky items
    $bulky_items   = array();
    $regular_items = array();

    // Sort bulky from regular
    foreach ( WC()->cart->get_cart() as $item ) {
        if ( $item['data']->needs_shipping() ) {
            if ( $item['data']->get_shipping_class() == 'free' ) {
                $bulky_items[] = $item;
            } else {
                $regular_items[] = $item;
            }
        }
    }

    // Put inside packages
    if ( $bulky_items ) {
        $packages[] = array(
            'ship_via'        => array( 'flat_rate' ),
            'contents'        => $bulky_items,
            'contents_cost'   => array_sum( wp_list_pluck( $bulky_items, 'line_total' ) ),
            'applied_coupons' => WC()->cart->applied_coupons,
            'destination'     => array(
                'country'   => WC()->customer->get_shipping_country(),
                'state'     => WC()->customer->get_shipping_state(),
                'postcode'  => WC()->customer->get_shipping_postcode(),
                'city'      => WC()->customer->get_shipping_city(),
                'address'   => WC()->customer->get_shipping_address(),
                'address_2' => WC()->customer->get_shipping_address_2()
            )
        );
    }
    if ( $regular_items ) {
        $packages[] = array(
            'contents'        => $regular_items,
            'contents_cost'   => array_sum( wp_list_pluck( $regular_items, 'line_total' ) ),
            'applied_coupons' => WC()->cart->applied_coupons,
            'destination'     => array(
                'country'   => WC()->customer->get_shipping_country(),
                'state'     => WC()->customer->get_shipping_state(),
                'postcode'  => WC()->customer->get_shipping_postcode(),
                'city'      => WC()->customer->get_shipping_city(),
                'address'   => WC()->customer->get_shipping_address(),
                'address_2' => WC()->customer->get_shipping_address_2()
            )
        );
    }

    return $packages;
}

Please note that you need to use the free shipping method slug at line 14. And the final result in the cart is the following:

cart

Shipping packages filtered in cart

That’s it! I hope that will save you some headaches! If you enjoyed this post, make sure you [share it on Twitter](http://www.twitter.com/share?text=WooCommerce: Free Shipping on a Per Product Basis in the Same Cart by @remicorson&url=https://www.remicorson.com/?p=6949)!

Similar posts by awesome folks: