Как отключить способы оплаты в WooCommerce по товарам и категориям

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

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

Когда это нужно и как выглядит проблема

Чаще всего ограничение оплаты требуется в таких случаях:

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

Проблема обычно проявляется не в админке, а на checkout: метод виден, но не должен быть доступен. Иногда после установки платёжного плагина он начинает показываться везде, потому что сам плагин не знает ваших бизнес-правил.

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

Перед тем как писать условие, проверьте, где именно возникает лишний метод оплаты. Это экономит время и помогает не сломать checkout.

  • Откройте корзину с проблемным товаром и посмотрите, какие способы оплаты отображаются.
  • Проверьте, не влияет ли на выбор доставка: некоторые шлюзы завязаны на метод доставки.
  • Убедитесь, что товар действительно находится в нужной категории, а не в дочерней или скрытой.
  • Посмотрите, не включён ли кэш на странице оформления заказа. Для checkout кэширование часто даёт ложную картину.
  • Временно отключите плагины, которые тоже меняют payment gateways, чтобы исключить конфликт.

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

Пошаговое решение через фильтр WooCommerce

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

Базовый пример: отключить оплату при получении для конкретной категории

Допустим, для категории preorder нужно скрыть cod — стандартный метод наложенного платежа.

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpturbo_limit_payment_gateways_by_category' );

function wpturbo_limit_payment_gateways_by_category( $gateways ) {
    if ( is_admin() ) {
        return $gateways;
    }

    if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
        return $gateways;
    }

    $restricted_category = 'preorder';
    $has_restricted_item = false;

    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product_id = $cart_item['product_id'];

        if ( has_term( $restricted_category, 'product_cat', $product_id ) ) {
            $has_restricted_item = true;
            break;
        }
    }

    if ( $has_restricted_item && isset( $gateways['cod'] ) ) {
        unset( $gateways['cod'] );
    }

    return $gateways;
}

Этот код проходит по корзине, ищет товар из нужной категории и убирает метод cod. Логика простая, но уже закрывает частый кейс.

Расширенный вариант: разные правила для нескольких категорий

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

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpturbo_filter_gateways_by_cart_rules' );

function wpturbo_filter_gateways_by_cart_rules( $gateways ) {
    if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->cart ) {
        return $gateways;
    }

    $rules = array(
        'preorder'   => array( 'cod', 'cheque' ),
        'digital'    => array( 'cod' ),
        'wholesale'  => array( 'cod', 'paypal' ),
    );

    $matched_gateways = array();

    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product_id = $cart_item['product_id'];

        foreach ( $rules as $category_slug => $blocked_gateways ) {
            if ( has_term( $category_slug, 'product_cat', $product_id ) ) {
                $matched_gateways = array_merge( $matched_gateways, $blocked_gateways );
            }
        }
    }

    $matched_gateways = array_unique( $matched_gateways );

    foreach ( $matched_gateways as $gateway_id ) {
        if ( isset( $gateways[ $gateway_id ] ) ) {
            unset( $gateways[ $gateway_id ] );
        }
    }

    return $gateways;
}

Здесь важно не путать $product_id и $variation_id. Для вариативных товаров категория обычно проверяется у родительского товара, поэтому product_id в большинстве случаев достаточно.

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

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

Например, можно скрывать cod только если в корзине есть категория preorder и выбран самовывоз. Это помогает не ломать сценарии, где тот же товар отправляется курьером.

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpturbo_gateways_by_shipping_and_category' );

function wpturbo_gateways_by_shipping_and_category( $gateways ) {
    if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->cart || ! WC()->session ) {
        return $gateways;
    }

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

    $chosen_shipping_methods = (array) WC()->session->get( 'chosen_shipping_methods', array() );
    $is_pickup = false;

    foreach ( $chosen_shipping_methods as $shipping_method ) {
        if ( strpos( $shipping_method, 'local_pickup' ) !== false ) {
            $is_pickup = true;
            break;
        }
    }

    if ( $has_preorder && $is_pickup && isset( $gateways['cod'] ) ) {
        unset( $gateways['cod'] );
    }

    return $gateways;
}

Обратите внимание: выбранный способ доставки хранится в сессии. Если вы тестируете код в админке или без нормальной сессии WooCommerce, условие может не сработать так, как на живом checkout.

Сравнение подходов: код, плагин, ручная настройка

ПодходКогда подходитПлюсыМинусы
Код через фильтрНужны точные правила по категориям, товарам, доставкеГибко, прозрачно, без лишних зависимостейНужно тестировать после обновлений
Плагин для conditional payment gatewaysПравил много, нужен интерфейс в админкеБыстрее настраивать без разработкиДополнительная нагрузка и риск конфликтов
Ручная настройка в платёжном модулеОдин простой сценарийМинимум кодаОбычно не хватает для сложных условий

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

Как проверить, что решение сработало

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

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

Для быстрой диагностики можно временно добавить логирование. Например, записывать в debug.log, какие шлюзы были доступны до и после фильтра. Это помогает понять, сработало ли условие вообще или проблема в другом месте.

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpturbo_debug_gateways', 99 );

function wpturbo_debug_gateways( $gateways ) {
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        error_log( 'Available gateways: ' . implode( ', ', array_keys( $gateways ) ) );
    }

    return $gateways;
}

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

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

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

Проверяют не тот ID шлюза

В WooCommerce ID метода оплаты не всегда совпадает с его названием в интерфейсе. У cod это наложенный платёж, у банковского перевода часто используется bacs. Если скрыли не тот ID, визуально кажется, что код не работает.

Не учитывают пустую корзину или админку

Если не добавить проверки WC()->cart и is_admin(), можно получить предупреждения PHP или неожиданные эффекты в бэкенде.

Пытаются проверять категорию у variation ID

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

Кэшируют checkout

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

Безопасность и производительность

Фильтр woocommerce_available_payment_gateways вызывается на checkout, поэтому код должен быть коротким и предсказуемым. Не делайте тяжёлые запросы к базе в цикле по каждому товару, если можно обойтись проверкой терминов таксономии.

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

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

Если вам нужно не только скрывать способы оплаты, но и чистить checkout от лишних элементов, иногда удобнее собрать это в одном небольшом служебном плагине. Для смежных задач по очистке и оптимизации WooCommerce можно также посмотреть инструменты уровня Clearfy Pro: https://wpshop.ru/plugins/clearfy.

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

⭐⭐⭐⭐⭐