
Nudge Pay API
Приём оплат в крипте для сайтов, ботов и каналов в Telegram. Покупатель платит со своего баланса в Nudge Wallet, деньги приходят на баланс магазина сразу.
Как это работает
- Твой сервер создаёт счёт и получает ссылку на оплату.
- Ты отдаёшь ссылку покупателю: кнопкой в боте, на сайте, в сообщении.
- Покупатель открывает ссылку в Telegram и платит в два нажатия.
- Nudge присылает на твой сервер вебхук
invoice.paid. Деньги уже на балансе магазина.
Комиссия — 1% от суммы, удерживается с магазина. Покупатель платит ровно сумму счёта. Принимаем USDT, TON и TRX.
Начало
Открой Nudge Wallet → Профиль → Nudge Pay, подключи приём оплат и скопируй ключ API. Ключ показывается один раз; если потерял — создай новый, старый сразу перестанет работать.
Базовый адрес API: https://…
Ключ передаётся в заголовке:
Authorization: Bearer nk_live_…
Храни ключ только на сервере. С ним можно создавать и отменять счета магазина, но нельзя выводить деньги.
Создать счёт
/pay/v1/invoices| Поле | Тип | Описание |
|---|---|---|
asset | string | Монета: USDT, TON или TRX. |
amount | number | Сумма, которую заплатит покупатель. Не меньше $0.10. |
description | string | Необязательно. Что покупают, до 200 символов. Видно покупателю. |
external_id | string | Необязательно. Твой номер заказа, до 128 символов. Повторный запрос с тем же external_id вернёт уже созданный счёт, а не новый. |
payload | string / object | Необязательно. Любые данные до 1024 символов, вернутся в вебхуке как есть. |
expires_in | number | Необязательно. Сколько секунд счёт действует: от 60 до 2 592 000 (30 дней). По умолчанию сутки. |
curl -X POST https://…/pay/v1/invoices \
-H "Authorization: Bearer $NUDGE_KEY" \
-H "Content-Type: application/json" \
-d '{"asset":"USDT","amount":5,"description":"Подписка на месяц","external_id":"order-1042"}'
Ответ 201:
{
"id": "inv_Xa9…",
"status": "pending",
"asset": "USDT",
"amount": 5,
"fee": 0,
"amount_received": null,
"description": "Подписка на месяц",
"external_id": "order-1042",
"payload": null,
"pay_url": "https://t.me/NudgeWallet_bot?startapp=p_inv_Xa9…",
"web_url": null,
"created_at": "2026-09-23T18:00:00.000Z",
"expires_at": "2026-09-24T18:00:00.000Z",
"paid_at": null
}
pay_url — ссылка, которую нужно отдать покупателю. После оплаты в fee будет удержанная комиссия, в amount_received — сколько пришло магазину.
Другие запросы
/pay/v1/invoices/{id} — счёт по id./pay/v1/invoices?limit=20 — последние счета, до 100./pay/v1/invoices/{id}/cancel — отменить неоплаченный счёт./pay/v1/me — проверить ключ: название магазина и комиссия.Статусы счёта
| Статус | Что значит |
|---|---|
pending | Ждёт оплаты. |
paid | Оплачен, деньги на балансе магазина. Окончательный статус. |
expired | Истёк срок, оплатить уже нельзя. |
cancelled | Отменён магазином. |
Вебхуки
Укажи адрес в кабинете (Nudge Pay → Адрес вебхука). Адрес должен быть публичным и начинаться с https://. Кнопка «Проверить» отправит событие test.
Nudge отправляет POST с JSON:
{ "event": "invoice.paid", "invoice": { …тот же объект, что в API… } }
События: invoice.paid, invoice.expired, invoice.cancelled, test.
Ответь любым кодом 2xx. Если ответа нет или код другой, повторим до 8 раз с растущей паузой: 30 секунд, минута, 2 минуты и так далее. Одно и то же событие может прийти дважды, поэтому проверяй invoice.id и не выдавай товар повторно.
Проверка подписи
В заголовке X-Nudge-Signature: t=1758650000,v1=… лежит время отправки и HMAC-SHA256 от строки {t}.{тело запроса} с секретом вебхука из кабинета. Проверяй подпись по сырому телу запроса, до разбора JSON, и отклоняй запросы старше 5 минут.
// Node.js (Express)
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/nudge-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const header = req.get('X-Nudge-Signature') ?? '';
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', process.env.NUDGE_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const valid = parts.v1 && expected.length === parts.v1.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
if (!fresh || !valid) return res.sendStatus(400);
const { event, invoice } = JSON.parse(req.body);
if (event === 'invoice.paid') {
// выдай товар по invoice.external_id, если ещё не выдан
}
res.sendStatus(200);
});
# Python
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = f"{parts['t']}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts["t"])) < 300
return fresh and hmac.compare_digest(expected, parts.get("v1", ""))
Ошибки
При ошибке ответ — JSON { "error": "код", "message": "текст" }.
| HTTP | error | Когда |
|---|---|---|
| 401 | unauthorized | Нет ключа или он неверный. |
| 400 | bad_asset, bad_amount, too_small, bad_request | Неверные поля запроса. |
| 404 | not_found | Счёта нет или он другого магазина. |
| 409 | already_paid | Отмена оплаченного или закрытого счёта. |
| 429 | rate_limited | Больше 60 запросов в минуту. |
Ограничения
- Платить могут только пользователи Nudge Wallet. Если у покупателя не хватает денег, он пополнит кошелёк и вернётся по той же ссылке.
- Не больше 60 запросов в минуту с одного адреса.
- Деньги от оплат лежат на балансе магазина в Nudge. Вывести их можно как обычно, через «Отправить».