Исходы, ставки и купоны — техническая документация АПИ
Этот файл содержит минимальный самостоятельный сценарий подключения к Системе расчета купонов SportAPI. Подробности и редкие случаи находятся в полной документации.
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_code | string | Публичный 12-значный код купона. Храните строкой, чтобы не потерять ведущие нули. |
amount | number | Сумма купона, переданная при создании. |
win | number | Текущая отображаемая сумма выигрыша. |
potential_win | number | Возможный выигрыш до окончательного расчета. |
real_win | number/null | Фактическая сумма выплаты после расчета; до расчета — null. |
coef | number | Текущий или итоговый коэффициент купона. |
original_coef | number | Общий коэффициент купона в момент его создания. |
calculate_coef | number/null | Итоговый расчетный коэффициент; до расчета — null. |
has_return | boolean | true, если в купоне есть ставка со статусом возврата. |
date | integer | Дата создания купона, Unix timestamp в миллисекундах. |
status | integer | Текущий статус купона. Значения описаны в разделе «Статусы купона и ставок». |
asian | boolean | Признак наличия азиатского расчета, включая половинный выигрыш или проигрыш. |
calculate_date | integer/null | Дата расчета купона в миллисекундах; до расчета — null. |
coupon_type | integer | Тип купона: 1 — ординар, 2 — экспресс. |
events_count | integer | Количество ставок внутри купона. |
events_data | array | Полный массив ставок, входящих в купон. |
Поля ставки
Каждый элемент events_data[] описывает конкретную принятую ставку внутри купона.
| Поле | Тип | Значение |
|---|---|---|
id | integer/null | Внутренний ID принятой ставки. Используется вместе с coupon_code для поиска ставки внутри купона. |
game_id | integer | ID события или саб-события, на которое сделана ставка. |
main_game_id | integer/null | ID основного матча, к которому относится событие или саб-событие. |
is_sub_game | boolean | true, если ставка относится к тайму, периоду, сету, угловым или другому саб-событию. |
parent_game_id | integer/null | ID непосредственного родительского события, если оно существует. |
sub_game_key | string/null | Технический ключ саб-события или периода. |
raw_pointer | string | Исходный указатель ставки, принятый API. |
line_type | string | Тип линии: line — прематч, live — событие в реальном времени. |
is_live | boolean | true, если ставка была создана по live-линии. |
bet_group_id | integer | ID группы ставок или рынка. |
bet_group_name | string/null | Локализованное название группы ставок. |
bet_id | integer | ID выбранного исхода внутри группы ставок. |
bet_name | string/null | Локализованное полное название выбранного исхода. |
sport_id | integer/null | ID вида спорта. |
sport_name | string/null | Локализованное название вида спорта. |
tournament_id | integer/null | ID турнира. |
tournament | string/null | Локализованное название турнира. |
event_date | integer/null | Дата начала события, Unix timestamp в миллисекундах. |
status | integer | Текущий статус расчета ставки. Не путайте со статусом купона. |
opp1 | string/null | Название первой команды или участника. |
opp2 | string/null | Название второй команды или участника. |
coef | number | Коэффициент ставки, с которым она была принята. |
calc_coef | number/null | Расчетный множитель ставки; до расчета — null. |
bet_score | string | Совместимое поле старого API, содержащее указатель ставки, а не счет матча. |
calculate_date | integer/null | Дата расчета ставки в миллисекундах; до расчета — null. |
calculate_score | string/null | Счет, использованный при расчете ставки. |
settlement_reason_code | string/null | Стабильный машинный код причины расчета или возврата; до расчета — null. |
settlement_reason | string/null | Поясняющий текст причины расчета или возврата; до расчета — null. |
placement_score_full | string/null | Общий счет события в момент создания ставки, если доступен. |
placement_score_periods | string/null | Счет по периодам в момент создания ставки, если доступен. |
calculation_score_full | string/null | Общий счет события в момент расчета ставки. |
calculation_score_periods | string/null | Счет по периодам в момент расчета ставки. |
timer | integer/null | Таймер события в момент сохранения данных, если доступен. |
dop_name | string/null | Название саб-события: тайма, периода, сета, иннинга и т. п. |
rate | string | Параметр исхода, например значение тотала или форы; для исхода без параметра — "0". |
sgame_id | string/null | Внешний ключ саб-события. |
game_num | integer/null | Номер игры, если он предоставлен источником. |
stat_id | string/null | Внешний статистический ID события. |
team1_id | integer/null | ID первой команды или участника. |
team2_id | integer/null | ID второй команды или участника. |
opp_icon1 | integer/null | Совместимый ID иконки первой команды; совпадает с team1_id. |
opp_icon2 | integer/null | Совместимый ID иконки второй команды; совпадает с team2_id. |
Значение null является нормальным для еще не рассчитанных или недоступных данных. Не заменяйте его автоматически на 0 или пустую строку.
Считайте купон принятым только при code = 1 и наличии объектов в body.coupons. До этого корзина является предварительным выбором: исход мог исчезнуть, заблокироваться или изменить коэффициент.
После успеха:
- сохраните все объекты
body.coupons, а не только первый; - сохраните
coupon_codeстрокой с ведущими нулями; - сохраните нужные данные из
events_data; - сопоставьте купон с конечным пользователем;
- зафиксируйте финансовую операцию пользователя в системе партнера.
Идентификаторы:
| Поле | Назначение |
|---|---|
coupon_code | Публичный код купона. |
events_data[].id | ID конкретной принятой ставки внутри купона. |
callback events_data[].uuid | Тот же ID ставки, переданный строкой. |
batchId | ID версии пакета callback, а не купона или ставки. |
currency не возвращается в полной модели и callback. Если валюта нужна, сохраните значение из запроса создания.
7. Ошибки создания
| Код | Причина | Действие |
|---|---|---|
10 | Нет тела запроса. | Исправить запрос. |
11 | Неверный указатель. | Получить актуальный указатель из линии. |
12 | Неверный amount. | Передать положительное число. |
501 | Коэффициент изменился. | Показать новое значение или изменить mode. |
502 | Исход отсутствует. | Удалить/обновить ставку в корзине. |
503 | Исход заблокирован. | Сообщить о временной недоступности. |
504 | Ошибка проверки исхода. | Не считать купон принятым; повторить позже. |
506 | В экспрессе ставки одного матча. | Оставить один исход или использовать отдельные ординары. |
507 | Недостаточный клиентский баланс. | Пополнить баланс или уменьшить общую сумму. |
1002 | Неверный набор параметров. | Исправить параметры. |
10000 | Внутренняя ошибка. | Зафиксировать ошибку и проверить результат перед повтором. |
Для 501–504 новый 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/balance | body.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 |
23 | Push. | 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
}
]
}
]
}
Обязательные правила:
- вычисляйте
sha256=<hex(HMAC-SHA256(raw_body, callback_secret))>по точным исходным байтам тела до разбора JSON и сравнивайте подписи безопасным способом; - обрабатывайте все элементы
coupons, пакет может содержать до 100 купонов; - храните
batchIdс уникальным индексом; - повторный
batchIdне должен повторять списание или начисление; - один
coupon_codeможет приходить с разнымиbatchIdпри прогрессивном расчете и перерасчете; - возвращайте 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, ограничения экспресса и ошибки501–507. - Учтено автоматическое списание клиентского баланса.
- Сохранены все
coupon_codeи ID ставок. - Финальная выплата берется из
real_win. - Callback проверяется по исходным байтам и дедуплицируется по
batchId. - Настроен резервный polling.
- Пользовательский баланс отделен от клиентского баланса SportAPI.
Подробная документация начинается с обзора API. Полная карта endpoint находится в справочнике.