В WooCommerce иногда нужно не просто изменить статус заказа, а именно запретить повторную оплату для заказов, которые уже находятся в определённом состоянии. Типичный сценарий: заказ переведён в on-hold, processing или в кастомный статус, и дальше клиент не должен видеть способы оплаты на странице заказа или в личном кабинете.
Решение здесь зависит от того, где вы хотите отключить оплату: только на странице конкретного заказа, в checkout для части корзин или вообще для заказов с определённым статусом. Ниже — рабочий вариант для сценария с заказами, у которых уже есть статус, и проверка, что логика не ломает обычный checkout.
Когда это реально нужно
Чаще всего задача появляется после интеграции с ручной модерацией, B2B-оплатой, частичной оплатой или внешней системой, которая меняет статус заказа до завершения платежа. Если оставить оплату доступной, клиент может увидеть кнопку оплаты повторно и попытаться провести платёж ещё раз. Это уже не вопрос удобства, а вопрос корректности процесса.
Типовые сценарии
- заказ уже отправлен в обработку, но клиент всё ещё видит кнопку оплаты;
- менеджер перевёл заказ в статус ожидания, а WooCommerce продолжает предлагать оплату;
- кастомный статус используется как маркер «оплачивать больше не нужно»;
- нужно скрыть конкретные платёжные методы только для части заказов.
Диагностика проблемы перед изменениями
Сначала проверьте, как именно у вас устроен поток заказа. В WooCommerce есть разница между статусом заказа, наличием неоплаченного баланса и доступностью ссылки на оплату. Если вы отключите оплату слишком агрессивно, можно случайно сломать стандартный сценарий для новых заказов.
Посмотрите:
- какой статус получает заказ после оформления;
- есть ли у вас кастомные статусы через плагин или код;
- используется ли страница
order-pay; - нужно ли скрывать оплату только в личном кабинете, а не в checkout;
- не завязана ли логика на конкретный платёжный шлюз.
Если проблема проявляется только на странице заказа, а checkout работает нормально, значит править нужно не список шлюзов в целом, а условия, при которых WooCommerce показывает оплату для конкретного заказа.
Рабочий способ через фильтр WooCommerce
Для страницы оплаты заказа удобно использовать фильтр woocommerce_valid_order_statuses_for_payment. Он определяет, для каких статусов WooCommerce считает оплату допустимой. Если убрать из списка нужный статус, кнопка оплаты и связанные действия перестанут отображаться для заказов в этом статусе.
Добавьте код в functions.php дочерней темы или в собственный мини-плагин:
add_filter( 'woocommerce_valid_order_statuses_for_payment', function( $statuses, $order ) {
if ( ! $order instanceof WC_Order ) {
return $statuses;
}
// Для этих статусов повторная оплата не нужна.
$blocked_statuses = array( 'processing', 'on-hold' );
if ( in_array( $order->get_status(), $blocked_statuses, true ) ) {
return array_diff( $statuses, $blocked_statuses );
}
return $statuses;
}, 10, 2 );Этот вариант подходит, если вы хотите убрать возможность оплаты именно для уже созданного заказа. Но есть важная деталь: фильтр влияет на логику доступности оплаты, а не на сам статус заказа. Поэтому он не меняет данные в базе и не трогает checkout для новых заказов.
Если нужен кастомный статус
Если у вас свой статус, например awaiting-review, его тоже можно исключить. Главное — использовать реальный slug статуса, который зарегистрирован в системе.
add_filter( 'woocommerce_valid_order_statuses_for_payment', function( $statuses, $order ) {
if ( ! $order instanceof WC_Order ) {
return $statuses;
}
$blocked_statuses = array( 'awaiting-review' );
if ( in_array( $order->get_status(), $blocked_statuses, true ) ) {
return array_diff( $statuses, $blocked_statuses );
}
return $statuses;
}, 10, 2 );Если нужно скрыть оплату только для части способов
Иногда задача не в полном запрете оплаты, а в том, чтобы убрать конкретный шлюз. Например, для заказов со статусом on-hold оставить только банковский перевод, а карту скрыть. Тогда удобнее использовать фильтр woocommerce_available_payment_gateways.
add_filter( 'woocommerce_available_payment_gateways', function( $gateways ) {
if ( is_admin() ) {
return $gateways;
}
if ( ! function_exists( 'is_wc_endpoint_url' ) || ! is_wc_endpoint_url( 'order-pay' ) ) {
return $gateways;
}
$order_id = absint( get_query_var( 'order-pay' ) );
if ( ! $order_id ) {
return $gateways;
}
$order = wc_get_order( $order_id );
if ( ! $order ) {
return $gateways;
}
if ( 'on-hold' === $order->get_status() ) {
unset( $gateways['cod'] );
unset( $gateways['stripe'] );
}
return $gateways;
} );Здесь важно не путать ID шлюза и его название в интерфейсе. В коде используется внутренний идентификатор, например cod, bacs, stripe. Его нужно проверить в настройках или в коде конкретного плагина оплаты.
Сравнение подходов
| Подход | Что делает | Когда использовать | Ограничение |
|---|---|---|---|
Фильтр woocommerce_valid_order_statuses_for_payment | Отключает оплату для заказа по статусу | Нужно убрать кнопку оплаты целиком | Не управляет отдельными шлюзами |
Фильтр woocommerce_available_payment_gateways | Скрывает выбранные методы оплаты | Нужно оставить часть способов оплаты | Надо аккуратно проверять контекст страницы |
| Изменение шаблонов | Меняет вывод кнопок и сообщений | Нужна кастомная вёрстка | Сложнее поддерживать после обновлений |
Пошаговое внедрение без сюрпризов
- Определите статус заказа, для которого оплата должна быть недоступна.
- Проверьте, нужен ли полный запрет оплаты или только скрытие отдельных шлюзов.
- Добавьте код в дочернюю тему или в отдельный мини-плагин.
- Очистите кеш, если у вас есть кеширование страниц личного кабинета.
- Протестируйте заказ в нужном статусе под обычным клиентом, а не из админки.
Если у вас уже есть плагин для оптимизации и чистки WordPress, например Clearfy Pro, проверьте, не включены ли дополнительные настройки, которые меняют поведение WooCommerce-страниц или кеширование пользовательских разделов. В таких случаях лучше тестировать на staging-копии, а не на боевом сайте.
Как проверить, что решение сработало
Проверка должна быть не только визуальной. Нужны минимум три сценария:
- заказ в нужном статусе — кнопка оплаты не показывается;
- обычный новый заказ — оплата доступна как раньше;
- админский просмотр заказа — данные не сломаны, статус не меняется сам по себе.
Дополнительно откройте страницу заказа в личном кабинете и проверьте HTML: если кнопка скрыта, но ссылка на оплату осталась в разметке, значит где-то вмешивается тема или шаблон WooCommerce. Тогда нужно смотреть переопределённые шаблоны в теме и плагины, которые модифицируют страницу заказа.
Полезно также включить логирование WooCommerce на время теста и посмотреть, не появляются ли ошибки при открытии страницы оплаты. Если ошибка возникает только для конкретного шлюза, проблема может быть не в статусе, а в самом платёжном плагине.
Частые ошибки и как их исправить
Используют не тот slug статуса
В админке статус может отображаться как «В обработке», но в коде он называется processing. Если подставить русское название, условие не сработает. Проверяйте именно внутренний slug.
Ломают checkout вместо страницы заказа
Если фильтр написан без проверки контекста, можно случайно скрыть оплату и на этапе оформления заказа. Для задач с уже созданными заказами всегда проверяйте, что код работает только там, где это нужно.
Не учитывают кеш
Если страница личного кабинета кешируется, клиент может видеть старую версию страницы с кнопкой оплаты. Для таких страниц кеш обычно нужно исключать или настраивать отдельно.
Скрывают шлюз без проверки ID
Неверный идентификатор платёжного метода приводит к тому, что код вроде бы работает, но ничего не меняет. Сначала проверьте ID шлюза в настройках или в документации плагина оплаты.
Безопасность и производительность
Не встраивайте такую логику в случайный плагин из каталога, если он не нужен для других задач. Чем меньше лишнего кода в процессе оплаты, тем проще отлаживать поведение. Для небольшого правила лучше отдельный мини-плагин или дочерняя тема.
Если вы используете кастомные статусы и дополнительные проверки, не делайте тяжёлые запросы к базе на каждом открытии страницы заказа. Для этой задачи достаточно объекта WC_Order и проверки статуса через get_status().
Если вам нужно не только скрыть оплату, но и добавить уведомления менеджеру, это лучше делать отдельным хуком, а не смешивать с логикой платежей. Так проще понять, где именно возникла ошибка.
Что делать, если нужен более гибкий сценарий
Иногда правило зависит не только от статуса, но и от суммы заказа, способа доставки или роли пользователя. Тогда лучше вынести условие в отдельную функцию и использовать её в фильтре. Это проще поддерживать, чем размазывать проверки по нескольким местам.
function wpbegin_disable_payment_for_order( WC_Order $order ): bool {
if ( in_array( $order->get_status(), array( 'on-hold', 'awaiting-review' ), true ) ) {
return true;
}
if ( (float) $order->get_total() > 100000 ) {
return true;
}
return false;
}
add_filter( 'woocommerce_valid_order_statuses_for_payment', function( $statuses, $order ) {
if ( $order instanceof WC_Order && wpbegin_disable_payment_for_order( $order ) ) {
return array();
}
return $statuses;
}, 10, 2 );Такой подход удобен, когда правило меняется со временем. Вы правите одну функцию, а не переписываете логику в нескольких фильтрах.