Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Fees, discounts and the price in the cart

WooCommerce recalculates the cart total itself on every change. You can step into that calculation in two places: add a separate line — a fee or a discount — or change the price of the item itself. Both hooks work in the classic and in the block cart.

The code of this lesson goes into includes/cart.php.

A fee

// wp-content/plugins/acme-shop/includes/cart.php
add_action('woocommerce_cart_calculate_fees', function (WC_Cart $cart) {
    if ($cart->get_subtotal() < 20) {
        $cart->add_fee('Small order', 3.00);
    }
});

The hook fires on every recalculation and receives the cart. add_fee() adds a line with a name and an amount. Put one mug for €9.90 into the cart and open /cart/: the totals block shows the line Small order — €3.00. Add two more and the line disappears.

A discount

A fee with a negative amount works as a discount:

// wp-content/plugins/acme-shop/includes/cart.php
add_action('woocommerce_cart_calculate_fees', function (WC_Cart $cart) {
    if (is_user_logged_in() && $cart->get_subtotal() >= 50) {
        $cart->add_fee('Loyal customer discount', -5.00);
    }
});

A logged-in customer with a cart of €50 or more sees a line with a minus sign, and the total drops by €5.

add_fee parameters

ArgumentWhat it sets
$nameThe line name in the totals
$amountThe amount, negative for a discount
$taxableWhether it is taxed, false by default
$tax_classThe tax class for the fee

Fees are not stored in the cart: they are added again on every recalculation, so the condition is always checked against the current state.

The item price

To change the price of the product itself in the cart, a different hook is used — it fires before the totals are counted:

// wp-content/plugins/acme-shop/includes/cart.php
add_action('woocommerce_before_calculate_totals', function (WC_Cart $cart) {
    foreach ($cart->get_cart() as $item) {
        if ($item['quantity'] >= 10) {
            $regular = (float) $item['data']->get_regular_price();
 
            $item['data']->set_price($regular * 0.9);
        }
    }
});

Set the mug quantity in the cart to 10: the unit price becomes €8.91 instead of €9.90. Only the product object in the cart changes — save() is not called, and the catalog price stays the same.

What to choose

TaskWay
A surcharge for packaging, a small orderadd_fee() with a positive amount
A discount on the whole orderadd_fee() with a negative amount or a coupon
A bulk unit priceset_price() in woocommerce_before_calculate_totals
A discount by codeA coupon — there is a separate lesson on them

The price is taken from the regular one

In the example the new price is computed from get_regular_price(), not from get_price(). The hook may fire several times in one request, and computing from the current price would give a discount on a discount: 9.90 → 8.91 → 8.02.

Common mistake

A fee is added once — for example, in the add-to-cart hook. On the next page it is gone, because fees live only within one recalculation. The add_fee() call must sit in woocommerce_cart_calculate_fees, with the condition checked inside it.

Thanks for reading!

Donate to the site — we work hard for you.