WooCommerce

Как показать в корзине WooCommerce «до бесплатной доставки осталось …»

M
Markabus
·25 марта 2021 г.
Фотореалистичный кадр упаковочного стола интернет-магазина: планшет в подставке со страницей корзины, где вверху зелёная полоса прогресса до бесплатной доставки, вокруг картонные коробки — открытая с товаром в бумаге и запечатанная с этикеткой, диспенсер скотча, пузырчатая плёнка и сканер штрихкодов — иллюстрация к статье о сообщении про бесплатную доставку в WooCommerce

Сообщение «до бесплатной доставки осталось 800 ₽» — один из самых дешёвых способов поднять средний чек. Покупатель видит конкретную сумму, до которой не хватает, и часто докладывает в корзину ещё одну мелочь, лишь бы не платить за доставку. Ниже — как сделать это в WooCommerce без плагинов.

Рабочий вариант

Код добавляется в functions.php дочерней темы. Сообщение выводится только в корзине и на странице оформления, сразу в нужном месте, и содержит остаток до порога:

add_action( 'woocommerce_before_cart', 'shop_free_shipping_notice' );
add_action( 'woocommerce_before_checkout_form', 'shop_free_shipping_notice' );
function shop_free_shipping_notice() {
    if ( ! WC()->cart || WC()->cart->is_empty() ) {
        return;
    }

    $threshold = 5000;                                // порог бесплатной доставки
    $subtotal  = (float) WC()->cart->get_subtotal();  // число, без валюты и HTML
    $remaining = $threshold - $subtotal;

    if ( $remaining > 0 ) {
        wc_print_notice(
            sprintf( 'Добавьте товаров ещё на %s — и доставка будет бесплатной.', wc_price( $remaining ) ),
            'notice'
        );
    } else {
        wc_print_notice( 'Доставка по этому заказу бесплатная.', 'success' );
    }
}

Как это устроено. Хуки woocommerce_before_cart и woocommerce_before_checkout_form срабатывают только на нужных страницах, а wc_print_notice() — в отличие от wc_add_notice() — выводит сообщение сразу, без очереди, поэтому дублей не будет. Функция wc_price() форматирует остаток по настройкам магазина: с нужной валютой, разделителями и количеством знаков.

Какую сумму сравнивать с порогом

Тут есть тонкость. У метода «Бесплатная доставка» в настройках WooCommerce есть переключатель «Применять правило минимальной суммы до скидки по купону». Если он выключен, порог сравнивается с суммой уже после скидок, и в коде стоит брать get_cart_contents_total() вместо get_subtotal(). Иначе сообщение пообещает бесплатную доставку, а система её не даст — или наоборот. Сверьте эти две настройки между собой.

Порог — из настроек, а не из кода

Хардкод «5000» в коде рано или поздно разойдётся с настройкой метода доставки: порог поменяли в админке, а в сообщении осталась старая цифра. Надёжнее читать минимальную сумму прямо из зоны доставки:

function shop_get_free_shipping_threshold() {
    foreach ( WC_Shipping_Zones::get_zones() as $zone ) {
        foreach ( $zone['shipping_methods'] as $method ) {
            if ( 'free_shipping' === $method->id
                && 'yes' === $method->enabled
                && ! empty( $method->min_amount ) ) {
                return (float) $method->min_amount;
            }
        }
    }
    return 0; // бесплатной доставки по сумме нет
}

Тогда в основной функции строка с порогом становится такой: $threshold = shop_get_free_shipping_threshold(); — и если функция вернула ноль, сообщение выводить не нужно. Одна оговорка: get_zones() не включает зону «Остальные регионы мира»; если бесплатная доставка настроена именно там, её надо проверить отдельно через WC_Shipping_Zones::get_zone( 0 ).

Где ещё показывать

Корзина и чекаут — обязательный минимум. Есть смысл добавить сообщение и в мини-корзину в шапке: там его увидят раньше, ещё в каталоге. Для этого подключите ту же функцию к хуку woocommerce_widget_shopping_cart_before_buttons. Больше трёх мест лучше не делать — навязчивость работает против конверсии.

Если у вас блочная корзина

На новых магазинах корзина и чекаут собраны на блоках, и хуки woocommerce_before_cart и woocommerce_before_checkout_form там не срабатывают — это шаблонные хуки классических страниц на шорткодах. Самый простой обходной путь — обернуть ту же логику в шорткод и поставить его блоком над корзиной в редакторе страницы:

add_shortcode( 'free_shipping_notice', function () {
    if ( ! WC()->cart || WC()->cart->is_empty() ) {
        return '';
    }
    $threshold = shop_get_free_shipping_threshold();
    if ( ! $threshold ) {
        return '';
    }
    $remaining = $threshold - (float) WC()->cart->get_subtotal();
    $text = $remaining > 0
        ? sprintf( 'Добавьте товаров ещё на %s — и доставка будет бесплатной.', wc_price( $remaining ) )
        : 'Доставка по этому заказу бесплатная.';
    return '<div class="woocommerce-info">' . $text . '</div>';
} );

Минус варианта в том, что сумма посчитается при загрузке страницы и не обновится, когда покупатель изменит количество в блочной корзине без перезагрузки. Для живого пересчёта нужен уже JavaScript, подписанный на события корзины — или плагин, который это умеет.

Частые ошибки

Брать сумму из «отображаемых» методов. Всё, что называется get_cart_subtotal(), get_cart_total() и похоже на них, возвращает строки для показа. Для арифметики — get_subtotal(), get_cart_contents_total(), get_total( 'edit' ).

Ставить уведомление в очередь. wc_add_notice() нужен, когда сообщение должно пережить редирект — например, после добавления в корзину. Для вывода «здесь и сейчас» — wc_print_notice().

Порог в коде не совпадает с настройкой доставки. Сообщение обещает одно, система считает другое. Читайте порог из зоны.

Не учитывать скидки. Сверьте, до или после купона считается минимальная сумма в методе доставки, и берите ту же базу в коде.

Коротко

Сообщение об остатке до бесплатной доставки — это двадцать строк в дочерней теме: число из get_subtotal(), порог из настроек зоны, вывод через wc_print_notice() в корзине и на чекауте. На блочной корзине те же расчёты заворачиваются в шорткод. Главное — не сравнивать строки с числами и не ставить уведомления в очередь на каждой странице.

Частые вопросы

Q.Почему сравнение get_cart_subtotal() с числом не работает?

Потому что этот метод возвращает готовую строку для вывода — с валютой и HTML-разметкой. Для расчётов нужны числовые методы: get_subtotal() или get_cart_contents_total().

Q.Чем wc_print_notice() отличается от wc_add_notice()?

wc_add_notice() ставит сообщение в очередь и показывает на ближайшей подходящей странице — его задача пережить редирект. wc_print_notice() выводит сразу в том месте, где вызван, поэтому дублей не бывает.

Q.Как не хардкодить порог 5000 в коде?

Прочитать минимальную сумму из метода «Бесплатная доставка» в зоне доставки через WC_Shipping_Zones::get_zones() — свойство min_amount у метода free_shipping. Тогда сообщение всегда совпадает с настройкой.

Q.Сработает ли это на блочной корзине?

Хуки классической корзины на блоке не срабатывают. Обходной путь — шорткод с той же логикой, поставленный блоком над корзиной; сумма при этом обновится только после перезагрузки страницы.

Источники

Предыдущая
Как изменить, скрыть и переименовать поля оформления заказа в WooCommerce
Следующая
Уведомление «Товар добавлен в корзину» в WooCommerce: как скрыть или изменить

Читайте также