Интеграция
Колбэки кошелька
Платформа не хранит собственного баланса. На каждое действие игрока, затрагивающее деньги, она вызывает ваш endpoint кошелька. Вы реализуете один HTTPS-endpoint, который обрабатывает несколько значений method.
Transport#
| Свойство | Значение |
|---|---|
| URL | POST <URL колбэка>/<валюта>, например https://casino.example/wallet/EUR. Валюта в пути — любой из 157 кодов со страницы Валюты. |
| Где задать URL | Админ-панель → Projects → ваш проект → Integration (см. Админ-панель) |
| Тело | JSON-объект с отсортированными ключами; содержит api_key. Суммы — JSON-числа с двумя знаками (1.50). |
| Заголовки | Content-Type: application/json, X-Notell-Timestamp, X-Notell-Signature (Подпись колбэков) |
| Аутентификация | api_key проекта в теле: сравнивайте его со своим ключом. Каждый запрос также подписан. |
| Таймаут | 2 секунды на попытку; редиректы не выполняются |
| Успех | HTTP 2xx с JSON-ответом из раздела Ответ. Статус не 2xx считается сбоем, даже если в теле ok |
| Идемпотентность | Каждый bet / win / refund несёт уникальный meta.transaction; повтор содержит тот же id и тело |
Соединения. Если хост вашего URL колбэка не за Cloudflare, платформа держит соединения с ним открытыми (простаивающие закрываются через 60 секунд, а запрос, упавший на переиспользованном соединении, один раз повторяется на новом). Иначе каждый запрос отправляется с Connection: close. Если кошелёк находится за Cloudflare или скрывает свой origin, настройка проекта Direct IP (админ-панель, Integration) привязывает соединение к публичному IP-адресу вне Cloudflare (имя в TLS-сертификате по-прежнему проверяется).
Если у проекта не задан URL колбэка, платформа использует встроенный тестовый кошелёк (начальный баланс 10 000.00 на аккаунт), чтобы вы могли попробовать игры до готовности вашего кошелька.
Подпись колбэков#
Каждый колбэк несёт два заголовка, по которым можно убедиться, что запрос пришёл от платформы и не изменён (api_key в теле остаётся как был; проверка подписи необязательна, но рекомендуется):
| Заголовок | Значение |
|---|---|
X-Notell-Timestamp | Unix-время в секундах, когда подписана эта попытка (повтор подписывается заново) |
X-Notell-Signature | v1= + HMAC-SHA256 в hex строчными буквами |
Каноническая строка (UTF-8, строки разделены одним \n, без завершающего перевода строки):
<X-Notell-Timestamp>
POST
<URL path of the request, e.g. /wallet/EUR>
<raw request body, byte for byte>Секрет. По умолчанию он выводится из api_key проекта: secret = hex(HMAC-SHA256(key = api_key, message = "notell-callback-signing-v1")), поэтому сам ключ никогда не используется как ключ HMAC, а перевыпуск API-ключа меняет секрет. Если нужен независимый секрет, попросите менеджера задать его для вашего проекта.
Считайте подпись по исходным байтам тела как они получены. Суммы записаны как 1.50; парсер, который пересериализует JSON, превратит их в 1.5, и подпись перестанет совпадать.
import {createHmac, timingSafeEqual} from 'node:crypto';
const secret = createHmac('sha256', API_KEY).update('notell-callback-signing-v1').digest('hex');
function verify(req, rawBody) { // rawBody: точные полученные байты
const ts = req.headers['x-notell-timestamp'], sig = req.headers['x-notell-signature'] ?? '';
if (!/^\d{9,12}$/.test(ts ?? '') || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // окно защиты от повтора
const mac = createHmac('sha256', secret).update(`${ts}\nPOST\n${new URL(req.url, 'http://x').pathname}\n${rawBody}`).digest('hex');
return sig.length === mac.length + 3 && timingSafeEqual(Buffer.from(sig), Buffer.from('v1=' + mac));
}Тестовый вектор. При api_key = YOUR_API_KEY секрет равен
43a216923c5d33308ab3944616a873baed88975e9781a84ab18618cb710b0942Для timestamp 1800000000, пути /wallet/EUR и исходного тела (минимальное тело; реальные содержат больше полей meta)
{"amount":1.50,"api_key":"YOUR_API_KEY","meta":{"round_id":"b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234","transaction":"6f1d0c9e-3b7a-4c52-8e14-a9d05b6c7e21"},"method":"bet","player_id":"player-1024"}заголовок равен
X-Notell-Signature: v1=78d40d425a7ed15ef7a9eaeaedb28baba83babc3d25b2d1c84873f4db608932bIP-адреса наших колбэков#
Колбэки уходят с фиксированных адресов. GET /capabilities перечисляет их в callbackSourceIps; разрешите на файрволе именно их. Значения для окружений — на странице Окружения. Сообщите менеджеру, прежде чем переносить кошелёк в новый диапазон адресов; сам URL колбэка задаётся в админ-панели.
Ответ#
Любой метод отвечает одним и тем же JSON-объектом, в том числе когда сообщает об ошибке:
{"status": "ok", "balance": 998.50, "currency": "EUR"}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
status | enum | да | "ok" или "error". |
balance | number | да | Баланс игрока после операции, JSON-число (не строка): неотрицательное, не более 2 знаков после запятой, до 13 цифр до точки, без экспоненты (998.5, 1000, 0.50 допустимы; 1e3, -1, 988.5000000001 — нет). Обязателен и в ответах "error". |
currency | string | да | Должна совпадать с валютой в пути запроса, иначе ответ отвергается как недопустимый. Обязательна и в ответах "error". |
message | string | со status "error" | Код, например INSUFFICIENT_FUNDS. |
Ответ, нарушающий эти правила (нет balance или он некорректен, неверная currency, не JSON), читается не как отказ, а как неисправный кошелёк: INVALID_CALLBACK_RESPONSE. Операция при этом считается неизвестной (см. Повторы и сбои), а запуск завершается ошибкой.
session_info#
session_infoОтправляется один раз при запуске игры (не при возобновлении незавершённого раунда). Ничего не меняется; ответьте текущим балансом.
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"api_key":"YOUR_API_KEY","method":"session_info","player_id":"player-1024","session_id":"9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21"}'{
"api_key": "YOUR_API_KEY",
"method": "session_info",
"player_id": "player-1024",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "session_info". |
player_id | string | да | Ваш идентификатор игрока. |
session_id | string | да | Сессия, созданная запуском. |
api_key | string | да | API-ключ вашего проекта. |
Ответ#
{"status": "ok", "balance": 1000.00, "currency": "EUR"}| Field | Type | Description |
|---|---|---|
status, balance, currency | Как в разделе Ответ. |
Ошибки#
| Код | HTTP | Когда |
|---|---|---|
BALANCE_UNAVAILABLE | 400 (возвращает запуск) | Вы ответили "status":"error"; запуск завершается ошибкой, сессия не создаётся. |
CALLBACK_HTTP_ERROR | 400 (возвращает запуск) | Вы ответили HTTP-статусом не 2xx. |
INVALID_CALLBACK_RESPONSE | 400 (возвращает запуск) | Ответ недопустим. |
bet#
betОтправляется до розыгрыша платного раунда. Спишите amount и ответьте.
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"amount":1.50,"api_key":"YOUR_API_KEY","meta":{"round_id":"b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234","session_id":"9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21","symbol":"gates-of-olympus","time":1800000000,"transaction":"6f1d0c9e-3b7a-4c52-8e14-a9d05b6c7e21"},"method":"bet","player_id":"player-1024"}'{
"amount": 1.50,
"api_key": "YOUR_API_KEY",
"meta": {
"round_id": "b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"symbol": "gates-of-olympus",
"time": 1800000000,
"transaction": "6f1d0c9e-3b7a-4c52-8e14-a9d05b6c7e21"
},
"method": "bet",
"player_id": "player-1024"
}{
"amount": 0.00,
"api_key": "YOUR_API_KEY",
"meta": {
"freespins": {"external_id": "welcome-2027-01", "grant_id": "7b1c4e92-0a3d-4f68-b5c1-2e9d8a6f3b40", "nominal_bet": 1, "remaining": 9},
"is_freespins": true,
"round_id": "d4e8a1b6-93c2-4075-8f1e-5b0a7c2d9e63",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"symbol": "gates-of-olympus",
"time": 1800000300,
"transaction": "0b6e5d3a-8c14-4f97-a2d0-1e7c9b4f6a58"
},
"method": "bet",
"player_id": "player-1024"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "bet". |
player_id | string | да | Ваш идентификатор игрока. |
amount | number | да | Ставка в единицах валюты, положительная; 0 для раунда фриспинов. |
meta.transaction | string | да | Уникальный id этого списания; повтор содержит тот же. |
meta.round_id | string | да | Идентификатор раунда. |
meta.session_id | string | да | Идентификатор сессии. |
meta.symbol | string | да | Идентификатор игры. |
meta.time | integer | да | Unix-время в секундах. |
meta.is_freespins | boolean | раунды фриспинов | true, если раунд относится к выдаче фриспинов оператора; принимайте нулевую ставку. |
meta.freespins | object | раунды фриспинов | grant_id, external_id (или null), nominal_bet (число) и remaining — оставшиеся спины. |
api_key | string | да | API-ключ вашего проекта. |
Ответ#
Спишите ставку и ответьте новым балансом:
{"status": "ok", "balance": 998.50, "currency": "EUR"}При реальной нехватке средств ответьте HTTP 200 с телом:
{"status": "error", "message": "INSUFFICIENT_FUNDS", "balance": 0.50, "currency": "EUR"}Раунд отклоняется, ничего не разыгрывается. Возвращайте этот код только при реальной нехватке средств.
Ошибки#
| Код | HTTP | Когда |
|---|---|---|
INSUFFICIENT_FUNDS | 200 | Ваш ответ: недостаточно средств. Раунд отклоняется сразу; повторов нет. |
TRANSACTION_CANCELLED | 200 | Ваш ответ: списание пришло после того, как вы записали refund по этой транзакции. |
| любой другой | 4xx, кроме 408 и 429 | Ваш HTTP-статус: окончательный отказ. Раунд отклоняется сразу, ставится в очередь refund. |
| любой другой | 5xx, 408, 429, таймаут | Списание неизвестно и повторяется (Повторы и сбои). |
win#
winОтправляется, когда раунд выиграл. amount — общий выигрыш раунда (положительный; нулевые выигрыши не отправляются).
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"amount":12.75,"api_key":"YOUR_API_KEY","meta":{"round_id":"b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234","session_id":"9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21","symbol":"gates-of-olympus","time":1800000012,"transaction":"0b6e5d3a-8c14-4f97-a2d0-1e7c9b4f6a58"},"method":"win","player_id":"player-1024"}'{
"amount": 12.75,
"api_key": "YOUR_API_KEY",
"meta": {
"round_id": "b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"symbol": "gates-of-olympus",
"time": 1800000012,
"transaction": "0b6e5d3a-8c14-4f97-a2d0-1e7c9b4f6a58"
},
"method": "win",
"player_id": "player-1024"
}{
"amount": 23.50,
"api_key": "YOUR_API_KEY",
"meta": {
"freespins": {"external_id": "welcome-2027-01", "final_credit": true, "grant_id": "7b1c4e92-0a3d-4f68-b5c1-2e9d8a6f3b40", "nominal_bet": 1, "total_win": 23.5},
"is_freespins": true,
"round_id": "e7a02c58-1f94-4b36-9d8a-3c5e6b7f0a12",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"symbol": "gates-of-olympus",
"time": 1800000900,
"transaction": "a3f6c1d8-52e7-4b09-8c14-7d2e9a0b5f63"
},
"method": "win",
"player_id": "player-1024"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "win". |
player_id | string | да | Ваш идентификатор игрока. |
amount | number | да | Общий выигрыш раунда, положительный. Для итогового начисления фриспинов — сумма всех выигрышей выдачи. |
meta.transaction | string | да | Уникальный id этого начисления; повтор содержит тот же. |
meta.round_id, meta.session_id, meta.symbol, meta.time | да | Как в bet. Для итогового начисления round_id — последний раунд выдачи, он может отличаться от раунда более ранней bet. | |
meta.is_freespins | boolean | итоговое начисление фриспинов | true. |
meta.freespins | object | итоговое начисление фриспинов | grant_id, external_id (или null), nominal_bet, total_win (равен amount) и final_credit: true. |
api_key | string | да | API-ключ вашего проекта. |
Выигрыши фриспинов выплачиваются один раз. Выигрыши раундов выдачи фриспинов оператора не отправляются по каждому спину. После последнего спина платформа отправляет один win на всю выдачу с meta.freespins.final_credit: true перед итоговым freespins_finished. total_win итога равен этому начислению. Не ждите win на каждый выигрышный спин и не сверяйте такой win с bet его round_id.
Ответ#
{"status": "ok", "balance": 1011.25, "currency": "EUR"}Ошибки#
| Код | HTTP | Когда |
|---|---|---|
| любой | любой | Начисление никогда не отменяется: если вы не ответили ok, оно повторяется до подтверждения. |
refund#
refundОтправляется, когда раунд отменён после того, как его bet мог быть списан: ставка осталась неподтверждённой дольше политики отмены (10 минут или 600 попыток, что наступит раньше), кошелёк окончательно отказал ей статусом 4xx, либо вы отменили раунд через round_cancel.
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"amount":1.50,"api_key":"YOUR_API_KEY","meta":{"original_transaction":"6f1d0c9e-3b7a-4c52-8e14-a9d05b6c7e21","reason":"wallet_unconfirmed","round_id":"b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234","session_id":"9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21","symbol":"gates-of-olympus","time":1800000600,"transaction":"c41e8a07-6d2b-4f35-9e1c-0a8b7d3f5c92"},"method":"refund","player_id":"player-1024"}'{
"amount": 1.50,
"api_key": "YOUR_API_KEY",
"meta": {
"original_transaction": "6f1d0c9e-3b7a-4c52-8e14-a9d05b6c7e21",
"reason": "wallet_unconfirmed",
"round_id": "b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"symbol": "gates-of-olympus",
"time": 1800000600,
"transaction": "c41e8a07-6d2b-4f35-9e1c-0a8b7d3f5c92"
},
"method": "refund",
"player_id": "player-1024"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "refund". |
player_id | string | да | Ваш идентификатор игрока. |
amount | number | да | Равна сумме ставки. |
meta.transaction | string | да | Id самого возврата. |
meta.original_transaction | string | да | meta.transaction возвращаемой ставки. |
meta.reason | enum | да | wallet_unconfirmed или operator_cancel. |
meta.round_id, meta.session_id, meta.symbol, meta.time | да | Как в bet. | |
api_key | string | да | API-ключ вашего проекта. |
Правила для вашего кошелька:
- Исходная ставка была списана и ещё не возвращена: верните
amountна баланс, ответьтеokс новым балансом. - Исходная ставка к вам не приходила (или вы её отклонили): запомните, что этот id транзакции отменён, ничего не меняйте и ответьте
ok. Если эта ставка придёт позже, откажите ей ответом{"status":"error","message":"TRANSACTION_CANCELLED","balance":1000.00,"currency":"EUR"}. - Повтор того же возврата (тот же
meta.transaction) возвращает первый ответ. Одна ставка возвращается не более одного раза. - Ответ с ошибкой повторяется, пока вы не ответите
ok; после создания возврата платформа больше не отправляет эту ставку.
Ответ#
{"status": "ok", "balance": 1000.00, "currency": "EUR"}Ошибки#
| Код | HTTP | Когда |
|---|---|---|
| любой | любой | Если вы не ответили ok, возврат повторяется (2 с, с удвоением, не более 5 минут) до подтверждения. |
round_info#
round_infoИнформационное уведомление о раунде: сохраните его, ничего не меняйте, ответьте ok с текущим балансом. На раунд приходят два, по порядку: start (после подтверждения списания) и finish (после подтверждения начисления или после раунда без выигрыша). finish доставляется только после подтверждения start.
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"api_key":"YOUR_API_KEY","bet_amount":1.50,"bet_type":"spin","bonus_buy":false,"bonus_trigger":true,"currency":"EUR","game_symbol":"gates-of-olympus","has_win":true,"method":"round_info","multiplier":8.5,"player_id":"player-1024","round_id":"b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234","session_id":"9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21","status":"finish","timestamp":1800000015,"win_amount":12.75}'{
"api_key": "YOUR_API_KEY",
"bet_amount": 1.50,
"bet_type": "spin",
"bonus_buy": false,
"bonus_trigger": true,
"currency": "EUR",
"game_symbol": "gates-of-olympus",
"has_win": true,
"method": "round_info",
"multiplier": 8.5,
"player_id": "player-1024",
"round_id": "b2a7c1d4-5e60-4f1b-9a3c-7d2e8f901234",
"session_id": "9d0c3f7e-41b8-4a52-9c0e-6d3f5a7b8c21",
"status": "finish",
"timestamp": 1800000015,
"win_amount": 12.75
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "round_info". |
status | enum | да | start или finish. |
player_id, round_id, session_id, game_symbol, currency | string | да | Идентификаторы раунда. |
timestamp | integer | да | Unix-время в секундах. |
bet_amount | number | да | Ставка раунда (номинальная ставка для раундов фриспинов). |
bet_type | string | да | Тип ставки раунда, например spin или purchase. |
bonus_buy | boolean | да | Раунд был покупкой бонуса. |
bonus_trigger | boolean | да | Раунд запустил бонусную функцию. |
win_amount | number | при finish | Общий выигрыш раунда. |
multiplier | number | при finish | Выигрыш, делённый на ставку. |
has_win | boolean | при finish | Положителен ли выигрыш. |
forced | boolean | нет | true, если раунд создан под целевой исход оператора. |
api_key | string | да | API-ключ вашего проекта. |
Уведомление finish — итог раунда: используйте его для отчётности; деньги уже прошли через bet и win.
Ответ#
{"status": "ok", "balance": 1011.25, "currency": "EUR"}Ошибки#
| Код | HTTP | Когда |
|---|---|---|
| любой | любой | Если вы не ответили ok, уведомление повторяется позже; проект, который не отвечает, на время пропускается, чтобы не блокировать остальных. |
freespins_finished#
freespins_finishedПо одному на каждую завершённую выдачу фриспинов, отправляется после итогового win. Отвечайте в обычном формате; он повторяется, пока вы не ответите.
Запрос#
curl -X POST https://casino.example/wallet/EUR \
-H 'Content-Type: application/json' \
-d '{"api_key":"YOUR_API_KEY","bet":"1.00","currency":"EUR","external_id":"welcome-2027-01","finished_at":"2027-01-15T12:30:00.000Z","game":"gates-of-olympus","grant_id":"7b1c4e92-0a3d-4f68-b5c1-2e9d8a6f3b40","method":"freespins_finished","played":10,"player_id":"player-1024","status":"used","total":10,"total_win":"23.50"}'{
"api_key": "YOUR_API_KEY",
"bet": "1.00",
"currency": "EUR",
"external_id": "welcome-2027-01",
"finished_at": "2027-01-15T12:30:00.000Z",
"game": "gates-of-olympus",
"grant_id": "7b1c4e92-0a3d-4f68-b5c1-2e9d8a6f3b40",
"method": "freespins_finished",
"played": 10,
"player_id": "player-1024",
"status": "used",
"total": 10,
"total_win": "23.50"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
method | string | да | "freespins_finished". |
grant_id | string | да | Идентификатор выдачи. |
external_id | string | нет | Ваш id выдачи, если вы его задавали. |
player_id, currency, game | string | да | Игрок, валюта и игра выдачи. |
status | enum | да | Итоговый статус выдачи, например used; значения: Фриспины. |
total | integer | да | Выдано спинов. |
played | integer | да | Сыграно спинов. |
bet | money | да | Ставка каждого спина. |
total_win | money | да | Общий выигрыш выдачи; равен amount итогового win. |
finished_at | string | да | Время в формате ISO 8601. |
api_key | string | да | API-ключ вашего проекта. |
Ответ#
{"status": "ok", "balance": 1011.25, "currency": "EUR"}Ошибки#
| Код | HTTP | Когда |
|---|---|---|
| любой | любой | Если вы не ответили ok, повтор: 10 с × 2<sup>попытки</sup>, не более 1 часа. |
Порядок вызовов в раунде#
bet -> (debit confirmed) -> round_info start -> [play] -> win (if > 0) -> (credit confirmed) -> round_info finishВыдачи фриспинов: bet (сумма 0) на каждый спин, без win на спин, затем один win с meta.freespins.final_credit: true, затем freespins_finished.
Повторы и сбои#
| Ситуация | Что делает платформа |
|---|---|
На bet ответ INSUFFICIENT_FUNDS | Раунд отклоняется; повторов нет |
На bet HTTP 4xx, кроме 408 и 429 | Окончательный отказ: раунд сразу отклоняется, ставится в очередь refund |
bet с HTTP 5xx, 408, 429, таймаутом, сетевой ошибкой или недопустимым ответом | Списание неизвестно и повторяется с тем же id транзакции и телом: первые 4 попытки сразу, затем через 1 с с удвоением до 60 с. Если через 10 минут или 600 попыток (что наступит раньше) оно всё ещё не подтверждено, раунд отменяется и отправляется refund. Всё это время раунд ожидает |
То же для win | Повторяется до подтверждения (выигрыш никогда не отменяется) |
refund без ответа ok | Повторяется с растущими задержками (2 с с удвоением, не более 5 минут) до подтверждения |
round_info без ответа ok | Повторяется позже; проект, который не отвечает, на время пропускается, чтобы не блокировать остальных |
freespins_finished без ответа ok | Повторяется с растущими задержками: 10 с × 2<sup>попытки</sup>, не более 1 часа |
Повторы ожидаемы, поэтому ваш кошелёк обязан быть идемпотентным: запоминайте ответ на каждый meta.transaction и возвращайте его же на повтор того же запроса. Повтор никогда не должен списывать или начислять дважды.
Чек-лист для вашего кошелька#
- [ ] Проверяйте
api_key; работайте только по HTTPS; отвечайте за 2 секунды. - [ ] Разбирайте
amountкак десятичное; возвращайтеbalanceJSON-числом (неотрицательным, не более 2 знаков) в каждом ответе, включая ошибки. - [ ] Возвращайте в
currencyвалюту из пути URL. - [ ] Идемпотентность по
meta.transaction(тот же id, то же тело → тот же ответ). - [ ]
INSUFFICIENT_FUNDSкак HTTP 200; никаких HTTP 4xx для временных проблем. - [ ] Принимайте
amount: 0дляbetприmeta.is_freespinsравномtrue; ожидайте один итоговыйwinна выдачу фриспинов. - [ ] Отвечайте
okнаround_infoиfreespins_finished(status,balance,currency). - [ ] Реализуйте
refund(идемпотентно, с пометкой об отмене для неизвестных транзакций) и, если можете, проверяйтеX-Notell-Signatureпо исходному телу. - [ ] Разрешите IP-адреса колбэков из
/capabilities. - [ ] При тестировании используйте Test connection в админ-панели и следите за API Logs.