Если после обновления CMS, модуля, SDK или API перестали рассчитываться тарифы, создаваться отправления, печататься этикетки либо обновляться статусы, сначала нужно сохранить заказы и остановить неконтролируемые повторы. Интеграция доставки затрагивает оплату, склад и уведомления клиенту, поэтому случайный откат или многократная отправка одного запроса способны создать дубли накладных и лишние списания.
Надежная диагностика начинается с одной контрольной операции и точного времени изменения. Нужно проследить путь заказа от магазина до API перевозчика и обратно: подготовка данных, очередь, HTTP-запрос, ответ, запись внешнего ID, webhook статуса и отображение результата. Так определяется конкретный слой несовместимости, а не просто факт «модуль не работает».
Сначала ограничьте ущерб
- не удаляйте очередь и не запускайте массовый повтор всех заказов;
- сохраните новые заказы локально, даже если создание отправления временно недоступно;
- отключите только неисправный шаг через feature flag, если архитектура это позволяет;
- зафиксируйте время обновления и последнюю успешно обработанную операцию;
- экспортируйте список заказов со статусом pending, processing, failed и unknown;
- запретите параллельный ручной и автоматический импорт одних заказов;
- подготовьте временный ручной процесс для срочных отправлений;
- не публикуйте клиенту трек-номер, пока перевозчик не подтвердил его создание.
Если клиент продолжает оформлять заказы, сайт не обязательно полностью выключать. Безопаснее отделить прием заказа от оформления доставки: сохранить бизнес-операцию в локальной базе, поставить ее в outbox и обработать после восстановления интеграции.
Определите масштаб сбоя
Интеграция доставки состоит из нескольких независимых функций. Проверяйте их по отдельности, потому что расчет тарифа может работать, а создание накладной — нет.
- загрузка списка городов, терминалов и пунктов выдачи;
- расчет стоимости и срока доставки;
- валидация адреса и индекса;
- создание отправления или заказа у перевозчика;
- получение номера накладной и трек-номера;
- печать этикетки, акта или реестра;
- вызов курьера или формирование забора;
- отмена и изменение отправления;
- прием webhook и обновление статусов;
- синхронизация наложенного платежа и возврата.
Составьте матрицу: функция, старый результат, новый результат, HTTP-код, время, тип заказа и окружение. Если сбой проявляется только у международных адресов, определенного тарифа или заказов с наложенным платежом, это быстрее укажет на изменившееся поле или правило валидации.
Что изменилось одновременно с обновлением
- версия CMS, плагина или собственного приложения;
- версия PHP, Node.js, Java или другой среды;
- SDK и HTTP-клиент;
- версия API и базовый URL перевозчика;
- формат авторизации, токен, scopes и учетная запись;
- схема базы и фоновые миграции;
- очередь, cron, supervisor или контейнеры;
- TLS-сертификаты, DNS, proxy и firewall;
- настройки склада, договора, тарифа и отправителя;
- webhook URL, подпись и список разрешенных событий.
Фраза «обновили только модуль» не исключает другие изменения: установка могла подтянуть зависимости, выполнить миграцию, очистить кеш, заменить конфигурационный файл или перезапустить воркеры со старыми переменными окружения. Нужен фактический diff развертывания и журнала инфраструктуры.
Соберите контрольный заказ
Выберите один тестовый или обезличенный заказ, который воспроизводит ошибку. Не отправляйте в журналы полный адрес, телефон, токены и данные оплаты. Для анализа обычно достаточно технических идентификаторов и структуры полей.
- внутренний order_id и стабильный idempotency key;
- организация, склад и договор доставки;
- тип получателя без персональных значений;
- страна, регион, город и формат индекса;
- вес, габариты, число мест и единицы измерения;
- тариф, способ оплаты и признак наложенного платежа;
- время постановки и номер попытки очереди;
- HTTP endpoint, method, status и provider request ID;
- внешний shipment ID, если он успел появиться;
- версия приложения и экземпляр воркера.
Сравните рабочий и сломанный запрос
Возьмите последний успешный запрос до обновления и первый неуспешный после него. Удалите секреты и персональные значения, затем сравните структуру: названия полей, типы, обязательность, единицы, enum, вложенность и заголовки. Простое визуальное сравнение JSON часто пропускает null, пустую строку и число, переданное как текст.
Сравнивать нужно: - URL и HTTP method - API version и Content-Type - заголовок авторизации и scopes - обязательные поля и их типы - коды тарифов, услуг и статусов - вес и размеры с единицами - null, отсутствующее поле и пустую строку - формат даты, времени и часовой пояс - кодировку и нормализацию адреса - структуру успешного и ошибочного ответаЕсли старый запрос нельзя получить из журнала из-за политики хранения, воспроизведите его в резервной копии приложения или сравните старую версию адаптера по системе контроля версий. Не включайте полное логирование рабочих запросов с персональными данными только ради диагностики.
Частые причины после обновления
Изменилась версия или схема API
Новое SDK может обращаться к другому endpoint, требовать дополнительное поле или возвращать измененную структуру. Старый код ожидает tracking_number в корне, а новый ответ помещает его внутрь shipment. Интеграция может получить HTTP 200, но не сохранить результат из-за неверного парсинга.
Изменились коды тарифов и статусов
Код услуги, терминала или состояния мог стать недоступным для договора либо сменить значение. Если приложение жестко сопоставляет строки, неизвестный статус иногда превращается в «доставлено» или «ошибка». Не используйте опасное значение по умолчанию: сохраняйте исходный статус и отправляйте неизвестный код в отдельную очередь разбора.
Сбросилась авторизация
После обновления конфигурация может читать другой secret, потерять refresh token или использовать учетную запись без нужного scope. HTTP 401 обычно указывает на неверные credentials, а 403 — на недостаточные права, договор или ограничение функции. Не печатайте токен в журнал и не вставляйте его в URL.
Миграция базы не завершилась
Код ожидает поле provider_order_id или новое состояние очереди, а схема осталась старой. Обратная ситуация возникает при откате кода после необратимой миграции. Проверяйте статус миграций, индексы и совместимость старой версии до rollback.
Фоновый воркер работает со старой версией
Веб-интерфейс обновился, но supervisor не перезапустил worker либо часть контейнеров осталась на предыдущем образе. В очередь попадают задания нового формата, которые старый потребитель не понимает. Добавьте версию producer и schema_version в payload, а версию worker — в технический журнал.
Изменились единицы и округление
После перехода на новый клиент вес может передаваться в килограммах вместо граммов, а размеры — в миллиметрах вместо сантиметров. Расчет тарифа станет нереалистичным или API отклонит груз. Денежные суммы передавайте в документированных единицах и округляйте в одном месте.
Адрес не проходит новую валидацию
Обновленный модуль может требовать отдельные region, city, street и postal_code, тогда как раньше отправлял одну строку. Проверьте Unicode, пробелы, номер корпуса, страну и соответствие города выбранному тарифу. Не заменяйте адрес молча: спорные результаты должны подтверждаться оператором или клиентом.
Webhook больше не подтверждается
Изменение маршрутизации, proxy или обработчика подписи приводит к HTTP 404, 401 или тайм-ауту. Отправление создается, но статусы не обновляются. Проверяйте подпись по исходному телу запроса, отвечайте быстро и переносите тяжелую обработку в очередь. Повторная доставка одного события должна быть идемпотентной.
Кеш хранит несовместимые данные
После обновления в кеше могут остаться тарифы, точки выдачи или сериализованные объекты старого формата. Очищайте адресный namespace и только после оценки последствий. Полный сброс Redis способен удалить сессии и очередь, если разные типы данных хранятся вместе.
Как читать HTTP-ошибки
- 400 — синтаксис, формат или неподдерживаемый Content-Type;
- 401 — отсутствует или недействителен токен;
- 403 — прав недостаточно либо функция недоступна договору;
- 404 — неверный endpoint, версия API или внешний объект;
- 409 — конфликт состояния или повтор операции;
- 422 — структура принята, но бизнес-валидация не пройдена;
- 429 — превышен лимит, нужен контролируемый backoff;
- 5xx — временный сбой провайдера или proxy, но не гарантия отсутствия результата;
- timeout — результат неизвестен: запрос мог выполниться до потери ответа.
Сохраняйте код, безопасное тело ошибки и provider request ID. Не превращайте любой ответ в сообщение «служба доставки недоступна»: оператору нужна классификация, а пользователю — понятный альтернативный сценарий.
Проверьте очередь и cron
- процесс запущен и использует актуальный release;
- задания не остаются в processing после падения;
- время visibility timeout больше обычной длительности, но не скрывает зависания;
- повтор не создает второе отправление;
- permanent errors не повторяются бесконечно;
- rate limit учитывает Retry-After, если он предоставлен;
- dead-letter очередь контролируется и имеет процедуру возврата;
- cron не запускает тот же импорт параллельно;
- часы сервера и часовой пояс корректны.
Безопасный откат
Rollback полезен, если точно известна рабочая версия и схема базы совместима. До отката сохраните новые заказы, очередь и внешние ID. Если обновление уже создало миграции или изменило формат заданий, простой возврат файлов может усугубить сбой.
- Остановите только потребителей неисправного типа заданий.
- Зафиксируйте состояние очереди и список операций unknown.
- Проверьте совместимость кода, базы и payload очереди.
- Разверните рабочую версию на одном экземпляре.
- Выполните контрольный расчет и одно тестовое создание.
- Сверьте внешний ID до разрешения повторов.
- Постепенно верните очередь с ограниченной скоростью.
- После стабилизации разберите несовместимость новой версии отдельно.
Как не создать дубли отправлений
Тайм-аут не означает, что перевозчик не создал отправление. Для каждого заказа формируйте стабильный idempotency key и сохраняйте его до вызова API. Если провайдер поддерживает такой ключ, передавайте его при каждой попытке. Если не поддерживает, храните локальную операцию и после неоднозначного ответа сначала ищите отправление по reference заказа.
planned - запрос еще не отправлен sending - попытка выполняется created - внешний shipment_id подтвержден retryable_error - временная ошибка без результата permanent_error - запрос нужно исправить unknown - запрос мог выполниться, требуется сверкаУникальный индекс по provider и reference заказа должен запрещать вторую активную операцию. Кнопка «повторить» переводит существующую операцию в новую попытку, а не создает независимую запись.
Восстановление по слоям
Расчет тарифа
Проверьте исходные и нормализованные адреса, вес, размеры, тариф, валюту и список доступных услуг. Отделите отсутствие подходящего тарифа от технической ошибки API. Если расчет временно недоступен, не подставляйте нулевую стоимость без явного бизнес-правила.
Пункты выдачи
Сверьте географический фильтр, тип пункта, ограничения по весу, пагинацию и срок кеша. После обновления клиент может читать только первую страницу или исключать точки с новым значением enum.
Создание отправления
Добавьте контрактную валидацию до API, стабильный reference и сохранение исхода операции. Внешний ID фиксируйте в той же локальной транзакции, где заказ переводится в подтвержденное состояние. Не отправляйте уведомление клиенту до фиксации результата.
Этикетки и документы
Проверьте content type, формат ответа, размер, кодировку и готовность документа. Некоторые API создают файл асинхронно: первый ответ возвращает job ID, а не PDF. Не сохраняйте JSON с ошибкой под расширением .pdf.
Статусы и webhook
Храните исходный provider status отдельно от внутреннего. Сопоставление должно быть явным и версионированным. Повтор или запоздалое событие не должен возвращать заказ из конечного состояния в промежуточное без разрешенного перехода.
Пошаговый план исправления
- Зафиксируйте время обновления, версии компонентов и последнюю успешную операцию.
- Разделите сбой по функциям: тарифы, точки, создание, этикетки или статусы.
- Сохраните очередь и выделите заказы со статусом unknown.
- Выберите один контрольный заказ без персональных данных.
- Сравните рабочий и новый HTTP-контракт.
- Проверьте авторизацию, договор, endpoint и версию API.
- Сверьте миграции базы и версии web-процесса и воркеров.
- Исправьте адаптер, валидацию или конфигурацию в тестовом окружении.
- Добавьте idempotency key и сверку неоднозначных результатов.
- Прогоните контрактные и негативные тесты.
- Верните обработку ограниченной порцией и контролируйте метрики.
- Только после сверки обработайте накопившуюся очередь.
Как проверить восстановленную интеграцию
- курьерская доставка и пункт выдачи;
- адрес с квартирой, корпусом и нестандартным индексом;
- несколько мест, дробный вес и предельные габариты;
- предоплата и наложенный платеж;
- заказ без подходящего тарифа;
- повтор создания с тем же idempotency key;
- тайм-аут после успешного ответа провайдера;
- HTTP 401, 403, 422, 429 и временный 5xx;
- формирование этикетки и проверка реального PDF;
- повторный и запоздалый webhook;
- отмена до и после передачи в доставку;
- перезапуск воркера во время обработки;
- одновременная работа нескольких экземпляров.
Результат считается устойчивым, если каждый локальный заказ связан максимум с одним активным внешним отправлением, повтор безопасен, неизвестные статусы не теряются, а накопившаяся очередь проходит без ручного редактирования базы.
Типичные ошибки
- массово повторять все failed и unknown заказы;
- откатывать файлы без проверки миграций базы;
- удалять очередь, чтобы скрыть ошибку;
- логировать токены и полные адреса клиентов;
- считать timeout гарантированным отсутствием результата;
- создавать новый reference при каждой попытке;
- обрабатывать неизвестный статус как доставленный;
- менять production-конфигурацию без тестового заказа;
- смешивать sandbox и production credentials;
- очищать общий Redis вместе с сессиями и заданиями;
- проверять только расчет тарифа и не тестировать webhook;
- включать всю накопленную очередь сразу после исправления.
Как предотвратить повторение
- фиксировать версии API, SDK и схемы payload;
- иметь адаптер перевозчика с канонической моделью заказа;
- проверять контракт на записанных обезличенных примерах;
- выполнять canary-развертывание на одном экземпляре;
- делать создание и прием webhook идемпотентными;
- хранить outbox, inbox и dead-letter очередь;
- мониторить долю ошибок по endpoint и коду ответа;
- отслеживать возраст старейшего задания и число unknown;
- оповещать об неизвестных тарифах и статусах;
- разделять кеш, сессии и очередь по namespace;
- документировать безопасный rollback и ручной режим;
- проверять обновление в staging на реальных сценариях без персональных данных.
Когда нужна помощь с интеграцией доставки
Если интеграция доставки сломалась после обновления, я могу определить точку несовместимости, проверить API-контракт, авторизацию, очередь, миграции, этикетки и webhooks, затем восстановить обработку без дублей отправлений. Для оценки пришлите версии до и после обновления, обезличенный request/response, HTTP-код, provider request ID, состояние контрольного заказа и фрагмент журнала без токенов, адресов и телефонов.