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

  1. Остановите только потребителей неисправного типа заданий.
  2. Зафиксируйте состояние очереди и список операций unknown.
  3. Проверьте совместимость кода, базы и payload очереди.
  4. Разверните рабочую версию на одном экземпляре.
  5. Выполните контрольный расчет и одно тестовое создание.
  6. Сверьте внешний ID до разрешения повторов.
  7. Постепенно верните очередь с ограниченной скоростью.
  8. После стабилизации разберите несовместимость новой версии отдельно.

Как не создать дубли отправлений

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

Пошаговый план исправления

  1. Зафиксируйте время обновления, версии компонентов и последнюю успешную операцию.
  2. Разделите сбой по функциям: тарифы, точки, создание, этикетки или статусы.
  3. Сохраните очередь и выделите заказы со статусом unknown.
  4. Выберите один контрольный заказ без персональных данных.
  5. Сравните рабочий и новый HTTP-контракт.
  6. Проверьте авторизацию, договор, endpoint и версию API.
  7. Сверьте миграции базы и версии web-процесса и воркеров.
  8. Исправьте адаптер, валидацию или конфигурацию в тестовом окружении.
  9. Добавьте idempotency key и сверку неоднозначных результатов.
  10. Прогоните контрактные и негативные тесты.
  11. Верните обработку ограниченной порцией и контролируйте метрики.
  12. Только после сверки обработайте накопившуюся очередь.

Как проверить восстановленную интеграцию

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