Оптовая платформа цифровых товаров

Типичные ошибки при интеграции API цифрового поставщика — разбор 2026

Дубли заказов, потерянные коды и расхождения по балансу — восемь ошибок интеграции, которые дороже всего обходятся, и фиксы к каждой.

Типичные ошибки при интеграции API цифрового поставщика

Интеграция с поставщиком цифровых товаров ломается не там, где ожидают. Счастливый путь — «создали заказ, получили код» — пишется за день. Всё остальное время уходит на состояния, о которых в первой версии никто не подумал. Ниже восемь режимов отказа, которые в этой отрасли встречаются чаще всего, каждый с конкретным фиксом.

1. Retry без дедупликации → дубли заказов

Что происходит. Ваш сервис отправляет POST /orders. Поставщик создаёт заказ и начинает выдачу, но ответ теряется по сети. HTTP-клиент по своей политике повторяет запрос. Поставщик видит новый запрос — создаёт второй заказ и второй раз списывает с депозита. Покупатель получил один код, вы заплатили за два.

Многие интеграторы считают, что от этого защищает заголовок идемпотентности. Проверьте, поддерживает ли ваш поставщик его на самом деле — далеко не все делают серверный dedup, и отправка заголовка «на всякий случай» не даёт ничего.

Фикс. Дедупликация на вашей стороне, всегда:

  1. Записываете локальную pending-запись с собственным UUID до вызова API.
  2. Вызываете создание заказа.
  3. При успехе сохраняете ID заказа поставщика в эту же запись.
  4. При таймауте или сетевой ошибке — не повторяете вслепую, а опрашиваете список заказов и ищете совпадение по SKU, количеству и временному окну.
  5. Создаёте новый заказ только если совпадения нет.

Подробный разбор паттерна — в материале про идемпотентность и безопасные повторы.

2. Вера в вебхуки как в гарантию

Что происходит. Обработчик написан так, будто каждое событие придёт ровно один раз и строго по порядку. В реальности вебхуки теряются при вашем деплое, приходят дважды после ретрая на стороне отправителя и обгоняют друг друга: completed может прийти раньше processing.

Результат — заказы, навсегда застрявшие в промежуточном статусе, и коды, выданные покупателю дважды, потому что обработчик отработал повторную доставку как новое событие.

Фикс.

  • Обработчик обязан быть идемпотентным: повторное событие с тем же ID не должно менять состояние.
  • Введите монотонность: игнорируйте событие, которое описывает состояние более раннее, чем уже записанное.
  • Считайте вебхук подсказкой «сходи и проверь», а не фактом: по событию делайте запрос статуса заказа и записывайте то, что вернул API.
  • Держите опрос как страховку для заказов, по которым долго нет финального статуса.

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

3. Retry без backoff и джиттера

Что происходит. Поставщик отдаёт 429 или 503. Ваш клиент повторяет через фиксированную секунду, в десять потоков, по всем зависшим заказам сразу. Вы превращаете короткую деградацию на его стороне в устойчивый шторм, продлеваете её и рискуете попасть под rate-limit надолго.

Фикс.

  • Экспоненциальная задержка с потолком плюс случайный джиттер, чтобы клиенты не синхронизировались.
  • Ретраить только то, что безопасно ретраить: GET — всегда, создание заказа — только по схеме из пункта 1.
  • Различать классы ошибок: 4xx из-за неверных данных повторять бессмысленно, 429 и 5xx — да, но с уважением к Retry-After.
  • Circuit breaker: после серии отказов переставать долбить и уводить трафик на альтернативный источник.

4. Отсутствие задачи сверки

Что происходит. Пока всё работает, никто не сравнивает свои данные с данными поставщика. Расхождения копятся тихо: заказ оплачен, но код не записан; код записан, но продажа отменена; депозит списан без успешной выдачи. Всплывает это через месяц, когда логи уже ротированы, а окно претензий к поставщику может быть закрыто.

Фикс. Ежедневная автоматическая сверка трёх списков: ваши продажи, заказы у поставщика, движения по депозиту. Задача должна помечать расхождения в отдельную таблицу и создавать оператору задачу на разбор, а не просто писать в лог. Отдельно проверяйте «зависшие»: заказы, которые дольше разумного времени не в финальном статусе.

5. Коды в открытом виде и в логах

Что происходит. Ключи и PIN лежат в базе plaintext, попадают в отладочные логи, в трейсы APM и в тела писем поддержки. Один дамп базы или один слишком подробный лог-агрегатор — и весь актив уходит.

Здесь важно понимать масштаб: цифровой код — это деньги на предъявителя. Отменить активацию нельзя, вернуть — тоже.

Фикс.

  • Шифрование в покое отдельным ключом, ключ — в менеджере секретов.
  • Строгий запрет на попадание кодов в логи, трейсы и тексты ошибок. Отдельный редактор для маскировки в логгере.
  • Ограниченный список сервисов с правом расшифровки, аудит каждого обращения.
  • Политика хранения: доставленные коды вне окна споров не должны храниться вечно.

6. Игнорирование частичного исполнения и стокаута

Что происходит. Заказано десять единиц, выдано семь. Код обрабатывает ответ как булево «успех/неуспех» — и либо отдаёт покупателю семь кодов, взяв деньги за десять, либо считает весь заказ провалившимся и не отдаёт ничего, хотя семь уже списаны с депозита.

Сюда же относятся стокаут и отзыв: и то и другое — штатные состояния, а не аварии.

Фикс.

  • Модель заказа должна быть позиционной: статус и результат хранятся на уровне каждой позиции, а не всего заказа.
  • Явные состояния: fulfilled, partially_fulfilled, out_of_stock, failed, revoked.
  • Для частичного исполнения — определённая политика: доставить выданное, вернуть деньги за невыданное, дозаказать остаток из альтернативного источника.
  • На витрине — честный статус вместо ошибки, а в бэкенде — маршрутизация на альтернативный SKU или регион, как в мультиисточниковой маршрутизации.

7. Отсутствие тестирования на sandbox и на реальных отказах

Что происходит. Интеграцию проверили тремя успешными заказами и выкатили. Первый же таймаут, первый 429 и первый частично исполненный заказ встречаются на живых деньгах и живых покупателях.

Фикс.

  • Используйте тестовый режим поставщика, если он есть, и прогоняйте не только успех: отказ, таймаут, частичное исполнение, недостаток средств на депозите, недоступный SKU.
  • Тестируйте свою сторону через мок с искусственными задержками, обрывами и дублирующимися событиями.
  • Отдельно проверьте, что происходит при деплое во время активных заказов — самый недооценённый сценарий.
  • Полный набор состояний, которые нужно покрыть, описан в объяснении order flow.

8. Захардкоженный курс и валюта

Что происходит. Курс конвертации зашит константой в коде или взят один раз при старте приложения. Через месяц вы продаёте ниже себестоимости и не понимаете, почему маржа уехала. Вариант той же ошибки — предполагать, что цена в API всегда придёт в одной валюте.

Фикс.

  • Курс — конфигурируемый параметр с явным источником, временем обновления и буфером на движение, а не константа.
  • Валюту читайте из ответа API, а не предполагайте.
  • Все денежные величины — в минорных единицах целым числом, никакой плавающей точки для денег.
  • Логируйте курс, применённый к каждому заказу: без этого сверка маржи задним числом невозможна.
  • Как курс встраивается в итоговую цену, разобрано в расчёте скидки реселлера.

Короткий чек-лист перед выкаткой

Проверка Готово?
Локальная pending-запись создаётся до вызова API
Повтор создания заказа проходит через проверку существующих
Обработчик событий идемпотентен и игнорирует устаревшие
Ретраи с экспоненциальной задержкой, джиттером и circuit breaker
Ежедневная сверка продаж, заказов и депозита
Коды зашифрованы, отсутствуют в логах и трейсах
Частичное исполнение и стокаут — явные состояния
Отказы протестированы, не только счастливый путь
Курс конфигурируем, валюта читается из ответа

Где брать товар

Половина перечисленных проблем снимается на уровне выбора поставщика, а не кода: понятная модель статусов заказа, честные состояния при нехватке стока, широкий каталог, чтобы не поддерживать пять разных интеграций с пятью разными наборами багов. FoxReload даёт 900+ SKU по ключам, подарочным картам, топ-апам, eSIM и софт-лицензиям через один REST API с автовыдачей — то есть один контракт, одна логика ретраев и одна сверка вместо зоопарка.

Дальше логично прочитать быстрый старт по API и сравнение предзакупки и выдачи по запросу — оно определяет, какую часть этого списка вам вообще придётся реализовывать.

Часто задаваемые вопросы

Что делать, если запрос на создание заказа завершился таймаутом?
Ни в коем случае не отправлять его повторно вслепую. Таймаут не означает, что заказ не создан — он означает, что вы не знаете результат. Правильная последовательность: сохранить локальную pending-запись ещё до вызова, затем при таймауте опросить список заказов у поставщика и попытаться сопоставить существующий заказ с этой записью по SKU, количеству и временному окну. Новый заказ создавать только если совпадения нет.
Можно ли доверять вебхукам как единственному источнику статуса?
Нет. Даже когда поставщик поддерживает вебхуки, доставка бывает потерянной, повторной или не по порядку — сеть, деплой на вашей стороне, перезапуск воркера. Вебхук стоит воспринимать как подсказку «сходи и проверь», а не как факт. Источником истины должен быть ваш опрос статуса заказа, а обработчик обязан быть идемпотентным и игнорировать событие, которое старше уже записанного состояния. Некоторые поставщики вебхуков вообще не дают, и тогда опрос остаётся единственным механизмом.
Как хранить выданные коды безопасно?
Шифровать в покое, отдельным ключом, с ключом в менеджере секретов, а не в репозитории и не в переменной окружения рядом с кодом приложения. Доступ к расшифровке должен быть у минимального числа сервисов, каждое обращение — логироваться. В логи, трейсы и сообщения об ошибках сам код не должен попадать никогда, включая частичную маскировку, которой достаточно для восстановления. И заведите срок хранения: код, уже доставленный покупателю и вышедший из окна споров, не обязан лежать у вас вечно.
Зачем нужна отдельная задача сверки, если статусы и так приходят?
Потому что расхождения возникают именно там, где обе стороны считают, что всё в порядке. Ежедневная сверка сопоставляет три списка — ваши продажи, заказы у поставщика и движения по депозиту — и ловит заказы, зависшие в промежуточном статусе, списания без выданного кода, выданные коды без соответствующей продажи. Без такой задачи вы узнаёте о проблеме от покупателя или из месячного отчёта, когда логи уже частично ротированы, а окно претензий к поставщику может быть закрыто.
Смотреть оптовые цены FoxReload

Похожие статьи