Как показывать разные способы оплаты в WooCommerce в зависимости от способа доставки

Типичный сценарий: клиент выбирает самовывоз, а на странице оформления всё равно видит оплату картой курьеру; или наоборот — при доставке почтой остаётся наложенный платёж, хотя он доступен только для конкретного способа доставки. В WooCommerce это решается без тяжёлых плагинов: платежные методы можно фильтровать по выбранной доставке на этапе расчёта заказа.

Ниже разберём рабочую схему для классического checkout WooCommerce. Если у вас кастомная тема или блоки оформления заказа, сначала проверьте, что используется стандартная логика платежных шлюзов WooCommerce, иначе часть хуков может не сработать так, как ожидается.

Когда это действительно нужно

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

Если у вас несколько способов доставки с одинаковым названием, но разными ID, ориентироваться нужно не на текст, а на идентификатор метода. Именно его WooCommerce передаёт в сессии checkout.

Диагностика: что проверить до правки кода

Прежде чем писать фильтр, посмотрите, как WooCommerce видит доставку и платежи в вашей установке. Это экономит время, когда правило не срабатывает из-за неверного ID или из-за того, что checkout обновляется через AJAX не так, как вы ожидаете.

Какие данные нужно собрать

  • ID способа доставки, например local_pickup:1 или flat_rate:3.
  • Список активных платежных шлюзов в WooCommerce → Настройки → Платежи.
  • Используется ли стандартная страница оформления заказа, а не кастомный checkout от темы или конструктора.
  • Обновляется ли блок доставки после выбора метода без перезагрузки страницы.

Если вы не уверены в ID доставки, временно выведите его в лог или посмотрите в HTML формы оформления заказа через инструменты разработчика. В большинстве случаев WooCommerce хранит выбранный способ доставки в сессии как массив, и именно его нужно читать при фильтрации платежей.

Сравнение подходов: плагин, код или компромисс

ПодходПлюсыМинусыКогда выбирать
Плагин для условий оплатыБыстро, без кодаЛишняя зависимость, возможны конфликтыЕсли нужно без разработки и логика простая
Код через фильтр WooCommerceКонтроль, минимум накладных расходовНужно аккуратно поддерживатьЕсли правило одно или несколько и важна стабильность
Компромисс: код + тестовый плагинМожно быстро проверить логикуВ проде лучше оставить только кодЕсли сначала нужно валидировать сценарий на тестовом сайте

Решение: скрываем платежные методы по выбранной доставке

Самый надёжный вариант — фильтр woocommerce_available_payment_gateways. Он позволяет убрать из списка те шлюзы, которые не должны показываться при конкретном способе доставки.

Ниже пример: при самовывозе оставляем только оплату наличными и картой, а при доставке курьером скрываем наложенный платёж. Логику можно расширить под свои ID.

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpcourse_filter_payment_gateways_by_shipping' );
function wpcourse_filter_payment_gateways_by_shipping( $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 ) || ! is_array( $chosen_methods ) ) {
        return $gateways;
    }

    $chosen_shipping = $chosen_methods[0];

    // Пример: самовывоз.
    if ( strpos( $chosen_shipping, 'local_pickup' ) !== false ) {
        unset( $gateways['cod'] ); // Наложенный платёж.
        return $gateways;
    }

    // Пример: доставка курьером или почтой.
    if ( strpos( $chosen_shipping, 'flat_rate' ) !== false ) {
        unset( $gateways['cod'] );
    }

    return $gateways;
}

Если у вас несколько зон доставки, лучше проверять не только часть строки local_pickup или flat_rate, а конкретный ID метода целиком. Это особенно важно, когда в одной зоне есть несколько тарифов с разными условиями.

Как сделать правило точнее

Когда нужно связать конкретный способ доставки с конкретным шлюзом, удобнее использовать массив соответствий. Тогда код проще поддерживать, чем набор вложенных if.

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpcourse_payment_gateways_map' );
function wpcourse_payment_gateways_map( $gateways ) {
    if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->session ) {
        return $gateways;
    }

    $chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
    $chosen_shipping = is_array( $chosen_methods ) && ! empty( $chosen_methods[0] ) ? $chosen_methods[0] : '';

    $rules = array(
        'local_pickup:1' => array( 'cod' ),
        'flat_rate:3'    => array( 'cod' ),
        'free_shipping:2' => array( 'cod' ),
    );

    if ( isset( $rules[ $chosen_shipping ] ) ) {
        foreach ( $rules[ $chosen_shipping ] as $gateway_id ) {
            unset( $gateways[ $gateway_id ] );
        }
    }

    return $gateways;
}

В этом варианте вы явно задаёте, какие шлюзы скрывать для каждого способа доставки. Это удобнее, если магазин живёт не один месяц и правила будут меняться.

Если нужно не скрывать, а оставлять только один способ оплаты

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

<?php
add_filter( 'woocommerce_available_payment_gateways', 'wpcourse_keep_only_selected_gateway' );
function wpcourse_keep_only_selected_gateway( $gateways ) {
    if ( is_admin() || ! function_exists( 'WC' ) || ! WC()->session ) {
        return $gateways;
    }

    $chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
    $chosen_shipping = is_array( $chosen_methods ) && ! empty( $chosen_methods[0] ) ? $chosen_methods[0] : '';

    if ( strpos( $chosen_shipping, 'local_pickup' ) !== false ) {
        $allowed = array( 'cod' );
        $gateways = array_intersect_key( $gateways, array_flip( $allowed ) );
    }

    return $gateways;
}

Такой подход полезен, если в магазине есть жёсткие операционные ограничения: например, при самовывозе вы принимаете только наличные, а при доставке по региону — только онлайн-оплату.

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

После добавления кода не ограничивайтесь визуальной проверкой на одной странице. WooCommerce пересчитывает checkout через AJAX, и ошибка может проявиться только после смены доставки или после обновления корзины.

  • Откройте страницу оформления заказа в режиме инкогнито.
  • Выберите каждый способ доставки по очереди.
  • Проверьте, что список платежей меняется без ручной перезагрузки.
  • Убедитесь, что выбранный ранее способ оплаты не остаётся активным, если он должен быть скрыт.
  • Сделайте тестовый заказ и проверьте, что в админке и письмах отображается корректный метод оплаты.

Если платежный метод исчезает только после обновления страницы, а не сразу при выборе доставки, значит проблема может быть в теме или в скриптах checkout. Тогда смотрите, не отключён ли стандартный AJAX WooCommerce и не перехватывает ли событие другой плагин.

Частые ошибки и как их исправить

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

Самая частая причина — в коде указан flat_rate, а фактически выбран метод flat_rate:3. Для WooCommerce это разные значения. Сначала проверьте реальный ID в сессии или в HTML checkout, потом уже пишите условие.

Код добавлен не туда

Если вставить фильтр в файл, который не загружается на фронтенде, ничего не произойдёт. Надёжнее использовать functions.php дочерней темы или собственный мини-плагин. Для боевого сайта мини-плагин обычно безопаснее, потому что логика не пропадёт после обновления темы.

Проверка ломается в админке

Без защиты is_admin() фильтр может мешать настройкам и тестам в панели. В примерах выше это учтено. Если у вас есть AJAX-запросы из админки, не убирайте эту проверку без необходимости.

Платёжный шлюз скрыт, но заказ всё равно проходит

Это означает, что фильтр влияет только на отображение, а не на серверную валидацию. В большинстве случаев WooCommerce не даст оформить заказ с недоступным шлюзом, но если у вас кастомная логика, стоит дополнительно проверять выбранный метод оплаты на сервере через woocommerce_after_checkout_validation или аналогичный сценарий в вашем коде.

Что учесть по безопасности и производительности

Фильтр платежных шлюзов сам по себе лёгкий, но есть две практические детали. Во-первых, не тяните лишние запросы к базе на каждом рендере checkout, если можно обойтись данными сессии. Во-вторых, не храните правила в случайных опциях без валидации: если администратор ошибётся в ID, магазин начнёт показывать не те методы оплаты.

Если логика сложнее двух-трёх правил, вынесите её в отдельный файл плагина и добавьте комментарии рядом с каждым ID доставки. Через пару месяцев это сэкономит время вам или другому разработчику.

Когда лучше не писать код вручную

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

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

Главный критерий проверки простой: после выбора доставки список доступных способов оплаты должен меняться предсказуемо, без ручных костылей и без конфликтов с оформлением заказа. Если это так, решение работает.

Хотите научиться создавать сайты и зарабатывать на этом от 30 000 рублей в месяц?

Записаться на курс сейчас