SportAPI Документация
RU
C Документация продуктаCoupon API
v1
Услуга и цены ↗ Получить доступ ↗
Coupon API / Callback результатов

Callback результатов

Callback позволяет получать изменения расчета купонов без постоянного опроса API.

Использование callback необязательно. Партнер может получать результаты только через методы чтения купонов либо использовать callback вместе с периодической сверкой.

Подключение callback

Если партнер хочет использовать callback:

  1. сообщите об этом менеджеру;
  2. передайте менеджеру необходимые данные для подключения;
  3. менеджер включит callback и создаст секретную фразу callback_secret;
  4. сохраните секрет на серверной стороне;
  5. передавайте URL обработчика в callback_url при создании каждого купона.

Секретная фраза используется для проверки HMAC-подписи. Она не возвращается клиентским API и не должна попадать в браузер, логи или публичный код.

Условия отправки

Callback отправляется, только если одновременно:

  • менеджер включил callback для аккаунта;
  • для аккаунта настроена секретная фраза;
  • при создании конкретного купона передан непустой callback_url.

Если хотя бы одно условие не выполнено, результат нужно получать запросами API.

URL обработчика

callback_url партнер определяет самостоятельно и передает при создании купона:

{
  "callback_url": "https://partner.example.com/api/coupon-result"
}

Правила:

  • в production используйте только https://;
  • в тестовом окружении допускается http://;
  • один купон может иметь только один callback_url;
  • разные купоны могут использовать разные URL;
  • URL сохраняется вместе с купоном и используется для его последующих обновлений;
  • endpoint должен принимать метод POST и JSON.

При multi = true URL сохраняется для каждого созданного ординара.

Если callback не используется, поле можно не передавать, передать null, пустую строку или основной домен сайта партнера. Основной домен не является обработчиком callback и должен использоваться только при отключенном callback.

Сетевой доступ и IP allowlist

Endpoint должен быть доступен для входящих запросов Системы расчета купонов SportAPI.

IP-фильтрация на стороне партнера необязательна. Конкретный исходящий IP в публичной документации не фиксируется. Если партнер хочет использовать allowlist, актуальный адрес необходимо получить у менеджера.

Независимо от IP-фильтрации проверка HMAC-подписи обязательна.

HTTP-запрос

Система отправляет:

POST {callback_url} HTTP/1.1
Content-Type: application/json
Accept: application/json
X-Coupon-Signature: sha256=<hex_hmac_sha256>

Подпись вычисляется по точным байтам JSON-тела. Не разбирайте и не сериализуйте JSON повторно до проверки подписи.

Полный алгоритм и примеры кода приведены в разделе «Проверка подписи callback».

Тип события

Поле event всегда содержит:

coupons.settled

Это значение используется для:

  • окончательного расчета ординара;
  • промежуточного состояния экспресса;
  • первого проигрыша экспресса;
  • финального состояния экспресса;
  • возврата купона на перерасчет;
  • нового результата после перерасчета.

Название события не означает, что каждый полученный снимок обязательно является финальным. Всегда проверяйте coupons[].status и статусы ставок.

Пакетная доставка

Один HTTP-запрос может содержать до 100 снимков купонов:

{
  "couponCount": 2,
  "coupons": [
    {},
    {}
  ]
}

couponCount показывает количество элементов в coupons. Обработчик должен перебрать весь массив, а не только первый купон.

В один пакет входят снимки одного аккаунта и одного callback_url.

Пример payload

Пример промежуточного состояния экспресса:

{
  "event": "coupons.settled",
  "batchId": "d407e986f3a64d9d36a77bf532322ef8",
  "clientId": 17,
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000272",
      "realWin": 0,
      "calculate_coefficient": 0,
      "status": 0,
      "calculate_date": 1784897500000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 2,
          "calculate_date": 1784897500000,
          "calculate_score": "2:1",
          "settlement_reason_code": "REMOTE_WIN",
          "settlement_reason": "Win",
          "timer": 0
        },
        {
          "uuid": "913",
          "status": 0,
          "calculate_coefficient": null,
          "calculate_date": null,
          "calculate_score": "",
          "settlement_reason_code": null,
          "settlement_reason": null,
          "timer": 0
        }
      ]
    }
  ]
}

В этом примере первая ставка рассчитана, а вторая еще активна. Поэтому купон сохраняет статус 0, а realWin и calculate_coefficient пока равны 0.

Поля верхнего уровня

ПолеТипОписание
eventstringТип события. Всегда coupons.settled.
batchIdstringИдентификатор конкретной версии пакета callback. Используется для идемпотентности.
clientIdintegerВнутренний ID аккаунта партнера. Имеет информационный характер.
couponCountintegerКоличество элементов в массиве coupons.
couponsarrayРасчетные снимки купонов.

Поля снимка купона

ПолеТипОписание
coupon_codestringПубличный код купона.
realWinnumberФактическая выплата. В промежуточном снимке — 0.
calculate_coefficientnumberИтоговый расчетный коэффициент. В промежуточном снимке — 0.
statusintegerТекущий статус купона.
calculate_dateinteger/nullПоследняя дата расчета среди рассчитанных ставок, Unix-миллисекунды.
events_dataarrayВсе ставки купона, включая еще не рассчитанные.

Поля ставки

ПолеТипОписание
uuidstringСтабильный внутренний ID ставки внутри купона.
statusintegerТекущий статус ставки.
calculate_coefficientnumber/nullРасчетный множитель ставки.
calculate_dateinteger/nullДата расчета ставки, Unix-миллисекунды.
calculate_scorestringСчет расчета; для активной ставки может быть пустой строкой.
settlement_reason_codestring/nullСтабильный машиночитаемый код причины расчета или возврата; до расчета null.
settlement_reasonstring/nullТекст причины расчета или возврата; до расчета null.
timerintegerПоле совместимости, сейчас имеет значение 0.

Используйте settlement_reason_code для аналитики и программного ветвления. settlement_reason может содержать исходный текст причины и предназначен прежде всего для хранения, журналирования или отображения. Набор кодов может расширяться, поэтому неизвестный код не должен приводить к отклонению callback.

Если партнер показывает пользователю причину результата, лучше локализовать собственное сообщение по коду. Например, MATCH_POSTPONED означает перенос матча, MATCH_CANCELLED — отмену, а MARKET_PUSH — возврат по правилам рынка. При неизвестном коде используйте settlement_reason как запасной текст, но не как ключ финансовой логики.

Что отсутствует в callback

Callback содержит расчетный снимок, а не полную карточку купона. В нем нет:

  • amount;
  • валюты;
  • названий спорта и турнира;
  • названий команд;
  • названий рынков и исходов;
  • исходного указателя ставки.

При этом callback содержит settlement_reason_code и settlement_reason для каждой ставки.

Эти данные следует сохранить после создания купона либо получить через методы чтения купона.

Идентификаторы

Не путайте назначение идентификаторов:

ПолеЧто идентифицирует
coupon_codeКупон.
events_data[].uuidКонкретную ставку внутри купона.
batchIdКонкретную версию пакета callback.

В полной модели купона ставка имеет числовое поле events_data[].id. В callback тот же ID передается строкой в uuid.

Рекомендуемые связи в базе партнера:

купон: coupon_code
ставка: coupon_code + uuid
доставка callback: batchId

batchId не является ID купона или ставки.

Несколько callback одного купона

Один coupon_code может штатно прийти несколько раз:

  • экспресс обновляется по мере расчета ставок;
  • первый проигрыш отправляется сразу;
  • после расчета всех ставок проигравшего экспресса приходит финальный снимок;
  • перерасчет создает новое состояние.

Каждое новое состояние получает новый batchId. Повторная HTTP-доставка уже созданного пакета сохраняет прежний batchId.

Нельзя считать новое появление coupon_code дубликатом. Обновляйте состояние купона по coupon_code, а повтор одной доставки определяйте по batchId.

Ординар и экспресс

Для ординара callback отправляется после появления результата.

Для экспресса:

  1. пока экспресс не проиграл, после расчета каждой следующей ставки отправляется новый снимок;
  2. при первом проигравшем исходе сразу отправляется снимок со статусом купона 4;
  3. следующие промежуточные изменения проигравшего экспресса не отправляются;
  4. после расчета всех ставок отправляется финальный полный снимок.

Подробный сценарий описан в разделе «Жизненный цикл расчета».

Перерасчет

При возврате купона на перерасчет:

  • купон получает статус 15;
  • соответствующая ставка получает статус 4;
  • снимок с этим состоянием отправляется сразу;
  • после перерасчета отправляется новый снимок с актуальными статусами.

Если предыдущий результат уже был финансово обработан, при первом получении статуса 15 партнер:

  1. повторно списывает сумму ставки amount;
  2. ожидает новый финальный результат;
  3. начисляет новое значение realWin.

Callback не содержит amount, поэтому сумму нужно сохранить при создании купона либо получить через API чтения.

Финансовая операция выполняется один раз. Повторная доставка с тем же batchId не должна повторно списывать или начислять средства.

Минимальная обработка

Рекомендуемый порядок:

  1. прочитать исходное тело запроса как байты;
  2. проверить X-Coupon-Signature;
  3. начать транзакцию;
  4. сохранить batchId с уникальным ограничением;
  5. если batchId уже обработан, не повторять бизнес-операции;
  6. обработать все элементы coupons;
  7. обновить купоны по coupon_code и ставки по uuid;
  8. зафиксировать финансовые операции;
  9. завершить транзакцию;
  10. вернуть HTTP 200.

Подробности: «Повторы и идемпотентность».

Успешный ответ

Минимальный успешный ответ:

HTTP/1.1 200 OK

Тело может быть пустым.

Рекомендуемый расширенный ответ:

{
  "success": true,
  "processed": 1
}

Значение processed должно быть равно couponCount.

Возвращайте HTTP 200 только после успешной фиксации всех элементов пакета.

Ответы 201, 202 и 204 не считаются успешным подтверждением доставки: текущий контракт требует именно HTTP 200.

Отрицательное или частичное подтверждение

Даже при HTTP 200 тело может сообщить об ошибке:

  • success: false — пакет не обработан;
  • processed < couponCount — обработана только часть пакета.

Такое подтверждение считается финальной ошибкой и автоматически не повторяется. Поэтому не возвращайте success: false или частичный processed в расчете на повторную доставку.

Временные ошибки должны возвращать один из предусмотренных HTTP-кодов. Расписание и условия повторов описаны в разделе «Повторы и идемпотентность».

Следующий раздел: «Проверка подписи callback».