Как отключить платные способы доставки в WooCommerce при наличии конкретного товара в корзине

Типичный сценарий: в магазине есть товар, который нельзя отправлять обычной доставкой, либо он требует отдельной логистики. В корзине при его добавлении нужно скрыть часть способов доставки — например, курьера, самовывоз или платные тарифы, оставив только подходящий вариант. Делать это вручную через настройки неудобно: правило должно срабатывать автоматически и только для нужных товаров.

Ниже — рабочий вариант без выдуманных хуков и без лишней магии. Логика строится на фильтре WooCommerce woocommerce_package_rates: он позволяет отфильтровать доступные методы доставки до вывода на checkout.

Когда это действительно нужно

Чаще всего задача возникает в таких случаях:

  • в корзине есть крупногабаритный товар, который нельзя отправить частью стандартных тарифов;
  • один товар доступен только через самовывоз или отдельную службу доставки;
  • для определённой категории товаров нужно убрать бесплатную доставку, чтобы не было ошибочного выбора;
  • в магазине смешиваются обычные и особые товары, а правила доставки должны зависеть от состава корзины.

Если у вас просто один тариф стал недоступен из-за настроек зоны доставки, это другая проблема. Здесь речь именно о динамическом скрытии методов в зависимости от содержимого корзины.

Диагностика проблемы перед правкой кода

Сначала проверьте, что именно вы хотите скрывать и по какому признаку. Ошибка на этом этапе обычно приводит к тому, что доставка исчезает у всех заказов или, наоборот, не отключается вовсе.

Что нужно выяснить

  • какой товар является триггером: конкретный ID, категория, тег или метаполе;
  • какие методы доставки нужно скрыть: flat_rate, free_shipping, local_pickup или кастомный способ;
  • в каких зонах доставки это должно работать;
  • нужно ли правило для всей корзины или только если товар присутствует хотя бы в одной позиции.

Чтобы не гадать, откройте страницу оформления заказа с тестовой корзиной и посмотрите, какие методы реально возвращает WooCommerce. Если у вас включён кэш на уровне страницы checkout, сначала отключите его для этой страницы — иначе вы будете проверять не код, а сохранённый HTML.

Решение через фильтр woocommerce_package_rates

Самый надёжный способ — добавить небольшой фрагмент в functions.php дочерней темы или в собственный мини-плагин. Лучше второй вариант: код не потеряется при смене темы.

Пример ниже скрывает все платные методы доставки, если в корзине есть товар с указанным ID. При необходимости вы можете заменить проверку на категорию.

add_filter( 'woocommerce_package_rates', 'wpstudy_hide_shipping_rates_for_specific_product', 10, 2 );

function wpstudy_hide_shipping_rates_for_specific_product( $rates, $package ) {
    if ( is_admin() && ! wp_doing_ajax() ) {
        return $rates;
    }

    $target_product_id = 123; // ID товара, который влияет на доставку
    $found = false;

    foreach ( WC()->cart->get_cart() as $cart_item ) {
        if ( (int) $cart_item['product_id'] === $target_product_id ) {
            $found = true;
            break;
        }
    }

    if ( ! $found ) {
        return $rates;
    }

    foreach ( $rates as $rate_id => $rate ) {
        // Оставляем только самовывоз, всё остальное скрываем
        if ( 'local_pickup' !== $rate->method_id ) {
            unset( $rates[ $rate_id ] );
        }
    }

    return $rates;
}

Если нужно скрывать методы не по ID товара, а по категории, логика почти та же. Проверка меняется на has_term():

add_filter( 'woocommerce_package_rates', 'wpstudy_hide_shipping_rates_for_category', 10, 2 );

function wpstudy_hide_shipping_rates_for_category( $rates, $package ) {
    if ( is_admin() && ! wp_doing_ajax() ) {
        return $rates;
    }

    $target_category = 'oversized';
    $found = false;

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

    if ( ! $found ) {
        return $rates;
    }

    foreach ( $rates as $rate_id => $rate ) {
        if ( 'local_pickup' !== $rate->method_id ) {
            unset( $rates[ $rate_id ] );
        }
    }

    return $rates;
}

Если нужно скрыть только один конкретный тариф

Иногда задача проще: не убирать все платные способы, а скрыть только один метод, например flat_rate:3. Тогда ориентируйтесь на полный ID тарифа, а не только на method_id.

add_filter( 'woocommerce_package_rates', 'wpstudy_hide_one_shipping_rate', 10, 2 );

function wpstudy_hide_one_shipping_rate( $rates, $package ) {
    $target_product_id = 123;
    $hide_rate_id      = 'flat_rate:3';
    $found             = false;

    foreach ( WC()->cart->get_cart() as $cart_item ) {
        if ( (int) $cart_item['product_id'] === $target_product_id ) {
            $found = true;
            break;
        }
    }

    if ( $found && isset( $rates[ $hide_rate_id ] ) ) {
        unset( $rates[ $hide_rate_id ] );
    }

    return $rates;
}

Какой вариант выбрать: код, плагин или настройка зоны

Если правило одно и оно завязано на состав корзины, код обычно проще и прозрачнее. Если правил много и они меняются менеджером без разработчика, имеет смысл смотреть в сторону плагина с условиями доставки. Но не стоит ставить тяжёлое решение ради одной проверки.

ПодходКогда подходитМинус
Код через woocommerce_package_ratesНужно точечное правило по товару или категорииТребует поддержки в теме или мини-плагине
Настройки зон доставкиМетоды зависят только от регионаНе учитывает состав корзины
Плагин с условиями доставкиМного правил, ими управляет контент-менеджерДополнительная нагрузка и зависимость от плагина

Если у вас уже стоит плагин для очистки и оптимизации WooCommerce, например Clearfy Pro, часть лишних дублей и служебных элементов можно убрать отдельно, но саму логику доставки лучше оставить в коде — так она будет предсказуемее.

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

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

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

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

add_filter( 'woocommerce_package_rates', 'wpstudy_log_shipping_rates', 99, 2 );
function wpstudy_log_shipping_rates( $rates, $package ) {
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        error_log( 'Shipping rates: ' . print_r( array_keys( $rates ), true ) );
    }
    return $rates;
}

Если в логе нужный тариф есть, но на checkout он не отображается, проблема уже не в фильтре, а в кэше, конфликте с другим плагином доставки или в настройках зоны.

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

Проверяют не тот ID

У товара есть product_id, а у вариации — отдельный variation_id. Если в корзине вариативный товар, а вы сравниваете только с ID родительского товара, правило может не сработать. В таком случае проверьте и product_id, и variation_id.

Скрывают методы в админке

Если не добавить защиту is_admin() и wp_doing_ajax(), фильтр может мешать служебным запросам WooCommerce. Это особенно заметно при обновлении корзины и на AJAX-операциях checkout.

Сравнивают только method_id

У нескольких тарифов может быть одинаковый method_id, но разные экземпляры. Если нужно убрать один конкретный способ, используйте полный ID вроде flat_rate:3.

Код кладут в родительскую тему

После обновления темы правило исчезнет. Для таких задач безопаснее мини-плагин или дочерняя тема.

Не очищают кэш

Если checkout кэшируется сторонним плагином или сервером, вы можете долго видеть старое поведение. После правки сбросьте кэш страницы, объектный кэш и кэш CDN, если он используется.

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

Сам фильтр лёгкий, но есть несколько моментов, которые стоит учесть:

  • не делайте сложные запросы к базе внутри цикла по корзине;
  • если правило завязано на категории, используйте has_term(), а не отдельные запросы через WP_Query;
  • не храните ID товара в открытом виде в нескольких местах — вынесите в константу или настройку мини-плагина;
  • если логика доставки сложная, разделите её на несколько маленьких функций, а не на один большой блок с десятком условий.

Когда правило начинает разрастаться, лучше вынести его в отдельный плагин для магазина. Это проще сопровождать, чем держать логику в шаблоне и потом искать её после обновления темы.

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

Оптимизация загрузки библиотек в WordPress для ускорения сайта
07.02.2026
Как создать автоподгрузку постов в WordPress без плагинов
22.02.2026
Как создать автоматические уведомления о обновлениях в WordPress
03.01.2026
WooCommerce: автоматическое удаление заказов по статусу и дате
24.04.2026
WooCommerce: как избежать проблем с кешированием при обновлении корзины
24.07.2026