Before we dive in this week’s function, please note that it’s usually against any payment provider’s Terms of Service (like PayPal’s) to add fees to a transaction based on the customer’s chosen payment gateway, so please make sure to use “cart fees” in a legal way.
You got it — in this issue we’ll study the add_fee
WooCommerce function, which indeed gives us the power to add custom fees to orders at checkout based on any criteria you define.
As usual, we’ll study the WooCommerce core function code, see where and why it’s used, and finally we’ll cover a quick case study. Enjoy!
Function code
The add_fee
function can be found under \woocommerce\includes\class-wc-cart-fees.php
:
/**
* Add a fee. Fee IDs must be unique.
*
* @since 3.2.0
* @param array $args Array of fee properties.
* @return object Either a fee object if added, or a WP_Error if it failed.
*/
public function add_fee( $args = array() ) {
$fee_props = (object) wp_parse_args( $args, $this->default_fee_props );
$fee_props->name = $fee_props->name ? $fee_props->name : __( 'Fee', 'woocommerce' );
$fee_props->tax_class = in_array( $fee_props->tax_class, array_merge( WC_Tax::get_tax_classes(), WC_Tax::get_tax_class_slugs() ), true ) ? $fee_props->tax_class : '';
$fee_props->taxable = wc_string_to_bool( $fee_props->taxable );
$fee_props->amount = wc_format_decimal( $fee_props->amount );
if ( empty( $fee_props->id ) ) {
$fee_props->id = $this->generate_id( $fee_props );
}
if ( array_key_exists( $fee_props->id, $this->fees ) ) {
return new WP_Error( 'fee_exists', __( 'Fee has already been added.', 'woocommerce' ) );
}
$this->fees[ $fee_props->id ] = $fee_props;
return $this->fees[ $fee_props->id ];
}
The function accepts an array of arguments (name, tax_class, taxable, amount)
. If not specified, default_fee_props
holds their default values:
private $default_fee_props = array(
'id' => '',
'name' => '',
'tax_class' => '',
'taxable' => false,
'amount' => 0,
'total' => 0,
);
This statement creates a unique fee id…
if ( empty( $fee_props->id ) ) {
$fee_props->id = $this->generate_id( $fee_props );
}
…as generate_id()
creates an ID out of the fee name:
private function generate_id( $fee ) {
return sanitize_title( $fee->name );
}
Basically, as long as fees have a different name, they will all work. Otherwise, the “Fee has already been added” error will be thrown.
Function usage
Now that we know how add_fee()
works, it’s time to look at how it’s used by WooCommerce.
If we do a file search for this function we get just one result under woocommerce\includes\class-wc-cart.php
:
/**
* Add additional fee to the cart.
*
* This method should be called on a callback attached to the
* woocommerce_cart_calculate_fees action during cart/checkout. Fees do not
* persist.
*
* @uses WC_Cart_Fees::add_fee
* @param string $name Unique name for the fee. Multiple fees of the same name cannot be added.
* @param float $amount Fee amount (do not enter negative amounts).
* @param bool $taxable Is the fee taxable? (default: false).
* @param string $tax_class The tax class for the fee if taxable. A blank string is standard tax class. (default: '').
*/
public function add_fee( $name, $amount, $taxable = false, $tax_class = '' ) {
$this->fees_api()->add_fee(
array(
'name' => $name,
'amount' => (float) $amount,
'taxable' => $taxable,
'tax_class' => $tax_class,
)
);
}
The statement that calls our function is $this->fees_api()->add_fee()
because in fact it calls the cart fees API and passes the four arguments we mentioned earlier: name
, amount
, taxable
, and tax_class
.
Curiously, this cart function is called add_fee()
as well, which is somewhat confusing.
In a nutshell:
- Call
add_fee()
from the cart. add_fee()
callsadd_fee()
from the fees API.- Fee information is returned and the fee is added to the cart.
TLDR: to add a fee to Cart, simply use:
WC()->cart->add_fee()
…or if you have access to the cart object:
$cart->add_fee()
Inside the function call, you also must add at least the name and amount, because $taxable
is false by default and $tax_class
is none.
Let’s say you want to add a cart fee of $5.00 called “Handling Fee.” You’d do this:
WC()->cart->add_fee( 'Handling Fee', 5 );
But “when” should we add this fee? Well, the function description tells us:
This method should be called on a callback attached to the
woocommerce_cart_calculate_fees
action during cart checkout.
Better coded than said:
/**
* @snippet Add fee to WooCommerce Cart
* @how-to Get CustomizeWoo.com FREE
* @author Rodolfo Melogli
* @compatible WooCommerce 6
* @donate $9 https://businessbloomer.com/bloomer-armada/
*/
add_action( 'woocommerce_cart_calculate_fees', 'bbloomer_add_handling_fee' );
function bbloomer_add_handling_fee( $cart ) {
$cart->add_fee( 'Handling Fee', 5 );
}
Screenshot of the results:
The fact that we have $cart
object available allows us to do conditional work and only show the fee when certain criteria is met.
This is what we’ll do in the next section.
Case studies
Let’s say we want to add a fee only if the order contains physical products.
A “physical” cart requires shipping, unlike virtual products. And there’s a cool WooCommerce function called needs_shipping()
that can help us:
/**
* @snippet Add fee to WooCommerce Cart if shipping needed
* @how-to Get CustomizeWoo.com FREE
* @author Rodolfo Melogli
* @compatible WooCommerce 6
* @donate $9 https://businessbloomer.com/bloomer-armada/
*/
add_action( 'woocommerce_cart_calculate_fees', 'bbloomer_add_handling_fee_physical' );
function bbloomer_add_handling_fee_physical( $cart ) {
if ( ! $cart->needs_shipping() ) return;
$cart->add_fee( 'Handling Fee', 5 );
}
Alternatively, let’s decide that the fee applies only if a certain product ID is in the cart:
/**
* @snippet Add fee to WooCommerce Cart if product ID
* @how-to Get CustomizeWoo.com FREE
* @author Rodolfo Melogli
* @compatible WooCommerce 6
* @donate $9 https://businessbloomer.com/bloomer-armada/
*/
add_action( 'woocommerce_cart_calculate_fees', 'bbloomer_add_handling_fee_product' );
function bbloomer_add_handling_fee_product( $cart ) {
$product_id = 123;
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
if ( ! $in_cart ) return;
$cart->add_fee( 'Handling Fee', 5 );
}
As I said at the beginning, you could also add a fee for your payment gateway, but check their terms and conditions before doing so.
great function, i use this for adding a custom fee depending on gateway chosen
Nice!