Сценарий типичный: для одной зоны доставки доступны все способы оплаты, а для другой нужно оставить только предоплату или, наоборот, убрать онлайн-эквайринг при самовывозе. В WooCommerce это лучше решать не через ручные настройки каждого метода оплаты, а через фильтр, который меняет список доступных шлюзов на этапе оформления заказа.
Ниже разберём рабочий вариант без плагинов, а также покажу, где чаще всего ломается логика на checkout и как это проверить до выката на боевой магазин.
Когда это действительно нужно
Ограничение оплаты по зоне доставки полезно в магазинах с разными правилами обработки заказов:
- самовывоз — только наличные или оплата при получении;
- курьерская доставка по городу — онлайн-оплата обязательна;
- доставка в регионы — только предоплата, чтобы не ловить неоплаченные заказы;
- отдельная зона для оптовых клиентов — свой набор платежных методов.
Если у вас правила завязаны именно на зону доставки, а не на категорию товара или сумму корзины, фильтрация по shipping zone обычно оказывается самым чистым решением.
Диагностика: что проверить до правки кода
Прежде чем писать код, убедитесь, что проблема действительно в зоне доставки, а не в другом условии WooCommerce. На checkout на список доступных оплат влияют:
- выбранный способ доставки;
- адрес покупателя и определение shipping zone;
- наличие виртуальных товаров;
- настройки самих платежных шлюзов;
- дополнительные плагины, которые тоже фильтруют методы оплаты.
Если платежный метод пропадает только после выбора конкретной доставки, значит, логика должна смотреть именно на выбранный shipping method и зону доставки, к которой он относится.
Как понять, какая зона сработала
WooCommerce определяет зону доставки по адресу и затем подставляет доступные методы. На практике удобнее сначала вывести в лог, какой shipping method выбран на checkout, а потом уже привязывать к нему оплату. Для этого можно временно добавить небольшой отладочный код в functions.php дочерней темы или в собственный мини-плагин.
add_action( 'woocommerce_checkout_update_order_review', function( $posted_data ) {
parse_str( $posted_data, $data );
if ( ! empty( $data['shipping_method'] ) ) {
error_log( 'Selected shipping method: ' . print_r( $data['shipping_method'], true ) );
}
} );После смены доставки на checkout проверьте debug.log. Это поможет понять, какой идентификатор метода доставки приходит в запросе.
Решение: скрываем способы оплаты по зоне доставки
Самый надёжный путь — фильтр woocommerce_available_payment_gateways. Он позволяет убрать ненужные методы оплаты до того, как WooCommerce покажет их покупателю.
Ниже пример: если выбрана доставка из зоны pickup, оставляем только оплату при получении. Если зона local, скрываем cod и оставляем онлайн-оплату.
add_filter( 'woocommerce_available_payment_gateways', function( $gateways ) {
if ( is_admin() ) {
return $gateways;
}
if ( ! 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 ( ! $chosen_method ) {
return $gateways;
}
// Пример идентификаторов shipping method:
// local_pickup:3, flat_rate:1, free_shipping:2
if ( strpos( $chosen_method, 'local_pickup' ) !== false ) {
// Для самовывоза оставляем только оплату при получении.
foreach ( $gateways as $gateway_id => $gateway ) {
if ( 'cod' !== $gateway_id ) {
unset( $gateways[ $gateway_id ] );
}
}
}
if ( strpos( $chosen_method, 'flat_rate' ) !== false ) {
// Для курьерской доставки убираем наложенный платеж.
unset( $gateways['cod'] );
}
return $gateways;
} );Это базовый шаблон. В реальном магазине лучше привязываться не к общему названию метода, а к конкретному shipping method ID, если у вас несколько зон с одинаковым типом доставки.
Если нужно ориентироваться на конкретную зону
WooCommerce не всегда удобно отдаёт зону напрямую в checkout, поэтому на практике чаще проверяют выбранный shipping method и сопоставляют его с правилами. Если же у вас строгая логика по зонам, можно получить зону по адресу покупателя через API WooCommerce и уже от неё принять решение.
function wpbono_get_shipping_zone_name() {
if ( ! function_exists( 'WC' ) || ! WC()->customer ) {
return '';
}
$package = array(
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2(),
),
);
$zone = WC_Shipping_Zones::get_zone_matching_package( $package );
return $zone ? $zone->get_zone_name() : '';
}Дальше эту функцию можно использовать внутри фильтра и отключать шлюзы по имени зоны. Такой вариант удобен, если у вас зоны называются осмысленно: Самовывоз, Москва, Регионы.
Сравнение подходов
| Подход | Когда подходит | Плюсы | Минусы |
|---|---|---|---|
| Настройки платежного шлюза | Простые правила без привязки к доставке | Не нужен код | Не умеет нормально учитывать зону доставки |
Код через woocommerce_available_payment_gateways | Нужна точная логика по зоне или методу доставки | Гибко, прозрачно, без лишних зависимостей | Нужно тестировать checkout и учесть кеш/сессию |
| Плагин-условия для checkout | Если нет разработчика и правила часто меняются | Быстрый старт | Дополнительная нагрузка и риск конфликтов |
Как проверить, что решение сработало
Проверка должна быть не только визуальной. На checkout легко увидеть нужные кнопки, но важно убедиться, что заказ действительно сохраняется с правильным способом оплаты.
- Откройте магазин в режиме инкогнито или очистите сессию WooCommerce.
- Добавьте товар в корзину.
- Выберите доставку из зоны, для которой вы ограничили оплаты.
- Проверьте, что лишние методы исчезли из блока оплаты.
- Оформите тестовый заказ и посмотрите, какой
payment_methodзаписался в заказ. - Повторите сценарий для другой зоны доставки.
Если у вас включён лог, полезно также проверить, не остаётся ли в сессии старый выбранный метод оплаты после смены доставки. Иногда WooCommerce визуально обновляет checkout, но сессия ещё хранит прежний выбор.
Частые ошибки и как их исправить
Методы оплаты не меняются после смены доставки
Обычно причина в кэше фрагментов checkout или в том, что тема/плагин не триггерит обновление заказа. Проверьте, что на странице оформления заказа не отключён стандартный JavaScript WooCommerce и что нет конфликтов с оптимизаторами скриптов.
Фильтр срабатывает, но не на всех страницах
Не добавляйте такую логику в код, который выполняется только на фронтенде без проверки контекста WooCommerce. Для checkout нужен доступ к сессии, а в админке фильтр лучше не трогать.
Отключается не тот способ оплаты
Частая ошибка — сравнивать человекочитаемое название доставки вместо технического идентификатора. В WooCommerce важен не текст Самовывоз, а строка вроде local_pickup:3 или flat_rate:1.
После обновления темы код пропал
Если правка была внесена в functions.php родительской темы, она исчезнет при обновлении. Для таких задач безопаснее использовать дочернюю тему или маленький кастомный плагин.
Практические советы по безопасности и производительности
Логика ограничения оплат не должна тормозить checkout. Здесь важно не городить лишние запросы к базе на каждом рендере. Если правило можно выразить через выбранный shipping method, делайте это через сессию WooCommerce и фильтр, а не через тяжёлые запросы к заказам или метаданным.
Ещё один момент — тестируйте изменения на staging-копии. На боевом магазине ошибка в checkout сразу бьёт по конверсии, а неудачный фильтр может скрыть все способы оплаты и сделать оформление заказа невозможным.
Если вам нужно быстро закрыть задачу без разработки, можно посмотреть в сторону плагинов для условной логики checkout. Но в магазинах с нестандартной доставкой код обычно надёжнее: он меньше зависит от интерфейса плагина и проще контролируется в репозитории.
Для магазинов на WooCommerce с большим количеством правил по доставке и оплате имеет смысл держать такие изменения отдельно от темы — в мини-плагине или mu-plugin. Тогда при смене дизайна логика не потеряется и её проще сопровождать.