Интеграция с NOWPayments

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Интеграция с NOWPayments
Простой
~2-3 дня
Часто задаваемые вопросы

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

Этапы блокчейн-разработки

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

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

Интеграция с NOWPayments

NOWPayments — hosted платёжный шлюз, который обрабатывает генерацию адресов, мониторинг блокчейна и конвертацию курсов за вас. Правильная интеграция занимает 2–3 дня, но есть несколько нюансов, на которых теряют время: верификация IPN подписей, обработка частичных платежей и idempotent обработка webhook'ов.

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.

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

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

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"}'

Что стоит реализовать дополнительно

  • Polling как fallback: если webhook не пришёл в течение 30 минут после создания платежа — опрашиваем /v1/payment/{id} сами
  • Хранение payment_id от NOWPayments в вашей таблице заказов — нужен для reconciliation
  • Логирование всех сырых webhook payload — помогает при debugging и disputes
  • Алерт на partially_paid — требует ручного решения: принять, запросить доплату или вернуть