Собственный REST API
Собственный API — это REST-интерфейс blockwallets: счета в центах США, идемпотентный ref для каждого заказа, статические депозитные аккаунты на всех сетях и вебхуки с подписью HMAC-SHA256. Он создаёт такие же счета на той же странице оплаты, что и API, совместимый с Cryptomus, с теми же комиссиями и балансом.
Ключи вы создаёте сами в кабинете, в разделе «Разработчикам › Интеграция и ключи»: ключ Live и ключ Test и секреты подписи их вебхуков. Каждый показывается один раз, в списке виден только префикс. Если в аккаунте включена двухфакторная аутентификация, для создания или обновления ключа Live нужно ввести код. Ещё нет аккаунта продавца? Запросите доступ. Уже работаете с Cryptomus? Посмотрите API, совместимый с Cryptomus (как выбрать).
Ключи и запросы
Базовый URL: https://blockwallets.io. Запросы и ответы — JSON. Каждый запрос содержит:
Authorization: Bearer bw_live_…илиBearer bw_test_…: префикс определяет режим, поэтому ключ Test видит только счета Test и работает в тестовых сетяхContent-Type: application/jsonв запросахPOST- Храните ключи только на сервере, никогда не размещайте их на веб-странице или в приложении; мы храним только их хеш
Ошибки возвращаются с HTTP-статусом и телом {"error": {"code": "…", "message": "…"}}: 400 invalid_request (отсутствует поле или оно неверного формата), 401 unauthorized (ключ отсутствует, неизвестен или отключён), 404 not_found, 409 conflict (запрос противоречит текущему состоянию счёта), 422 currency_unsupported.
Эндпоинты
| Эндпоинт | Что делает |
|---|---|
GET /v1/networks | Сети, которые можно принимать прямо сейчас в режиме этого ключа: минимум, подтверждения, обычное время поступления и оценка комиссии; для монет с курсом указан текущий курс |
POST /v1/invoices | Создаёт счёт (201) или возвращает существующий для того же ref (200) |
GET /v1/invoices?ref=… | Находит счёт по вашему ref |
GET /v1/invoices/:id | Находит счёт по id |
POST /v1/invoices/:id/network | Самостоятельный выбор сети для счёта (страница оплаты в вашем оформлении): возвращает address и pay_amount для показа клиенту |
POST /v1/invoices/:id/close | Закрывает ожидающий оплаты счёт, по которому не было переводов |
POST /v1/deposit-accounts | Открывает (201) или возвращает (200) депозитный аккаунт одного клиента: постоянный адрес в каждой сети |
GET /v1/deposit-accounts?customer_ref=… | Находит депозитный аккаунт по customer_ref |
GET /v1/deposit-accounts/:id | Находит депозитный аккаунт по id |
GET /v1/deposits | Список депозитов одного аккаунта (account_id или customer_ref, limit 1–200, по умолчанию 50) |
GET /v1/deposits/:id | Находит один депозит |
GET /v1/events | Доставки вебхуков по одному счёту или депозиту (invoice_id или deposit_id) |
POST /v1/events/:id/redeliver | Повторно отправляет вебхук (202) |
Добавьте ?probe=1 к запросу счёта или депозитного аккаунта, чтобы заодно проверить блокчейн (не чаще раза в 10 секунд); без него вы получите последнее состояние от нашего сканера, который проверяет каждую минуту.
Создание счёта POST /v1/invoices
Вызовите POST /v1/invoices, когда клиент оформляет заказ, и перенаправьте его на pay_url из ответа.
curl -X POST https://blockwallets.io/v1/invoices \
-H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
-d '{"ref":"order_1042","amount":4200,"title":"Order #1042",
"return_url":"https://shop.example/cart","success_url":"https://shop.example/thanks?order=1042",
"meta":{"user_id":42}}'| Поле | Обязательное | Описание |
|---|---|---|
ref | Да | Номер вашего заказа: 1–64 символа — буквы, цифры и _ . : -. Уникален в пределах режима; повторная отправка того же ref с теми же полями возвращает тот же счёт, с другими полями — 409 |
amount | Да | Сумма в центах США целым числом: 4200 = $42.00. После выбора сети не меньше минимума этой сети |
title | Да | Что клиент видит на странице оплаты, до 64 символов |
return_url | Да | Куда ведёт «Назад» на странице оплаты; должен быть на одном из доменов вашего сайта |
success_url | Нет | Куда попадает клиент после оплаты; по умолчанию return_url |
network | Нет | Заранее выбрать id сети из таблицы сетей; если не указать, клиент выберет на странице оплаты |
customer_ref | Нет | Ваш id клиента, до 64 символов, возвращается как есть |
lifetime | Нет | Срок жизни в секундах, 600 – 43200, по умолчанию 3600 |
amount_rule | Нет | "any" = зачислять всё, что пришло (пополнения): paid сразу после подтверждения. Если не указать, действует «Правило суммы» в разделе «Настройки › Правила приёма платежей и ставки» кабинета |
accuracy_percent | Нет | Какая недоплата ещё считается оплатой, 0 – 5 (процентов), по умолчанию 0 |
subtract_percent | Нет | Доля комиссии платформы, которую платит клиент, 0 – 100 (процентов), добавляется к pay_amount; если не указать, действует значение по умолчанию из кабинета |
payment_multiple | Нет | Можно ли доплатить недостающую сумму на тот же адрес; по умолчанию берётся из кабинета (включено, если вы не меняли). false: первый же недоплаченный перевод завершается как wrong_amount |
meta | Нет | Любой JSON-объект до 2 КБ, возвращается как есть в запросах и вебхуках |
currency | Нет | Только USD (по умолчанию); любое другое значение даёт 422 |
Каждый URL должен быть на одном из доменов вашего сайта (кабинет → «Разработчикам › Интеграция и ключи»). Ответ — объект счёта ниже; network, address и pay_amount заполняются после выбора сети.
Объект счёта
Идентификаторы начинаются с inv_. amount — в центах США; pay_amount и paid_amount — количество монет десятичной строкой в выбранной сети.
{
"id": "inv_01J8…",
"ref": "order_1042",
"mode": "live",
"status": "pending",
"amount": 4200,
"currency": "USD",
"title": "Order #1042",
"meta": { "user_id": 42 },
"network": null,
"address": null,
"pay_amount": null,
"paid_amount": null,
"payments": [],
"pay_url": "https://blockwallets.io/pay/inv_01J8…",
"return_url": "https://shop.example/cart",
"success_url": "https://shop.example/thanks?order=1042",
"created_at": "2026-11-03T08:00:00.000Z",
"expires_at": "2026-11-03T09:00:00.000Z",
"paid_at": null,
"closed_at": null
}| status | Значение | Что делаете вы |
|---|---|---|
pending | Ожидает оплаты | — |
confirming | Перевод виден и подтверждается, либо пришло меньше нужного и можно доплатить | — |
paid | Оплачен | Отгрузите или зачислите заказ |
paid_over | Оплачен с переплатой | То же; излишек на вашем балансе |
wrong_amount | Завершён с суммой меньше полной (по истечении срока или при первом недоплаченном переводе, если payment_multiple равен false) | Сервисы с пополнением могут зачислить пришедшее (paid_amount; у монет с курсом есть и paid_cents); магазины просят клиента доплатить остаток |
closed | Истёк, ничего не получено, или вы закрыли его | — (деньги, пришедшие позже, всё равно зачисляются и приходят в уведомлении) |
Другие поля: pay_url (страница оплаты), payments (каждый перевод с txid, amount, confirmations и confirmed), expires_at, paid_at, closed_at; у BTC, LTC, DOGE, BCH и родных монет есть также rate (долларов США за монету, фиксируется на 30 минут), rate_locked_until и paid_cents (сколько уже пришло, в центах США).
Сети
Передавайте id как network. GET /v1/networks возвращает сети, открытые сейчас в вашем режиме; сеть может быть приостановлена, поэтому лучше читать этот список, чем зашивать значения в код.
| network | Монета | Блокчейн | Минимум |
|---|---|---|---|
USDT_TRC20 | USDT | TRON | $1 |
USDT_BEP20 | USDT | BNB Chain | $1 |
USDC_BEP20 | USDC | BNB Chain | $1 |
USDT_ERC20 | USDT | Ethereum | $5 |
USDC_ERC20 | USDC | Ethereum | $5 |
USDC_BASE | USDC | Base | $1 |
USDT_POLYGON | USDT | Polygon | $1 |
USDC_POLYGON | USDC | Polygon | $1 |
USDT_ARBITRUM | USDT | Arbitrum | $1 |
USDC_ARBITRUM | USDC | Arbitrum | $1 |
USDT_OPTIMISM | USDT | Optimism | $1 |
USDC_OPTIMISM | USDC | Optimism | $1 |
USDT_AVALANCHE | USDT | Avalanche | $1 |
USDC_AVALANCHE | USDC | Avalanche | $1 |
USDT_XLAYER | USDT | X Layer | $1 |
USDC_XLAYER | USDC | X Layer | $1 |
USD1_BEP20 | USD1 | BNB Chain | $1 |
FDUSD_BEP20 | FDUSD | BNB Chain | $1 |
DAI_ERC20 | DAI | Ethereum | $5 |
DAI_ARBITRUM | DAI | Arbitrum | $1 |
DAI_BASE | DAI | Base | $1 |
BTC | BTC | Bitcoin | $5 |
LTC | LTC | Litecoin | $1 |
DOGE | DOGE | Dogecoin | $1 |
BCH | BCH | Bitcoin Cash | $1 |
TRX | TRX | TRON | $1 |
BNB | BNB | BNB Chain | $1 |
ETH | ETH | Ethereum | $5 |
ETH_BASE | ETH | Base | $1 |
ETH_ARBITRUM | ETH | Arbitrum | $1 |
ETH_OPTIMISM | ETH | Optimism | $1 |
POL | POL | Polygon | $1 |
AVAX | AVAX | Avalanche | $1 |
OKB | OKB | X Layer | $1 |
USDT_SOL | USDT | Solana | $1 |
USDC_SOL | USDC | Solana | $1 |
SOL | SOL | Solana | $1 |
USDT_TON | USDT | TON | $1 |
TON | TON | TON | $1 |
Депозитные аккаунты (статические адреса) POST /v1/deposit-accounts
POST /v1/deposit-accounts с {"customer_ref": "user_42"} выдаёт этому клиенту постоянный адрес в каждой сети (все сети EVM используют один адрес). Всё, что он отправит, зачисляется в долларах США, по одному вебхуку deposit.credited на каждый перевод.
curl -X POST https://blockwallets.io/v1/deposit-accounts \
-H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
-d '{"customer_ref":"user_42"}'{
"id": "dac_01J8…",
"mode": "live",
"customer_ref": "user_42",
"deposit_url": "https://blockwallets.io/deposit/dac_01J8…",
"min_amount": 100,
"currency": "USD",
"networks": [
{ "id": "USDT_TRC20", "chain": "tron", "token": "USDT", "address": "TXy4…8kQ2", "min_amount": 100, "confirmations": 1, … },
{ "id": "USDC_BASE", "chain": "base", "token": "USDC", "address": "0x5aE1…", "min_amount": 100, "confirmations": 20, … }
]
}customer_ref: 1–128 символов — буквы, цифры и_ . : @ -; одно и то же значение всегда возвращает один и тот же аккаунт. Необязательныйreturn_urlна ваших доменах- В ответе есть
deposit_url(страница с адресами, QR-кодами и последними депозитами) иnetworks(для каждой сетиid,address,min_amount,confirmations) - Идентификаторы депозитов начинаются с
dep_;amount— центы США,amount_token— количество монет, а у монет с курсом указан применённыйrate
Переводы ниже минимума (обычно $1, BTC $5) записываются как below_min и автоматически не зачисляются; чтобы их зачислили, свяжитесь с нами.
Вебхуки
Мы отправляем POST с JSON на URL вебхука из кабинета («Разработчикам › Интеграция и ключи»; у Live и Test свои URL и секрет). Тело — это событие:
POST /your/webhook
X-BW-Event: ev_01J8…
X-BW-Delivery: 1
X-BW-Signature: t=1793693052,v1=5f0c9b…
{"id":"ev_01J8…","type":"invoice.paid","created_at":"2026-11-03T08:04:12.000Z","invoice":{"id":"inv_01J8…","ref":"order_1042","status":"paid","amount":4200,…}}X-BW-Signature:t=<unix seconds>,v1=<hex>, гдеv1= HMAC-SHA256(секрет вебхука этого режима,t+.+ исходное тело)X-BW-Event: id события (ev_…); убирайте дубликаты по немуX-BW-Delivery: номер попытки, начиная с 1
| Событие | Когда |
|---|---|
invoice.paid | Счёт стал paid или paid_over (включая деньги, пришедшие после истечения срока) |
invoice.wrong_amount | Счёт завершился как wrong_amount |
invoice.closed | Счёт истёк, ничего не получено, или вы закрыли его |
deposit.credited | Перевод на депозитный аккаунт зачислен |
Проверяйте по исходному телу до разбора, отклоняйте t, отличающийся от ваших часов более чем на 300 секунд, и сравнивайте за постоянное время. invoice / deposit в теле — тот же объект, что возвращает запрос.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), header: X-BW-Signature
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return typeof parts.v1 === "string" && parts.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}Ответ 2xx считается доставкой; иначе повторяем через 1 минуту, 5 минут, 30 минут, 2 часа, 12 часов, 24 часа и ещё 24 часа (всего 8 попыток).
События и повторная отправка
GET /v1/events?invoice_id=inv_… (или deposit_id=dep_…) показывает вебхуки по одному счёту или депозиту с их state, attempts, last_status и last_error. POST /v1/events/ev_…/redeliver сразу ставит вебхук в очередь заново, например после того, как вы исправили свой эндпоинт. Раздел «Разработчикам › Журнал вебхуков» в кабинете показывает каждую попытку с запросом и ответом.
Тестовый режим
Счета, созданные ключом bw_test_, работают в тестовых сетях и не затрагивают настоящие деньги. На странице оплаты есть кнопка «Симулировать оплату» (полная / половина / переплата), так что вы увидите смену статусов и вебхуки, не добывая тестовые монеты.