SportApi
Документация API · версия 1.2.0

Исходы, ставки и купоны — техническая документация АПИ

Этот файл содержит минимальный самостоятельный сценарий подключения к Системе расчета купонов SportAPI. Подробности и редкие случаи находятся в полной документации.

Основной сценарий http
POST /api/partner/login
POST /api/partner/coupons/place
GET  /api/partner/coupons/calculated?time=10

1. Что нужно получить у менеджера

  • базовый URL API;
  • логин и пароль клиентского аккаунта;
  • при использовании callback — включение функции и секретную фразу callback_secret.

В примерах используется условный адрес:

BASE_URL="https://coupon-api.example.com"

В production используйте HTTPS. Все даты передаются как Unix timestamp в миллисекундах, а денежные значения — как десятичные числа без фиксированной точности.

2. Формат ответа API

Успех:

{
  "code": 1,
  "body": {},
  "error_code": null,
  "error_message": null,
  "date": 1784970000000,
  "time_ms": 5,
  "path": "/api/partner/example"
}

Бизнес-ошибка обычно также приходит с HTTP 200:

{
  "code": 0,
  "body": null,
  "error_code": 1002,
  "error_message": "Not all params",
  "date": 1784970000000,
  "time_ms": 5,
  "path": "/api/partner/example"
}

Всегда проверяйте HTTP-код, затем code, затем error_code. Не используйте текст error_message как программный ключ.

3. Авторизация

POST /api/partner/login
Content-Type: application/json
{
  "username": "partner-demo",
  "password": "strong-password"
}

Поле login поддерживается как совместимый алиас username.

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

{
  "code": 1,
  "body": {
    "token": "<jwt-token>",
    "user_id": 17,
    "username": "partner-demo"
  },
  "error_code": null,
  "error_message": null
}

Во всех защищенных запросах передавайте:

Authorization: Bearer <jwt-token>

Основные ошибки входа:

КодПричина
1002Не передан логин или пароль.
1003Неизвестный логин или неверный пароль.
1004Клиентский аккаунт отключен.
1006Истекла дата доступа.
1007Клиентский баланс равен нулю или отрицательный.

Эти ошибки возвращаются с HTTP 200, code = 0. HTTP 401 защищенного метода означает, что JWT отсутствует, неверен, истек или отозван; выполните вход повторно. HTTP 403 означает неподходящую роль или запрет доступа.

4. Указатель ставки

Каждый выбранный исход передается готовой строкой из спортивной линии:

line_type#game_id#group_id#type_id#rate#coefficient[#player_id]

Примеры:

line#737779544#1#1#0#1.85
live#738917381|119|5869|0.5#3.6#149439538
  • line_type: line или live;
  • rate: параметр тотала/форы либо 0;
  • player_id: необязательный ID игрока;
  • разделители # и | поддерживаются.

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

5. Создание купона

POST /api/partner/coupons/place
Authorization: Bearer <jwt-token>
Content-Type: application/json
{
  "list_bets": [
    "line#737779544#1#1#0#1.85"
  ],
  "amount": 10,
  "currency": "USD",
  "callback_url": "https://partner.example.com/api/coupon-result",
  "lang": "en",
  "mode": "reject",
  "mode_type": null,
  "multi": false
}
ПолеОбязательноеНазначение
list_betsДаОдин или несколько указателей.
amountДаПоложительная сумма одного создаваемого купона.
currencyНетЛюбое строковое обозначение, включая виртуальную валюту.
callback_urlНетURL callback; без callback можно не передавать, передать null, пустую строку или домен сайта.
langНетДвухбуквенный код одного из примерно 50 поддерживаемых языков. Язык названий фиксируется при создании.
modeНетreject или accept; по умолчанию reject.
mode_typeДля acceptДопустимое направление изменения коэффициента.
multiНетОдин общий купон или отдельные ординары; по умолчанию false.

Коэффициенты

НастройкаПоведение
mode = rejectОтклонить создание при изменении коэффициента.
mode = accept, mode_type = 1Принимать только повышение.
mode = accept, mode_type = 2Принимать только понижение.
mode = accept, mode_type = 3Принимать любое изменение.

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

ЗапросРезультатСписание клиентского баланса
Один исходОдин ординарamount
Несколько исходов, multi = falseОдин экспрессamount
Несколько исходов, multi = trueОтдельный ординар на каждый исходamount × количество созданных купонов

Один экспресс может содержать не более 15 событий. Нельзя объединять несколько ставок одного матча, включая основной матч, таймы, периоды, угловые, фолы и другие связанные саб-события. Такая комбинация возвращает 506.

6. Подтверждение создания купона и ставки

Полный успешный ответ с данными купона и принятой ставки внутри events_data:

{
  "code": 1,
  "body": {
    "coupons": [
      {
        "coupon_code": "000000000272",
        "amount": 10,
        "win": 18.5,
        "potential_win": 18.5,
        "real_win": null,
        "coef": 1.85,
        "original_coef": 1.85,
        "calculate_coef": null,
        "has_return": false,
        "date": 1784970000000,
        "status": 0,
        "asian": false,
        "calculate_date": null,
        "coupon_type": 1,
        "events_count": 1,
        "events_data": [
          {
            "id": 912,
            "game_id": 737779544,
            "main_game_id": 737779544,
            "is_sub_game": false,
            "parent_game_id": null,
            "sub_game_key": null,
            "raw_pointer": "line#737779544#1#1#0#1.85",
            "line_type": "line",
            "is_live": false,
            "bet_group_id": 1,
            "bet_group_name": "Match result",
            "bet_id": 1,
            "bet_name": "First team to win",
            "sport_id": 1,
            "sport_name": "Football",
            "tournament_id": 10001,
            "tournament": "National League",
            "event_date": 1784971800000,
            "status": 0,
            "opp1": "Team A",
            "opp2": "Team B",
            "coef": 1.85,
            "calc_coef": null,
            "bet_score": "line#737779544#1#1#0#1.85",
            "calculate_date": null,
            "calculate_score": null,
            "settlement_reason_code": null,
            "settlement_reason": null,
            "placement_score_full": null,
            "placement_score_periods": null,
            "calculation_score_full": null,
            "calculation_score_periods": null,
            "timer": null,
            "dop_name": null,
            "rate": "0",
            "sgame_id": null,
            "game_num": null,
            "stat_id": null,
            "team1_id": 101,
            "team2_id": 102,
            "opp_icon1": 101,
            "opp_icon2": 102
          }
        ]
      }
    ]
  },
  "error_code": null,
  "error_message": null,
  "date": 1784970000015,
  "time_ms": 45,
  "path": "/api/partner/coupons/place"
}

Поля купона

Каждый элемент body.coupons[] — отдельный созданный купон.

ПолеТипЗначение
coupon_codestringПубличный 12-значный код купона. Храните строкой, чтобы не потерять ведущие нули.
amountnumberСумма купона, переданная при создании.
winnumberТекущая отображаемая сумма выигрыша.
potential_winnumberВозможный выигрыш до окончательного расчета.
real_winnumber/nullФактическая сумма выплаты после расчета; до расчета — null.
coefnumberТекущий или итоговый коэффициент купона.
original_coefnumberОбщий коэффициент купона в момент его создания.
calculate_coefnumber/nullИтоговый расчетный коэффициент; до расчета — null.
has_returnbooleantrue, если в купоне есть ставка со статусом возврата.
dateintegerДата создания купона, Unix timestamp в миллисекундах.
statusintegerТекущий статус купона. Значения описаны в разделе «Статусы купона и ставок».
asianbooleanПризнак наличия азиатского расчета, включая половинный выигрыш или проигрыш.
calculate_dateinteger/nullДата расчета купона в миллисекундах; до расчета — null.
coupon_typeintegerТип купона: 1 — ординар, 2 — экспресс.
events_countintegerКоличество ставок внутри купона.
events_dataarrayПолный массив ставок, входящих в купон.

Поля ставки

Каждый элемент events_data[] описывает конкретную принятую ставку внутри купона.

ПолеТипЗначение
idinteger/nullВнутренний ID принятой ставки. Используется вместе с coupon_code для поиска ставки внутри купона.
game_idintegerID события или саб-события, на которое сделана ставка.
main_game_idinteger/nullID основного матча, к которому относится событие или саб-событие.
is_sub_gamebooleantrue, если ставка относится к тайму, периоду, сету, угловым или другому саб-событию.
parent_game_idinteger/nullID непосредственного родительского события, если оно существует.
sub_game_keystring/nullТехнический ключ саб-события или периода.
raw_pointerstringИсходный указатель ставки, принятый API.
line_typestringТип линии: line — прематч, live — событие в реальном времени.
is_livebooleantrue, если ставка была создана по live-линии.
bet_group_idintegerID группы ставок или рынка.
bet_group_namestring/nullЛокализованное название группы ставок.
bet_idintegerID выбранного исхода внутри группы ставок.
bet_namestring/nullЛокализованное полное название выбранного исхода.
sport_idinteger/nullID вида спорта.
sport_namestring/nullЛокализованное название вида спорта.
tournament_idinteger/nullID турнира.
tournamentstring/nullЛокализованное название турнира.
event_dateinteger/nullДата начала события, Unix timestamp в миллисекундах.
statusintegerТекущий статус расчета ставки. Не путайте со статусом купона.
opp1string/nullНазвание первой команды или участника.
opp2string/nullНазвание второй команды или участника.
coefnumberКоэффициент ставки, с которым она была принята.
calc_coefnumber/nullРасчетный множитель ставки; до расчета — null.
bet_scorestringСовместимое поле старого API, содержащее указатель ставки, а не счет матча.
calculate_dateinteger/nullДата расчета ставки в миллисекундах; до расчета — null.
calculate_scorestring/nullСчет, использованный при расчете ставки.
settlement_reason_codestring/nullСтабильный машинный код причины расчета или возврата; до расчета — null.
settlement_reasonstring/nullПоясняющий текст причины расчета или возврата; до расчета — null.
placement_score_fullstring/nullОбщий счет события в момент создания ставки, если доступен.
placement_score_periodsstring/nullСчет по периодам в момент создания ставки, если доступен.
calculation_score_fullstring/nullОбщий счет события в момент расчета ставки.
calculation_score_periodsstring/nullСчет по периодам в момент расчета ставки.
timerinteger/nullТаймер события в момент сохранения данных, если доступен.
dop_namestring/nullНазвание саб-события: тайма, периода, сета, иннинга и т. п.
ratestringПараметр исхода, например значение тотала или форы; для исхода без параметра — "0".
sgame_idstring/nullВнешний ключ саб-события.
game_numinteger/nullНомер игры, если он предоставлен источником.
stat_idstring/nullВнешний статистический ID события.
team1_idinteger/nullID первой команды или участника.
team2_idinteger/nullID второй команды или участника.
opp_icon1integer/nullСовместимый ID иконки первой команды; совпадает с team1_id.
opp_icon2integer/nullСовместимый ID иконки второй команды; совпадает с team2_id.

Значение null является нормальным для еще не рассчитанных или недоступных данных. Не заменяйте его автоматически на 0 или пустую строку.

Считайте купон принятым только при code = 1 и наличии объектов в body.coupons. До этого корзина является предварительным выбором: исход мог исчезнуть, заблокироваться или изменить коэффициент.

После успеха:

  1. сохраните все объекты body.coupons, а не только первый;
  2. сохраните coupon_code строкой с ведущими нулями;
  3. сохраните нужные данные из events_data;
  4. сопоставьте купон с конечным пользователем;
  5. зафиксируйте финансовую операцию пользователя в системе партнера.

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

ПолеНазначение
coupon_codeПубличный код купона.
events_data[].idID конкретной принятой ставки внутри купона.
callback events_data[].uuidТот же ID ставки, переданный строкой.
batchIdID версии пакета callback, а не купона или ставки.

currency не возвращается в полной модели и callback. Если валюта нужна, сохраните значение из запроса создания.

7. Ошибки создания

КодПричинаДействие
10Нет тела запроса.Исправить запрос.
11Неверный указатель.Получить актуальный указатель из линии.
12Неверный amount.Передать положительное число.
501Коэффициент изменился.Показать новое значение или изменить mode.
502Исход отсутствует.Удалить/обновить ставку в корзине.
503Исход заблокирован.Сообщить о временной недоступности.
504Ошибка проверки исхода.Не считать купон принятым; повторить позже.
506В экспрессе ставки одного матча.Оставить один исход или использовать отдельные ординары.
507Недостаточный клиентский баланс.Пополнить баланс или уменьшить общую сумму.
1002Неверный набор параметров.Исправить параметры.
10000Внутренняя ошибка.Зафиксировать ошибку и проверить результат перед повтором.

Для 501504 новый API возвращает проблемные ставки в body.changes[]. Поле change_type: 1 — коэффициент вырос, 2 — снизился, null — направление неприменимо.

Не повторяйте POST /coupons/place вслепую после timeout: первый запрос мог быть принят, и повтор создаст дубликат.

8. Получение купонов

ОперацияEndpointРезультат
Один купонGET /api/partner/coupons/get?coupon_code={code}Купон в body.
АктивныеGET /api/partner/coupons/activeМассив в body[].
Недавние расчетыGET /api/partner/coupons/calculated?time=10Массив в body[]; максимум 120 минут.
По кодам/периодуPOST /api/partner/coupons/resultsМассив в body.coupons.
Клиентский балансGET /api/partner/balancebody.balance.

По кодам можно передать до 100 значений:

{
  "coupon_ids": ["000000000272", "000000000273"]
}

Либо передать период создания не более 24 часов:

{
  "start_date": 1784880000000,
  "end_date": 1784966400000
}

Не объединяйте coupon_ids и даты в одном запросе. results фильтрует по времени создания, а calculated — по времени окончательного расчета.

9. Статусы купона и ставок

Статусы купона:

КодЗначениеФинальный
0Активен или частично рассчитан.Нет
2Выиграл.Да
4Проиграл.Да
8Полностью возвращен.Да
15Возвращен на перерасчет; ожидайте новый результат.Нет

Статусы ставки:

КодЗначениеcalc_coef
0Не рассчитана.null
1Выигрыш.Исходный коэффициент
2Проигрыш.0
3Возврат.1
4Ожидает перерасчета.null
21Половинный выигрыш.(coef + 1) / 2
22Половинный проигрыш.0.5
23Push.1

Для финансового начисления конечному пользователю используйте готовое real_win. Не используйте potential_win и не пересчитывайте выплату самостоятельно. До расчета real_win, calculate_coef, calc_coef и calculate_date равны null, а не 0.

При первом статусе купона 15, если предыдущий результат уже был финансово обработан, партнер один раз повторно списывает amount с конечного пользователя, ожидает новый финальный статус и начисляет новое real_win. Защитите операции от повторной обработки.

10. Причина расчета

У каждой ставки в полной модели и callback есть:

ПолеНазначение
settlement_reason_codeСтабильный код причины расчета/возврата.
settlement_reasonПоясняющий исходный текст.

До расчета оба поля равны null. Для локализованного уведомления используйте код, например:

  • MATCH_POSTPONED — матч перенесен;
  • MATCH_CANCELLED — матч отменен;
  • MARKET_PUSH — возврат по правилам рынка.

При неизвестном коде сохраните его и используйте непустой settlement_reason как запасной текст. Не рассчитывайте выплату по причине — используйте статусы и real_win.

Эти поля объясняют уже выполненный расчет или возврат и не являются отдельной realtime-лентой состояния матча.

11. Callback

Callback необязателен. Партнер может работать только через polling. Для callback менеджер должен включить функцию и создать callback_secret; URL передается в каждом создаваемом купоне.

URL callback партнер определяет самостоятельно. В production он должен использовать https://; в тестовом окружении допускается http://.

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

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

Сокращенный payload:

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

Обязательные правила:

  1. вычисляйте sha256=<hex(HMAC-SHA256(raw_body, callback_secret))> по точным исходным байтам тела до разбора JSON и сравнивайте подписи безопасным способом;
  2. обрабатывайте все элементы coupons, пакет может содержать до 100 купонов;
  3. храните batchId с уникальным индексом;
  4. повторный batchId не должен повторять списание или начисление;
  5. один coupon_code может приходить с разными batchId при прогрессивном расчете и перерасчете;
  6. возвращайте HTTP 200 только после надежного сохранения всего пакета.

Минимальный успешный ответ — пустой HTTP 200. Расширенный:

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

processed должен равняться couponCount. Ответы 201, 202 и 204 не считаются успешными.

Повторы выполняются только при timeout, транспортной ошибке или HTTP 500, 502, 503, 504: сразу, затем через 1, 5, 15 и 60 минут — не более пяти отправок. На HTTP 200 с success: false или частичным processed автоматического повтора нет.

12. Резервный polling

Даже при callback периодически сверяйте результаты:

GET /api/partner/coupons/calculated?time=10

После перерыва более 120 минут используйте POST /api/partner/coupons/results по сохраненным coupon_ids либо периодам создания до 24 часов. Повторное получение того же состояния не должно повторять финансовые операции.

13. Безопасность и хранение

  • храните логин, пароль, JWT и callback_secret только на сервере;
  • не передавайте JWT в URL и не записывайте полный токен в логи;
  • используйте десятичный тип для денег;
  • храните coupon_code строкой;
  • сохраняйте amount, нужные данные ставок и currency, если она используется;
  • различайте статусы купона и ставки;
  • неизвестные поля и коды причин принимайте без ошибки;
  • все финансовые операции конечного пользователя делайте идемпотентными.

14. Старый API и Cashout

Старые маршруты продолжают поддерживаться, но новые интеграции должны использовать /api/partner/**. Форматы старых ответов и ошибок отличаются. Для обновления существующего клиента используйте единый файл «Переход со старого API».

Cashout находится в разработке, полностью не протестирован и не рекомендуется для production.

15. Финальный чек-лист

  • Получены BASE_URL, логин и пароль.
  • JWT передается как Bearer token.
  • Указатели берутся из линии без изменений.
  • Корзина сохраняется как принятый купон только после code = 1.
  • Обработаны multi, ограничения экспресса и ошибки 501507.
  • Учтено автоматическое списание клиентского баланса.
  • Сохранены все coupon_code и ID ставок.
  • Финальная выплата берется из real_win.
  • Callback проверяется по исходным байтам и дедуплицируется по batchId.
  • Настроен резервный polling.
  • Пользовательский баланс отделен от клиентского баланса SportAPI.

Подробная документация начинается с обзора API. Полная карта endpoint находится в справочнике.

Нужны дополнительные материалы или помощь с подключением?