Синхронизация заказов с маркетплейсом: как не потерять и не задвоить заказ

Синхронизация заказов с маркетплейсом кажется простой: получить список новых заказов, сохранить их в базе, обновить статус. На практике ошибки появляются быстро. API может вернуть один и тот же заказ повторно, запрос может оборваться, статус может измениться между запросами, очередь может запустить две задачи параллельно.

Если не заложить защиту сразу, заказы начинают теряться, дублироваться или зависать в неправильных статусах.

External ID обязателен

У каждого заказа маркетплейса есть внешний идентификатор. Его нужно хранить в локальной базе и сделать уникальным в рамках источника.

marketplace: ozon
external_order_id: 123456789

Индекс:

CREATE UNIQUE INDEX ux_orders_marketplace_external
ON marketplace_orders (marketplace, external_order_id);

Без такого индекса два параллельных процесса могут создать дубль даже при проверке в PHP-коде.

Не считать повторный заказ ошибкой

API может вернуть уже сохранённый заказ при следующем запросе. Это нормальный сценарий. Код должен обновить существующую запись или пропустить её, а не падать.

INSERT INTO marketplace_orders (marketplace, external_order_id, status)
VALUES (:marketplace, :external_order_id, :status)
ON DUPLICATE KEY UPDATE
    status = VALUES(status),
    updated_at = UNIX_TIMESTAMP();

Такой подход делает повторный запуск безопаснее.

Статусы лучше хранить явно

Не стоит хранить только “обработан” или “не обработан”. Для интеграции нужны промежуточные состояния.

  • new — заказ получен;
  • processing — идёт обработка;
  • synced — успешно сохранён и передан дальше;
  • failed — ошибка обработки;
  • cancelled — отменён на стороне маркетплейса;
  • ignored — осознанно пропущен.

Так проще понять, где остановился заказ.

Окно загрузки

Если заказы загружаются по дате изменения, нужно брать небольшой overlap. Например, каждый запуск запрашивает не строго с последнего времени, а с запасом 5-10 минут назад.

date_from = last_sync_at - 10 minutes

Повторы при этом безопасны благодаря external_id. Зато снижается риск пропустить заказ из-за задержки API или разницы времени.

Блокировка синхронизации

Один и тот же импорт заказов не должен запускаться параллельно. Нужен lock.

$lockFile = fopen(__DIR__ . '/runtime/orders-sync.lock', 'c');

if (!$lockFile || !flock($lockFile, LOCK_EX | LOCK_NB)) {
    exit(0);
}

Если интеграция разделена по маркетплейсам или складам, lock можно делать на конкретный источник, чтобы независимые процессы не мешали друг другу.

Логи по каждому запуску

После синхронизации должен оставаться понятный итог:

  • время запуска;
  • маркетплейс;
  • период запроса;
  • сколько заказов пришло из API;
  • сколько создано;
  • сколько обновлено;
  • сколько ошибок;
  • сколько длился запрос.
[
    'event' => 'marketplace_orders_sync_finished',
    'marketplace' => 'ozon',
    'received' => 120,
    'created' => 5,
    'updated' => 115,
    'errors' => 0,
]

Ошибки API

Нужно различать временные и постоянные ошибки. Timeout, 429, 502, 503 можно повторить позже. Ошибка авторизации или неправильный формат запроса требует вмешательства.

if (in_array($httpCode, [429, 500, 502, 503, 504], true)) {
    scheduleRetry();
}

if ($httpCode === 401 || $httpCode === 403) {
    markIntegrationBroken();
}

Если токен истёк, бесконечные retry только засорят очередь.

Передача заказа дальше

Часто заказ маркетплейса нужно не только сохранить, но и передать в CRM, 1С или внутреннюю систему. Это лучше делать отдельной задачей очереди. Получение заказов и дальнейшая обработка — разные этапы.

marketplace_order_received
order_saved
send_to_crm_queued
crm_synced

Так легче понять, заказ потерялся при получении или при передаче дальше.

Чек-лист

  1. Хранить marketplace и external_order_id.
  2. Добавить уникальный индекс на внешний заказ.
  3. Сделать повторную загрузку безопасной.
  4. Хранить понятные статусы обработки.
  5. Использовать окно загрузки с overlap.
  6. Поставить lock от параллельного запуска.
  7. Логировать итог каждого запуска.
  8. Разделять временные и постоянные ошибки API.
  9. Передачу в CRM или 1С делать отдельным этапом.

Надёжная синхронизация заказов строится не на надежде, что API всегда ответит правильно. Она строится на повторах, уникальных ключах, статусах, логах и понятном восстановлении после сбоя.

Комментарии (0)

Пока нет комментариев. Будьте первым!