API, совместимый с NOWPayments

Открыть в ChatGPTОткрыть в Claude

Пути, заголовок x-api-key, поля запросов и ответов, статусы и подпись IPN x-nowpayments-sig соответствуют документации NOWPayments, поэтому существующему коду обычно нужны только новый базовый URL, API key и IPN secret. Переходите откуда-то ещё? См. обзор перехода.

Внутри это обычный счёт blockwallets: адреса для приёма ваши, страница оплаты — наша размещённая страница, а платёж засчитывается только после подтверждения в блокчейне и сверки со вторым узлом. Далее: три шага, затем что поддерживает каждый API и чем он отличается от оригинала — параметры, которые мы не реализовали, возвращают ошибку, а не молча игнорируются.

Три шага

  1. Получите аккаунт продавца (запросите доступ на главной странице) и добавьте свои домены в разделе «Домены сайта» на странице «Разработчикам › Интеграция и ключи» в кабинете. URL уведомлений (ipn_callback_url) и URL перенаправления (success_url / cancel_url / redirect_url) должны находиться на этих доменах или на домене вашего URL уведомлений — утёкший ключ не сможет направить вебхуки в другое место.
  2. Создайте совместимые учётные данные в кабинете продавца в разделе «Разработчикам › Интеграция и ключи», один набор для live и один для test: для NOWPayments — API key + IPN secret, для Coinbase Commerce — API key + webhook shared secret. Они показываются один раз; мы храним только хеши / шифртекст.
  3. Направьте код на базовый URL ниже и используйте новые ключи. Сначала запустите его в тестовых сетях с тестовыми учётными данными, затем переключитесь на live.
ДоПослеЗаголовок авторизацииПодпись вебхука
https://api.nowpayments.io/v1https://blockwallets.io/nowpayments/v1x-api-keyx-nowpayments-sig (HMAC-SHA512)

NOWPayments

JavaScript
- const NP = "https://api.nowpayments.io/v1";
+ const NP = "https://blockwallets.io/nowpayments/v1";

const res = await fetch(`${NP}/payment`, {
  method: "POST",
  headers: { "x-api-key": process.env.NOWPAYMENTS_API_KEY, "content-type": "application/json" },
  body: JSON.stringify({
    price_amount: 49.9, price_currency: "usd", pay_currency: "usdttrc20",
    order_id: "A-1001", order_description: "Pro plan",
    ipn_callback_url: "https://shop.example/ipn/nowpayments",
  }),
});
const { payment_id, pay_address, pay_amount } = await res.json();

Поддерживаемые эндпоинты

ЭндпоинтПримечания
GET /status{"message":"OK"}, ключ не нужен
GET /currencies · /merchant/coins · /full-currenciesМонеты, которые можно принимать прямо сейчас (тикеры ниже); fixed_rate игнорируется
GET /min-amount · GET /estimateМинимумы и оценки; цены только в USD (USDT / USDC считаются 1:1)
POST /paymentСоздать платёж; сразу возвращает pay_address / pay_amount (white-label)
POST /invoiceСоздать инвойс; invoice_url — наша размещённая страница оплаты, где клиент выбирает монету и сеть
POST /invoice-paymentВыбрать монету для инвойса, возвращает платёж
GET /payment/:idСтатус платежа
GET /payment/Список: limit (1–500) / page / orderBy / dateFrom / dateTo / invoiceId; sortBy только created_at
POST /authДля списка нужен только x-api-key, JWT не требуется; здесь возвращается непроверяемый токен-заглушка, чтобы код вида «войти, затем получить список» продолжал работать (email / пароль не проверяются)

Недоступно: выплаты, конвертации, субпартнёры с кастодиальным хранением, подписки, update-merchant-estimate, фиатные платежи. Эти пути возвращают 404.

IPN-уведомления

Когда платёж достигает конечного статуса, мы отправляем POST на ipn_callback_url, указанный при создании, с x-nowpayments-sig = HMAC-SHA512(IPN secret, JSON, рекурсивно отсортированный по ключам) — в точности как в коде проверки NOWPayments. Тело, которое мы отправляем, уже представляет собой отсортированный компактный JSON, поэтому HMAC по исходному телу тоже подходит (и это самый надёжный вариант в PHP / Python, у которых повторное кодирование очень малых десятичных чисел может отличаться от JS). Неудачные уведомления повторяются через 1 мин, 5 мин, 30 мин, … — всего 8 попыток.

Node.js
import crypto from "node:crypto";

// Same as the NOWPayments docs (our body is already sorted, so hashing the raw body works too)
function sortObject(obj) {
  return Object.keys(obj).sort().reduce((r, k) => {
    r[k] = obj[k] && typeof obj[k] === "object" ? sortObject(obj[k]) : obj[k];
    return r;
  }, {});
}
function verifyIpn(params, sig, ipnSecret) {
  const hmac = crypto.createHmac("sha512", ipnSecret).update(JSON.stringify(sortObject(params))).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sig || ""));
}
// verifyIpn(req.body, req.headers["x-nowpayments-sig"], process.env.NOWPAYMENTS_IPN_SECRET)
PHP
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
tksort($data);  // the recursive ksort from the NOWPayments docs
$hmac = hash_hmac('sha512', json_encode($data, JSON_UNESCAPED_SLASHES), trim($ipnSecret));
$ok = hash_equals($hmac, $_SERVER['HTTP_X_NOWPAYMENTS_SIG'] ?? '');
payment_statusКогдаОтправляется ли IPN
waitingОжидает оплаты (также инвойс, в котором монета ещё не выбрана)—
confirmingПлатёж виден в блокчейне, ждём подтверждений— (виден при запросе)
finishedОплачен (в том числе с переплатой; actually_paid — сумма, которая пришла)✓
partially_paidНедоплата: клиент ещё может доплатить на тот же адрес до истечения срока (отправляется один раз при первой недоплате), и ещё раз, если к истечению всё ещё не хватает✓
expiredИстёк без оплаты✓
refundedВы вернули весь платёж из кабинета, и возврат отправлен— (виден при запросе)

Статусы confirmed, sending и failed не встречаются: после подтверждения платёж получает finished и зачисляется на ваш баланс; ничего никуда не пересылается.

Тикеры монет

МонетаСетьpay_currencynetworkМинимум
USDTTRONusdttrc20trx$1
USDTBNB Chainusdtbsc usdtbep20bsc$1
USDCBNB Chainusdcbsc usdcbep20bsc$1
USDTEthereumusdterc20eth$5
USDCEthereumusdc usdcerc20eth$5
USDCBaseusdcbasebase$1
USDTPolygonusdtmatic usdtpolygonmatic$1
USDCPolygonusdcmatic usdcpolygonmatic$1
USDTArbitrumusdtarbarbitrum$1
USDCArbitrumusdcarbarbitrum$1
USDTOptimismusdtopop$1
USDCOptimismusdcopop$1
USDTAvalancheusdtarc20 usdtavax / usdtavaxcavaxc$1
USDCAvalancheusdcarc20 usdcavax / usdcavaxcavaxc$1
USDTX Layerusdtxlayerxlayer$1
USDCX Layerusdcxlayerxlayer$1
USD1BNB Chainusd1bscbsc$1
FDUSDBNB Chainfdusdbscbsc$1
DAIEthereumdai daierc20eth$5
DAIArbitrumdaiarbarbitrum$1
DAIBasedaibasebase$1
BTCBitcoinbtcbtc$5
LTCLitecoinltcltc$1
DOGEDogecoindogedoge$1
BCHBitcoin Cashbchbch$1
TRXTRONtrxtrx$1
BNBBNB Chainbnbbsc bnbbsc$1
ETHEthereumetheth$5
ETHBaseethbasebase$1
ETHArbitrumetharbarbitrum$1
ETHOptimismethopop$1
POLPolygonmaticmainnet pol / polmainnet / maticmatic$1
AVAXAvalancheavaxc avaxavaxc$1
OKBX Layerokbxlayerxlayer$1
USDTSolanausdtsolsol$1
USDCSolanausdcsolsol$1
SOLSolanasolsol$1
USDTTONusdttonton$1
TONTONtonton$1

Отличия от NOWPayments

Тестовый режим

Charge и платежи, созданные с тестовыми учётными данными, работают в тестовой сети каждого блокчейна, а их вебхуки подписываются тестовым IPN secret / shared secret — полностью отдельно от live: тестовый ключ не видит live-платежи.

Всё остальное (страница оплаты, подтверждения, сети и минимумы, возвраты) работает так же, как в основной документации.