На этой странице
Руководство по интеграции
Документация для разработчиков
Создайте счёт, отправьте клиента на страницу оплаты и получите подписанный вебхук после оплаты. Начните с четырёх шагов ниже.
Что поддерживается
Шлюз принимает USDT и USDC. Эта таблица совпадает с GET /v1/services по состоянию на 4 ноября 2026 г. Сверьтесь с актуальным каталогом, прежде чем делать выбор способа оплаты.
| Актив | Сеть | Подтверждения |
|---|---|---|
| USDT | Tron | 20 |
| USDT | Ethereum | 12 |
| USDT | Solana | 32 |
| USDT | Polygon | 8 |
| USDT | Arbitrum | 12 |
| USDT | TON | 5 |
| USDC | Ethereum | 12 |
| USDC | Solana | 32 |
| USDC | Polygon | 8 |
| USDC | Base | 12 |
| USDC | Arbitrum | 12 |
Платежи кастодиальные: средства поступают на адреса под управлением платформы. Ваш баланс мерчанта — это сумма, которую мы вам должны. Отдельный кошелёк в Telegram — некастодиальный.
Ограничения
- Выплаты и возвраты пока не работают:
GET /v1/servicesвозвращаетboundaries.livePayoutsиboundaries.liveRefundsсо значениемfalse. - Входящие платежи не проверяются KYT-провайдером.
- Счета, в которых комиссии превысили бы 20% суммы, отклоняются с ошибкой
AMOUNT_BELOW_MINIMUM. - Договорного SLA нет.
Стартовая комиссия — 0,40% плюс комиссия сети. Запросите расчёт комиссии, чтобы узнать итог и минимальную сумму; подробнее — в разделе тарифы.
Аутентификация
Вызовы между серверами используют x-api-key. Ключ проекта действует только в одном проекте мерчанта. Храните ключи на своём сервере.
Тестовые ключи начинаются с ncp_test_…, боевые — с ncp_live_…. Ключи, выпущенные раньше, начинаются с ncp_sandbox_ и продолжают работать.
curl -sS "https://api.veilnebu.la/v1/balances" \
-H "x-api-key: $PROJECT_API_KEY"Подпись изменяющих запросов
GET-запросам нужен только x-api-key. Каждому запросу POST, PUT, PATCH и DELETE с ключом проекта нужны ещё два заголовка, иначе API ответит 401 Invalid API signature:
x-ncp-timestamp: ISO-8601, не дальше 5 минут от текущего времени.x-ncp-signature:hex(HMAC-SHA256(your API key, "<timestamp>.<METHOD>.<path incl. query>.<raw body>")).
Пишите методы заглавными буквами. Подписывайте ровно те байты, которые отправляете, включая строку запроса в пути. Для запроса без тела подписывается пустая строка. Хелперам ниже нужен ключ проекта; shell-хелперу нужны Bash, curl и OpenSSL, а хелпер для Node.js использует встроенный fetch.
export API_BASE_URL="https://api.veilnebu.la"
export PROJECT_API_KEY="ncp_test_…"
ncp() {
local method="$1" path="$2" body="" ts sig
shift 2
if [ "$#" -gt 0 ]; then body="$1"; shift; fi
ts=$(date -u +%Y-%m-%dT%H:%M:%S.000Z) || return
sig=$(printf '%s.%s.%s.%s' "$ts" "$method" "$path" "$body" |
openssl dgst -sha256 -hmac "$PROJECT_API_KEY" -hex) || return
sig=${sig##* }
if [ -n "$body" ]; then
curl -sS --request "$method" "$API_BASE_URL$path" \
-H "x-api-key: $PROJECT_API_KEY" \
-H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" \
-H "content-type: application/json" --data-binary "$body" "$@"
else
curl -sS --request "$method" "$API_BASE_URL$path" \
-H "x-api-key: $PROJECT_API_KEY" \
-H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" "$@"
fi
}Создайте первый счёт
Создайте проект в личном кабинете, скопируйте его API-ключ и создайте счёт. В ответе есть checkoutUrl: отправьте туда клиента.
ncp POST /v1/payments '{
"amountMinor":"10000",
"currencyCode":"USD",
"settlementAssetCode":"USDT",
"externalId":"order-1001"
}' -H "idempotency-key: order-1001"Фрагмент ответа. ID генерируется для каждого счёта.
{
"id": "cm3k9x2f40001qz8e7h5v1abc",
"status": "new",
"amountMinor": "10000",
"currencyCode": "USD",
"settlementAssetCode": "USDT",
"checkoutUrl": "https://app.veilnebu.la/checkout/cm3k9x2f40001qz8e7h5v1abc"
}amountMinor — целое число в виде строки в минимальных единицах валюты: 10000 = 100.00 USD. Десятичное поле amount принимает только браузерный эндпоинт /v1/public/payments.
Оплата из браузера и разрешённые источники
Создайте публикуемый ключ в личном кабинете и перечислите точные источники (origins), которые использует ваш магазин. Браузерные запросы передают его как x-publishable-key в POST /v1/public/payments.
Origin, successUrl и cancelUrl должны входить в список разрешённых источников ключа. Источник — это схема, хост и необязательный порт, без пути и подстановочных знаков. Запросы без источника или с источником не из списка отклоняются.
Десятичная сумма допускает до двух знаков после запятой для фиатных валют и до собственного числа знаков актива для криптовалют (USDT и USDC: 6 во всех сетях, которые мы сейчас поддерживаем).
const response = await fetch('https://api.veilnebu.la/v1/public/payments', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-publishable-key': 'pk_test_…',
},
body: JSON.stringify({
amount: '100.00',
currencyCode: 'USD',
settlementAssetCode: 'USDT',
successUrl: 'https://shop.example/thanks',
cancelUrl: 'https://shop.example/cart',
}),
});
if (!response.ok) throw new Error(await response.text());
const invoice = await response.json();
window.location.assign(invoice.checkoutUrl);Статусы платежей
| Статус | Следующее действие |
|---|---|
new | Отправьте ссылку на оплату |
pending | Ожидаем оплату |
confirming | Ожидаем подтверждения |
paid | Готово к зачислению |
partially_paid | Попросите клиента доплатить |
overpaid | Проверьте зачисленную переплату |
expired | Создайте новый счёт |
cancelled | Действий не требуется |
failed | Откройте подробности |
refunded | Действий не требуется |
Ключи идемпотентности
При создании счёта передавайте стабильный Idempotency-Key, полученный из ID вашего заказа. Повтор с тем же ключом и телом вернёт исходный счёт. Повторное использование ключа с другим телом вернёт 409. При повторе после тайм-аута не меняйте ни ключ, ни тело.
Сам запрос смотрите в примере первого счёта.
Ошибки
Ошибки возвращаются в JSON. message — строка либо массив строк для ошибок валидации.
{
"statusCode": 401,
"error": "Unauthorized",
"message": "Invalid API key"
}| HTTP-статус | Значение и действие |
|---|---|
400 | Неверное поле; его название указано в "message". Исправьте его перед повтором. |
401 | Invalid API key или Invalid API signature. Проверьте ключ, подписанные байты и метку времени. |
404 | Payment invoice not found. Проверьте ID и проект. |
409 | Тот же Idempotency-Key с другим телом или Insufficient merchant payable balance. |
429 | Превышен лимит запросов. Подождите перед повтором. |
5xx | Повторите запрос с тем же Idempotency-Key и телом. |
Вебхуки (HMAC-SHA256)
Каждая доставка содержит x-nebula-signature: t=<unix>,v1=<hex>. Проверьте HMAC секретом эндпоинта, который вы получили при его создании. Отклоняйте метки времени, отстоящие от текущего момента больше чем на 300 секунд.
Проверка в Node.js
Верификатор для Node.js использует только встроенный модуль crypto. Передайте ему заголовок подписи, исходные байты тела и секрет эндпоинта.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(header, rawBody, secret) {
const parts = {};
for (const piece of (header ?? '').split(',')) {
const separator = piece.indexOf('=');
if (separator <= 0) continue;
parts[piece.slice(0, separator).trim()] = piece.slice(separator + 1).trim();
}
if (!/^[0-9]{1,16}$/.test(parts.t ?? '')) return false;
if (!/^[0-9a-f]{64}$/.test(parts.v1 ?? '')) return false;
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac('sha256', secret)
.update(parts.t + '.')
.update(rawBody)
.digest('hex');
if (expected.length !== parts.v1.length) return false;
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Проверяйте подпись по исходному телу запроса до разбора JSON. Доставка гарантируется как минимум один раз: убирайте дубли по x-nebula-event-id, который совпадает с eventId в теле. Автоматические повторы сохраняют ID доставки; ручная повторная отправка создаёт новый ID доставки с тем же ID события. Сохраните событие, прежде чем ответить 2xx.
Локальное тестирование вебхуков
URL вебхука должен указывать на публичный хост в интернете. Локальные и частные адреса отклоняются. Откройте доступ к своему приёмнику через публичный HTTPS-туннель, а затем зарегистрируйте его URL с помощью хелпера подписи.
ncp POST /v1/webhooks/endpoints '{
"url":"https://receiver.example/webhook",
"events":["payment.paid","payment.reorged"]
}'Замените URL на адрес своего приёмника. Сохраните секрет эндпоинта, который вернётся при создании.
Типы событий
Выплаты и возвраты пока не работают (GET /v1/services возвращает boundaries.livePayouts со значением false). До их запуска выплаты и пакеты выплат только фиксируют перевод в вашем балансе; payout.completed и refund.completed не означают, что средства ушли.
Каталог ниже совпадает с GET /v1/services. Наличие записи в каталоге не означает, что соответствующее движение денег уже работает.
| Событие | Значение |
|---|---|
webhook. | Тестовая доставка, запрошенная для эндпоинта вебхука. |
payment. | Клиент выбрал способ оплаты; ожидаем первый депозит. |
payment. | Депозит обнаружен; ожидаем нужное число подтверждений. |
payment. | Счёт полностью оплачен и проведён по книге учёта. |
payment. | Получено меньше запрошенного; ожидаем остаток. |
payment. | Получено больше запрошенного; проведено по книге учёта вместе с переплатой. |
payment. | Срок счёта истёк, а средств поступило недостаточно. |
payment. | Счёт отменён до оплаты. |
payment. | Платёж не прошёл из-за ошибки сети, политики комиссий или наблюдения. |
payment. | Информационное событие о риске по уже зачисленному депозиту. Проверка входящих платежей через KYT не работает. |
payment. | Подтверждённый депозит пропал из-за реорганизации блокчейна; счёт больше не считается оплаченным, поэтому приостановите или отмените выполнение заказа. |
payment. | Поступил неподдерживаемый токен; он не зачислен. |
payout. | Выплата в обработке. Реальные переводы не открыты. |
payout. | Выплата записана в баланс; это не значит, что средства ушли. |
payout. | Выплата не прошла. |
payout. | Выплата отменена до завершения. |
refund. | Возврат в обработке. Реальные возвраты не открыты. |
refund. | Возврат записан в баланс; это не значит, что средства ушли. |
refund. | Возврат не прошёл. |
internal_transfer. | Перевод баланса между мерчантами проведён у отправителя. |
internal_transfer. | Перевод баланса между мерчантами зачислен получателю. |
static_wallet. | Для клиента создан постоянный адрес для депозитов. |
static_wallet. | Статический кошелёк заблокирован. |
static_wallet. | Блокировка статического кошелька снята. |
static_wallet. | Депозит на статический кошелёк обнаружен и проведён по книге учёта. |
dispute. | По счёту открыт спор. |
dispute. | Спор урегулирован. Само урегулирование не доказывает возврат в блокчейне. |
dispute. | Спор отклонён; от мерчанта действий не требуется. |
subscription. | Платёжный цикл создал новый счёт. |
Политика повторов
Если ваш эндпоинт не отвечает 2xx, мы делаем 8 попыток в сумме: первая попытка, затем повторы через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов, 12 часов и 24 часа. Каждая задержка отсчитывается от предыдущей неудачной попытки. Сетевые ошибки и тайм-ауты тоже считаются неудачами. Для тестовых доставок — одна попытка.
После последней неудачи доставка помечается как неуспешная; отправьте её повторно через POST /v1/webhooks/deliveries/{deliveryId}/resend. Для сверки в качестве запасного варианта используйте GET /v1/payments/{id}.
Выплаты и возвраты пока не работают (GET /v1/services возвращает boundaries.livePayouts со значением false). До их запуска выплаты и пакеты выплат только фиксируют перевод в вашем балансе; payout.completed и refund.completed не означают, что средства ушли.
Пакеты выплат
Пакет объединяет записи о переводах. Его политика одобрения и нужное число одобрений возвращаются в approvalPolicy и approvalsRequired. Если одобряющих несколько, создатель не может одобрить свой пакет. Единственный допущенный оператор может одобрить пакет, созданный в его же сессии. Пакеты одобряются через WebAuthn в личном кабинете.
export PROJECT_API_KEY="ncp_test_…"
ncp POST /v1/payout-batches '{
"assetCode":"USDT",
"networkCode":"TRON",
"externalId":"batch-1001",
"items":[{
"destinationAddress":"T…",
"amountMinor":"15000000",
"externalRef":"recipient-1001"
}]
}' -H "idempotency-key: batch-1001"Замените адрес назначения действительным адресом в своём тестовом проекте.
Регулярные платежи
Подписка создаёт новый счёт в каждом платёжном цикле и сообщает о нём событием subscription.invoice_created. Она не списывает средства с кошелька клиента автоматически. Отправьте клиенту новый URL страницы оплаты.
interval: DAY, WEEK, MONTH или YEAR.
ncp POST /v1/subscriptions '{
"customerRef":"customer-42",
"assetCode":"USDT",
"networkCode":"TRON",
"amountMinor":"9000000",
"interval":"MONTH",
"intervalCount":1
}'Приостанавливайте, возобновляйте или отменяйте подписку операциями жизненного цикла из справочника API.
Проверка рисков
Адреса назначения выплат и возвратов проверяются по публичным санкционным спискам до создания перевода; при совпадении запрос отклоняется. Входящие платежи не проверяются.
Бот Telegram для мерчантов
@veilnebula_bot работает с одним проектом за раз. Привяжите его в личном чате с помощью API-ключа этого проекта. Бот пытается удалить ваше сообщение и хранит ключ в зашифрованном виде. Если удалить не получилось, отзовите ключ и выпустите новый. Никогда не публикуйте ключ в общих чатах.
Уведомления относятся к привязанному проекту и активному ключу. Чтобы одобрить выплаты, откройте личный кабинет.
/link <API_KEY>
/balance
/invoices
/notify on
/unlink