Сценарий типичный: в магазине есть самовывоз, и для него доступны не все способы оплаты. Например, при самовывозе нужно оставить только оплату на месте, а онлайн-эквайринг скрыть. Если этого не сделать, покупатель увидит лишние варианты на checkout, а оператору потом придётся вручную разруливать некорректные заказы.
В WooCommerce это лучше решать не через правку шаблонов, а через фильтр доступных платёжных методов. Так логика остаётся в одном месте и не зависит от темы оформления.
Когда проблема проявляется
Обычно ошибка заметна в одном из трёх случаев:
- способ доставки меняется на самовывоз, но список оплат не обновляется;
- платёжный шлюз виден всегда, хотя для самовывоза он не нужен;
- после обновления checkout часть методов оплаты начинает исчезать не там, где ожидалось, из-за конфликтов с плагинами доставки или кэшем.
Если у вас checkout на AJAX, сначала проверьте, действительно ли WooCommerce получает выбранный метод доставки. Иногда проблема не в фильтре оплат, а в том, что выбранный shipping method не сохраняется в сессии.
Что проверить до правки кода
- самовывоз добавлен как отдельный метод доставки, а не как текстовая пометка;
- у способа доставки есть стабильный идентификатор, например
local_pickup:1или похожий; - включён ли кэш на checkout-странице через тему, плагин оптимизации или CDN;
- не переопределяет ли платёжные методы другой плагин через свои фильтры.
Решение через фильтр доступных способов оплаты
Самый надёжный вариант — использовать фильтр woocommerce_available_payment_gateways. Он позволяет убрать лишние методы оплаты уже после того, как WooCommerce определил корзину, адрес и выбранную доставку.
Ниже пример: если в заказе выбран самовывоз, мы оставляем только оплату наличными при получении. Идентификатор метода оплаты нужно заменить на свой.
add_filter( 'woocommerce_available_payment_gateways', 'wpbono_hide_gateways_for_local_pickup' );
function wpbono_hide_gateways_for_local_pickup( $gateways ) {
if ( is_admin() ) {
return $gateways;
}
if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
return $gateways;
}
$chosen_methods = WC()->session ? WC()->session->get( 'chosen_shipping_methods' ) : array();
$chosen_method = is_array( $chosen_methods ) && ! empty( $chosen_methods ) ? $chosen_methods[0] : '';
if ( strpos( $chosen_method, 'local_pickup' ) === false ) {
return $gateways;
}
// Оставляем только оплату при получении.
$allowed_gateways = array( 'cod' );
foreach ( $gateways as $gateway_id => $gateway ) {
if ( ! in_array( $gateway_id, $allowed_gateways, true ) ) {
unset( $gateways[ $gateway_id ] );
}
}
return $gateways;
}Если вам нужно не оставить один способ, а наоборот скрыть конкретный шлюз, логика будет ещё проще: проверяете выбранную доставку и удаляете только нужный $gateway_id.
add_filter( 'woocommerce_available_payment_gateways', 'wpbono_hide_card_gateway_for_pickup' );
function wpbono_hide_card_gateway_for_pickup( $gateways ) {
if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->session ) {
return $gateways;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_method = is_array( $chosen_methods ) ? reset( $chosen_methods ) : '';
if ( strpos( $chosen_method, 'local_pickup' ) !== false && isset( $gateways['stripe'] ) ) {
unset( $gateways['stripe'] );
}
return $gateways;
}Пошаговая настройка без лишней магии
Если задача точечная и не требует отдельного плагина, действуйте так:
- Определите, какой именно shipping method используется для самовывоза. Это важно: у разных плагинов доставки идентификатор может отличаться.
- Решите, какие способы оплаты должны остаться видимыми при самовывозе.
- Добавьте фильтр в дочернюю тему или в небольшой mu-plugin, а не в файлы родительской темы.
- Проверьте checkout в обычном режиме и в режиме инкогнито, чтобы исключить влияние сохранённой сессии.
- Протестируйте сценарий с разными адресами и разными товарами, если у вас есть ограничения по категориям или складу.
Если вы предпочитаете не писать код, можно использовать плагин для условий на checkout, но в большинстве магазинов это приводит к лишней зависимости. Для одного правила код обычно надёжнее и прозрачнее.
Сравнение подходов
| Подход | Когда подходит | Плюсы | Минусы |
|---|---|---|---|
Код через woocommerce_available_payment_gateways | Нужно одно или несколько точных правил | Быстро, без лишних плагинов, легко отлаживать | Нужно знать ID доставки и оплаты |
| Плагин с условиями для checkout | Правил много, ими управляет менеджер | Удобно менять без разработчика | Дополнительная нагрузка и риск конфликта |
| Правка шаблонов checkout | Редко оправдана | Можно изменить внешний вид | Не решает бизнес-логику и ломается при обновлениях |
Как проверить, что решение сработало
Проверка должна быть не визуальной, а функциональной. Откройте checkout и выполните несколько сценариев:
- выберите обычную доставку — все нужные способы оплаты должны быть видны;
- переключитесь на самовывоз — лишние методы должны исчезнуть без перезагрузки страницы;
- обновите страницу checkout — состояние должно сохраниться, если доставка выбрана корректно;
- создайте тестовый заказ и убедитесь, что в админке отображается ожидаемый способ оплаты.
Если методы оплаты не меняются сразу, откройте консоль браузера и проверьте, нет ли ошибок JavaScript. Иногда сторонний плагин доставки не отправляет корректный AJAX-ответ, и тогда WooCommerce не обновляет блок оплаты.
Частые ошибки и как их исправить
Неверный ID способа доставки
Самая частая причина — в коде проверяют local_pickup, а фактически метод называется иначе. Посмотрите реальный идентификатор в настройках доставки или временно выведите его в лог.
Скрывают не тот платёжный шлюз
У многих шлюзов ID не совпадает с названием на витрине. Например, в интерфейсе может быть «Оплата картой», а в коде нужен stripe, yookassa или другой внутренний идентификатор плагина.
Код добавили в functions.php родительской темы
После обновления темы правило пропадёт. Для таких задач лучше использовать дочернюю тему или отдельный мини-плагин.
Кэш мешает обновлению checkout
Если страница оформления заказа кэшируется агрессивно, покупатель может видеть устаревший список оплат. Checkout и корзину обычно исключают из кэша на уровне плагина, сервера или CDN.
Логика конфликтует с другими условиями
Если у вас уже есть правила по категориям, сумме заказа или стране доставки, не пишите несколько независимых фильтров без проверки порядка выполнения. Иногда один фильтр удаляет шлюз, а другой потом пытается его вернуть.
Безопасность и производительность
Для такой задачи не нужен тяжёлый плагин, если правило одно и оно понятно бизнесу. Чем меньше лишней логики на checkout, тем проще поддержка и ниже риск конфликтов.
Если код выносите в отдельный мини-плагин, не забудьте:
- проверять наличие WooCommerce перед выполнением логики;
- не обращаться к
WC()->sessionбез проверки; - не использовать жёстко захардкоженные ID без комментария, откуда они взялись;
- тестировать правило после обновления плагина доставки или платёжного шлюза.
Когда правил становится много, имеет смысл вынести их в отдельный слой конфигурации или использовать специализированный плагин условий. Но для задачи «самовывоз = ограниченный набор оплат» код обычно остаётся самым чистым вариантом.