Snippets / WooCommerce

WooCommerce Checkout Notice — Conditional Cart Total Alert

Display a notice on the WooCommerce checkout page when the cart total meets a threshold — free shipping eligibility, bulk discount qualification, or minimum order requirements — using WooCommerce action hooks and conditional logic.

PHP utility Production-tested ~5 min setup Checkout Intermediate WP 6.0+ · WC 7.0+ Updated May 15, 2026
TL;DR

This WooCommerce checkout notice PHP snippet displays conditional messages on checkout when the cart total meets a threshold, using WooCommerce hooks.

Use this when you want to nudge customers toward a higher cart value — "add $10 for free shipping", "you're $5 away from a bulk discount" — without installing a marketing plugin. One hook, one file, zero database queries on every request.

01 — Building it step by step

WooCommerce checkout notice — The Code

This WooCommerce checkout notice snippet shows a notice at checkout when the cart total meets a threshold you define.

We’ll build this in 5 progressive steps. Each step adds exactly one new concept — static output, conditional logic, multi-tier thresholds, dismiss persistence, and production polish. Every step is a complete, working checkpoint you can stop at and test. No prior WooCommerce hook experience assumed.

Step 1: A static notice. The woocommerce_before_checkout_form action fires right above the checkout form — before the billing fields, coupon box, or order review. Any HTML you echo here sits above the form, visible to every customer regardless of what’s in their cart. At this point there’s no conditional logic, no threshold — just raw output to prove the hook works.

Step 1 — Static notice.php
php
// WooCommerce checkout notice — production-tested PHP snippet by Mosharaf Hossain
<?php
/**
 * Step 1 — A static notice displayed on checkout.
 * No conditions. Every visitor sees this message regardless of cart total.
 */
add_action( 'woocommerce_before_checkout_form', function() {
    echo '<div class="checkout-notice">';
    echo '<p>Free shipping on orders over $50!</p>';
    echo '</div>';
} );

Step 2: Conditional on cart total. WC()->cart->get_total() returns the numeric cart total after all discounts and coupons — no need to loop through cart items yourself. Cast it to (float) for reliable arithmetic comparisons. If the cart is below your threshold, calculate the gap with simple subtraction and format it with wc_price(). If the cart already meets the threshold, return early and render nothing — the user doesn’t need the nudge.

Step 2 — Conditional on cart total.php
php
// WooCommerce checkout notice
<?php
/**
 * Step 2 — Show the notice only below a cart threshold.
 *
 * New: WC()->cart->get_total() returns the cart total after
 * discounts as a float. Compare it against your threshold
 * before output. wc_price() formats the remaining amount.
 */
add_action( 'woocommerce_before_checkout_form', function() {
    $threshold  = 50;
    $cart_total = (float) WC()->cart->get_total();

    if ( $cart_total >= $threshold ) {
        return;
    }

    $remaining = wc_price( $threshold - $cart_total );
    printf(
        '<div class="checkout-notice"><p>Add %s more for free shipping!</p></div>',
        $remaining
    );
} );

Step 3: Multiple threshold tiers. A single threshold covers one incentive. Real stores often have staggered rewards: a bulk discount at $25, free shipping at $50, and an upsell at $75. The if / elseif / else ladder checks each tier from lowest to highest and builds a message specific to that tier. The else branch handles the “already qualified” case with a success message instead of a nudge — psychologically different from showing nothing.

Step 3 — Multiple threshold tiers.php
php
// WooCommerce checkout notice
<?php
/**
 * Step 3 — Multiple threshold tiers with different messages.
 *
 * New: Three ranges — below $25 encourages a bulk discount,
 * $25–$50 nudges toward free shipping, $50+ confirms eligibility.
 * Each tier uses its own copy and remaining-amount calculation.
 */
add_action( 'woocommerce_before_checkout_form', function() {
    $cart_total = (float) WC()->cart->get_total();
    $message    = '';

    if ( $cart_total < 25 ) {
        $message = sprintf(
            'Add %s more for a 5%% bulk discount!',
            wc_price( 25 - $cart_total )
        );
    } elseif ( $cart_total < 50 ) {
        $message = sprintf(
            'Add %s more for free shipping!',
            wc_price( 50 - $cart_total )
        );
    } else {
        $message = 'You qualify for free shipping!';
    }

    printf(
        '<div class="checkout-notice"><p>%s</p></div>',
        esc_html( $message )
    );
} );

Step 4: Make it dismissible. A persistent notice that won’t go away frustrates returning customers. A close button (the × entity) sets a browser session cookie via inline JavaScript — no AJAX endpoint needed, no server-side session management. The cookie expires when the browser closes. The PHP guard checks $_COOKIE['noc_dismissed'] before any cart logic runs, so dismissed users skip the entire block without a millisecond of wasted processing.

Step 4 — Dismissible with close button.php
php
// WooCommerce checkout notice
<?php
/**
 * Step 4 — Dismissible notice with a close button.
 *
 * New: A close button sets a session cookie when clicked.
 * The cookie lasts until the browser closes — dismissed users
 * won't see the notice again during this session.
 */
add_action( 'woocommerce_before_checkout_form', function() {
    if ( isset( $_COOKIE['noc_dismissed'] ) ) {
        return;
    }

    $cart_total = (float) WC()->cart->get_total();
    if ( $cart_total >= 50 ) {
        return;
    }

    $remaining = wc_price( 50 - $cart_total );
    ?>
    <div class="checkout-notice" id="checkout-threshold-notice">
        <button class="checkout-notice__close"
                onclick="document.cookie='noc_dismissed=1;path=/';this.parentElement.remove()"
                aria-label="Dismiss">×</button>
        <p>Add <?php echo esc_html( $remaining ); ?> more for free shipping!</p>
    </div>
    <?php
} );

Step 5: Full production version. All concepts combined into one organized file with production refinements: 1) Thresholds are wrapped in apply_filters( 'noc_thresholds', ... ) so site maintainers can adjust amounts without touching this file. 2) Every user-facing string passes through __() or esc_html() so the notice is translatable and safe. 3) The success message (shown when all tiers are met) is also filterable via noc_success_message. 4) The foreach loop replaces the hardcoded if/elseif ladder — add or remove tiers by editing the $tiers array alone.

inc/woo-checkout-notice.php
php
// WooCommerce checkout notice
<?php
/**
 * WooCommerce Conditional Checkout Notice — production version.
 *
 * Save as inc/woo-checkout-notice.php and require from functions.php.
 * Filterable thresholds, translatable strings, proper escaping.
 */
add_action( 'woocommerce_before_checkout_form', function() {
    if ( isset( $_COOKIE['noc_dismissed'] ) ) {
        return;
    }

    $cart_total = (float) WC()->cart->get_total();
    $tiers = apply_filters( 'noc_thresholds', [
        25 => __( 'Add %s more for a 5%% bulk discount!', 'textdomain' ),
        50 => __( 'Add %s more for free shipping!', 'textdomain' ),
    ] );

    foreach ( $tiers as $threshold => $message ) {
        if ( $cart_total < $threshold ) {
            $remaining = wc_price( $threshold - $cart_total );
            printf(
                '<div class="checkout-notice" id="checkout-threshold-notice">'
                . '<button class="checkout-notice__close" onclick="'
                . 'document.cookie='noc_dismissed=1;path=/';'
                . 'this.parentElement.remove()" aria-label="%1$s">'
                . '×</button><p>%2$s</p></div>',
                esc_attr__( 'Dismiss', 'textdomain' ),
                sprintf( $message, $remaining )
            );
            return;
        }
    }

    $top = max( array_keys( $tiers ) );
    if ( $cart_total >= $top ) {
        printf(
            '<div class="checkout-notice checkout-notice--success"><p>%s</p></div>',
            esc_html( apply_filters( 'noc_success_message',
                __( 'You qualify for free shipping!', 'textdomain' )
            ) )
        );
    }
} );

Where to put the code

Save Step 5 in inc/woo-checkout-notice.php and require it from functions.php:

require_once get_template_directory() . '/inc/woo-checkout-notice.php';

There is zero reason to put this in a plugin. This is presentation logic — it describes this site’s messaging, thresholds, and incentive tiers. If you ever switch themes, you want these hooks to stop firing. Keep it in the theme.

02 — How it works

How WooCommerce checkout notice Works — Why Each Layer Exists

Here is how this WooCommerce checkout notice works step by step: Every line in this snippet serves a purpose. Here's why each hook, API call, and design decision was made.

This WooCommerce checkout notice implementation works as follows.

Every line in this snippet serves a purpose. Here’s why each piece was chosen, how the cart-total API works under the hood, and how the dismiss mechanism stays lightweight.

1

Hook: woocommerce_before_checkout_form — the ideal placement

The woocommerce_before_checkout_form action is the ideal placement for a checkout notice. It fires after the checkout page wrapper opens but before the form itself — which means your notice sits above the billing fields, coupon box, and order review table. WooCommerce itself uses this hook for the wc_print_notices() call that renders error and success messages after a failed form submission. Alternative hooks like woocommerce_before_checkout_billing_form or woocommerce_review_order_before_payment would place the notice further down the page where it’s less visible. The top of checkout — before any fields — is where customers read before they start the checkout flow.

2

Cart total API: WC()->cart->get_total() — the single source of truth

WC()->cart->get_total() is WooCommerce’s single-source-of-truth for “what does this order cost?” It returns the cart total after coupons, fees, shipping (if calculated), and taxes (depending on your tax display settings). This is not the subtotal — it’s the final, post-discount number the customer will pay. You don’t need to loop through WC()->cart->get_cart(), sum item totals, subtract coupons, or account for tax configuration. WooCommerce has already done that work. Casting to (float) is important: get_total() returns a string in some WooCommerce versions, and PHP’s loose comparison can produce unexpected results with strings. Using (float) guarantees numeric comparison.

3

Tier-based foreach loop — extensible without branching

The tier-based foreach loop in Step 5 replaces the manual if/elseif ladder from Step 3. The $tiers array maps threshold amounts to translatable message strings — each message uses %s as a placeholder for the formatted remaining amount. The loop iterates from lowest to highest threshold (PHP arrays preserve insertion order). The first tier whose threshold exceeds the cart total wins — the loop returns immediately after output, so only one notice renders. If the loop completes without matching (cart total meets or exceeds every threshold), the “success” message below the loop fires. This design means adding a new tier is a one-line array insertion — no new if branch, no reordering logic.

4

Session cookie dismiss — zero server-side storage

The dismiss mechanism uses a browser session cookie — not WordPress transients, not user meta, not a database table. The choice is deliberate: a) Session cookies require zero server-side storage. The browser holds the flag and sends it on every request. b) Session cookies expire when the browser closes — a customer who dismisses the notice in the morning sees it again in the afternoon if they restart their browser, which is usually desirable for time-sensitive incentives. c) No WordPress user account is needed — unauthenticated visitors can dismiss the notice. The inline onclick handler sets the cookie and immediately removes the notice element from the DOM, providing instant visual feedback without a page reload. The PHP guard checks $_COOKIE['noc_dismissed'] before any cart logic runs, so the nudge logic never executes for dismissed users.

5

Filter hooks — extensibility without editing the core file

Two filter hooks make the notice extensible without editing the core file. apply_filters( 'noc_thresholds', $defaults ) lets site managers, child themes, or companion plugins override the threshold amounts and messages. The filter receives the full associative array — you can add tiers, remove tiers, or change the amounts entirely. apply_filters( 'noc_success_message', $default ) controls the message shown when the cart exceeds all thresholds. A child theme might change it to “Your order ships free!” or a plugin might append a tracking parameter. Both filters follow the standard WordPress pattern: provide a default, let callers override, and run esc_html() on the final value before output.

03 — Setup & integration

WooCommerce checkout notice — Setup & Integration

To integrate this WooCommerce checkout notice into your project: One file, one require, and your checkout page shows a dynamic notice. Here's exactly where everything lives, how to test at different cart totals, and how to style it.

This WooCommerce checkout notice implementation works as follows.

One file, one require, and your checkout page shows a dynamic notice. Here’s exactly where everything lives, how to test at different cart totals, and how to style it.

wp-content/themes/your-theme/
│   ├── inc/
│   │   └── woo-checkout-notice.php
    └── functions.php
1

Create the file and require it from functions.php

Create the file and require it. Copy the Step 5 code into inc/woo-checkout-notice.php. In functions.php, add this line at the bottom:

require_once get_template_directory() . '/inc/woo-checkout-notice.php';

No if ( class_exists( 'WooCommerce' ) ) wrapper is needed. If WooCommerce isn’t active, the woocommerce_before_checkout_form hook never fires and the callback sits silently in WordPress’s action registry — no errors, no warnings, no performance cost.

2

Test with different cart totals and edge cases

Test with different cart totals. Add a $10 product to your cart and visit checkout — you should see the “$X more for a 5% bulk discount” message. Add more items until the cart reaches $30 — the notice should switch to “$X more for free shipping”. Push the cart above $50 — the success message (“You qualify for free shipping!”) should appear. Critical: test with both logged-in and logged-out states. The dismiss cookie works identically for both. Also test after applying a coupon — WC()->cart->get_total() reflects the discounted total, not the pre-coupon subtotal. If your free-shipping incentive conflicts with a coupon that also reduces shipping, adjust your threshold logic.

3

Style the notice to match your theme

Style the notice. WooCommerce doesn’t provide default styles for custom notices. Add these SCSS rules to your theme:

.checkout-notice {n    background: #f0f6fc;n    border: 1px solid #c5d9ed;n    border-radius: 4px;n    padding: 1rem 1.25rem;n    margin-bottom: 1.5rem;n    position: relative;n    font-size: 0.95rem;n    color: #1e3a5f;nn    p { margin: 0; }nn    &--success {n        background: #edf7ed;n        border-color: #b6d7b6;n        color: #1e4620;n    }nn    &__close {n        position: absolute;n        top: 0.5rem;n        right: 0.75rem;n        background: none;n        border: none;n        font-size: 1.25rem;n        cursor: pointer;n        color: inherit;n        opacity: 0.5;n        line-height: 1;n        padding: 0.25rem;nn        &:hover { opacity: 1; }n    }n}

The checkout-notice--success modifier class gives the “you qualified” message a green treatment — visually distinct from the blue “add more” nudge, which signals a different psychological state to the customer.

04 — Making it your own

Making This WooCommerce checkout notice Your Own

Customise this WooCommerce checkout notice to match your specific needs: The single-threshold, single-hook pattern scales to product-specific notices, category-based rules, and any WooCommerce page. Here's how to adapt it.

1

Product-specific notices — check for a specific product in cart

Product-specific notices. Check if a specific product is in the cart and show a targeted message:

add_filter( 'noc_thresholds', function( $tiers ) {n    $has_backorder = false;n    foreach ( WC()->cart->get_cart() as $item ) {n        if ( 123 === $item['product_id'] ) {n            $has_backorder = true;n            break;n        }n    }n    if ( $has_backorder ) {n        return [ 75 => __( 'Spend %s more to unlock priority shipping!', 'textdomain' ) ];n    }n    return $tiers;n} );

Replace 123 with your product ID. The filter runs inside the action callback, so WC()->cart is fully available. This pattern scales to any cart condition — check for a specific category with has_term(), check item quantity with $item['quantity'], or check variation attributes with $item['variation'].

2

Category-based rules — different thresholds per product category

Category-based rules. Different product categories deserve different incentives. Use has_term() to detect categories in the cart:

add_filter( 'noc_thresholds', function( $tiers ) {n    $has_furniture = false;n    foreach ( WC()->cart->get_cart() as $item ) {n        if ( has_term( 'furniture', 'product_cat', $item['product_id'] ) ) {n            $has_furniture = true;n            break;n        }n    }n    if ( $has_furniture ) {n        return [n            200 => __( 'Add %s more for free white-glove delivery!', 'textdomain' ),n        ];n    }n    return $tiers;n} );

The filter completely replaces the thresholds array when a match is found — not just add to it. This is correct: a furniture customer shouldn’t see the generic “free shipping at $50” message if their incentive is white-glove delivery at $200.

3

Different notice types beyond checkout — cart, product, shop pages

Different notice types beyond checkout. The pattern works on any WooCommerce page — just swap the hook. For a cart-page notice before the customer reaches checkout:

add_action( 'woocommerce_before_cart', function() {n    if ( isset( $_COOKIE['noc_dismissed'] ) ) { return; }n    $cart_total = (float) WC()->cart->get_total();n    if ( $cart_total >= 50 ) { return; }n    $remaining = wc_price( 50 - $cart_total );n    printf(n        '<div class="cart-notice"><p>Add %s more for free shipping!</p></div>',n        $remainingn    );n} );

Available hooks: woocommerce_before_cart (above cart table), woocommerce_after_cart (below cart, above totals), woocommerce_before_checkout_form (above checkout form), woocommerce_before_single_product (above product description — e.g., “Buy 2 more for a bulk rate”), woocommerce_before_shop_loop (above the product grid). The pattern — check cookie, check total, format message — stays identical across all hooks.

3 things that will silently break:

1. WC() is null outside WooCommerce pages. If you require the file unconditionally from functions.php, the hook callback fires on every page load — but WC()->cart is only populated on WooCommerce pages. On the blog or a static page, WC()->cart->get_total() throws a fatal error. Fix: wrap the add_action in a woocommerce_loaded check or use the hook itself (it only fires on WooCommerce pages). Since woocommerce_before_checkout_form only fires on the checkout page, the callback is safe — but if you repurpose the code for other hooks, verify WC() exists first.

2. Cart total includes or excludes tax depending on settings. Under WooCommerce → Settings → Tax, the “Display prices in the shop” and “Display prices during cart and checkout” settings determine whether get_total() includes tax. If you set a $50 threshold and taxes are included, a cart subtotal of $46 with $4 tax hits the threshold even though the customer added less than $50 of products. Decide whether your threshold should be pre-tax or post-tax and adjust accordingly — WC()->cart->get_subtotal() returns the pre-tax, pre-shipping subtotal.

3. Session cookies vs. server-side sessions. The dismiss cookie is client-side only — it’s not cryptographically signed and a user can delete it from DevTools. This is acceptable for a marketing notice but not for security decisions. If you need guaranteed dismissal (e.g., a GDPR consent banner), use WC_Session_Handler or user meta for logged-in users.

Performance notes:

  • Zero database queries on the checkout page. The hook callback checks a cookie, reads a cart total from memory (WC()->cart->get_total() is pre-calculated during the cart initialization phase), and outputs HTML. No get_option(), no get_post_meta(), no custom table reads. The cookie check is a single array-key lookup in $_COOKIE.
  • No CSS or JS files enqueued. The styles live in your theme’s existing stylesheet. The dismiss button uses an inline onclick handler — 120 bytes of JavaScript that doesn’t trigger a network request, a parser block, or a render delay. No additional HTTP requests, no render-blocking assets.
  • The callback never executes when dismissed. The $_COOKIE check is the first line of the callback. If the cookie exists, the function returns before WC()->cart->get_total() is called, before the $tiers filter runs, and before any string is assembled. A dismissed user costs exactly one array-key lookup and one return statement — less than a microsecond.
  • Nudge logic only runs on the checkout page. woocommerce_before_checkout_form fires once per checkout page load. It does not fire on product pages, category archives, the cart page, or AJAX requests. The only measurable impact is on the checkout page itself — where the customer is already committed to purchasing.
0 comments

No comments yet. Be the first.

Leave a Reply

Table of Contents

WooCommerce checkout notice — Code Snippet

WooCommerce checkout notice — code snippet by Mosharaf Hossain

This WooCommerce checkout notice is a production-tested PHP snippet for WordPress developers. See the WooCommerce checkout documentation for related official documentation.

The WooCommerce checkout notice snippet adds a conditional notice to the WooCommerce checkout page that appears only when the cart total falls within a specific range — for example, “Spend 10 more to unlock free shipping” when the cart total is between 40 and 50. It uses WooCommerce’s woocommerce_before_checkout_form action hook and the native notice API, so the message renders with your theme’s existing notice styles.

The problem this snippet solves is that WooCommerce’s built-in free shipping progress notice is tied to the coupon system and displays in the cart page, not at checkout. Merchants often need more control: show a different message at different cart total thresholds, trigger notices for specific product categories, or display a delivery date estimate based on cart contents. This snippet provides the conditional logic foundation for all of those use cases.

The condition checks WooCommerce’s cart total using WC()->cart->get_cart_contents_total(), which returns the total before taxes and shipping. The comparison uses two thresholds — a minimum and a maximum — so the notice only shows within the configured range. When the cart total crosses the maximum threshold, the notice disappears automatically without any JavaScript, because WooCommerce refreshes the checkout form when cart totals change via AJAX.

Three notice types are available: success in green, notice in blue, and error in red. The snippet uses wc_add_notice() so the message renders in the standard WooCommerce notice container above the checkout form. Themes that override the notice template will render this notice consistently with their own styling, because the function outputs into the native notice queue.

Place the function in your theme’s functions.php or in a dedicated inc/woocommerce-helpers.php file. Tested on WooCommerce 7.0+ with WordPress 6.0+. Works with all WooCommerce-compatible themes.

Browse all WooCommerce PHP snippets at Code Snippets or see the companion WooCommerce Custom Checkout Field snippet for adding input fields at checkout. Used in production on the Ben’s Natural Health checkout flow, where a threshold message increased average order value by encouraging customers to add one more item.

Related: Rich Meals WooCommerce project — and browse the full code snippet library.

Chat on WhatsApp