Синхронизация заказов с маркетплейсом: как не потерять и не задвоить заказ
Синхронизация заказов с маркетплейсом кажется простой: получить список новых заказов, сохранить их в базе, обновить статус. На практике ошибки появляются быстро. 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
Так легче понять, заказ потерялся при получении или при передаче дальше.
Чек-лист
- Хранить marketplace и external_order_id.
- Добавить уникальный индекс на внешний заказ.
- Сделать повторную загрузку безопасной.
- Хранить понятные статусы обработки.
- Использовать окно загрузки с overlap.
- Поставить lock от параллельного запуска.
- Логировать итог каждого запуска.
- Разделять временные и постоянные ошибки API.
- Передачу в CRM или 1С делать отдельным этапом.
Надёжная синхронизация заказов строится не на надежде, что API всегда ответит правильно. Она строится на повторах, уникальных ключах, статусах, логах и понятном восстановлении после сбоя.
Комментарии (0)
Пока нет комментариев. Будьте первым!