Динамическая скидка в корзине по категориям товаров в WooCommerce
Владелец интернет-магазина электроники обратился к нам с задачей: он хотел давать скидку 10% на аксессуары, если покупатель добавляет в корзину товар из категории «Смартфоны». Казалось бы, мелочь. Но стандартный WooCommerce такого не умеет. Ни купоны, ни встроенные настройки скидок не позволяют автоматически менять стоимость товаров в зависимости от того, что лежит в корзине. А ведь это мощный инструмент увеличения среднего чека.
Именно динамическая скидка в корзине на основе категорий товаров — один из самых недооценённых маркетинговых приёмов в электронной коммерции. По данным исследований, персонализированные предложения на этапе оформления заказа повышают конверсию на 15–25%. И сегодня разберём, как это реализовать на WordPress с WooCommerce.
Зачем нужны скидки, привязанные к категориям
Представьте простой сценарий. Клиент покупает зимние сапоги — и автоматически получает скидку на носки, стельки и средства по уходу за обувью. Или: добавил ноутбук — мышки и чехлы дешевеют прямо в корзине. Покупатель видит выгоду до оплаты, а не после. Это принципиально.
Купоны тут не работают хорошо. Покупатель должен сам ввести код — а он этого не сделает. Или забудет. Или не узнает, что скидка существует. А когда цена меняется автоматически, прямо в корзине — это совсем другой уровень сервиса.
Вот типичные бизнес-задачи, которые решает такой механизм:
- Кросс-продажи — стимулировать покупку сопутствующих товаров из другой категории
- Повышение среднего чека — давать скидку на дешёвую категорию при покупке дорогой
- Продвижение новой категории — скидка на новинки при покупке товаров из основных категорий
- Сезонные акции — автоматическое применение сезонных скидок для конкретных категорий
Как это работает технически
С точки зрения WordPress и WooCommerce, логика следующая:
- Плагин (или кастомный код) перехватывает содержимое корзины
- Проверяет, есть ли товары из целевых категорий
- Если условие выполняется — применяет скидку к товарам из другой категории
- При каждом обновлении корзины проверка запускается заново
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
Добавить комментарий