WooCommerce Hooks and Filters: Australian Store Examples
WooCommerce hooks let you change how a store behaves without editing WooCommerce itself: an action runs your code at a set moment, and a filter lets you change a value before WooCommerce uses it. Below are three changes Australian stores often ask for (an ABN field at checkout, GST wording on prices, and shipping rules by postcode), with the setting to try first, the code when settings are not enough, and how to keep the code safe through updates.
Before you add any code
Put it in a small plugin, not the parent theme
Code added to your theme’s functions.php is lost when the theme updates, and it disappears if you change theme. A small plugin of your own keeps store changes separate and survives both. Create a folder such as wp-content/plugins/my-store-tweaks/ with one file, my-store-tweaks.php, that starts like this:
<?php
/
* Plugin Name: My Store Tweaks
* Description: Small WooCommerce changes for this store.
* Version: 1.0.0
*/
defined( 'ABSPATH' ) || exit;
// Tell WooCommerce this plugin works with HPOS and the Cart and Checkout blocks.
add_action( 'before_woocommerce_init', function () {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', FILE, true );
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', FILE, true );
}
} );
Then activate it under Plugins. If you already use a child theme, its functions.php also works, but a plugin is easier to switch off when something goes wrong.
Test on staging first
A typo in a PHP file can take the whole site down, and a checkout change can stop orders. Make every change on a staging copy, place real test orders through every payment and shipping method you offer, then copy the plugin to the live site. Take a backup before you do.
Know which checkout you have
WooCommerce has two checkouts, and they are customised differently. Edit your Checkout page: if it contains the Checkout block, you have the block checkout, which new stores get by default. If it contains the [woocommerce_checkout] shortcode, you have the classic checkout. Many older snippets online only work with the classic one, which is why “I added the code and nothing happened” is so common.
Respect HPOS
Since WooCommerce 8.2, new stores keep orders in their own database tables, called High Performance Order Storage (HPOS), instead of the WordPress posts table. You can see which your store uses under WooCommerce > Settings > Advanced > Features. Code that reads or writes order data with get_post_meta() or update_post_meta() will not work reliably under HPOS. Use the order object instead: $order->get_meta(), $order->update_meta_data() then $order->save(), and wc_get_orders() to find orders. The WooCommerce HPOS documentation has the details. All the snippets below use the order object.
Example 1: Add an ABN field at checkout
Business customers often need their ABN on the tax invoice. Both versions below use the same check, which follows the ABN format published by the Australian Business Register: an ABN has 11 digits, and a weighted sum of them must divide evenly by 89. The ABR’s ABN format page explains the method. Add this function to your plugin first:
function mystore_abn_is_valid( $abn ) {
$abn = preg_replace( '/\s+/', '', (string) $abn );
if ( ! preg_match( '/^\d{11}$/', $abn ) ) {
return false;
}
$weights = array( 10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 );
$sum = 0;
foreach ( str_split( $abn ) as $i => $digit ) {
$digit = (int) $digit;
if ( 0 === $i ) {
$digit -= 1;
}
$sum += $digit * $weights[ $i ];
}
return 0 === $sum % 89;
}
This only checks that the number is well formed. It does not confirm the ABN is active or belongs to the customer; that needs a lookup against the ABR, which is a bigger job.
Block checkout version
The block checkout has its own Additional Checkout Fields API. This registers an optional ABN field in the contact section, which WooCommerce saves to the order and to the customer’s account, and validates it when the order is placed:
add_action( 'woocommerce_init', function () {
if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
return;
}
woocommerce_register_additional_checkout_field(
array(
'id' => 'mystore/abn',
'label' => 'ABN',
'optionalLabel' => 'ABN (optional)',
'location' => 'contact',
'type' => 'text',
'required' => false,
'attributes' => array(
'autocomplete' => 'off',
'maxLength' => 14,
),
)
);
} );
add_action( 'woocommerce_validate_additional_field', function ( WP_Error $errors, $field_key, $field_value ) {
if ( 'mystore/abn' === $field_key && '' !== trim( (string) $field_value ) && ! mystore_abn_is_valid( $field_value ) ) {
$errors->add( 'invalid_abn', 'Please enter a valid 11 digit ABN, or leave the field empty.' );
}
}, 10, 3 );
The Additional Checkout Fields documentation lists the other locations (address and order) and field types. After a test order, check that the ABN appears on the order screen and in the emails your bookkeeper relies on.
Classic checkout version
On the shortcode checkout, add the field to the billing fields. Fields whose names start with billing_ are saved to the order automatically, so the ABN ends up in the order’s _billing_abn meta:
add_filter( 'woocommerce_billing_fields', function ( $fields ) {
$fields['billing_abn'] = array(
'label' => 'ABN',
'required' => false,
'class' => array( 'form-row-wide' ),
'priority' => 35,
);
return $fields;
} );
add_action( 'woocommerce_after_checkout_validation', function ( $data, $errors ) {
if ( ! empty( $data['billing_abn'] ) && ! mystore_abn_is_valid( $data['billing_abn'] ) ) {
$errors->add( 'validation', 'Please enter a valid 11 digit ABN, or leave the field empty.' );
}
}, 10, 2 );
// Show the ABN on the order screen in wp-admin.
add_action( 'woocommerce_admin_order_data_after_billing_address', function ( $order ) {
$abn = $order->get_meta( '_billing_abn' );
if ( $abn ) {
echo '<p><strong>ABN:</strong> ' . esc_html( $abn ) . '</p>';
}
} );
A priority of 35 places the field just after the company name. Change it to move the field up or down.
Example 2: GST wording on prices
Start with the settings, because most stores need no code here. Under WooCommerce > Settings > General, tick “Enable tax rates and calculations”. Then under WooCommerce > Settings > Tax:
- Choose whether you enter prices inclusive of tax. Most Australian stores enter prices including GST, because that is the price customers expect to see.
- Set “Display prices in the shop” and “Display prices during cart and checkout” to including tax.
- Add a Price display suffix such as
inc. GST. It shows after every price. The suffix also accepts{price_excluding_tax}if trade customers want to see the ex GST figure. - In the Standard rates table, add a rate for country
AUat 10%, name itGST, and tick Shipping if your delivery charges include GST.
Code earns its place when the wording must differ by product. If you sell some GST free items (for example some basic foods) and set their tax status to None, this filter shows “GST free” on those items instead of the general suffix:
add_filter( 'woocommerce_get_price_suffix', function ( $suffix, $product ) {
if ( wc_tax_enabled() && 'none' === $product->get_tax_status() ) {
return ' <small class="woocommerce-price-suffix">GST free</small>';
}
return $suffix;
}, 10, 2 );
Only use wording like this if it is true for your business: whether an item is GST free is set by the ATO’s rules, not by WooCommerce. If you are not registered for GST, do not show GST wording at all. Our page on getting WooCommerce GST set up correctly covers the settings in more depth.
Example 3: Shipping rules by postcode
Again, try the built-in feature first. Shipping zones can match postcodes, ranges and wildcards. Under WooCommerce > Settings > Shipping, add a zone, choose Australia as the region, and limit it to specific postcodes: one per line, a range such as 3000...3207, or a wildcard such as 30*. Then add the methods that zone should offer. WooCommerce uses the first zone that matches, so put the most specific zones at the top.
Use code when the rule cannot be expressed as a zone, for example “free shipping is only for our local delivery area, but every other method is offered everywhere”. This filter removes Free shipping from the list when the delivery postcode is outside a range:
add_filter( 'woocommerce_package_rates', function ( $rates, $package ) {
$country = isset( $package['destination']['country'] ) ? $package['destination']['country'] : '';
$postcode = isset( $package['destination']['postcode'] ) ? (int) preg_replace( '/\D/', '', $package['destination']['postcode'] ) : 0;
if ( 'AU' !== $country ) {
return $rates;
}
// Example range only. Replace with the postcodes you actually deliver to.
$in_area = ( $postcode >= 3000 && $postcode <= 3207 );
if ( ! $in_area ) {
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->get_method_id() ) {
unset( $rates[ $rate_id ] );
}
}
}
return $rates;
}, 10, 2 );
This filter runs for both the classic and the block checkout. WooCommerce caches shipping rates per cart, so while you test, turn on shipping debug mode in the shipping settings, which bypasses the cache and shows which zone matched. Turn it off before going live.
If the problem is that live Australia Post prices are not showing at all, that is usually a settings or connection fault rather than something a hook fixes; see Australia Post rates not showing at checkout.
When hooks are not the right tool
Hooks are ideal for small, contained changes. Connecting the store to another system, such as an accounting package, a warehouse or a CRM, is a different kind of job: it needs error handling, retries and a way to see when a sync fails. For accounts, see our page on connecting WooCommerce to MYOB. Before writing code for anything bigger, check whether WooCommerce or a well maintained extension already does it, because every custom snippet is code someone has to keep working through future updates.
If you would rather have it built and tested for you, we do custom WooCommerce work for Australian stores, from a single checkout field to full integrations.
Frequently asked questions
Why does my checkout snippet do nothing?
Most often the store uses the Checkout block, and the snippet was written for the classic shortcode checkout. Hooks such as woocommerce_checkout_fields and woocommerce_after_order_notes do not change the block checkout. Use the Additional Checkout Fields API instead, or check which checkout your Checkout page contains.
Where should I put custom WooCommerce code?
In a small site-specific plugin, or in a child theme’s functions.php. Never in the parent theme or in WooCommerce’s own files, because updates overwrite them.
What is HPOS and does it affect my code?
High Performance Order Storage keeps orders in dedicated database tables instead of the posts table, and it is the default for new stores since WooCommerce 8.2. Code that uses get_post_meta() or direct database queries on orders can break. Use the WooCommerce order object and wc_get_orders(), and declare HPOS compatibility in your plugin.
Can WooCommerce check that an ABN is real?
Not on its own. The snippet above checks that the number is correctly formed using the ABR’s published method. Confirming that an ABN is active and matches the business name needs a lookup against the Australian Business Register.
Be the first to comment