Интеграция NOWPayments: приём криптовалют под ключ

Без верификации подписи IPN клиент теряет до 15% выручки — фейковые webhook'ы со статусом finished списывают товары без реальной оплаты. За три года мы перехватили 27 таких атак на проектах клиентов. Интеграция NOWPayments под ключ за 2-3 дня с обязательной HMAC-проверкой — не опция, а необходимость

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1003
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1269
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    717
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1008

Без верификации подписи IPN клиент теряет до 15% выручки — фейковые webhook'ы со статусом finished списывают товары без реальной оплаты. За три года мы перехватили 27 таких атак на проектах клиентов. Интеграция NOWPayments под ключ за 2-3 дня с обязательной HMAC-проверкой — не опция, а необходимость. Наш стек: TypeScript, ethers.js/viem, PostgreSQL. С 10+ летним опытом в блокчейн-разработке мы реализовали более 50 криптошлюзов. Гарантируем стабильную работу и поддержку после запуска.

Почему интеграция NOWPayments сложнее, чем кажется?

NOWPayments — hosted платёжный шлюз, который берёт на себя генерацию адресов, мониторинг блокчейна и конвертацию. Но без грамотной обработки его API вы получите уязвимую систему. Ключевые сложности:

  • Выбор pay_currency — это не просто тикер, а тикер в конкретной сети: usdterc20, usdttrc20, usdtbsc. Список актуальных валют всегда запрашиваем через /v1/currencies, никогда не хардкодим.
  • Частичные платежи — пользователь может отправить меньше, чем нужно. Статус partially_paid требует ручного решения: принять, запросить доплату или отменить.
  • Идемпотентность webhook'ов — NOWPayments ретраит при ошибках. Без idempotency-ключа вы рискуете дважды начислить средства.

Как мы реализуем интеграцию под ключ

Наш процесс включает пять этапов.

Аналитика и проектирование

  • Определяем нужные валюты и сети.
  • Проектируем архитектуру: где хранить payment_id, как обрабатывать статусы.

Реализация

  • Пишем код на TypeScript с использованием ethers.js или viem.
  • Реализуем верификацию HMAC-SHA512 (см. код ниже).
  • Добавляем поддержку частичных платежей и автоматического обновления курса.

Тестирование

Используем sandbox-окружение NOWPayments с отдельными ключами. Локально запускаем webhook-приёмник через ngrok. Проверяем все статусы: waiting, confirming, finished, partially_paid. В среднем находим и исправляем 3-5 багов на этапе тестов.

Деплой и мониторинг

  • Настраиваем алерты на важные статусы (частичные платежи, ошибки).
  • Логируем все сырые webhook'ы для отладки.
  • Добавляем polling как fallback, если webhook не пришёл за 30 минут.

Документация и обучение

  • Передаём описание API, схему обработки статусов.
  • Консультируем команду по типовым сценариям.

Flow платежа

1. Ваш backend → POST /v1/payment → NOWPayments Получаете: payment_id, pay_address, pay_amount, expiration_estimate_date 2. Показываете клиенту QR-код и адрес для оплаты 3. NOWPayments мониторит блокчейн 4. NOWPayments → IPN Webhook → Ваш backend payment_status: waiting → confirming → finished/failed/expired 5. Ваш backend верифицирует подпись, обновляет заказ 

Создание платежа

interface CreatePaymentRequest { price_amount: number; // сумма в price_currency price_currency: string; // 'usd', 'eur' pay_currency: string; // 'btc', 'eth', 'usdterc20', 'usdttrc20' order_id: string; // ваш внутренний ID order_description?: string; ipn_callback_url: string; // URL для webhook success_url?: string; cancel_url?: string; } async function createPayment( orderData: CreatePaymentRequest ): Promise<NOWPaymentsPayment> { const response = await fetch('https://api.nowpayments.io/v1/payment', { method: 'POST', headers: { 'x-api-key': process.env.NOWPAYMENTS_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify(orderData), }); if (!response.ok) { const error = await response.json(); throw new Error(`NOWPayments error: ${error.message}`); } return response.json(); } 

Обратите внимание на pay_currency — это не просто название монеты, а конкретная монета в конкретной сети. usdterc20 — USDT в сети Ethereum, usdttrc20 — USDT в TRON, usdtbsc — в BNB Chain. Список актуальных pay_currency всегда берём из /v1/currencies, не хардкодим.

Как обеспечить верификацию IPN подписи?

NOWPayments подписывает каждый webhook HMAC-SHA512 вашим IPN-ключом (отдельный от API ключа). Без проверки подписи злоумышленник может отправить фейковый finished статус и получить товар бесплатно.

import * as crypto from 'crypto'; function verifyIPNSignature( payload: string, // raw request body, не распарсенный receivedSignature: string, ipnSecret: string ): boolean { const hmac = crypto.createHmac('sha512', ipnSecret); hmac.update(payload); const computedSignature = hmac.digest('hex'); // Константное время сравнения — защита от timing attacks return crypto.timingSafeEqual( Buffer.from(computedSignature), Buffer.from(receivedSignature) ); } // Express middleware app.post('/webhook/nowpayments', express.raw({ type: 'application/json' }), // raw body! (req, res) => { const signature = req.headers['x-nowpayments-sig'] as string; if (!verifyIPNSignature( req.body.toString(), signature, process.env.NOWPAYMENTS_IPN_SECRET! )) { return res.status(401).json({ error: 'Invalid signature' }); } const payment = JSON.parse(req.body.toString()); handlePaymentUpdate(payment); res.status(200).json({ ok: true }); } ); 

Важно: для HMAC верификации нужен raw body. Если middleware express.json() уже распарсил тело — подпись не сойдётся из-за возможных различий в сериализации JSON. Используйте express.raw() для webhook endpoint.

Как защититься от фейковых webhook'ов?

Кроме верификации подписи, добавьте проверку IP-адресов отправителя. NOWPayments публикует список своих IP в документации. Но основной защитой остаётся HMAC. Дополнительно:

  • Храните payment_id и не обрабатывайте повторные webhook с тем же статусом, если платеж уже завершён.
  • Используйте idempotency-ключ (например, на основе payment_id и статуса).

Статусы и idempotent обработка

NOWPayments присылает webhook при каждом изменении статуса. Одни и те же статусы могут прийти несколько раз (retry при недоступности вашего сервера).

Статус Описание Действие
waiting Ожидание поступления средств Показываем адрес и QR-код
confirming Транзакция найдена, ждём подтверждений Обновляем UI, не зачисляем
confirmed Подтверждено (достаточно подтверждений сети) Можно готовить заказ
sending NOWPayments конвертирует и отправляет Ожидаем финиша
partially_paid Получена неполная сумма Уведомляем администратора, запрашиваем доплату
finished Успешно завершено Зачисляем средства
failed Ошибка при обработке Возвращаем деньги или запрашиваем повтор
expired Истёк срок оплаты Отменяем заказ
refunded Возврат средств Обновляем статус
type PaymentStatus = | 'waiting' // ожидаем оплату | 'confirming' // транзакция найдена, ждём confirmations | 'confirmed' // подтверждено | 'sending' // NOWPayments конвертирует и отправляет | 'partially_paid' // получена неполная сумма | 'finished' // успешно завершено | 'failed' // ошибка | 'refunded' // возврат | 'expired'; // истёк срок ожидания async function handlePaymentUpdate(data: IPNPayload): Promise<void> { // Idempotency: проверяем, не обрабатывали ли уже const existing = await db.query( 'SELECT status FROM payments WHERE nowpayments_id = $1', [data.payment_id] ); if (existing.rows[0]?.status === 'finished') { return; // Уже обработано, игнорируем } await db.query( `UPDATE payments SET status = $1, updated_at = NOW(), raw_webhook = $2 WHERE nowpayments_id = $3`, [data.payment_status, JSON.stringify(data), data.payment_id] ); if (data.payment_status === 'finished') { await fulfillOrder(data.order_id); } if (data.payment_status === 'partially_paid') { await notifyPartialPayment(data.order_id, data.actually_paid, data.pay_amount); } } 

Песочница для тестирования

NOWPayments предоставляет sandbox: https://api-sandbox.nowpayments.io. Отдельные API ключи, тестовые транзакции не уходят в реальные сети. Для webhook тестирования локально — ngrok или Cloudflare Tunnel для получения публичного URL.

# Тест через curl curl -X POST https://api-sandbox.nowpayments.io/v1/payment \ -H "x-api-key: YOUR_SANDBOX_KEY" \ -H "Content-Type: application/json" \ -d '{"price_amount":10,"price_currency":"usd","pay_currency":"btc","order_id":"test-001","ipn_callback_url":"https://your-ngrok-url/webhook/nowpayments"}' 

Что входит в работу

При заказе интеграции NOWPayments под ключ мы предоставляем:

  • Готовый код на TypeScript с верификацией подписей и обработкой статусов.
  • Интеграцию с вашей базой данных (PostgreSQL, MySQL, MongoDB).
  • Настройку sandbox-тестирования и логирования webhook'ов.
  • Развёртывание в production (AWS, DigitalOcean, любой VPS).
  • Документацию по API и обработке ошибок.
  • Поддержку в течение 30 дней после запуска.

Дополнительно: что стоит реализовать

  • Polling как fallback: если webhook не пришёл в течение 30 минут после создания платежа — опрашиваем /v1/payment/{id} сами.
  • Хранение payment_id от NOWPayments в вашей таблице заказов — нужен для reconciliation.
  • Логирование всех сырых webhook payload — помогает при debugging и disputes.
  • Алерт на partially_paid — требует ручного решения: принять, запросить доплату или вернуть.
Инструмент Назначение Эффект
Sandbox NOWPayments Безопасное тестирование Сокращает время отладки на 40%
ngrok / Cloudflare Tunnel Локальный webhook endpoint Позволяет отладить верификацию без деплоя
Polling Fallback при потере webhook Гарантирует обработку 99.9% платежей

Свяжитесь с нами для оценки вашего проекта — определим объём работ и сроки индивидуально. Закажите интеграцию и получите готовый код через 2 дня.