Как отключить оплату в WooCommerce для определённых способов доставки

Типичная задача в магазине: клиент выбирает самовывоз или доставку курьером, и после этого нужно показать только подходящие способы оплаты. Например, при самовывозе доступен наличный расчёт, а при доставке в другой регион — только онлайн-оплата. Если это не настроить, покупатель видит лишние методы, а менеджер потом вручную правит заказы.

В 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-правил всё равно лучше отдельный код или специализированный плагин.

Проверка результата после внедрения

После добавления кода проверьте не только визуально, но и в реальном сценарии оформления:

  1. Откройте корзину в режиме инкогнито.
  2. Выберите товар и перейдите к checkout.
  3. Смените способ доставки на самовывоз.
  4. Убедитесь, что лишние платёжные методы исчезли.
  5. Поменяйте доставку на курьерскую и проверьте, что онлайн-оплата осталась доступной.
  6. Оформите тестовый заказ до конца, чтобы убедиться, что 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;
}

Этот пример показывает принцип: условие можно расширять, но каждую переменную нужно проверять отдельно. Иначе отладка станет дороже, чем сама доработка.

Как сделать динамические заголовки H1 в WordPress
13.02.2026
Автоматизация создания резервных копий WordPress с помощью плагинов
08.03.2026
WooCommerce не отображает товары после обновления: как исправить
13.06.2026
Как удалить или отключить XML-RPC в WordPress: практическое руководство
12.01.2026
WooCommerce: автоматическое изменение статуса заказа по региональному признаку
10.07.2026