Сценарий знакомый: клиент оформил заказ, но платёж не завершил. В админке заказ висит в статусе pending payment или on-hold, склад уже зарезервирован, а менеджер не понимает, когда можно освобождать товар. Если таких заказов немного, проблему ещё можно закрывать руками. Если поток регулярный, нужна понятная схема: дать покупателю время на оплату и автоматически отменять просроченные заказы.
В WooCommerce это решается без «магии»: часть логики уже есть в ядре, а остальное можно добрать небольшим кодом или настройкой платёжного шлюза. Ниже — рабочий вариант, который подходит для магазинов с оплатой по ссылке, банковским переводом, счётом для юрлиц и любыми сценариями, где заказ создаётся раньше фактического поступления денег.
Когда проблема действительно в отложенной оплате
Не каждый «зависший» заказ нужно отменять. Сначала важно понять, что именно происходит на стороне магазина и платёжного метода. В WooCommerce статусы pending payment и on-hold часто означают разные вещи:
pending payment— заказ создан, но платёж ещё не подтверждён;on-hold— заказ ждёт ручной проверки, перевода или подтверждения шлюза;failed— платёж не прошёл;cancelled— заказ отменён вручную или автоматически.
Диагностика: что проверить в первую очередь
Перед настройкой автоматической отмены проверьте, не ломается ли сам платёжный сценарий. Иначе вы начнёте отменять заказы, которые должны были оплатиться, но не дошли до подтверждения из-за ошибки интеграции.
- Есть ли у платёжного шлюза callback/webhook и доходят ли запросы до сайта.
- Меняется ли статус заказа после успешной оплаты.
- Не блокирует ли сайт входящие запросы защитой, кешем или Basic Auth.
- Не стоит ли слишком короткий таймаут ожидания оплаты у самого шлюза.
- Не создаёт ли магазин заказы в статусе
on-holdвместоpending paymentпо логике конкретного плагина.
Если подтверждение платежа уже работает, но заказы всё равно копятся, тогда задача именно в сроке жизни неоплаченного заказа и в автоматической отмене.
Какой способ выбрать: плагин, настройки или код
Для этой задачи есть три практических варианта. Выбор зависит от того, насколько сложная у вас логика и нужен ли контроль над статусами.
| Подход | Когда подходит | Минус |
|---|---|---|
| Настройки WooCommerce | Простой магазин, стандартные статусы | Мало контроля над условиями отмены |
| Плагин для автоматизации | Нужны правила без кода | Дополнительная зависимость и нагрузка |
| Небольшой код в теме или мини-плагине | Нужна точная логика по статусам и срокам | Нужно поддерживать код самостоятельно |
Если у вас стандартный магазин, сначала проверьте встроенные механизмы WooCommerce. Если нужна отмена только для конкретных статусов или только после определённого времени, проще и надёжнее добавить свой обработчик через wp_schedule_event и wc_get_orders().
Пошаговая настройка автоматической отмены неоплаченных заказов
Шаг 1. Задайте понятный срок ожидания оплаты
В WooCommerce нет универсального «правильного» времени. Срок зависит от платёжного метода и бизнес-процесса. Для банковского перевода или счёта срок обычно длиннее, чем для онлайн-эквайринга. Главное — не смешивать разные сценарии в одну логику.
Если вы используете стандартные статусы WooCommerce, ориентируйтесь на заказ в pending payment и отменяйте его только после того, как покупатель не оплатил заказ в заданный срок.
Шаг 2. Добавьте планировщик проверки просроченных заказов
Ниже пример кода, который раз в час ищет неоплаченные заказы старше 2 часов и переводит их в cancelled. Логику лучше вынести в мини-плагин, а не в functions.php, чтобы не потерять её при смене темы.
<?php
/**
* Plugin Name: WC Auto Cancel Unpaid Orders
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
register_activation_hook( __FILE__, function () {
if ( ! wp_next_scheduled( 'wppay_cancel_unpaid_orders_event' ) ) {
wp_schedule_event( time() + HOUR_IN_SECONDS, 'hourly', 'wppay_cancel_unpaid_orders_event' );
}
} );
register_deactivation_hook( __FILE__, function () {
$timestamp = wp_next_scheduled( 'wppay_cancel_unpaid_orders_event' );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, 'wppay_cancel_unpaid_orders_event' );
}
} );
add_action( 'wppay_cancel_unpaid_orders_event', function () {
if ( ! function_exists( 'wc_get_orders' ) ) {
return;
}
$cutoff = time() - 2 * HOUR_IN_SECONDS;
$orders = wc_get_orders( array(
'status' => array( 'pending', 'on-hold' ),
'limit' => 50,
'orderby' => 'date',
'order' => 'ASC',
'date_created' => '<' . gmdate( 'Y-m-d H:i:s', $cutoff ),
'return' => 'ids',
) );
foreach ( $orders as $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
continue;
}
if ( $order->has_status( 'cancelled' ) || $order->has_status( 'completed' ) ) {
continue;
}
$order->update_status( 'cancelled', 'Автоматическая отмена: заказ не был оплачен в срок.' );
}
} );Этот вариант простой, но рабочий. Он не трогает оплаченные заказы и не зависит от внешнего плагина. Если у вас большой магазин, ограничение 'limit' => 50 помогает не перегружать один запуск CRON.
Шаг 3. Исключите заказы, которые нельзя отменять автоматически
В реальном магазине не все неоплаченные заказы одинаковы. Например, заказ может быть уже передан в ручную обработку или содержать товар под предзаказ. Тогда отмена по таймеру будет ошибкой. Добавьте проверку по метке, способу оплаты или статусу.
<?php
add_action( 'wppay_cancel_unpaid_orders_event', function () {
$orders = wc_get_orders( array(
'status' => array( 'pending', 'on-hold' ),
'limit' => 50,
'return' => 'ids',
'date_created' => '<' . gmdate( 'Y-m-d H:i:s', time() - 2 * HOUR_IN_SECONDS ),
) );
foreach ( $orders as $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
continue;
}
if ( $order->get_meta( '_manual_review_required' ) === 'yes' ) {
continue;
}
if ( $order->get_payment_method() === 'bacs' ) {
continue;
}
$order->update_status( 'cancelled', 'Автоматическая отмена неоплаченного заказа.' );
}
} );Такой фильтр полезен, если вы хотите отменять только онлайн-заказы, но оставлять банковский перевод или ручную проверку.
Как проверить, что всё сработало
После внедрения важно не ограничиваться визуальной проверкой в админке. Нужно убедиться, что событие реально запускается и меняет статус заказа предсказуемо.
- Создайте тестовый заказ в статусе
pending paymentилиon-hold. - Поставьте дату заказа старше выбранного порога или временно уменьшите срок в коде до нескольких минут.
- Запустите WP-Cron вручную через посещение сайта или через системный cron, если он у вас настроен.
- Проверьте, что статус изменился на
cancelled. - Откройте заметки заказа и убедитесь, что там есть комментарий об автоматической отмене.
- Проверьте остатки на складе, если у вас включено резервирование товара.
Если статус не меняется, смотрите журнал ошибок PHP, логи WooCommerce и наличие реального запуска WP-Cron. На сайтах с низким трафиком WP-Cron может срабатывать с задержкой, потому что он запускается по посещениям, а не по расписанию сервера.
Частые ошибки и как их исправить
Отменяются уже оплаченные заказы
Обычно причина в том, что платёжный шлюз не успевает перевести заказ в processing или completed. В этом случае не надо увеличивать таймер вслепую. Сначала проверьте callback/webhook и логи шлюза. Если подтверждение приходит с задержкой, автоматическую отмену нужно делать позже или исключить этот метод оплаты из правила.
WP-Cron не запускается вовремя
Это типичная проблема на сайтах с редким трафиком. Решение — настроить системный cron на сервере и отключить псевдокрон в WordPress, если у вас есть доступ к хостингу. Иначе проверка просроченных заказов будет происходить нерегулярно.
Заказы в статусе on-hold не попадают под правило
Некоторые магазины проверяют только pending, хотя платёжный плагин создаёт заказы в on-hold. Нужно смотреть фактическое поведение конкретного шлюза и включать оба статуса, если это соответствует процессу.
Слишком агрессивная отмена
Если срок слишком короткий, покупатели не успевают оплатить заказ, особенно на мобильных устройствах или при оплате через банк. Здесь лучше не «ускорять продажи», а разделить сценарии: онлайн-оплата — короткий таймер, банковский перевод — длинный, ручная проверка — без автоотмены.
Практика безопасности и производительности
Автоматическая отмена заказов — не тяжёлая операция, но при большом количестве заказов важно не делать лишнюю нагрузку на базу. Несколько рабочих правил:
- не сканируйте все заказы без фильтра по статусу и дате;
- обрабатывайте пачками, а не одним большим запросом;
- не храните логику в теме, если она должна жить дольше смены дизайна;
- добавляйте комментарий к заказу, чтобы было понятно, почему статус изменился;
- не отменяйте заказы без проверки платёжного метода и бизнес-исключений.
Если вам нужно ещё и убирать лишние элементы из админки, чистить дубли и не тащить в магазин лишний мусор, в экосистеме WPShop для этого есть Clearfy Pro: https://wpshop.ru/plugins/clearfy?utm_source=wppay.ru&utm_medium=article&utm_campaign=woocommerce-otlozhennaya-oplata-i-avto-otmena-neoplachennyh-zakazov. Но саму логику отмены заказов лучше держать отдельным кодом, а не смешивать с оптимизацией интерфейса.
Что делать, если нужна более точная логика
Иногда простого таймера мало. Например, нужно отменять заказ только если клиент не оплатил его и не открыл письмо с ссылкой на оплату, или только если сумма меньше определённого порога. В таких случаях логику можно расширить через мета-поля заказа и дополнительные проверки перед update_status().
Главное правило здесь простое: сначала определите, какой именно заказ считается просроченным в вашем бизнес-процессе, а уже потом автоматизируйте отмену. Иначе вы получите не автоматизацию, а массовое удаление нормальных заказов под видом «очистки».