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

Сквозной пример: экспресс

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

Главные отличия от ординара:

  • в list_bets передается несколько указателей;
  • multi должен быть равен false;
  • создается один coupon_code;
  • amount относится ко всему экспрессу;
  • с клиентского баланса SportAPI автоматически списывается один amount;
  • партнер отдельно списывает один amount с конечного пользователя в своей системе;
  • ставки могут рассчитываться в разное время;
  • один купон может получить несколько последовательных callback.

Общая схема

Пользователь выбирает несколько разных матчей

Партнер формирует одну корзину экспресса

POST /api/partner/coupons/place
multi = false

code = 1

Один купон
Автоматическое списание amount с клиентского баланса SportAPI
Отдельное списание amount с пользователя в системе партнера

Последовательные изменения ставок

Финальный результат + выплата пользователю по real_win

Ограничения экспресса

Перед отправкой проверьте корзину:

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

Запрещенные сочетания:

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

Для определения общего матча учитывайте не только game_id, но и связь с основным событием. Готовую проверку также выполняет Система расчета купонов SportAPI.

Если правило нарушено, API возвращает:

error_code = 506

Шаг 1. Получите JWT

Авторизуйтесь через:

POST /api/partner/login

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

BASE_URL="https://coupon-api.example.com"
TOKEN="<jwt-token>"

Фактический BASE_URL и данные входа партнер получает у менеджера.

Подробности: «Авторизация».

Шаг 2. Подготовьте указатели

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

line#737779544#1#1#0#1.80
line#737880112#8#6#2.5#1.50
line#738004921#17#9#3.5#2.00

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

Исходные коэффициенты:

1.80 × 1.50 × 2.00 = 5.40

При сумме 10 исходная возможная выплата:

10 × 5.40 = 54

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

Шаг 3. Покажите пользователю корзину

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

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

В нашем примере:

тип: экспресс
ставок: 3
сумма: 10 USD
общая сумма списания: 10 USD
предварительный коэффициент: 5.40
предварительная выплата: 54 USD

Корзина является предварительным выбором, а не принятым купоном.

Шаг 4. Создайте экспресс

curl --request POST \
  --url "$BASE_URL/api/partner/coupons/place" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "list_bets": [
      "line#737779544#1#1#0#1.80",
      "line#737880112#8#6#2.5#1.50",
      "line#738004921#17#9#3.5#2.00"
    ],
    "amount": 10,
    "currency": "USD",
    "callback_url": "https://partner.example.com/api/coupon-result",
    "lang": "en",
    "mode": "reject",
    "mode_type": null,
    "multi": false
  }'

Ключевое поле:

{
  "multi": false
}

Оно объединяет все три элемента list_bets в один экспресс.

Если callback не используется, callback_url можно не передавать или передать null.

Не перепутайте с multi = true

При тех же трех указателях:

РежимРезультатОбщая сумма
multi = falseОдин экспресс из трех ставок10
multi = trueТри отдельных ординара10 × 3 = 30

multi = true не создает экспресс. Сумма amount применяется полностью к каждому созданному ординару.

Шаг 5. Обработайте успешное создание

Сокращенный пример ответа:

{
  "code": 1,
  "body": {
    "coupons": [
      {
        "coupon_code": "000000000350",
        "amount": 10,
        "win": 54,
        "potential_win": 54,
        "real_win": null,
        "coef": 5.4,
        "original_coef": 5.4,
        "calculate_coef": null,
        "has_return": false,
        "date": 1784970000000,
        "status": 0,
        "calculate_date": null,
        "coupon_type": 2,
        "events_count": 3,
        "events_data": [
          {
            "id": 912,
            "game_id": 737779544,
            "raw_pointer": "line#737779544#1#1#0#1.80",
            "bet_name": "First team to win",
            "status": 0,
            "coef": 1.8,
            "calc_coef": null,
            "calculate_date": null
          },
          {
            "id": 913,
            "game_id": 737880112,
            "raw_pointer": "line#737880112#8#6#2.5#1.50",
            "bet_name": "Total over 2.5",
            "status": 0,
            "coef": 1.5,
            "calc_coef": null,
            "calculate_date": null
          },
          {
            "id": 914,
            "game_id": 738004921,
            "raw_pointer": "line#738004921#17#9#3.5#2.00",
            "bet_name": "Total over 3.5",
            "status": 0,
            "coef": 2,
            "calc_coef": null,
            "calculate_date": null
          }
        ]
      }
    ]
  },
  "error_code": null,
  "error_message": null,
  "date": 1784970000015,
  "time_ms": 48,
  "path": "/api/partner/coupons/place"
}

Проверьте:

code = 1
body.coupons.length = 1
coupon_type = 2
events_count = 3
events_data.length = 3

Только после этого:

  1. сохраните купон;
  2. сохраните все три ставки;
  3. сохраните coupon_code как строку;
  4. зафиксируйте одно списание amount = 10 с конечного пользователя в системе партнера;
  5. отметьте локальный купон как принятый.

Рекомендуемые ключи ставок:

000000000350 + 912
000000000350 + 913
000000000350 + 914

Не создавайте три финансовых списания конечного пользователя: это ставки внутри одного экспресса, а не отдельные купоны. SportAPI со своей стороны уже списал один amount с клиентского баланса при успешном создании.

currency не входит в модель ответа и callback. Если она нужна, сохраните USD из исходного запроса.

Если экспресс отклонен

При code = 0:

  • не сохраняйте корзину как принятый купон;
  • не выполняйте окончательное списание;
  • освободите предварительный резерв, если он использовался;
  • обработайте error_code и body.changes.

Несколько ставок одного матча

Пример:

{
  "code": 0,
  "body": null,
  "error_code": 506,
  "error_message": "<нельзя объединить ставки одного матча>",
  "date": 1784970000000,
  "time_ms": 12,
  "path": "/api/partner/coupons/place"
}

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

Изменение или недоступность исхода

Любой элемент экспресса может не пройти проверку:

error_codeПричина
501Коэффициент изменился.
502Исход отсутствует в текущей линии.
503Исход заблокирован.
504Проверка исхода завершилась ошибкой.
507Недостаточно средств на клиентском балансе для создания экспресса.

Проблемные элементы возвращаются в body.changes. В режиме reject изменение даже одного коэффициента не позволяет принять исходный экспресс.

При 507 экспресс не создается, а клиентский баланс SportAPI не изменяется.

Шаг 6. Сохраните начальное состояние

Сразу после создания:

status купона = 0
status ставок = [0, 0, 0]
real_win = null
calculate_coef = null
calculate_date = null

Статус 0 означает, что экспресс еще не получил окончательный результат.

Шаг 7. Обрабатывайте последовательные состояния

Ставки экспресса могут завершаться в разное время. Каждый callback содержит полный текущий снимок всех ставок.

Для нашего экспресса возможна последовательность:

[0, 0, 0] → купон создан
[1, 0, 0] → первая ставка выиграла
[1, 3, 0] → вторая ставка возвращена
[1, 3, 1] → третья ставка выиграла, экспресс завершен

Здесь:

  • 0 — ставка не рассчитана;
  • 1 — выигрыш;
  • 3 — возврат.

Первое промежуточное состояние

После выигрыша первой ставки callback может выглядеть так:

{
  "event": "coupons.settled",
  "batchId": "batch-version-001",
  "clientId": 17,
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000350",
      "realWin": 0,
      "calculate_coefficient": 0,
      "status": 0,
      "calculate_date": 1784973600000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 1.8,
          "calculate_date": 1784973600000,
          "calculate_score": "2:1",
          "timer": 0
        },
        {
          "uuid": "913",
          "status": 0,
          "calculate_coefficient": null,
          "calculate_date": null,
          "calculate_score": "",
          "timer": 0
        },
        {
          "uuid": "914",
          "status": 0,
          "calculate_coefficient": null,
          "calculate_date": null,
          "calculate_score": "",
          "timer": 0
        }
      ]
    }
  ]
}

Партнер:

  1. проверяет HMAC;
  2. сохраняет новый batchId;
  3. обновляет ставку 912;
  4. оставляет купон активным;
  5. не начисляет realWin = 0, потому что status = 0;
  6. возвращает HTTP 200.

realWin = 0 в промежуточном снимке не означает проигрыш.

Второе промежуточное состояние

Вторая ставка возвращена:

batchId = batch-version-002
status купона = 0
status ставок = [1, 3, 0]

Это новое состояние того же купона:

  • coupon_code остается 000000000350;
  • batchId меняется;
  • ставка 913 получает расчетный множитель 1;
  • финансовую выплату пока выполнять нельзя.

Возврат одной ставки не возвращает весь экспресс. Эта ставка просто не увеличивает общий расчетный коэффициент.

Финальное выигрышное состояние

Третья ставка выиграла:

status ставок = [1, 3, 1]

Расчетные множители:

первая ставка: 1.80
вторая ставка: 1
третья ставка: 2.00

Итог:

calculate_coef = 1.80 × 1 × 2.00 = 3.60
real_win = 10 × 3.60 = 36
status купона = 2

Финальный callback:

{
  "event": "coupons.settled",
  "batchId": "batch-version-003",
  "clientId": 17,
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000350",
      "realWin": 36,
      "calculate_coefficient": 3.6,
      "status": 2,
      "calculate_date": 1784980800000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 1.8,
          "calculate_date": 1784973600000,
          "calculate_score": "2:1",
          "timer": 0
        },
        {
          "uuid": "913",
          "status": 3,
          "calculate_coefficient": 1,
          "calculate_date": 1784977200000,
          "calculate_score": "0:0",
          "timer": 0
        },
        {
          "uuid": "914",
          "status": 1,
          "calculate_coefficient": 2,
          "calculate_date": 1784980800000,
          "calculate_score": "4:0",
          "timer": 0
        }
      ]
    }
  ]
}

После проверки и сохранения партнер один раз начисляет пользователю:

realWin = 36

Используйте готовое значение из callback или real_win из полного API. Формула выше объясняет результат, но не должна заменять значение, рассчитанное API.

Отдельная ветка: первый проигрыш

Рассмотрим другой экспресс:

[1, 0, 0] → первая ставка выиграла
[1, 2, 0] → вторая ставка проиграла

Как только появилась ставка со статусом 2:

status купона = 4
realWin = 0

Система сразу отправляет callback о проигрыше, даже если третья ставка еще не рассчитана.

Партнер:

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

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

Когда рассчитаны все ставки, приходит полный снимок:

[1, 2, 1]
status купона = 4
realWin = 0
новый batchId

Это не второй проигрыш и не новая финансовая операция. Обновите статусы оставшихся ставок, но не повторяйте обработку баланса.

Несколько версий и дедупликация

Для одного экспресса:

Ситуацияcoupon_codebatchIdДействие
Новое промежуточное состояниеТот жеНовыйОбновить купон и ставки.
Финальный результатТот жеНовыйОбновить данные и один раз обработать выплату.
Повтор той же HTTP-доставкиТот жеТот жеНе повторять бизнес-операции, вернуть 200.

Нельзя считать каждый новый batchId новой ставкой или новой финансовой операцией.

Храните:

coupon_code → идентификатор купона
uuid        → идентификатор ставки внутри callback
batchId     → идентификатор версии доставки

Половинные результаты в экспрессе

Половинный выигрыш

Две ставки:

WIN:      coef = 1.80, factor = 1.80
HALF_WIN: coef = 2.00, factor = (2.00 + 1) / 2 = 1.50

При amount = 10:

calculate_coef = 1.80 × 1.50 = 2.70
real_win = 27

Половинный проигрыш

Две ставки:

WIN:       coef = 1.80, factor = 1.80
HALF_LOSE: factor = 0.5

При amount = 10:

calculate_coef = 1.80 × 0.5 = 0.90
real_win = 9

HALF_LOSE не обнуляет экспресс. Полный проигрыш со статусом ставки 2 имеет множитель 0 и обнуляет весь купон.

Для начисления всегда используйте готовое real_win, возвращенное API.

Получение через API

Текущее полное состояние доступно по коду:

COUPON_CODE="000000000350"

curl --request GET \
  --url "$BASE_URL/api/partner/coupons/get?coupon_code=$COUPON_CODE" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer $TOKEN"

При обработке ответа:

  • обновите купон по coupon_code;
  • переберите весь events_data;
  • обновите ставки по coupon_code + id;
  • не считайте статус 0 финальным;
  • используйте real_win только для еще не обработанного финального состояния.

Даже при работающем callback рекомендуется резервный polling:

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

Возврат на перерасчет

Если экспресс возвращен на перерасчет:

status купона = 15
одна или несколько ставок имеют status = 4

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

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

Повторное списание выполняется один раз для каждого нового перехода в статус 15, а не для каждой ставки со статусом 4.

Итоговый алгоритм

1. Проверить, что выбрано от 2 до 15 разных матчей.
2. Показать пользователю один экспресс и одну общую сумму.
3. Передать все указатели с multi = false.
4. При code = 0 не создавать принятый купон и не списывать сумму.
5. При code = 1 сохранить один купон и все events_data.
6. Один раз списать amount.
7. При каждом новом состоянии обновлять купон и ставки.
8. Не начислять деньги при status = 0.
9. При первом финансово новом финальном результате обработать real_win.
10. Повторный batchId подтверждать без повторной обработки.
11. Выполнять резервную сверку через API.

Контрольный список

  • Экспресс содержит от 2 до 15 ставок.
  • Каждая ставка относится к отдельному матчу.
  • Основное событие не объединяется с его саб-событиями.
  • multi равен false.
  • amount относится ко всему экспрессу.
  • Пользователь видит одну общую сумму списания.
  • Купон сохраняется только после code = 1.
  • Сохраняются все ставки из events_data.
  • Выполняется одно начальное списание.
  • Подпись каждого callback проверяется по исходным байтам тела.
  • Промежуточный статус купона 0 не вызывает выплату.
  • Новый batchId обновляет существующий coupon_code.
  • Повторный batchId не повторяет обработку.
  • Первый проигрыш обрабатывается сразу.
  • Финальный полный снимок проигравшего экспресса не создает вторую финансовую операцию.
  • HALF_WIN, HALF_LOSE, возврат и push учитываются через готовое real_win.
  • Статус 15 запускает один новый цикл ожидания результата.
  • Callback дополняется резервным polling.

Подробнее:

Следующий раздел: «Восстановление после пропущенного callback».