WooCommerce – Hide shipping rates when free shipping is available.

* Hide shipping rates when free shipping is available.
* Updated to support WooCommerce 2.6 Shipping Zones.
* @param array $rates Array of rates found for the package.
* @return array
function my_hide_shipping_when_free_is_available( $rates ) {
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
return ! empty( $free ) ? $free : $rates;
add_filter( 'woocommerce_package_rates', 'my_hide_shipping_when_free_is_available', 100 );

view raw
hosted with ❤ by GitHub

WooCommerce – Add all upsells of a product to the cart via custom link

<?php // Do not include this if already open!
* Code goes in theme functions.php.
* Product must have upsells, and this works with simple products only.
* Example link: yoursite.com?add-upsells-to-cart=X, X being your product ID.
add_action( 'wp_loaded', 'bulk_upsell_add_to_cart_action', 20 );
function bulk_upsell_add_to_cart_action() {
if ( ! empty( $_GET['add-upsells-to-cart'] ) ) {
$product_id = absint( $_GET['add-upsells-to-cart'] );
$product = wc_get_product( $product_id );
if ( $product ) {
$upsell_ids = $product->get_upsells();
if ( $upsell_ids ) {
$count = 0;
foreach ( $upsell_ids as $upsell_id ) {
if ( WC()->cart->add_to_cart( $upsell_id ) ) {
$count ++;
wc_add_notice( sprintf( _n( 'Added %d item to the cart', 'Added %d items to the cart', $count ), $count ) );

view raw
hosted with ❤ by GitHub

WooCommerce – enable free shipping only if product class is found in cart.

<?php // Do not include this if already open!
* Code goes in theme functions.php. Free Shipping Method must be enabled.
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'free_shipping_based_on_cart_shipping_class' );
function free_shipping_based_on_cart_shipping_class( $is_available ) {
* This example enables free shipping only when an item is found in the cart with a class named 'free_shipping'
$cart_items = WC()->cart->get_cart();
$found = false;
foreach ( $cart_items as $cart_item ) {
$product = $cart_item['data'];
$class = $product->get_shipping_class();
if ( 'free_shipping' === $class ) {
$found = true;
return $is_available && $found;

view raw
hosted with ❤ by GitHub

WooCommerce – Remove product data tabs and hook content in sequence instead

<?php // Do not include this if already open!
* Remove existing tabs from single product pages.
function remove_woocommerce_product_tabs( $tabs ) {
unset( $tabs['description'] );
unset( $tabs['reviews'] );
unset( $tabs['additional_information'] );
return $tabs;
add_filter( 'woocommerce_product_tabs', 'remove_woocommerce_product_tabs', 98 );
* Hook in each tabs callback function after single content.
add_action( 'woocommerce_after_single_product_summary', 'woocommerce_product_description_tab' );
add_action( 'woocommerce_after_single_product_summary', 'woocommerce_product_additional_information_tab' );
add_action( 'woocommerce_after_single_product_summary', 'comments_template' );

view raw
hosted with ❤ by GitHub

Showing quantity inputs in WooCommerce loops

Here is a quick snippet showing how you can add quantity inputs to WooCommerce loops for simple products. This used to be only possible through template edits, but is now doable through filters because they are magic.

* Code should be placed in your theme functions.php file.
add_filter( 'woocommerce_loop_add_to_cart_link', 'quantity_inputs_for_woocommerce_loop_add_to_cart_link', 10, 2 );
function quantity_inputs_for_woocommerce_loop_add_to_cart_link( $html, $product ) {
if ( $product && $product->is_type( 'simple' ) && $product->is_purchasable() && $product->is_in_stock() && ! $product->is_sold_individually() ) {
$html = '<form action="' . esc_url( $product->add_to_cart_url() ) . '" class="cart" method="post" enctype="multipart/form-data">';
$html .= woocommerce_quantity_input( array(), $product, false );
$html .= '<button type="submit" class="button alt">' . esc_html( $product->add_to_cart_text() ) . '</button>';
$html .= '</form>';
return $html;

view raw
hosted with ❤ by GitHub

The end result:

2016-04-21 at 18.21.png

Github to WordPress.org deploy script

Today I polished, documented, and open sourced a bash script I’ve been using to do plugin deployments from Github to WordPress.org (plus I wanted an excuse to use the new WordPress.com desktop app which is awesome by the way). It’s based on code Barry Kooij sent me a while back. You can find it here:


It handles Github tagging, removing junk files, and SVN tagging with minimal effort. It also has a quick checklist to stop you forgetting to set things in the readme.txt like the stable version (I’ve been guilty of forgetting this in the past).

Once downloaded to your machine, the script requires a Github access token, and a few edits to tell it which plugin you’re deploying.

After you’ve added the details specific to your project, you simply need to open it’s parent directory in terminal, and run the command:

sh release.sh

It then prompts you for a version, branch, and handles the deployment process.


The above demo is from a release of WP Job Manager I did today showing it working nicely. Hope it’s useful!

Debugging “unexpected token” in WooCommerce 2.4+

When the WooCommerce checkout is processed it requires that gateways return an array (which is then converted into JSON) telling the checkout whether or not it was successful. It’s been this way since v1. Here is a basic example from the PayPal gateway:

return array(
    'result'   => 'success',
    'redirect' => $paypal_request->get_request_url( $order, $this->testmode )

The above for example returns a successful response and tells the checkout to redirect the user to the URL provided.

In 2.4+ this was made more strict in that the response must be valid JSON. Anything else in the response (such as HTML, notices or whitespace) will invalidate this JSON and you may see an error along the lines of:

SyntaxError: Unexpected token

WooThemes own gateways have all been tested for 2.4, however, we’ve seen some less-supported gateways break due to returning invalid content.

I’ve seen a few vocal users complaining about this recently on the forums (some even feeling the need to attack me personally, thanks guys) and thought it best to post a few steps on how you can narrow down the issue.

First off its always best to rule out your plugins and theme by disabling them, even though a lot of users seem to ignore this step. It is only temporary and helps narrow down the issue. Much better than shouting at me on WordPress.org.

Lets assume this does not narrow down the issue for a moment. How would you know if content is being output? The answer is in your browser console.

Lets simulate an error. In the paypal gateway, just before the array is returned I’ll add:

trigger_error( 'Uh oh' );

This triggers the error as expected:

2015-11-12 at 16.29

Now lets try to debug this like someone who does not know where the error is coming from.

In Chrome, go to View > Developer > Javascript Console whilst viewing the checkout.

Now trigger the error again.

After the error is shown, in the console go to the Network tab and click XHR:

2015-11-12 at 16.31

In the list you’re see one which looks like this: ?wc-ajax=checkout. Click on that and go to the response tab:

2015-11-12 at 16.32

Notice in my above example, there is the notice. This invalidates the JSON.

A valid JSON response {starts and ends with a bracket} – if there is an error, white space or content before or after those brackets, that is your issue.

In some cases the error will be obvious and allow you to patch or disable the cause.

Hopefully that helps.

So I spoke at WordCamp Netherlands…

Last week I was lucky enough to attend WordCamp Netherlands in Utrecht, followed by a small WC team meetup in Amsterdam. It was great to talk about work, plan for the future, and meet new and old friends.

(Some of) Team WooCommerce in NL

Rather than just attend the WordCamp I was actually a speaker. I presented a talk on user onboarding for plugins, using WooCommerce and WP Job Manager as examples and explaining the benefits and importance of a good first time experience.

Big crowd. Yikes!

Being a bit of a sociophobe and as a first time speaker (!) I was extremely nervous. I’m still not quite sure how I managed to get the courage to do it, but a huge thanks to Barry for the encouragement, advice and support. 

Although I muddled my words somewhat, I think it went reasonably okay. You can watch my presentation below.

I’d love your feedback (both good and bad) if you watched my talk so I can improve in the future, assuming I ever talk again 🙂 Was it helpful? Did everything make sense? Did I waste 30 mins of your life?

Anyhow, thanks to all of those who attended!