Типичная задача в магазине: клиент выбирает самовывоз или доставку курьером, и после этого нужно показать только подходящие способы оплаты. Например, при самовывозе доступен наличный расчёт, а при доставке в другой регион — только онлайн-оплата. Если это не настроить, покупатель видит лишние методы, а менеджер потом вручную правит заказы.
В WooCommerce это решается без тяжёлых плагинов: можно скрывать платёжные методы по выбранному способу доставки через фильтр woocommerce_available_payment_gateways. Для точечных сценариев это надёжнее, чем пытаться «переучить» весь checkout через сторонний конструктор.
Когда это действительно нужно
Сценарий обычно всплывает в трёх случаях:
- самовывоз — только наличные или оплата на месте;
- доставка наложенным платежом — нужно скрыть предоплату;
- дорогая курьерская доставка — оставить только безнал, чтобы не собирать деньги вручную.
Если у вас несколько складов, разные зоны доставки или отдельные правила для B2B и B2C, логика может усложниться. Но базовый принцип один: сначала определяем выбранный shipping method, потом отключаем лишние gateways.
Диагностика: что проверить до правки кода
Перед внедрением посмотрите, как WooCommerce хранит выбранную доставку в сессии. На checkout это обычно массив chosen_shipping_methods, где первый элемент соответствует текущей зоне и методу доставки. Если код писать вслепую, легко промахнуться по ID метода и получить пустой список оплат.
Полезно проверить:
- какие именно IDs у способов доставки в настройках зон;
- какие IDs у платёжных шлюзов в WooCommerce;
- не меняется ли доставка через AJAX после выбора адреса;
- не конфликтует ли логика с плагинами для checkout-формы.
Если на сайте уже есть кастомизация checkout, сначала протестируйте правило на staging-копии. Ошибка в фильтре может скрыть вообще все способы оплаты, и тогда заказ не оформится.
Пошаговое решение через код
Самый практичный вариант — добавить фильтр в functions.php дочерней темы или в небольшой mu-plugin. Ниже пример, который скрывает bacs и cod при доставке local_pickup, а при доставке flat_rate оставляет только онлайн-оплату stripe и paypal.
<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpstock_filter_gateways_by_shipping_method' );
function wpstock_filter_gateways_by_shipping_method( $gateways ) {
if ( is_admin() ) {
return $gateways;
}
if ( ! function_exists( 'WC' ) || ! WC()->session ) {
return $gateways;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( empty( $chosen_methods ) || empty( $chosen_methods[0] ) ) {
return $gateways;
}
$shipping_method = $chosen_methods[0];
// Самовывоз: оставляем только оплату на месте.
if ( strpos( $shipping_method, 'local_pickup' ) !== false ) {
unset( $gateways['bacs'] );
unset( $gateways['cod'] );
unset( $gateways['stripe'] );
unset( $gateways['paypal'] );
}
// Курьерская доставка: только онлайн-оплата.
if ( strpos( $shipping_method, 'flat_rate' ) !== false ) {
unset( $gateways['cod'] );
unset( $gateways['bacs'] );
}
return $gateways;
}Этот код намеренно простой: он работает по строке метода доставки. В реальном проекте лучше сузить условие до конкретных instance ID, если у вас несколько зон с одинаковым типом доставки. Иначе правило сработает слишком широко.
Как сделать правило точнее по instance ID
У каждого способа доставки в зоне есть свой instance ID. Его можно увидеть в настройках зоны доставки или в значении метода, которое приходит в сессию. Формат обычно выглядит как flat_rate:3 или local_pickup:7. Если нужно отключать оплату только для конкретной зоны, проверяйте полное значение.
<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpstock_filter_gateways_by_specific_shipping_instance' );
function wpstock_filter_gateways_by_specific_shipping_instance( $gateways ) {
if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->session ) {
return $gateways;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$shipping_method = $chosen_methods[0] ?? '';
if ( $shipping_method === 'local_pickup:7' ) {
unset( $gateways['stripe'] );
unset( $gateways['paypal'] );
}
return $gateways;
}Такой подход удобен, если у вас, например, самовывоз из двух точек, но правила оплаты для них разные.
Если нужен вариант без кода
Иногда задачу можно закрыть плагином, если в магазине уже есть сложные правила checkout и вы не хотите поддерживать код. Но здесь важно понимать компромисс: плагины для conditional payment часто добавляют ещё один слой логики, который потом сложнее отлаживать при обновлениях WooCommerce.
| Подход | Когда подходит | Минус |
|---|---|---|
| Код через фильтр | Нужны точные правила и контроль | Нужно сопровождение в теме или mu-plugin |
| Плагин conditional payments | Правил много, нужен интерфейс | Риск конфликтов и лишняя нагрузка |
| Ручная настройка шлюзов | Один простой сценарий | Не масштабируется на разные зоны |
Если магазин уже использует набор утилит для чистки и оптимизации, например Clearfy Pro, это не заменяет логику оплаты, но помогает держать сайт без лишнего мусора и дублей настроек. Для checkout-правил всё равно лучше отдельный код или специализированный плагин.
Проверка результата после внедрения
После добавления кода проверьте не только визуально, но и в реальном сценарии оформления:
- Откройте корзину в режиме инкогнито.
- Выберите товар и перейдите к checkout.
- Смените способ доставки на самовывоз.
- Убедитесь, что лишние платёжные методы исчезли.
- Поменяйте доставку на курьерскую и проверьте, что онлайн-оплата осталась доступной.
- Оформите тестовый заказ до конца, чтобы убедиться, что WooCommerce не возвращает скрытые gateways после AJAX-обновления.
Если методы оплаты не меняются сразу, очистите кэш страницы checkout и проверьте, не кэшируется ли корзина на уровне плагина или сервера. Для checkout кэширование часто ломает динамические правила.
Частые ошибки и как их исправить
Неверный ID платёжного шлюза
В коде нужно использовать именно ID шлюза, а не его название в админке. Например, у Stripe ID может отличаться в зависимости от плагина. Если вы укажете несуществующий ключ, ничего не произойдёт, и это сложно заметить без отладки.
Проверка только по типу доставки
Условие flat_rate или local_pickup слишком общее, если в магазине несколько зон. Тогда правило начнёт работать шире, чем нужно. Для точной логики используйте полный идентификатор method_id:instance_id.
Код добавлен в родительскую тему
После обновления темы правило исчезнет. Для таких задач лучше дочерняя тема или отдельный mu-plugin. Это особенно важно, если магазин живёт долго и обновляется регулярно.
Скрыли все способы оплаты
Такое бывает, если условие слишком агрессивное и не оставляет ни одного gateway. Перед удалением шлюза всегда оставляйте хотя бы один рабочий вариант, иначе оформление заказа остановится на этапе выбора оплаты.
Практические советы по безопасности и производительности
Логику лучше держать в маленьком отдельном файле, а не разбрасывать по шаблонам. Так проще отключить правило, если оно начнёт мешать. Если у вас несколько условий, выносите IDs способов доставки и оплат в массивы — это проще читать и сопровождать.
Для магазинов с высокой нагрузкой не стоит строить правило на тяжёлых запросах к базе на каждом рендере checkout. Здесь достаточно данных сессии WooCommerce. Это быстрее и надёжнее, чем каждый раз искать заказы или мета-поля.
Если нужно больше гибкости без собственного кода, можно рассмотреть специализированный плагин для conditional payments. Но перед установкой проверьте, как он работает с вашей версией WooCommerce и не ломает ли AJAX-обновление checkout.
Что делать, если правило должно зависеть не только от доставки
На практике часто нужен комбинированный сценарий: доставка плюс сумма корзины, страна, роль пользователя или конкретная категория товара. В этом случае не пытайтесь запихнуть всё в один громоздкий фильтр. Лучше разбить логику на несколько проверок и отдельно протестировать каждую.
Если у вас уже есть сложные условия в магазине, сначала решите, что является главным триггером: способ доставки, регион или тип товара. От этого зависит, где именно строить правило — в коде, в настройках плагина или на уровне shipping zones.
<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpstock_hide_cod_for_certain_shipping_and_total' );
function wpstock_hide_cod_for_certain_shipping_and_total( $gateways ) {
if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->cart ) {
return $gateways;
}
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$shipping_method = $chosen_methods[0] ?? '';
$cart_total = (float) WC()->cart->get_total( 'edit' );
if ( strpos( $shipping_method, 'local_pickup' ) !== false && $cart_total > 5000 ) {
unset( $gateways['cod'] );
}
return $gateways;
}Этот пример показывает принцип: условие можно расширять, но каждую переменную нужно проверять отдельно. Иначе отладка станет дороже, чем сама доработка.