Динамическая скидка в корзине по категориям товаров в WooCommerce

Владелец интернет-магазина электроники обратился к нам с задачей: он хотел давать скидку 10% на аксессуары, если покупатель добавляет в корзину товар из категории «Смартфоны». Казалось бы, мелочь. Но стандартный WooCommerce такого не умеет. Ни купоны, ни встроенные настройки скидок не позволяют автоматически менять стоимость товаров в зависимости от того, что лежит в корзине. А ведь это мощный инструмент увеличения среднего чека.

Именно динамическая скидка в корзине на основе категорий товаров — один из самых недооценённых маркетинговых приёмов в электронной коммерции. По данным исследований, персонализированные предложения на этапе оформления заказа повышают конверсию на 15–25%. И сегодня разберём, как это реализовать на WordPress с WooCommerce.

Зачем нужны скидки, привязанные к категориям

Представьте простой сценарий. Клиент покупает зимние сапоги — и автоматически получает скидку на носки, стельки и средства по уходу за обувью. Или: добавил ноутбук — мышки и чехлы дешевеют прямо в корзине. Покупатель видит выгоду до оплаты, а не после. Это принципиально.

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

Вот типичные бизнес-задачи, которые решает такой механизм:

  • Кросс-продажи — стимулировать покупку сопутствующих товаров из другой категории
  • Повышение среднего чека — давать скидку на дешёвую категорию при покупке дорогой
  • Продвижение новой категории — скидка на новинки при покупке товаров из основных категорий
  • Сезонные акции — автоматическое применение сезонных скидок для конкретных категорий

Как это работает технически

С точки зрения WordPress и WooCommerce, логика следующая:

  1. Плагин (или кастомный код) перехватывает содержимое корзины
  2. Проверяет, есть ли товары из целевых категорий
  3. Если условие выполняется — применяет скидку к товарам из другой категории
  4. При каждом обновлении корзины проверка запускается заново

WooCommerce предоставляет для этого несколько хуков. Самые удобные — woocommerce_before_calculate_totals для изменения цен и woocommerce_cart_calculated_fees для добавления наценок/скидок отдельной строкой.

Реализация через PHP-код (без плагинов)

Самый гибкий и надёжный способ. Вставьте этот код в файл functions.php вашей дочерней темы или в отдельный mu-плагин.

Пример 1: Скидка на категорию «Аксессуары» при покупке из «Смартфоны»

add_action('woocommerce_before_calculate_totals', 'apply_category_discount', 20, 1);

function apply_category_discount($cart) {
    if (is_admin() && !defined('DOING_AJAST')) {
        return;
    }

    if (did_action('woocommerce_before_calculate_totals') >= 2) {
        return;
    }

    // ID категорий — подставьте свои
    $trigger_category_id = 15;  // Смартфоны
    $discount_category_id = 22; // Аксессуары
    $discount_percent = 10;

    $has_trigger = false;

    // Проверяем, есть ли товар из триггерной категории
    foreach ($cart->get_cart() as $cart_item) {
        if (has_term($trigger_category_id, 'product_cat', $cart_item['product_id'])) {
            $has_trigger = true;
            break;
        }
    }

    // Применяем скидку, если условие выполнено
    if ($has_trigger) {
        foreach ($cart->get_cart() as $cart_item) {
            if (has_term($discount_category_id, 'product_cat', $cart_item['product_id'])) {
                $original_price = $cart_item['data']->get_price();
                $new_price = $original_price * (1 - $discount_percent / 100);
                $cart_item['data']->set_price($new_price);
            }
        }
    }
}

Этот код при каждом пересчёте корзины проверяет наличие товаров из нужной категории и меняет цену для целевой категории. Покупатель видит обновлённую стоимость мгновенно.

Пример 2: Скидка на несколько категорий одновременно

А если нужно применять разные скидки к разным категориям? Допустим, при покупке из «Мебель» — 5% на «Декор» и 15% на «Освещение». Организуем логику через массив:

add_action('woocommerce_before_calculate_totals', 'apply_multi_category_discount', 20, 1);

function apply_multi_category_discount($cart) {
    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }

    if (did_action('woocommerce_before_calculate_totals') >= 2) {
        return;
    }

    $rules = array(
        array(
            'trigger'   => 30,  // ID категории «Мебель»
            'target'    => 31,  // ID категории «Декор»
            'discount'  => 5,
        ),
        array(
            'trigger'   => 30,  // ID категории «Мебель»
            'target'    => 32,  // ID категории «Освещение»
            'discount'  => 15,
        ),
    );

    foreach ($rules as $rule) {
        $trigger_found = false;

        foreach ($cart->get_cart() as $cart_item) {
            if (has_term($rule['trigger'], 'product_cat', $cart_item['product_id'])) {
                $trigger_found = true;
                break;
            }
        }

        if ($trigger_found) {
            foreach ($cart->get_cart() as $cart_item) {
                if (has_term($rule['target'], 'product_cat', $cart_item['product_id'])) {
                    $price = $cart_item['data']->get_price();
                    $new_price = $price * (1 - $rule['discount'] / 100);
                    $cart_item['data']->set_price($new_price);
                }
            }
        }
    }
}

Теперь правила можно настраивать в одном месте — массиве $rules. Не нужно лезть в код каждый раз, когда маркетолог захочет поменять проценты.

Пример 3: Скидка от суммы товаров в категории

Другой популярный сценарий: если общая стоимость товаров из категории «Одежда» в корзине превышает 5000 рублей — скидка 10% на «Уход за одеждой».

add_action('woocommerce_before_calculate_totals', 'apply_threshold_category_discount', 20, 1);

function apply_threshold_category_discount($cart) {
    if (is_admin() && !defined('DOING_AJAX')) {
        return;
    }

    if (did_action('woocommerce_before_calculate_totals') >= 2) {
        return;
    }

    $threshold_category_id = 40;
    $discount_category_id  = 41;
    $min_total = 5000;
    $discount_percent = 10;

    $category_total = 0;

    // Считаем сумму товаров из триггерной категории
    foreach ($cart->get_cart() as $cart_item) {
        if (has_term($threshold_category_id, 'product_cat', $cart_item['product_id'])) {
            $category_total += $cart_item['data']->get_price() * $cart_item['quantity'];
        }
    }

    // Применяем скидку при достижении порога
    if ($category_total >= $min_total) {
        foreach ($cart->get_cart() as $cart_item) {
            if (has_term($discount_category_id, 'product_cat', $cart_item['product_id'])) {
                $price = $cart_item['data']->get_price();
                $new_price = $price * (1 - $discount_percent / 100);
                $cart_item['data']->set_price($new_price);
            }
        }
    }
}

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

Визуальное уведомление для покупателя

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

add_action('woocommerce_before_cart', 'show_category_discount_notice');

function show_category_discount_notice() {
    $cart = WC()->cart;
    $trigger_category_id = 15;

    foreach ($cart->get_cart() as $cart_item) {
        if (has_term($trigger_category_id, 'product_cat', $cart_item['product_id'])) {
            wc_print_notice(
                '🎉 Скидка 10% на аксессуары — автоматически применена к вашему заказу!',
                'success'
            );
            break;
        }
    }
}

Банальная зелёная полоска в корзине с текстом про скидку — и конверсия в оплату поднимается. Проверено не один раз.

Готовые плагины для тех, кто не хочет писать код

Не каждый владелец магазина готов лезть в functions.php. Для WooCommerce есть несколько решений, которые позволяют настроить скидки по категориям через интерфейс:

Advanced Dynamic Pricing for WooCommerce

Мощный плагин с визуальным конструктором правил. Позволяет задавать условия «если в корзине есть товар из категории X — скидка N% на категорию Y». Правила можно комбинировать, задавать приоритеты и ограничения по дате. Бесплатная версия покрывает базовые сценарии.

Discount Rules for WooCommerce

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

YITH Dynamic Pricing and Discounts

Решение от YITH с удобным интерфейсом. Поддерживает сложные условия: категории, теги, роли пользователей, количество товаров. Минус — продвинутые функции только в платной версии.

Плагин Бесплатная версия Визуальный конструктор Скидки по категориям
Advanced Dynamic Pricing Да Да Да
Discount Rules for WC Да Да Да
YITH Dynamic Pricing Нет Да Да

Но честно скажу: кастомный код надёжнее. Плагины — это лишние запросы к базе, возможные конфликты и зависимость от обновлений разработчика. Для простых сценариев хватит 20 строк PHP. Для сложных — му-плагин на 100–150 строк. Никаких проблем.

Распространённые ошибки при реализации

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

Скидка применяется повторно при обновлении страницы. Причина — отсутствие проверки did_action(). Хук woocommerce_before_calculate_totals срабатывает несколько раз за один запрос. Без этой проверки цена «съедается» дважды и клиент видит двойную скидку. Приятно, но убыточно.

Не учитываются вариативные товары. Функция has_term() работает с ID товара, но у вариативных товаров родительский и дочерние товары — разные объекты. Нужно проверять и $cart_item['product_id'], и $cart_item['variation_id'].

Скидка сохраняется при удалении триггерного товара. Это самая коварная ошибка. Если клиент сначала добавил смартфон, потом убрал его — скидка на аксессуары должна исчезнуть. Код выше это учитывает, но при доработках эту логику часто теряют.

Конфликт с купонами. Когда вы меняете цену через set_price(), купоны применяются уже к новой цене. Это может привести к двойной скидке. Если в магазине активно используются купоны — лучше применять скидку через хук woocommerce_cart_calculated_fees.

Безопасный вариант через «сборы»

add_action(‘woocommerce_cart_calculated_fees’, ‘apply_discount_as_fee’, 10, 1);

function apply_discount_as_fee($cart) {
$trigger_category_id = 15;
$discount_category_id = 22;
$discount_percent = 10;

$has_trigger = false;
$discount_total = 0;

foreach ($cart->get_cart() as $cart_item) {
if (has_term($trigger_category_id, ‘product_cat’, $cart_item[‘product_id’])) {
$has_trigger = true;
break;
}
}

if ($has_trigger) {
foreach ($cart->get_cart() as $cart_item) {
if (has_term($discount_category_id, ‘product_cat’, $cart_item[‘product_id’])) {
$line_total = $cart_item[‘data’]->get_price() * $cart_item[‘quantity’];
$discount_total += $line_total * ($discount_percent / 100);
}
}
}

if

Добавить комментарий

Разработка сайтов на Wordpress