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