Документация по интеграции для партнеров.
https://api.jetbot.pwhttps://dev.jetbot.pw (для тестов)
Для работы с API необходимо выполнить следующие шаги:
Важно: Баланс мерчанта в API напрямую связан с балансом вашего Telegram-аккаунта в боте. Для пополнения баланса необходимо произвести стандартную операцию пополнения (депозит) через Telegram-бота: @jetpaycryptobot.
Важно: Для Sandbox (песочницы) используются отдельные учетные данные, токены и URL. Запрашивайте доступ к песочнице отдельно для безопасного тестирования интеграции.
Все запросы к API должны содержать HTTP-заголовок авторизации:
Authorization: Bearer YOUR_TOKEN
Методы, изменяющие состояние (например, создание выплаты), требуют обязательного параметра signature для защиты от подделки запросов.
signature из этого словаря, если оно там есть.METHOD_PATH?key1=value1&key2=value2.
/api/payout?amount=1000&order_id=123&recipient=4444555566667777
signature в теле запроса.from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
import base64
def sign_request(method_path, data, private_key_path):
# 1. Сортировка и сборка строки
sorted_keys = sorted([k for k in data.keys() if k != 'signature'])
params = [f"{k}={data[k]}" for k in sorted_keys]
data_string = f"{method_path}?{'&'.join(params)}"
# 2. Загрузка ключа
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(), password=None
)
# 3. Подпись
signature = private_key.sign(
data_string.encode(),
padding.PKCS1v15(),
hashes.SHA512()
)
return base64.b64encode(signature).decode()
Получение текущего баланса вашего мерчант-аккаунта в USDT.
{
"balance": 5400.50,
"currency": "USDT"
}
Получение актуального курса USDT/RUB для вашего мерчант-аккаунта. Подпись не требуется.
{
"rate": "71.82"
}
Поле rate — строка с двумя знаками после точки (разделитель — точка), рублей за 1 USDT.
Создание новой заявки на выплату средств на банковскую карту (в рублях).
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
amount |
Float/Int | Да | Сумма выплаты в рублях (RUB). Сейчас: от 110 до 400 000 RUB. |
recipient |
String | Да | Номер банковской карты получателя (только цифры, без пробелов, 16 знаков). |
order_id |
String | Да | Уникальный идентификатор заказа в вашей системе. Повторный POST с тем же order_id (для этого мерчанта) возвращает 400 Duplicate order_id и не создаёт вторую выплату. Также используется в GET статуса. |
phone |
String | Нет | Номер телефона получателя в формате 79xxxxxxxxx (11 цифр). Если не указан, используется заглушка. |
name |
String | Нет | Имя получателя (латиница или кириллица). Полезно для некоторых банковских шлюзов. |
signature |
String | Да | RSA-SHA512 подпись запроса (см. раздел "Формирование подписи"). |
{
"status": "pending",
"id": "provider_abc123", // ID транзакции в процессинговом шлюзе
"order_id": "my_order_001",
"payout_id": 42, // Внутренний ID выплаты в JetBot
"debited_amount_usdt": 20.88554616, // Списано с баланса в USDT
"rate_used": 71.82 // Применённый курс RUB/USDT
}
debited_amount_usdt — точная сумма USDT, списанная с баланса при создании выплаты. Формула: amount / rate_used. Эти поля также возвращаются при запросе статуса выплаты.
Создание новой заявки на выплату через СБП (Систему быстрых платежей) — по номеру телефона и банку получателя, без номера карты. Это отдельный метод; /api/payout и его поведение не меняются.
Особенности СБП-выплат:
created.Подпись: формируется так же, как для /api/payout, но путь метода — /api/payout/sbp (пример строки: /api/payout/sbp?amount=5000&bank=Сбербанк&name=Иван Иванов&order_id=123&phone=79001234567).
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
amount |
Float/Int | Да | Сумма выплаты в рублях (RUB). Сейчас: от 110 до 20 000 RUB. |
bank |
String | Да | Название банка получателя (например, «Сбербанк», «Тинькофф»). 2–50 символов. |
name |
String | Да | Имя получателя (2–100 символов после trim). Значение - или пустая строка — ошибка Invalid recipient name. |
phone |
String | Да | Номер телефона получателя (российский мобильный, формат 79xxxxxxxxx; ввод с 8 или +7 нормализуется автоматически). |
order_id |
String | Да | Уникальный идентификатор заказа. Повтор с тем же значением → 400 Duplicate order_id. |
signature |
String | Да | RSA-SHA512 подпись запроса (путь метода — /api/payout/sbp). |
{
"status": "created", // СБП-выплата принята в обработку
"id": "19E3BABE0796332", // Публичный ID (используется для проверки статуса и чека)
"order_id": "my_order_001",
"payout_id": 42, // Внутренний ID выплаты в JetBot
"debited_amount_usdt": 69.61849067,
"rate_used": 71.82
}
Пакетное создание нескольких выплат (карта и/или СБП) одним запросом. Методы /api/payout и /api/payout/sbp не меняются. Максимум 50 заявок в батче.
Подпись: путь /api/payout/batch. Параметры batch_id и payouts; значение payouts в строке подписи — компактный JSON с отсортированными ключами объектов.
Пример тела:
{
"batch_id": "ORDER-124582",
"payouts": [
{
"order_id": "ORDER-124582-P1",
"type": "card",
"amount": 75000,
"recipient": "5469400023087641",
"name": "Иван Иванов"
},
{
"order_id": "ORDER-124582-P2",
"type": "sbp",
"amount": 15000,
"phone": "79991234567",
"bank": "Сбербанк",
"name": "Пётр Петров"
}
],
"signature": "BASE64_SIGNATURE"
}
Поле type — card или sbp. Остальные поля элемента — как у одиночных методов (без signature внутри элемента).
Ответ: status батча — accepted / partial / rejected. Ошибка по одной заявке не откатывает уже созданные.
{
"batch_id": "ORDER-124582",
"status": "accepted",
"payouts": [
{
"order_id": "ORDER-124582-P1",
"status": "pending",
"payout_id": 101,
"id": "19E3BABE0796332",
"debited_amount_usdt": 10.12,
"rate_used": 80.5
},
{
"order_id": "ORDER-124582-P2",
"status": "created",
"payout_id": 102,
"id": "A1B2C3D4E5F6071",
"debited_amount_usdt": 2.01,
"rate_used": 79.7
}
]
}
Получение актуального статуса выплаты. В качестве <ID> можно использовать:
payout_id - внутренний ID JetBot (возвращается при создании).order_id - ваш уникальный ID заказа (merchant_order_id).id - ID шлюза.POST /api/payout или POST /api/payout/sbp вернул HTTP 400 / 401 / 402 / 403, заявка не создана (исключение: SBP daily phone request limit exceeded — см. таблицу). Повторный GET /api/payout/<order_id> в этом случае будет 404 Payout not found. Это не «в обработке» и не зависание: опрашивать 404 бессмысленно. Исправьте тело запроса и создайте выплату заново с новым order_id (или тем же, если выплата так и не создалась).
{
"id": 42,
"order_id": "my_order_001",
"amount": 1500.00,
"status": "confirmed", // Возможные статусы: confirmed, pending, failed, cancelled
"jetbot_status": "success", // Детальный статус шлюза: success, process, error и т.д.
"payout_type": "card", // Тип выплаты: "card" (на карту) или "sbp" (СБП)
"recipient": "4276....1234", // Для СБП — реквизиты в 3 строки: имя, банк, телефон
"created_at": "2023-10-27 14:30:00",
"debited_amount_usdt": 20.88554616, // Списано с баланса в USDT
"rate_used": 71.82 // Применённый курс RUB/USDT
}
Скачивание PDF-чека по успешной выплате. В качестве <public_id> используется ID с чека (поле id, возвращаемое при создании выплаты). Внутренний номер выплаты (payout_id), order_id и другие идентификаторы для скачивания чека не принимаются. Подпись не требуется.
Ответ — файл в формате PDF (Content-Type: application/pdf). Время на чеке указано по московскому времени (МСК, UTC+3).
curl -H "Authorization: Bearer <TOKEN>" \
-o receipt.pdf \
https://api.jetbot.pw/api/payout/358472DC4B20E6A/receipt
409 + Receipt not ready, payout is still processing — выплата ещё не успешна (в обработке).409 + Receipt is only available for successful payouts — выплата завершилась неуспешно / отменена.404 + Payout not found — нет выплаты с таким публичным id.403 + Access denied — выплата принадлежит другому мерчанту.500 + Failed to generate receipt — не удалось сформировать PDF.Формат JSON при ошибке (все методы, кроме успешного PDF-чека):
{
"error": "текст из таблиц ниже",
"code": 400
}
HTTP-статус ответа совпадает с полем code. При ошибках создания выплаты дополнительно может быть order_id. Успешный ответ (HTTP 200) поля code не содержит.
Тексты error ниже — точные строки из API. Сверяйте по равенству строки, а не по подстроке «ошибка валидации».
| HTTP | Когда | Повторять запрос? |
|---|---|---|
| 400 | Валидация тела, дубль order_id, суточный лимит запросов СБП по телефону | Нет, пока не исправите данные. Дубль — не создавайте заново, берите payout_id/id из ответа |
| 401 | Нет/неверный Bearer token | Нет, пока не исправите заголовок |
| 402 | Недостаточно USDT на балансе мерчанта | После пополнения, с новым order_id если выплата не создалась |
| 403 | IP не в whitelist, неверная RSA-подпись, СБП выключен для аккаунта, чужая выплата | Нет, пока не исправите доступ/подпись |
| 404 | Выплата не найдена (в т.ч. после неуспешного POST — заявка не создана) | Не опрашивать «до появления». Создайте заново только если POST не был 200 |
| 409 | Чек: выплата не в успешном статусе | Чек — только после successful |
| 500 | Внутренняя ошибка при создании / генерации чека | Осторожный retry; проверьте GET по order_id, не создалась ли выплата |
| 503 | Nginx: больше 10 запросов/сек с IP (burst 20) | Да, с экспоненциальной задержкой |
Проверяются до разбора тела. Тела с подписью при 401/403 не обрабатываются.
| HTTP | error | Условие |
|---|---|---|
| 401 | Missing or invalid Authorization header | Нет заголовка или он не вида Bearer <token> |
| 401 | Invalid token | Токен неизвестен / отключён |
| 403 | Access denied | IP клиента не в белом списке мерчанта (тот же текст на GET чужой выплаты) |
POST /api/payout (карта)Поля до бизнес-логики (Flask): amount, recipient, order_id, signature.
| HTTP | error | Условие |
|---|---|---|
| 400 | No data provided | Пустое тело (нет JSON и form) |
| 400 | Missing field: amount / recipient / order_id / signature | Нет обязательного поля. Пустой order_id тоже даёт Missing field: order_id |
| 400 | Duplicate order_id | У этого мерчанта уже есть выплата с тем же order_id. Тело дополнительно: order_id, payout_id, id (публичный ID), status (текущий клиентский статус). Новую выплату не создаём — используйте эти идентификаторы |
| 400 | Invalid amount | amount не число |
| 400 | Amount must be at least 110 RUB. Provided: … | Сумма < 110 |
| 400 | Amount must not exceed 400000 RUB. Provided: … | Сумма > 400000 |
| 400 | Card number must contain exactly 16 digits. Provided: N digits | После удаления нецифровых символов не 16 цифр |
| 400 | Invalid card number (failed Luhn check) | 16 цифр, не проходит Луна |
| 402 | Insufficient balance | Не хватает USDT. Дополнительно: required, available. Выплата не создана |
| 403 | Invalid signature | RSA-SHA512 не совпала (путь подписи /api/payout) |
| 500 | Internal server error | Необработанное исключение при создании |
POST /api/payout/sbpОбязательные поля: amount, bank, name, phone, order_id, signature.
| HTTP | error | Условие |
|---|---|---|
| 400 | No data provided | Пустое тело |
| 400 | Missing field: … | Нет обязательного поля (в т.ч. пустой order_id) |
| 400 | Duplicate order_id | Как для карты: повтор того же order_id. В ответе payout_id, id, status |
| 400 | Invalid amount | amount не число |
| 400 | Amount must be at least 110 RUB. Provided: … | Сумма < 110 |
| 400 | SBP amount must not exceed 20000 RUB. Provided: … | Сумма > 20000 |
| 400 | Bank name must be 2-50 characters | Название банка после trim короче 2 или длиннее 50 символов (после нормализации — до 80) |
| 400 | Invalid recipient name | Имя после trim короче 2 или длиннее 100 символов. Типичный случай: name: "-" или пробелы. Выплата не создаётся. GET по order_id = 404. Не поллить. |
| 400 | Invalid phone number (expected Russian mobile, e.g. 79001234567) | Не российский мобильный (после нормализации не 11 цифр вида 79…) |
| 400 | SBP daily phone request limit exceeded | Номер в чёрном списке суточного лимита запросов СБП. Заявка создаётся и сразу отменяется с возвратом средств. В ответе есть payout_id и id. GET по ним найдёт выплату со статусом failure. Повтор с тем же order_id даст Duplicate |
| 402 | Insufficient balance | Как для карты |
| 403 | Invalid signature | Путь подписи /api/payout/sbp |
| 403 | SBP payouts are disabled for this account | СБП выключен мерчанту |
| 500 | Internal server error | Необработанное исключение |
POST /api/payout/batchОшибки уровня запроса (ни одна заявка не разбирается):
| HTTP | error |
|---|---|
| 400 | No data provided |
| 400 | Missing field: batch_id / payouts / signature |
| 400 | batch_id must be non-empty |
| 400 | payouts must be a JSON array (если передали строку, которая не JSON) |
| 400 | payouts must be a non-empty array |
| 400 | Too many payouts (max 50) |
| 403 | Invalid signature (путь /api/payout/batch) |
Если часть заявок валидна: HTTP 200, status: "partial", у ошибочных элементов status: "error" и те же error/code, что в §§4.3–4.4, плюс type must be 'card' or 'sbp', payouts[N] must be an object.
Если все заявки отклонены: HTTP 400 (или 402, если все ошибки — баланс; или 403, если все — запрет СБП), status: "rejected".
| Метод | HTTP | error | Условие |
|---|---|---|---|
| GET /api/payout/<ID> | 404 | Payout not found | Нет выплаты с таким внутренним id / public id / order_id этого мерчанта |
| GET /api/payout/<ID> | 403 | Access denied | Выплата есть, но user_id другой |
| GET …/receipt | 404 | Payout not found | Только поиск по публичному id с чека |
| GET …/receipt | 403 | Access denied | Чужая выплата |
| GET …/receipt | 409 | Receipt not ready, payout is still processing | Ещё не success |
| GET …/receipt | 409 | Receipt is only available for successful payouts | Терминальный неуспех |
| GET …/receipt | 500 | Failed to generate receipt | Сбой генерации PDF |
POST /api/payout/batch/status: те же принципы — No data provided, Missing field: signature, ids must be a JSON array / ids must be an array, Provide ids and/or batch_id, Too many ids (max 50), Invalid signature. В массиве результатов элемент может быть Empty id, Payout not found (code 404), Access denied.
HTTP/1.1 400 Bad Request
{
"error": "Duplicate order_id",
"code": 400,
"order_id": "my_order_001",
"payout_id": 42,
"id": "19E3BABE0796332",
"status": "in_progress"
}
Для обеспечения стабильности работы API применяются следующие ограничения:
503. Пожалуйста, реализуйте механизм повторных попыток (retry) с экспоненциальной задержкой.Вместо постоянного опроса метода GET /api/payout/<ID> вы можете получать push-уведомления при каждом изменении статуса выплаты. JetBot отправляет POST-запрос на указанный вами URL. Настройка (URL и секрет для подписи) выполняется на стороне JetBot — передайте их администратору.
failure отправляется только после окончательного отказа выплаты с возвратом средств. Если один платёжный шлюз отклонил выплату, а она была автоматически перенаправлена на другой шлюз, failure не отправляется. Можно безопасно строить бизнес-логику (например, возврат средств клиенту) на этом статусе.
Уведомление отправляется при смене клиентского статуса выплаты. Дублирующие уведомления с тем же статусом не отправляются.
| status | Значение |
|---|---|
created | Выплата принята и создана. |
in_progress | Выплата в обработке шлюзом (в т.ч. при переборе/смене шлюзов). |
successful | Выплата успешно завершена (терминальный статус). |
failure | Выплата окончательно отклонена, средства возвращены на баланс (терминальный статус). |
Метод POST, тело — JSON (Content-Type: application/json). Заголовки:
| Заголовок | Описание |
|---|---|
X-Webhook-Event | Тип события, всегда payout.status_updated. |
X-Webhook-Id | Уникальный ID события (event_id). Используйте для идемпотентности. |
X-Signature | Опционально. Подпись тела запроса в формате sha256=<hex> (HMAC-SHA256). Отсутствует, если подпись не настроена. |
{
"event": "payout.status_updated",
"event_id": "9f1c2b7e4a8d4f6e9b0c1d2e3f4a5b6c",
"status": "successful", // created | in_progress | successful | failure
"payout_id": "358472DC4B20E6A", // ID выплаты (как на чеке / поле id при создании)
"order_id": "my_order_001", // ваш merchant order_id
"amount": 1500.00,
"currency": "RUB",
"payout_type": "card",
"created_at": "2023-10-27 14:30:00",
"timestamp": "2023-10-27T11:31:05Z" // UTC, момент отправки уведомления
}
По умолчанию вебхуки отправляются без подписи. Подлинность отправителя проверяется по IP-адресу: все уведомления приходят со статического адреса 185.207.14.225. Добавьте его в whitelist и принимайте вебхуки только с него.
remote_addr), а не заголовки X-Forwarded-For / X-Real-IP — их можно подделать. Если ваш сервер стоит за обратным прокси (nginx, Cloudflare и т.п.), берите IP из доверенного источника прокси. Приём ведите только по HTTPS.
Если для вашего аккаунта настроен секрет, каждое уведомление дополнительно подписывается. Подпись рассчитывается как HMAC-SHA256 от сырого тела запроса (в том виде, в котором оно получено, без переформатирования JSON) с использованием вашего секрета. Сравните результат со значением из заголовка X-Signature (уберите префикс sha256=). Всегда используйте сравнение с постоянным временем (constant-time). Если подпись не настроена, заголовок X-Signature не отправляется.
import hmac, hashlib
from flask import request, abort
WEBHOOK_SECRET = b"your_webhook_secret"
@app.route("/jetbot/webhook", methods=["POST"])
def jetbot_webhook():
raw = request.get_data() # именно сырое тело, до парсинга JSON
received = request.headers.get("X-Signature", "").removeprefix("sha256=")
expected = hmac.new(WEBHOOK_SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(received, expected):
abort(403)
event = request.get_json()
# Идемпотентность: пропускаем уже обработанные event_id
# if already_processed(event["event_id"]): return "", 200
# ... обработка event["status"] ...
return "", 200 # верните 2xx для подтверждения приёма
2xx для подтверждения приёма. Любой другой код или таймаут считается неуспешной доставкой.1 мин → 5 мин → 30 мин → 2 ч → 6 ч → 12 ч → 24 ч, до 8 попыток.event_id (X-Webhook-Id).