Проблема повторной отправки писем в WooCommerce при изменении статуса заказа
В WooCommerce по умолчанию при каждом изменении статуса заказа отправляется соответствующее уведомление клиенту. В некоторых сценариях, например, при частом обновлении или автоматической синхронизации заказов, это приводит к множеству повторных писем, что раздражает клиентов и создает нагрузку на почтовый сервер.
Задача — предотвратить повторную отправку письма, если статус заказа изменился на тот же, или при определенных условиях, сохраняя при этом корректную отправку уведомлений при реальных изменениях.
Диагностика проблемы
Для диагностики нужно понять, в каких случаях WooCommerce отправляет письма:
- При переходе в новый статус заказа (например, с
processingнаcompleted). - При повторном сохранении заказа с тем же статусом (например, при изменении других метаполей).
Если письма приходят даже при повторном сохранении без изменения статуса — значит, нужно добавить проверку в цепочку отправки.
Пошаговое решение: отключение повторной отправки писем
WooCommerce отправляет письма через класс WC_Email и хук woocommerce_order_status_{status}. Чтобы исключить повторную отправку, реализуем проверку, что статус действительно изменился, и перехватим вызов функции отправки письма.
1. Добавляем фильтр для исключения повторной отправки
В файл functions.php вашей темы или в кастомный плагин добавьте следующий код:
add_filter('woocommerce_email_enabled_customer_completed_order', 'disable_redundant_completed_email', 10, 2);
function disable_redundant_completed_email($enabled, $order) {
// Проверяем, было ли изменение статуса с другого на completed
if (!$order instanceof WC_Order) {
return $enabled;
}
$last_status = get_post_meta($order->get_id(), '_last_order_status', true);
$current_status = $order->get_status();
if ($last_status === $current_status && $current_status === 'completed') {
// Статус не менялся, отключаем письмо
return false;
}
// Обновляем мета с текущим статусом
update_post_meta($order->get_id(), '_last_order_status', $current_status);
return $enabled;
}Этот фильтр специально отключает письмо с уведомлением о завершении заказа при повторном сохранении без смены статуса. Аналогично можно добавить фильтры для других типов писем, меняя хук woocommerce_email_enabled_customer_completed_order на соответствующие.
2. Общий универсальный подход для всех статусов
Если нужно контролировать отправку писем при любых изменениях статуса, можно использовать хук woocommerce_email_send_before и проверять, менялся ли статус:
add_action('woocommerce_email_send_before', 'prevent_redundant_email_send', 10, 4);
function prevent_redundant_email_send($email, $object, $email_id, $order) {
if (!is_a($order, 'WC_Order')) {
return;
}
$order_id = $order->get_id();
$last_status = get_post_meta($order_id, '_last_order_status_for_email', true);
$current_status = $order->get_status();
if ($last_status === $current_status) {
// Отменяем отправку письма
remove_action('woocommerce_email_send_before', 'prevent_redundant_email_send', 10);
return false;
}
update_post_meta($order_id, '_last_order_status_for_email', $current_status);
}Этот код предотвращает отправку повторного письма, если статус заказа не изменился с момента последней отправки.
Проверка результата после внедрения
Чтобы проверить, что решение сработало:
- Создайте тестовый заказ.
- Измените статус заказа на «Выполнен» (completed) — письмо должно отправиться.
- Повторно сохраните заказ без изменения статуса — письмо НЕ должно отправиться.
- Измените статус на другой (например, «Отменён») — письмо должно отправиться.
Отслеживайте логи почтового сервера или используйте плагины для логирования писем, например Clearfy с модулем логов, чтобы убедиться в корректности работы.
Частые ошибки и как их исправить
- Письма продолжают отправляться при повторных сохранениях: возможно, метаполе для хранения предыдущего статуса не обновляется корректно. Проверьте права записи и использование правильных ключей метаполей.
- Код отключает все письма: убедитесь, что фильтр применяется только к нужному типу письма или статусу, не глобально.
- Кэширование не позволяет видеть изменения: очистите кэш сайта и кэш почтового сервера.
- Конфликты с другими плагинами почты: временно отключите сторонние плагины, влияющие на отправку писем и протестируйте.
Практические советы по безопасности и производительности
- Используйте метаполя для хранения предыдущего статуса, чтобы не нагружать базу лишними запросами.
- Добавляйте проверки типа объекта (например,
WC_Order), чтобы избежать ошибок при вызове из других частей кода. - Тестируйте изменения на staging-сервере, чтобы не нарушить почтовую рассылку на живом сайте.
- Если на сайте высокое количество заказов, отключайте повторную отправку только для критичных статусов, чтобы не создавать узкие места.
Сравнение вариантов реализации
| Метод | Преимущества | Недостатки | Компромисс |
|---|---|---|---|
Фильтр woocommerce_email_enabled_customer_completed_order | Точный контроль для конкретного письма, простота | Требуется отдельный фильтр для каждого типа письма | Использовать для самых часто проблемных статусов |
Хук woocommerce_email_send_before с проверкой статуса | Универсальное решение для всех писем | Сложнее в реализации, возможны побочные эффекты | Рекомендуется для опытных разработчиков |
| Плагин для управления почтой (например, Clearfy) | Готовое решение с дополнительным функционалом | Зависимость от стороннего кода, возможные избыточные функции | Использовать для крупных проектов с большим трафиком |