RSPAY · v1
RSPAY API — приём платежей
Вы выставляете счёт, отдаёте плательщику ссылку, а деньги приходят на ваш кошелёк в сервисе за вычетом комиссии. Платить можно криптовалютой, с кошелька в приложении или по СБП. Платёжная страница открывается внутри Telegram-приложения.
Формат — JSON, UTF-8. Суммы всегда строки с десятичной точкой ("100", "12.5"), валюта счёта — USDT. Время — ISO 8601 (UTC).
Подключение
- Откройте бота → «Профиль» → RSPAY → «Подключить». Подключение доступно всем.
- «API и вебхуки» → создайте ключ (он показывается один раз) и, при желании, укажите адрес вебхука.
- Либо выставляйте счета вручную: «Выставить счёт» в том же разделе, без API.
Авторизация
Ключ вида rsp_live_… передаётся в заголовке Authorization: Bearer <ключ> или X-API-Key: <ключ>. У нас ключ хранится только в виде хеша; потерянный ключ отзывается в приложении и создаётся новый. Одновременно может быть до 5 активных ключей.
Лимит — 120 запросов в минуту на ключ. При превышении ответ 429 с заголовком Retry-After.
Создать счёт
{
"amount": "100",
"title": "Заказ №104",
"description": "Два товара",
"external_id": "ORD-104",
"payer_telegram_id": "5512340987",
"lifetime_sec": 86400
}| Поле | Обязательно | Описание |
|---|---|---|
amount | да | Сумма в USDT, число или строка. |
title | да | Название, до 120 символов. |
description | нет | Описание, до 500 символов. |
external_id | нет | Ваш номер заказа (A–Z a–z 0–9 . _ : -, до 64). Повторный запрос с тем же номером вернёт тот же счёт (200), с другими данными — 409. |
payer_telegram_id | нет | Telegram ID плательщика. Если указан, оплатить сможет только он. Если нет — счёт достанется тому, кто первым откроет ссылку. |
lifetime_sec | нет | Срок жизни, от 300 до 604800 секунд (по умолчанию сутки). |
Заголовок Idempotency-Key работает как external_id, если тот не передан. Свой счёт оплатить нельзя: мерчант не может быть плательщиком.
{
"invoice": {
"id": "210",
"status": "created",
"amount": "100",
"fee": "1",
"net_amount": "99",
"currency": "USDT",
"title": "Заказ №104",
"description": "Два товара",
"external_id": "ORD-104",
"payer_telegram_id": null,
"pay_url": "https://t.me/<бот>?startapp=serviceOrder_<токен>",
"created_at": "2026-10-04T16:00:00.000Z",
"expires_at": "2026-10-05T16:00:00.000Z",
"paid_at": null,
"payment_method": null,
"credited": null,
"network_fee": null
}
}pay_url вставляйте в кнопку вашего бота или отправляйте плательщику: ссылка открывает приложение с оплатой. Комиссия fee фиксируется в момент создания счёта и дальше не меняется. После оплаты криптой в credited видно, сколько реально зачислено вам в монете платежа, а в network_fee — какую комиссию сети удержали.
Получить, список, отмена
| Метод | Описание |
|---|---|
GET /invoices/{id} | Один счёт. |
GET /invoices?status=&limit=&before_id=&external_id= | Список, новые сверху. Постранично по next_before_id. По external_id находится ваш заказ. |
POST /invoices/{id}/cancel | Отменить неоплаченный счёт. Оплаченный отменить нельзя (409); возврат вы делаете сами из кошелька. |
GET /me | Ваша комиссия и лимиты счёта. |
Статусы счёта
| Статус | Значение |
|---|---|
created | Выставлен, плательщик ещё не открыл ссылку. |
awaiting_payment | Плательщик известен, ждём оплату. |
under_review | Платёж получен и проходит проверку (AML, а у оплаты по СБП — ещё и сверка ФИО плательщика с KYC). После неё счёт станет paid сам. |
paid | Оплачен, деньги зачислены вам. |
expired | Срок вышел без оплаты. |
cancelled | Отменён. |
payment_method: wallet (с кошелька), crypto (ончейн), sbp (по СБП; сумма по СБП зачисляется с заморозкой на сутки, T+1).
Оплата по СБП доступна только плательщикам с пройденным KYC, и платить нужно с личной карты на своё ФИО. Если банк плательщика передал ФИО и оно не совпадает с KYC, платёж уходит на ручную проверку: счёт в статусе under_review, деньги вам не зачисляются до решения поддержки.
Вебхуки
Укажите адрес в разделе «API и вебхуки» (только https, публичный адрес) — мы пришлём POST с JSON.
{
"id": "evt_91",
"type": "invoice.paid",
"created_at": "2026-10-04T16:20:00.000Z",
"data": { "invoice": { "id": "210", "status": "paid", "external_id": "ORD-104" } }
}События: invoice.paid, invoice.under_review, invoice.expired, invoice.cancelled. Повторы доставки возможны — обрабатывайте идемпотентно (по id события). invoice.paid может прийти после invoice.under_review через часы. Ориентируйтесь на invoice.paid.
Ответьте любым кодом 2xx в течение 10 секунд. Иначе мы повторим доставку через 1 мин, 5 мин, 30 мин, 2 ч, 6 ч, 12 ч, 24 ч (всего 8 попыток). Кнопка «Отправить тест» в приложении шлёт событие ping.
Проверка подписи
Заголовок X-RSPAY-Signature: t=<unix-секунды>,v1=<hex>, где v1 = HMAC-SHA256(секрет, "<t>.<тело как пришло>"). Проверяйте по сырому телу и отвергайте запросы со старой меткой t (например, старше 5 минут). Секрет показывается в разделе «API и вебхуки». Дополнительно приходят X-RSPAY-Event (тип) и X-RSPAY-Delivery (номер события).
const crypto = require('crypto');
// rawBody — тело запроса КАК ПРИШЛО (строка/Buffer), не пересобранный JSON
function verify(rawBody, header, secret, toleranceSec = 300) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
if (!m) return false;
if (Math.abs(Date.now() / 1000 - Number(m[1])) > toleranceSec) return false;
const expected = crypto.createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}import hmac, hashlib, re, time
def verify(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
if not m or abs(time.time() - int(m[1])) > tolerance:
return False
signed = m[1].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, m[2])Ошибки
{ "error": { "code": "amount_too_small", "message": "…", "details": { "min_amount": "1" } } }| HTTP | Коды |
|---|---|
| 400 | invalid_amount, amount_too_small, invalid_title, invalid_description, invalid_external_id, invalid_payer, invalid_lifetime, invalid_status, invalid_request |
| 401 | unauthorized |
| 403 | merchant_blocked |
| 404 | invoice_not_found, not_found |
| 409 | external_id_conflict, too_many_open_invoices, invoice_already_paid, invoice_has_payment |
| 429 | rate_limited |
| 500 | internal_error |
Деньги и безопасность
- Деньги зачисляются вам на кошелёк в той монете и сети, которой заплатил покупатель. Из суммы вычитаются комиссия RSPAY (процент, но не менее минимума за платёж) и, при оплате криптой, комиссия сети за перевод монет в общий пул.
- Каждый входящий платёж проходит AML-проверку. Пока проверка не пройдена, деньги вам не зачисляются, счёт в статусе
under_review. Адрес, на который пришли непроверенные монеты, больше не выдаётся под оплаты. - Выводить заработанное можно как обычно из кошелька, в любой доступной сети; адрес вывода также проверяется.
- Храните ключ и секрет вебхука на сервере. Если они попали не туда — отзовите ключ и перевыпустите секрет в приложении.
Как проверить
- Создайте ключ в «Профиль → RSPAY → API и вебхуки» и положите его в переменную
RSPAY_KEY. - Проверьте ключ:
GET /meдолжен вернуть вашу комиссию и лимиты. - Выставьте тестовый счёт на 1 USDT.
- Откройте
pay_urlс другого Telegram-аккаунта и оплатите (себе оплатить нельзя). - Убедитесь, что
GET /invoices/{id}вернулpaid, а на вашем кошельке появилась сумма за вычетом комиссии.
curl -X POST https://exchange.riskscan.net/api/rspay/v1/invoices \
-H "Authorization: Bearer $RSPAY_KEY" \
-H "Content-Type: application/json" \
-d '{"amount":"1","title":"Тестовый счёт","external_id":"TEST-1"}'