Cart Fee & Discount Generator
Conditional surcharges and negative-fee discounts on woocommerce_cart_calculate_fees — with a sample cart to prove the arithmetic before it reaches a shopper.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: Cart fees and discounts * Description: Conditional fees and negative-fee discounts on the WooCommerce cart. * Version: 1.0.0 * Requires PHP: 7.4 * Requires Plugins: woocommerce * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * Whether the cart holds a product in one of the categories. * * @param WC_Cart $cart Cart object. * @param array $slugs product_cat slugs. * @return bool */ function acme_cart_has_category( $cart, $slugs ) { foreach ( $cart->get_cart() as $item ) { if ( has_term( $slugs, 'product_cat', $item['product_id'] ) ) { return true; } } return false; } /** * Add the cart fees and discounts. * * @param WC_Cart $cart Cart object, passed by reference. */ function acme_add_cart_fees( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { return; } if ( ! $cart instanceof WC_Cart || $cart->is_empty() ) { return; } $subtotal = (float) $cart->get_subtotal(); $items = $cart->get_cart_contents_count(); $payment = (string) WC()->session->get( 'chosen_payment_method' ); $decimals = wc_get_price_decimals(); // Handling fee — 3.5% of get_subtotal(). $amount = round( $subtotal * ( 3.5 / 100 ), $decimals ); $amount = min( $amount, 12.00 ); if ( $amount > 0 && $subtotal >= 25.00 ) { $cart->add_fee( __( 'Handling fee', 'acme' ), $amount, true, '' ); } // Oversized item surcharge — 4.00 per item in the cart. $amount = round( 4.00 * $items, $decimals ); if ( $amount > 0 && acme_cart_has_category( $cart, array( 'oversized' ) ) ) { $cart->add_fee( __( 'Oversized item surcharge', 'acme' ), $amount, true, '' ); } // Cash on delivery fee — a flat 2.50. $amount = 2.50; if ( $amount > 0 && in_array( $payment, array( 'cod' ), true ) ) { $cart->add_fee( __( 'Cash on delivery fee', 'acme' ), $amount, false ); } } add_action( 'woocommerce_cart_calculate_fees', 'acme_add_cart_fees' ); /** * Recalculate the checkout totals when the shopper switches payment method. */ function acme_refresh_checkout_on_payment_change() { if ( ! is_checkout() ) { return; } wc_enqueue_js( "jQuery( document.body ).on( 'change', 'input[name=payment_method]', function() { jQuery( document.body ).trigger( 'update_checkout' ); } );" ); } add_action( 'wp_footer', 'acme_refresh_checkout_on_payment_change' );
About this generator
Cart Fees & Discount Generator Online
Build the WooCommerce cart fee you actually need — a cash-on-delivery surcharge, a weight-based handling charge, a wholesale-only discount — and copy it out as clean, commented PHP. Every rule becomes a real condition on woocommerce_cart_calculate_fees, so the file you download is the file that runs.
The sample cart above recalculates as you type: change the subtotal, apply a coupon, switch the payment gateway, and watch the WooCommerce totals box respond exactly as it would at checkout. Free to use, no account, and nothing you enter leaves the browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why the Cart Fees Generator beats a generic fee snippet
Most "add a cart fee" snippets on the web are one hard-coded add_fee() call with a comment telling you to change the number. That holds up until the fee needs to apply only to cash on delivery, only above £100, only in the UK — or until a taxable negative fee quietly distorts your VAT reporting. This generator writes the conditions together with the fee, then audits the result against the mistakes that cost the most time to find in production.
Conditions that actually compile
Minimum and maximum subtotal, item count, cart weight, destination country, shipping method, payment gateway, user role and product category each emit a real guard clause — never a // TODO comment for you to finish.
Only the helpers you use
shipping_method_matches(), user_has_role() and cart_has_category() are written into the file only when one of your rules needs them, so there is no dead code to justify in review.
A sample cart that does the arithmetic
Set a subtotal, coupon, item count, weight, country and gateway, then see which rules fire, what each one computes to, and the reason every skipped rule was skipped.
Seven costly traps checked for you
Duplicate fee ids from sanitize_title() collisions, gateway conditions with no checkout refresh, taxable negative fees, uncapped percentages, tax-inclusive basis on a taxable fee, a missing is_admin() guard and impossible min/max ranges. Most are one click to fix.
A percentage basis you choose deliberately
Take percentages from get_subtotal() before coupons, get_cart_contents_total() after coupons, or the subtotal including tax — the choice is named in the generated code so the next developer knows what you meant.
Honest advice about discounts
Negative fees work, and the generator writes them — but it also tells you when a coupon is the better answer for a shopper-visible discount, and can append the programmatic coupon alternative alongside.
How does the Cart Fees Generator work?
Four steps, no PHP required until you paste the result. Say what the fee is, say when it applies, prove it against a sample cart, then export.
- 01
Set the basics
Pick a function prefix and text domain, then decide where percentages are measured from and whether country rules read the shipping or billing address.
- 02
Describe the fee
Name it, then choose a flat amount, a percentage of the cart, an amount per item or per unit of weight. Cap it, make it taxable, or flip it to a discount.
- 03
Add the conditions
Attach as many conditions as the rule needs. Each one is checked live against the sample cart, so you can see the rule turn on and off as you tune the numbers.
- 04
Review, then export
Clear the flagged errors and warnings, then copy the snippet or download the
.phpfile as a plugin, afunctions.phpblock or a bare snippet.
Worked example — £4.90 cash-on-delivery fee, waived over £100
One rule, two conditions: gateway is cod and subtotal is at most 100. This is the whole output, formatted exactly as it downloads.
add_action( 'woocommerce_cart_calculate_fees', 'acme_cart_fees', 20 );
function acme_cart_fees( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
if ( ! $cart instanceof WC_Cart || $cart->is_empty() ) {
return;
}
$subtotal = (float) $cart->get_subtotal();
$payment = (string) WC()->session->get( 'chosen_payment_method' );
// Cash on delivery — a flat 4.90.
$amount = 4.90;
if ( $subtotal <= 100.00 && in_array( $payment, array( 'cod' ), true ) ) {
$cart->add_fee( __( 'Cash on delivery', 'acme' ), $amount, false );
}
}Because this fee depends on the chosen gateway, the generator also emits the one line of jQuery that fires update_checkout when the shopper switches payment method — without it WooCommerce shows a stale total until the page reloads.
Cart fees & discounts — frequently asked questions
The questions that come up most often when adding conditional fees to a WooCommerce store.
Can I charge a cart fee only for one payment method?
Yes — add a gateway condition to the rule and the generator reads WC()->session->get( 'chosen_payment_method' ) for you. It also emits the small script that triggers update_checkout when the shopper switches method, which is the step most snippets leave out.
How do I create a discount rather than a surcharge?
Switch the rule to a discount and the amount is passed to add_fee() as a negative number. It works, but a coupon is usually the better answer when the shopper should see a named saving — the generator says so, and can output the programmatic coupon alternative next to your code.
Are WooCommerce cart fees taxable?
Only if you say so. The third argument of add_fee() controls it and defaults to false in the generated code. Marking a negative fee as taxable is flagged as a warning, because a taxable discount distorts the tax lines on the order.
Where should I paste the generated PHP?
A child theme's functions.php, a code snippets plugin, or the standalone plugin file the generator can produce. Never the parent theme — an update will erase it. Pick the matching output mode before you copy.
Why doesn't the fee update until the page reloads?
Checkout only recalculates when something asks it to. Fees that depend on the gateway, the shipping method or the address need an update_checkout trigger; without one WooCommerce keeps showing the total it last calculated.
Can I add several fees at once?
As many as you need — each rule becomes its own add_fee() call with its own conditions. Give every fee a distinct name: WooCommerce derives the fee id from the name, so two similar names can collide and silently drop one of the fees. The generator checks for that.
Where do I paste the code from the Cart Fee & Discount generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other WooCommerce generators in the library, plus the WordPress tools most often used alongside this one.