WooCommerce: как программно изменить стоимость товара в корзине по условиям

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

В WooCommerce часто требуется изменить стоимость товара в корзине не просто по фиксированной скидке, а по сложным условиям — например, в зависимости от пользовательских ролей, выбранных атрибутов товара, количества в корзине или специальных промоакций. Стандартными средствами WooCommerce такие сценарии реализовать сложно, поэтому приходится писать собственный код с использованием хуков.

Основные сценарии изменения цены в корзине

  • Скидка на товар при определённом количестве (например, «3-й товар со скидкой 50%»)
  • Изменение цены в зависимости от атрибутов или вариаций
  • Скидка или наценка для определённых ролей пользователей (оптовики, подписчики и т.п.)
  • Изменение цены в корзине по промокоду с дополнительными условиями

Чтобы проверить, что цена меняется, нужно внимательно следить за итогом в корзине и на странице оформления заказа, а также учитывать возможные кеширующие плагины и кэш браузера.

Пошаговое решение: изменение цены товара в корзине через хук woocommerce_before_calculate_totals

Шаг 1: подключаем функцию в файл темы functions.php или в собственный плагин.

add_action('woocommerce_before_calculate_totals', 'custom_change_cart_item_price', 20, 1);
function custom_change_cart_item_price( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
        return;
    }
    // Проверяем, что корзина не пуста
    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) {
        return;
    }

    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
        $product = $cart_item['data'];
        $original_price = $product->get_price();

        // Пример условия: если количество товара >= 3, даём скидку 20%
        if ( $cart_item['quantity'] >= 3 ) {
            $new_price = $original_price * 0.8; // скидка 20%
            $product->set_price( $new_price );
        }

        // Дополнительное условие: для пользователей с ролью 'wholesale_customer' скидка 30%
        $user = wp_get_current_user();
        if ( in_array( 'wholesale_customer', (array) $user->roles ) ) {
            $new_price = $original_price * 0.7;
            $product->set_price( $new_price );
        }

        // Можно добавить и другие условия по атрибутам, метаданным и т.п.
    }
}

Шаг 2: Очищаем кеш WooCommerce, браузера, если есть кэширующие плагины, чтобы изменения отобразились корректно.

Пояснения к коду

  • Хук woocommerce_before_calculate_totals вызывается до подсчёта итоговой стоимости корзины.
  • Метод set_price() меняет цену товара для текущего экземпляра в корзине, не трогая цену в базе.
  • Обязательно проверяем, что код не выполняется в админке вне AJAX (чтобы не сломать админские операции).
  • Проверяем, чтобы хук не сработал несколько раз подряд (дважды), чтобы избежать багов.

Проверка результата после внедрения

  • Добавьте в корзину товар с количеством, подходящим под условие (например, 3 штуки).
  • Зайдите на страницу корзины и убедитесь, что цена изменилась (отобразилась с учётом скидки).
  • Проверьте оформление заказа: итоговая сумма должна совпадать с изменённой ценой.
  • Проверьте под разными ролями пользователей (если условие зависит от ролей).
  • Отключите кэш и очистите все кеши, если изменения не видны.

Частые ошибки и как их исправить

  • Цена не меняется после добавления кода: возможно, кэш не очищен или хук вызывается слишком рано/поздно. Проверьте приоритет и очистите кеш.
  • Цена меняется у всех товаров, а должна для определённых: проверьте условия в коде, возможно, логика фильтрации не корректна.
  • Ошибка с двойным применением скидки: убедитесь, что проверка did_action( 'woocommerce_before_calculate_totals' ) < 2 есть в функции.
  • Цена изменилась в базе данных товара: используйте set_price() у объекта товара в корзине, а не меняйте цену напрямую в базе.
  • Проблемы с совместимостью плагинов кеширования: добавьте динамический фрагмент кеша для страницы корзины и оформления заказа.

Практические советы по безопасности и производительности

  • Не используйте слишком сложные вычисления внутри хука — это может замедлить загрузку корзины.
  • Всегда проверяйте, что пользователь авторизован, если скидки зависят от ролей.
  • При работе с пользовательскими метаданными кешируйте результаты запросов, если используете их в условиях.
  • Тестируйте изменения на тестовой копии сайта, чтобы избежать ошибок на живом магазине.

Сравнение способов реализации изменения цены товара в корзине

МетодПлюсыМинусыКогда использовать
Хук woocommerce_before_calculate_totals и set_price()Гибкость, точечное изменение цены, не требует плагиновТребует навыков программирования, возможны ошибки при неправильной логикеКогда нужно динамическое изменение цены по сложным условиям
Плагины скидок (например, WooCommerce Dynamic Pricing)Простая настройка, много готовых условий, поддержкаПлатные решения, меньше гибкости, возможны конфликты с другими плагинамиДля стандартных промо-акций и массовых скидок
Использование пользовательских метаполей и фильтров ценыМожно хранить данные о скидках в метаполях, интеграция с внешними системамиСложнее реализовать логику, требует поддержкиПри интеграции с CRM или ERP

Сервис определения шаблона и плагинов на сайте