Сценарий знакомый: у клиента уже есть заказ в статусе on-hold или processing, а в корзине по-прежнему доступны все способы оплаты. В итоге пользователь пытается оплатить повторно, менеджер получает дубли, а в админке приходится вручную разруливать лишние транзакции. В WooCommerce это обычно решают не через «магическую» настройку, а через фильтр woocommerce_available_payment_gateways.
Ниже — рабочий вариант для типовой задачи: скрывать отдельные способы оплаты, если у пользователя есть заказ с определённым статусом. Подход подходит для магазинов, где повторная оплата в конкретных статусах нежелательна: предзаказы, ручная проверка, частичная оплата, B2B-сценарии.
Когда это действительно нужно
Не стоит отключать платежи «на всякий случай». Логика должна быть привязана к бизнес-процессу. Обычно это нужно, если:
- заказ уже создан, но оплата должна пройти только после подтверждения менеджером;
- нельзя принимать повторную оплату для заказов в статусе
pendingилиon-hold; - нужно убрать, например,
codилиbacsдля повторных попыток оплаты; - в магазине есть ручная сверка платежей и важно не плодить дубли.
Диагностика проблемы перед правкой
Сначала проверьте, что именно происходит в вашем магазине. Часто проблема не в WooCommerce, а в теме или плагине, который фильтрует список шлюзов оплаты раньше вашего кода.
Что проверить в админке и логах
- какие статусы заказов используются в вашем процессе;
- какие способы оплаты должны оставаться доступными;
- не меняет ли их сторонний плагин доставки, подписок или B2B-модуль;
- нет ли кэширования checkout-страницы на уровне сервера или CDN;
- включён ли режим отладки WooCommerce, если нужно понять, почему шлюз исчезает.
Если у вас включены плагины оптимизации, проверьте, не кэшируется ли страница оформления заказа. Для checkout это критично: список методов оплаты должен формироваться динамически.
Решение через фильтр woocommerce_available_payment_gateways
Самый надёжный путь — добавить небольшой код в мини-плагин или в functions.php дочерней темы. Лучше мини-плагин: так код не потеряется при смене темы.
Пример: скрываем cod и bacs, если у пользователя уже есть заказ в статусе on-hold или processing.
<?php
add_filter( 'woocommerce_available_payment_gateways', 'wponline_disable_gateways_for_existing_orders' );
function wponline_disable_gateways_for_existing_orders( $gateways ) {
if ( is_admin() ) {
return $gateways;
}
if ( ! is_user_logged_in() || ! function_exists( 'wc_get_orders' ) ) {
return $gateways;
}
$user_id = get_current_user_id();
$orders = wc_get_orders( array(
'customer_id' => $user_id,
'status' => array( 'on-hold', 'processing' ),
'limit' => 1,
'return' => 'ids',
) );
if ( empty( $orders ) ) {
return $gateways;
}
$blocked_gateways = array( 'cod', 'bacs' );
foreach ( $blocked_gateways as $gateway_id ) {
if ( isset( $gateways[ $gateway_id ] ) ) {
unset( $gateways[ $gateway_id ] );
}
}
return $gateways;
}
Что делает код:
- не трогает админку;
- работает только для авторизованных пользователей;
- ищет хотя бы один заказ нужного статуса;
- если заказ найден, удаляет выбранные способы оплаты из доступных.
Если нужно отключать оплату только для конкретного статуса
Иногда удобнее завязаться на один статус, например только на on-hold. Тогда логика становится проще и прозрачнее для поддержки.
<?php
add_filter( 'woocommerce_available_payment_gateways', 'wponline_disable_gateways_on_hold_only' );
function wponline_disable_gateways_on_hold_only( $gateways ) {
if ( ! is_user_logged_in() ) {
return $gateways;
}
$orders = wc_get_orders( array(
'customer_id' => get_current_user_id(),
'status' => array( 'on-hold' ),
'limit' => 1,
'return' => 'ids',
) );
if ( ! $orders ) {
return $gateways;
}
unset( $gateways['cod'] );
return $gateways;
}
Сравнение подходов
| Подход | Плюсы | Минусы |
|---|---|---|
| Плагин для управления платежами | Быстро настраивается без кода | Может не учитывать вашу логику по статусам заказов |
| Код через фильтр WooCommerce | Точный контроль, можно привязать к статусам и ролям | Нужна проверка после обновлений |
| Кастомизация через тему | Удобно для быстрого теста | Риск потерять изменения при смене темы |
Если задача простая и нужна только одна-две правки, код обычно надёжнее. Если же у вас много условий по ролям, странам и статусам, иногда проще собрать это в отдельный небольшой плагин, чем размазывать логику по теме.
Как проверить, что решение сработало
Проверка должна быть не «на глаз», а по сценарию.
- Создайте тестовый заказ под пользователем, у которого есть доступ к checkout.
- Переведите заказ в нужный статус, например
on-hold. - Откройте корзину или страницу оформления заказа под этим же аккаунтом.
- Проверьте, что указанные способы оплаты исчезли из списка.
- Смените статус заказа на обычный, например
completed, и убедитесь, что методы оплаты снова доступны, если это предусмотрено логикой.
Если метод оплаты не исчезает, проверьте:
- точный ID шлюза в WooCommerce;
- не используется ли другой фильтр, который возвращает шлюз обратно;
- не кэшируется ли checkout;
- не выполняется ли код слишком поздно или слишком рано.
Частые ошибки и как их исправить
Неверный ID способа оплаты
В WooCommerce ID шлюза не всегда совпадает с его названием в интерфейсе. Например, у банковского перевода обычно используется bacs, а у оплаты при получении — cod. Если ID указан неправильно, unset() просто не сработает.
Проверка заказов без ограничения по пользователю
Иногда разработчики забывают фильтр customer_id и ищут заказы по всему магазину. Это приводит к тому, что логика начинает влиять на всех покупателей сразу. Для персонального ограничения это ошибка.
Кэш на checkout
Если страница оформления заказа отдаётся из кэша, список методов оплаты может быть устаревшим. Для checkout и cart кэширование нужно отключать или настраивать очень аккуратно.
Слишком агрессивное отключение
Не убирайте все способы оплаты без разбора. Если пользователь должен иметь возможность оплатить, например, картой, а вы скрыли вообще всё, заказ станет тупиковым. Сначала определите, какой именно шлюз должен исчезать и почему.
Практические советы по безопасности и производительности
Запрос wc_get_orders() в таком виде обычно не создаёт проблем, если вы ограничиваете выборку через limit и ищете только нужные статусы. Но если у вас большой магазин, не делайте тяжёлые выборки на каждом рендере без необходимости.
- ограничивайте запрос одним результатом, если вам достаточно факта наличия заказа;
- не используйте лишние поля в выборке;
- держите логику в отдельном мини-плагине, чтобы не потерять её при обновлении темы;
- если код влияет на оплату, тестируйте его на staging-сайте до выката в продакшн.
Если вам нужно не только скрывать способы оплаты, но и чистить лишнюю логику в WooCommerce, полезно смотреть в сторону инструментов, которые помогают убирать дубли и следить за техническим мусором. Например, в Clearfy Pro есть функции для оптимизации и чистки сайта, но для самой логики оплаты всё равно лучше оставить точечный код: так проще контролировать поведение.
Когда лучше пойти другим путём
Если у вас не один магазинный сценарий, а сложная матрица условий — статус заказа, роль пользователя, страна, сумма корзины, способ доставки — один фильтр быстро превращается в набор условий, который трудно поддерживать. В таком случае лучше вынести правила в отдельный класс или небольшой плагин с понятной структурой и логированием.
Для большинства типовых магазинов достаточно одного фильтра и аккуратной проверки статусов. Главное — не прятать логику в случайный кусок темы и не полагаться на ручную проверку «кажется, работает».