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

API подарочных карт — получение кода и автоматическая выдача 2026

Полный путь кода подарочной карты — от создания заказа до доставки покупателю, без дублей и с доказательной базой для споров.

API подарочных карт — получение кода и автоматическая выдача

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

Пять этапов, которые нужно различать

Большинство поломок в выдаче кодов происходит потому, что разработчик считает всё это одной операцией. Это пять разных операций с разными режимами отказа:

Этап Что происходит Кто владеет состоянием
1. Заказ Вы отправляете запрос на покупку Ваша база + поставщик
2. Выпуск Поставщик резервирует и выпускает код Поставщик
3. Получение Вы забираете код из ответа Ваша база
4. Хранение Код зашифрован и лежит у вас Ваша база
5. Доставка Покупатель видит код Ваш фронтенд / письмо

Каждый переход должен быть отдельной записью в вашей базе с временной меткой. Если у вас одна колонка status на весь путь, при первом же сбое вы не сможете ответить на вопрос «код вообще выпустился или нет».

Этап 1. Заказ

Заказ создаётся с идентификатором товара и количеством:

curl -X POST "https://public-api.foxreload.com/api/orders" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "itemId": "product_01krgfgww8eth9xvvysd6y7r4j",
        "quantity": 1
      }
    ],
    "isMock": false
  }'

Ответ — объект заказа со статусом active, полями id, price, createdAt, paymentExpiresAt и массивом items[].

Три правила на этом этапе:

  • Записывайте в свою базу намерение до вызова поставщика, а не после. Строка «мы собираемся заказать это для клиента X» должна существовать раньше, чем уйдёт HTTP-запрос — иначе при падении процесса между отправкой и ответом у вас не останется следа.
  • Сохраняйте order_id поставщика сразу, как только он пришёл. Это единственный ключ, по которому вы потом найдёте заказ.
  • Не смешивайте мок и прод в одной таблице без явного флага. У FoxReload объект заказа содержит поле isMock — копируйте его к себе.

Этап 2. Выпуск на стороне поставщика

После создания заказ проходит activepaid (списание с баланса) → processing (выпуск у поставщика) → completed.

Этап processing — это чужая система, и он может длиться от секунд до заметно дольше в зависимости от типа карты. Ваша задача здесь только одна: не делать ничего разрушительного, пока состояние неизвестно. Никаких «наверное, не получилось, закажем ещё раз».

Этап 3. Получение кода

Код забирается опросом статуса заказа:

curl "https://public-api.foxreload.com/api/orders/{order_id}" \
  -H "X-API-Key: YOUR_API_KEY"

Когда status == "completed", каждая позиция items[] содержит:

  • product — объект товара с id, name, userGuide, attributes
  • externalData[] — массив выданных кодов или PIN
  • error — ошибка по этой конкретной позиции, если она есть

Поле userGuide не декоративное. Это инструкция по активации от поставщика, и её нужно доставлять покупателю вместе с кодом — половина обращений в поддержку «код не работает» на самом деле означает «покупатель активирует его не в том регионе или не в том разделе аккаунта».

Про вебхуки — проверьте своего поставщика

Некоторые поставщики умеют присылать уведомление о готовности заказа вебхуком, часто с HMAC-подписью. Проверьте, реализует ли это ваш конкретный поставщик. FoxReload вебхуков не поддерживает — результат получается только опросом.

Даже если вебхуки есть, опрос всё равно нужен как страховка: доставка вебхука никогда не гарантирована, а ваш обработчик может быть недоступен именно в тот момент. Вебхук — ускоритель, опрос — источник истины.

Частичное выполнение

В многопозиционном заказе позиции завершаются независимо. Возможна ситуация, когда одна позиция дала код, а другая ушла в ошибку с заполненным items[].error.

Правильная реакция:

  1. Всё, что реально выпущено, — сохранить и выдать покупателю. Эти деньги уже потрачены, код существует.
  2. По невыданной части — либо отдельная новая операция заказа, либо возврат клиенту.
  3. Никогда не помечать весь заказ провалившимся, если часть кодов уже ушла. Это приводит к возврату денег за товар, который клиент получил.

Этап 4. Защита от двойной выдачи

Это самая дорогая ошибка в категории. Она возникает в двух местах.

Дубль на уровне заказа

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

Некоторые API принимают заголовок Idempotency-Key, который решает это на стороне поставщика. Обязательно проверьте, поддерживает ли это ваш поставщик — многие не поддерживают. FoxReload заголовок Idempotency-Key не поддерживает.

Значит, защита строится у вас:

  1. Уникальный ключ операции сохраняется в вашей базе до вызова.
  2. При таймауте — сначала запрос списка заказов у поставщика с фильтром по статусам, проверка, не создался ли заказ.
  3. Повтор — только после подтверждения, что заказа нет.
curl "https://public-api.foxreload.com/api/orders/?statuses=active,processing&limit=20" \
  -H "X-API-Key: YOUR_API_KEY"

Дубль на уровне выдачи

Второй источник — гонка внутри вашей системы. Клиент дважды нажал «получить код», два параллельных запроса прочитали строку заказа как «код готов, не выдан» и оба пошли выдавать.

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

Ретраи

Общее правило: ретраить можно только то, что безопасно повторить.

  • 429 и 5xx — ретрай с экспоненциальным backoff и джиттером.
  • 4xx, кроме 429, — не ретраить никогда, ответ не изменится.
  • Таймаут на создании заказа — не ретрай, а сначала проверка состояния.

Подробнее — в статье про retry-паттерны.

Этап 5. Хранение — шифрование и доступ

Код подарочной карты — это предъявительский инструмент. Кто его прочитал, тот им и воспользовался.

  • Шифруйте значение кода на уровне приложения, ключом из секрет-менеджера. Шифрования диска недостаточно: дамп базы, украденный бэкап или SQL-инъекция читают диск уже расшифрованным.
  • Ограничьте расшифровку одним сервисом. Права на расшифровку не должны быть у аналитической реплики, у админки поддержки и у отладочных скриптов.
  • Никогда не пишите код в логи. Ни в обычные, ни в debug, ни в трейсы APM, ни в тело письма в тикете. Маскируйте значение на выходе из слоя доступа к данным, а не в каждом месте вывода по отдельности.
  • Храните рядом хэш кода. Он позволяет доказать «мы выдали именно этот код», не показывая значение повторно.
  • Определите срок хранения. После выдачи и истечения окна споров зашифрованное значение можно удалить, оставив хэш и метаданные.

Этап 6. Доставка покупателю и аудит

Доставка — это тоже операция, которая может провалиться. Письмо не дошло, вкладка закрылась, покупатель ввёл чужой e-mail.

Работающая схема:

  • Код показывается один раз на защищённой странице заказа, доступной только авторизованному покупателю.
  • Повторный доступ — через личный кабинет, с записью каждого просмотра в журнал.
  • Ссылка на разовый просмотр, если она есть, живёт ограниченное время и одноразова.
  • Факт первого показа фиксируется с временной меткой — это то, чем вы оперируете в споре.

Аудит-трейл

Минимальный набор событий, который нужно писать по каждому коду:

Событие Что фиксируем
Заказ создан Внутренний ID, товар, покупатель, время
ID поставщика получен order_id поставщика
Код получен Хэш кода, позиция заказа, время
Код показан покупателю Время, IP, идентификатор сессии
Открыт спор Ссылка на заказ и на тикет
Решение по спору Возврат, отказ, замена

Именно этот журнал — а не переписка в поддержке — отвечает на вопрос «был ли код выдан и когда». Про то, как эти данные работают в реальных спорах, — в материале про чарджбэки в цифровых товарах.

Отдельно держите в голове риски, которые не лечатся кодом: отзыв кода эмитентом, региональные ограничения активации, аннулирование карты при подозрении на фрод у эмитента. Разбор этих сценариев — в статье про отзыв кодов и региональные локи.

Где брать подарочные карты оптом

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

Читать дальше

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

Как получить код подарочной карты через API?
Сначала создаётся заказ с идентификатором товара и количеством, затем поставщик выпускает код, и вы забираете его, опрашивая статус заказа. У FoxReload код приходит в поле items[].externalData, когда статус заказа становится completed. Вебхуков в FoxReload нет, поэтому опрос — штатный и единственный способ узнать результат. Забирайте код сразу и сохраняйте на своей стороне в зашифрованном виде.
Как не выдать один код дважды при повторной попытке?
Правило одно — перед повторным созданием заказа всегда перечитайте состояние на стороне поставщика. Таймаут не означает, что заказ не создался. У FoxReload заголовок Idempotency-Key не поддерживается, поэтому защита от дублей строится на вашей стороне: уникальный ключ операции в вашей базе, запрос списка заказов перед ретраем и повторная попытка только после подтверждения, что заказа нет. Отдельно закройте гонку в самой выдаче — блокировкой строки заказа в транзакции.
Как правильно хранить коды подарочных карт?
Шифруйте их при хранении, а не только на диске — то есть само значение кода должно быть зашифровано на уровне приложения ключом из секрет-менеджера. Доступ к расшифровке даётся только сервису выдачи, а не всей команде. Код не должен попадать ни в обычные логи, ни в аналитику, ни в письма поддержки. Храните рядом хэш кода — он позволит подтвердить, что вы выдали именно этот код, без хранения его в открытом виде повторно.
Что делать, если заказ выполнен частично?
Обрабатывать позиции независимо. В многопозиционном заказе одна позиция может уйти в completed, а другая — в failed, и поле items[].error покажет причину по конкретной позиции. Покупателю выдаётся то, что реально выпущено, а по невыданной части делается либо повторный заказ отдельной операцией, либо возврат. Считать весь заказ проваленным и вернуть деньги целиком — ошибка, если часть кодов уже ушла клиенту.
Смотреть оптовые цены FoxReload

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