Как отладить синхронизацию статусов заказов WooCommerce через REST API

Когда синхронизация заказов уже настроена, а статусы все равно расходятся, проблема обычно не в самом 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, но для статусов заказов все равно лучше оставлять логику в коде, где вы контролируете каждый шаг.

Как сделать автоматические резервные копии WordPress с помощью плагинов
11.11.2025
Как отключить XML-RPC в WordPress без поломки REST API
15.08.2026
Как запретить загрузку внешних iframe в WordPress для безопасности сайта
18.02.2026
Как сделать автоматическую синхронизацию оповещений WordPress между сайтами
24.03.2026
Как исправить ошибку 429 «Слишком много запросов» в WooCommerce при синхронизации складских данных
03.07.2026