Когда синхронизация заказов уже настроена, а статусы все равно расходятся, проблема обычно не в самом REST API. Чаще ломается логика переходов: один сайт отправляет статус раньше времени, второй не принимает обновление из-за прав доступа, а третий перезаписывает его повторно через крон или вебхук. В итоге в админке видно одно, в CRM или на складе — другое.
Ниже разберем рабочий сценарий: как найти, где именно теряется статус, как безопасно передавать изменения между сайтами и как проверить, что заказ больше не «откатывается» назад.
Как выглядит проблема на практике
Типичный симптом — заказ на основном сайте уже completed, а на втором сайте он остается processing или on-hold. Иногда наоборот: статус меняется на целевом сайте, но через несколько минут возвращается к старому значению. Это почти всегда означает, что у вас есть не одна точка записи, а несколько.
Что проверить первым делом
- какой сайт является источником истины для статуса заказа;
- не отправляете ли вы одно и то же обновление через webhook и WP-Cron одновременно;
- не блокирует ли целевой сайт запросы по авторизации или nonce;
- не меняет ли статус сторонний плагин доставки, оплаты или складского учета;
- есть ли у заказа повторные события
woocommerce_order_status_changedна одном и том же переходе.
Диагностика: где именно ломается цепочка
Самый быстрый способ — логировать входящий и исходящий запрос. Не пытайтесь сразу чинить все в интерфейсе. Сначала нужно понять, доходит ли payload до второго сайта и принимает ли он его без ошибки.
На стороне отправителя удобно писать в лог только ключевые поля: ID заказа, старый статус, новый статус, время отправки и HTTP-ответ. На стороне получателя — фиксировать, что пришло в wp-json, и какой статус был установлен фактически.
add_action('woocommerce_order_status_changed', function( $order_id, $old_status, $new_status, $order ) {
$payload = array(
'order_id' => $order_id,
'old_status' => $old_status,
'new_status' => $new_status,
'updated_at' => current_time('mysql'),
);
error_log('Order sync payload: ' . wp_json_encode($payload));
}, 10, 4);Если лог есть на отправителе, но нет на получателе, проблема в маршрутизации, авторизации или блокировке запроса. Если лог есть в обоих местах, но статус не меняется, значит целевой сайт либо отклоняет переход, либо его тут же перезаписывает другой обработчик.
Пошаговое решение через REST API
Надежнее всего отправлять не весь заказ, а только минимальный набор данных для изменения статуса. Чем меньше лишних полей, тем меньше риск конфликтов с плагинами, которые тоже пишут в заказ.
1. Отправляем статус с исходного сайта
На сайте-источнике можно отправлять запрос в момент смены статуса. Важно добавить защиту от повторной отправки, иначе один и тот же переход может улететь несколько раз, особенно если заказ редактируется вручную.
add_action('woocommerce_order_status_changed', function( $order_id, $old_status, $new_status, $order ) {
if ( get_post_meta($order_id, '_sync_status_sent_' . $new_status, true) ) {
return;
}
$endpoint = 'https://target-site.ru/wp-json/wc-sync/v1/order-status';
$body = array(
'order_id' => $order_id,
'status' => $new_status,
'source' => home_url(),
'secret' => 'change-this-secret',
);
$response = wp_remote_post($endpoint, array(
'timeout' => 15,
'headers' => array('Content-Type' => 'application/json'),
'body' => wp_json_encode($body),
));
if ( ! is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200 ) {
update_post_meta($order_id, '_sync_status_sent_' . $new_status, time());
} else {
error_log('Order sync failed: ' . print_r($response, true));
}
}, 10, 4);2. Принимаем запрос на целевом сайте
На втором сайте лучше не использовать публичный endpoint без проверки секрета. Даже если это закрытая инфраструктура, лишний запрос может случайно поменять статус заказа.
add_action('rest_api_init', function() {
register_rest_route('wc-sync/v1', '/order-status', array(
'methods' => 'POST',
'callback' => 'wpsync_handle_order_status',
'permission_callback' => '__return_true',
));
});
function wpsync_handle_order_status( WP_REST_Request $request ) {
$params = $request->get_json_params();
if ( empty($params['secret']) || $params['secret'] !== 'change-this-secret' ) {
return new WP_REST_Response(array('message' => 'Forbidden'), 403);
}
$order_id = absint($params['order_id']);
$status = sanitize_key($params['status']);
$order = wc_get_order($order_id);
if ( ! $order ) {
return new WP_REST_Response(array('message' => 'Order not found'), 404);
}
$allowed = array_keys(wc_get_order_statuses());
$allowed = array_map(function($item) {
return str_replace('wc-', '', $item);
}, $allowed);
if ( ! in_array($status, $allowed, true) ) {
return new WP_REST_Response(array('message' => 'Invalid status'), 400);
}
if ( $order->get_status() === $status ) {
return new WP_REST_Response(array('message' => 'Already synced'), 200);
}
$order->update_status($status, 'Status synced from external site', true);
return new WP_REST_Response(array(
'message' => 'OK',
'order_id' => $order_id,
'status' => $status,
), 200);
}3. Не допускаем циклическую синхронизацию
Если оба сайта слушают woocommerce_order_status_changed и оба отправляют обновления друг другу, вы получите бесконечный обмен. Это одна из самых частых ошибок. Нужен флаг источника, чтобы сайт-получатель не отправлял то же изменение обратно.
Проще всего передавать в payload признак source и на стороне получателя не запускать исходящую синхронизацию, если изменение пришло извне. Для этого можно временно ставить метку в мета-поле заказа.
add_action('woocommerce_order_status_changed', function( $order_id, $old_status, $new_status, $order ) {
if ( get_post_meta($order_id, '_sync_in_progress', true) ) {
return;
}
// отправка запроса наружу
}, 10, 4);Сравнение подходов: webhook, REST API и ручная правка
| Подход | Когда подходит | Минус |
|---|---|---|
| Webhook из WooCommerce | Нужно быстро отправлять событие при смене статуса | Сложнее отлаживать повторные срабатывания и дубли |
| REST API с собственным endpoint | Нужен контроль над валидацией, логами и защитой | Нужно написать и поддерживать код |
| Ручная правка заказа | Редкие случаи и разовые исправления | Не решает проблему синхронизации |
Если у вас уже есть интеграция с внешним сервисом, REST API обычно удобнее: вы сами решаете, какие статусы разрешены, как обрабатывать ошибки и когда повторять запрос.
Как проверить, что решение сработало
Проверка должна быть не только визуальной в админке. После внедрения сделайте несколько тестовых переходов статуса и убедитесь, что данные совпадают в обеих системах.
- измените статус заказа вручную на сайте-источнике;
- проверьте HTTP-ответ целевого endpoint: должен быть
200; - сравните статус заказа на обоих сайтах через админку WooCommerce;
- посмотрите логи: не должно быть повторной отправки того же статуса;
- обновите заказ еще раз и убедитесь, что не возникает обратного цикла.
Если статус меняется, но через минуту откатывается, ищите второй источник записи: WP-Cron, вебхук, интеграцию склада или плагин доставки. В таких случаях полезно временно отключить все сторонние обработчики и оставить только один маршрут синхронизации.
Частые ошибки и как их исправить
Неверный статус в payload
WooCommerce хранит статусы без префикса wc- в объекте заказа, но в списке статусов префикс встречается. Если отправить неправильное значение, update_status() может не принять его или перевести заказ не туда, куда вы ожидали.
Нет проверки прав на endpoint
Открытый REST endpoint без секрета или подписи — это риск. Даже если сайт не публичный, лучше проверять хотя бы общий секрет в заголовке или теле запроса. Для более строгой схемы можно использовать Basic Auth или application passwords, если это подходит вашей архитектуре.
Дубли из-за повторного срабатывания хука
woocommerce_order_status_changed может сработать не только при оплате, но и при ручном редактировании заказа в админке. Если не ставить флаг уже отправленного статуса, вы получите лишние запросы и путаницу в логах.
Конфликт с плагином склада или доставки
Некоторые плагины сами меняют статус после обновления остатков или трекинга. В этом случае синхронизация статусов должна быть либо выше по приоритету, либо вынесена в отдельную очередь, чтобы не перетирать изменения сразу после записи.
Что учесть для безопасности и производительности
Не отправляйте весь объект заказа, если вам нужен только статус. Большие payload увеличивают время ответа и усложняют диагностику. Для массовых магазинов это особенно заметно, когда заказов много и каждый статус вызывает внешний запрос.
Если синхронизация идет между несколькими сайтами, добавьте очередь повторов с задержкой, а не бесконечные ретраи. Иначе при временной недоступности второго сайта вы создадите лавину запросов. Для фоновой обработки можно использовать Action Scheduler, который уже есть в WooCommerce и подходит для отложенных задач лучше, чем прямой вызов в браузерном запросе.
Еще один практический момент: логируйте только то, что нужно для отладки. Полные данные заказа в error log — это лишний риск для персональных данных и лишний шум при поиске проблемы.
Если вам нужно не только отладить статусы, но и выстроить более аккуратную синхронизацию данных между сайтами, имеет смысл вынести общие правила в отдельный сервисный слой или использовать готовую инфраструктуру для контентных и служебных блоков. Например, для редакционных блоков и вспомогательных элементов на сайте иногда удобнее подключать инструменты вроде Clearfy Pro, но для статусов заказов все равно лучше оставлять логику в коде, где вы контролируете каждый шаг.