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

> Перейдите с NOWPayments на blockwallets, сменив базовый URL и ключи: поддерживаемые эндпоинты, IPN-уведомления и подписи, тикеры монет и отличия.
>
> HTML: https://blockwallets.io/docs/nowpayments?lang=ru · English: https://blockwallets.io/docs/nowpayments.md?lang=en

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

Пути, заголовок `x-api-key`, поля запросов и ответов, статусы и подпись IPN `x-nowpayments-sig` соответствуют документации NOWPayments, поэтому существующему коду обычно нужны только новый базовый URL, API key и IPN secret. Переходите откуда-то ещё? См. [обзор перехода](https://blockwallets.io/docs/migrate?lang=ru).

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

### Три шага

- Получите аккаунт продавца ([запросите доступ на главной странице](https://blockwallets.io/?lang=ru#join)) и добавьте свои домены в разделе «Домены сайта» на странице «Разработчикам › Интеграция и ключи» в кабинете. URL уведомлений (`ipn_callback_url`) и URL перенаправления (`success_url` / `cancel_url` / `redirect_url`) должны находиться на этих доменах или на домене вашего URL уведомлений — утёкший ключ не сможет направить вебхуки в другое место.

- Создайте совместимые учётные данные в кабинете продавца в разделе «Разработчикам › Интеграция и ключи», один набор для live и один для test: для NOWPayments — **API key + IPN secret**, для Coinbase Commerce — **API key + webhook shared secret**. Они показываются один раз; мы храним только хеши / шифртекст.

- Направьте код на базовый URL ниже и используйте новые ключи. Сначала запустите его в тестовых сетях с тестовыми учётными данными, затем переключитесь на live.

| До | После | Заголовок авторизации | Подпись вебхука |
| --- | --- | --- | --- |
| `https://api.nowpayments.io/v1` | `https://blockwallets.io/nowpayments/v1` | `x-api-key` | `x-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 попыток.

```nodejs
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_currency | network | Минимум |
| --- | --- | --- | --- | --- |
| USDT | TRON | `usdttrc20` | `trx` | $1 |
| USDT | BNB Chain | `usdtbsc` usdtbep20 | `bsc` | $1 |
| USDC | BNB Chain | `usdcbsc` usdcbep20 | `bsc` | $1 |
| USDT | Ethereum | `usdterc20` | `eth` | $5 |
| USDC | Ethereum | `usdc` usdcerc20 | `eth` | $5 |
| USDC | Base | `usdcbase` | `base` | $1 |
| USDT | Polygon | `usdtmatic` usdtpolygon | `matic` | $1 |
| USDC | Polygon | `usdcmatic` usdcpolygon | `matic` | $1 |
| USDT | Arbitrum | `usdtarb` | `arbitrum` | $1 |
| USDC | Arbitrum | `usdcarb` | `arbitrum` | $1 |
| USDT | Optimism | `usdtop` | `op` | $1 |
| USDC | Optimism | `usdcop` | `op` | $1 |
| USDT | Avalanche | `usdtarc20` usdtavax / usdtavaxc | `avaxc` | $1 |
| USDC | Avalanche | `usdcarc20` usdcavax / usdcavaxc | `avaxc` | $1 |
| USDT | X Layer | `usdtxlayer` | `xlayer` | $1 |
| USDC | X Layer | `usdcxlayer` | `xlayer` | $1 |
| USD1 | BNB Chain | `usd1bsc` | `bsc` | $1 |
| FDUSD | BNB Chain | `fdusdbsc` | `bsc` | $1 |
| DAI | Ethereum | `dai` daierc20 | `eth` | $5 |
| DAI | Arbitrum | `daiarb` | `arbitrum` | $1 |
| DAI | Base | `daibase` | `base` | $1 |
| BTC | Bitcoin | `btc` | `btc` | $5 |
| LTC | Litecoin | `ltc` | `ltc` | $1 |
| DOGE | Dogecoin | `doge` | `doge` | $1 |
| BCH | Bitcoin Cash | `bch` | `bch` | $1 |
| TRX | TRON | `trx` | `trx` | $1 |
| BNB | BNB Chain | `bnbbsc` bnb | `bsc` | $1 |
| ETH | Ethereum | `eth` | `eth` | $5 |
| ETH | Base | `ethbase` | `base` | $1 |
| ETH | Arbitrum | `etharb` | `arbitrum` | $1 |
| ETH | Optimism | `ethop` | `op` | $1 |
| POL | Polygon | `maticmainnet` pol / polmainnet / matic | `matic` | $1 |
| AVAX | Avalanche | `avaxc` avax | `avaxc` | $1 |
| OKB | X Layer | `okbxlayer` | `xlayer` | $1 |
| USDT | Solana | `usdtsol` | `sol` | $1 |
| USDC | Solana | `usdcsol` | `sol` | $1 |
| SOL | Solana | `sol` | `sol` | $1 |
| USDT | TON | `usdtton` | `ton` | $1 |
| TON | TON | `ton` | `ton` | $1 |

#### Отличия от NOWPayments

- `price_currency` — только `usd` (плюс `usdt` / `usdc` 1:1); другие фиатные валюты возвращают 400.

- `pay_amount` (фиксированная сумма в криптовалюте), `payout_address` / `payout_currency` / `payout_extra_id` и `customer_email` не поддерживаются и возвращают 400.

- `is_fixed_rate` принимается, но ни на что не влияет: курс мы фиксируем всегда (BTC / LTC и др. — по медиане нескольких бирж, на 30 минут). Если `is_fee_paid_by_user` равен true, комиссия платформы добавляется к `pay_amount` (комиссия платформы 1%, при больших оборотах ниже; см. [цены](https://blockwallets.io/pricing?lang=ru)).

- Платежи действительны до 12 часов (NOWPayments: 7 дней). `partially_paid_url` сохраняется и возвращается как есть, но страница оплаты на него не перенаправляет.

- `payment_id` — 10-значное число; для инвойсов, созданных через `/invoice`, `payment_id` платежа равен `id` инвойса, и у инвойса ровно один платёж (повторных пополнений с `parent_payment_id` нет).

- Отправляются только IPN с конечными статусами и `partially_paid` (таблица выше), без `waiting` / `confirming`; `fee.serviceFee` — комиссия платформы (полученная сумма × ставка на момент создания счёта, стандартная 1%), остальные комиссии равны 0.

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

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

Всё остальное (страница оплаты, подтверждения, сети и минимумы, возвраты) работает так же, как в [основной документации](https://blockwallets.io/docs/cryptomus?lang=ru).
