В WooCommerce часто нужно не просто показать все доступные способы оплаты, а убрать часть из них по бизнес-логике: для самовывоза, для заказов с предзаказом, для определённых категорий товаров, при доставке в конкретный регион или если сумма заказа ниже порога. Если это не настроить, клиент видит лишние варианты, а менеджер потом вручную исправляет заказы и платежи.
Ниже — рабочий подход без выдуманных хуков и без тяжёлых плагинов: сначала разберём, как понять, где именно ломается логика оплаты, потом покажу код для woocommerce_available_payment_gateways, а в конце — как проверить результат и какие ошибки встречаются чаще всего.
Когда вообще нужно отключать оплату по условиям
Типовые сценарии в WooCommerce выглядят одинаково на уровне задачи, но отличаются по условиям:
- для самовывоза доступен только один способ оплаты;
- для товаров из категории
preorderнельзя принимать онлайн-оплату до подтверждения; - для заказов выше определённой суммы нужен только безналичный расчёт;
- для конкретного региона отключается наложенный платёж;
- если в корзине есть виртуальный товар, часть офлайн-методов не нужна;
- для B2B-клиентов показывается оплата по счёту, а для остальных — нет.
Если задача повторяется и зависит от нескольких признаков заказа, лучше решать её кодом. Плагин подойдёт только если логика очень простая и не меняется. Иначе вы быстро упрётесь в ограничения интерфейса.
Диагностика проблемы: что проверить до правки кода
Прежде чем писать фильтр, нужно понять, на каком этапе WooCommerce принимает решение о доступных платежах. Ошибка часто не в самом шлюзе, а в доставке, валюте, стране покупателя или в том, что условие проверяется слишком рано.
Проверьте базовые настройки
- включён ли сам способ оплаты в WooCommerce → Настройки → Платежи;
- не отключает ли его плагин доставки или мультивалютности;
- есть ли ограничения по странам в самом платёжном шлюзе;
- не влияет ли на выбор способа оплаты выбранный метод доставки;
- не меняется ли корзина после расчёта доставки через AJAX.
Если способ оплаты исчезает только после выбора доставки, это нормальное поведение: WooCommerce пересчитывает доступные шлюзы на основе адреса и shipping method. Если же он пропадает всегда, значит условие в коде слишком жёсткое или проверяется не тот объект.
Что смотреть в отладке
Для проверки удобно временно вывести список доступных шлюзов в лог. Это безопаснее, чем гадать по интерфейсу. Включите логирование в wp-config.php, если оно ещё не включено:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );После этого можно писать в лог названия доступных методов оплаты на этапе фильтра. Это помогает понять, что именно приходит в массив $available_gateways.
Решение: отключаем способы оплаты через фильтр WooCommerce
Самый надёжный вариант — использовать фильтр woocommerce_available_payment_gateways. Он позволяет убрать конкретный шлюз уже после того, как WooCommerce собрал список доступных методов для текущего заказа.
Ниже пример для functions.php дочерней темы или для собственного мини-плагина. Код отключает оплату cod и bacs, если в корзине есть товар из категории preorder.
add_filter( 'woocommerce_available_payment_gateways', 'wpticket_disable_gateways_for_preorder' );
function wpticket_disable_gateways_for_preorder( $available_gateways ) {
if ( is_admin() && ! wp_doing_ajax() ) {
return $available_gateways;
}
if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
return $available_gateways;
}
$has_preorder_product = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id'];
if ( has_term( 'preorder', 'product_cat', $product_id ) ) {
$has_preorder_product = true;
break;
}
}
if ( $has_preorder_product ) {
unset( $available_gateways['cod'] );
unset( $available_gateways['bacs'] );
}
return $available_gateways;
}Здесь важно два момента. Первый: проверка делается по WC()->cart, а не по заказу, потому что на странице оформления заказа заказ ещё не создан. Второй: мы не трогаем все шлюзы подряд, а убираем только нужные ID. У каждого платёжного метода свой идентификатор, и его надо брать из настроек или из кода шлюза.
Пример для отключения оплаты при самовывозе
Если логика завязана на способ доставки, лучше проверять выбранный shipping method. В WooCommerce он приходит как массив, и в нём обычно лежит строка вида local_pickup:3 или flat_rate:1.
add_filter( 'woocommerce_available_payment_gateways', 'wpticket_disable_gateways_for_local_pickup' );
function wpticket_disable_gateways_for_local_pickup( $available_gateways ) {
if ( is_admin() && ! wp_doing_ajax() ) {
return $available_gateways;
}
if ( ! WC()->session ) {
return $available_gateways;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_method = is_array( $chosen_methods ) && ! empty( $chosen_methods[0] ) ? $chosen_methods[0] : '';
if ( strpos( $chosen_method, 'local_pickup' ) !== false ) {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}Такой вариант полезен, если вы не хотите принимать наличные при самовывозе или наоборот хотите оставить только оплату на месте. Но если у вас несколько складов и разные правила по каждому, лучше расширить условие и проверять не только метод доставки, но и ID зоны доставки или метаданные заказа.
Сравнение подходов: плагин, код, гибрид
| Подход | Когда подходит | Минус |
|---|---|---|
| Плагин | Простое правило без исключений | Сложно поддерживать, если логика меняется |
| Код через фильтр | Нужны точные условия по корзине, доставке, стране | Нужно аккуратно тестировать |
| Гибрид | Часть правил в плагине, часть — в коде | Легко запутаться в приоритетах |
Если у вас уже стоит плагин для очистки и оптимизации сайта, например Clearfy Pro, он может помочь убрать лишние элементы и не мешать теме, но саму бизнес-логику оплаты всё равно лучше держать в коде. Это проще отлаживать и переносить между проектами.
Пошаговая настройка без лишних рисков
- Создайте дочернюю тему или отдельный мини-плагин для кастомного кода.
- Определите, по какому признаку нужно отключать оплату: товар, категория, сумма, доставка, страна.
- Найдите ID платёжных шлюзов, которые нужно скрывать.
- Добавьте фильтр
woocommerce_available_payment_gateways. - Проверьте условие сначала на одной странице оформления заказа.
- Протестируйте сценарий с гостевым покупателем и с авторизованным пользователем.
- Проверьте, не ломается ли пересчёт после смены доставки и адреса.
Если нужно отключать оплату по сумме заказа, пример будет похожим, только вместо категории проверяется WC()->cart->get_total() или сумма товаров до доставки и налогов. Но здесь важно не сравнивать строку с валютным символом, а приводить значение к числу.
add_filter( 'woocommerce_available_payment_gateways', 'wpticket_disable_gateways_by_cart_total' );
function wpticket_disable_gateways_by_cart_total( $available_gateways ) {
if ( ! WC()->cart ) {
return $available_gateways;
}
$cart_total = (float) WC()->cart->get_cart_contents_total();
if ( $cart_total < 3000 ) {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}Этот пример показывает важную деталь: для порогов лучше использовать именно числовые значения, а не текст из интерфейса. Иначе при смене валюты или формата отображения условие начнёт работать нестабильно.
Как проверить, что решение сработало
Проверка должна быть не только визуальной. Нужны минимум три сценария:
- корзина без условий — все нужные способы оплаты видны;
- корзина с условием — лишний способ оплаты исчезает;
- смена доставки или адреса — список методов обновляется корректно без ошибки в checkout.
Если вы тестируете через браузер, откройте страницу оформления заказа в режиме инкогнито и отдельно под авторизованным пользователем. WooCommerce может по-разному вести себя при наличии сохранённых адресов и сессии.
Полезно также посмотреть HTML на странице checkout: если шлюз скрыт правильно, его не должно быть в списке радиокнопок или выпадающем списке. Если он есть, но неактивен, значит другой плагин вмешивается позже по приоритету.
Частые ошибки и как их исправить
Проверяют не тот ID шлюза
Самая частая причина — в коде указан не тот ключ. Например, вы отключаете cod, а у вас установлен другой плагин наложенного платежа с собственным ID. Сначала посмотрите массив $available_gateways через лог или временный error_log().
Используют условие в админке без исключения AJAX
Если не добавить проверку ! wp_doing_ajax(), фильтр может мешать обновлению checkout в админке или при AJAX-пересчёте. В результате появляются странные ошибки при редактировании заказа.
Пишут логику слишком рано
Если проверять доставку до того, как WooCommerce сохранил выбранный метод в сессии, условие не сработает. Для таких сценариев лучше опираться на WC()->session и на фильтр доступных шлюзов, а не на ранние хуки загрузки страницы.
Сравнивают сумму как строку
Когда сумма берётся из отображаемого значения с валютой, сравнение ломается. Всегда приводите данные к числу и учитывайте, включён ли налог в сумму корзины.
Чек-лист перед публикацией на боевом сайте
- код вынесен в дочернюю тему или отдельный плагин;
- проверены реальные ID платёжных методов;
- протестированы гостевой и авторизованный сценарии;
- проверена смена доставки на checkout;
- в логах нет предупреждений и ошибок;
- резервная копия сделана до внедрения;
- условие не конфликтует с плагинами доставки и мультивалютности.
Безопасность и производительность
Такой код обычно лёгкий, но есть два практических правила. Первое: не кладите его в произвольный плагин, который может отключиться после обновления. Второе: не делайте тяжёлые запросы к базе внутри фильтра на каждом рендере checkout. Если условие можно вычислить из корзины и сессии, используйте именно эти данные.
Если логика сложная и зависит от нескольких ролей, регионов и категорий, лучше собрать её в отдельную функцию и оставить в коде только вызов. Так проще тестировать и менять правила без риска сломать оформление заказа.
Для проектов, где нужно часто менять правила отображения элементов checkout, иногда удобнее держать часть интерфейсных правок в теме, а бизнес-логику — в отдельном плагине. Это снижает риск, что обновление темы затронет оплату.