NOWPayments Integration

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
NOWPayments Integration
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1217
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1046
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Integrating with NOWPayments

NOWPayments is a hosted payment gateway that handles address generation, blockchain monitoring, and currency conversion for you. Proper integration takes 2–3 days, but there are several pitfalls: IPN signature verification, partial payment handling, and idempotent webhook processing.

Payment Flow

1. Your backend → POST /v1/payment → NOWPayments
   Receive: payment_id, pay_address, pay_amount, expiration_estimate_date

2. Show client QR-code and address to pay

3. NOWPayments monitors blockchain

4. NOWPayments → IPN Webhook → Your backend
   payment_status: waiting → confirming → finished/failed/expired

5. Your backend verifies signature, updates order

Creating a Payment

interface CreatePaymentRequest {
  price_amount: number;      // sum in price_currency
  price_currency: string;    // 'usd', 'eur'
  pay_currency: string;      // 'btc', 'eth', 'usdterc20', 'usdttrc20'
  order_id: string;          // your internal ID
  order_description?: string;
  ipn_callback_url: string;  // webhook URL
  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 attention to pay_currency — it's not just coin name, but specific coin in specific network. usdterc20 is USDT on Ethereum, usdttrc20 is USDT on TRON, usdtbsc is on BNB Chain. Always get current pay_currency list from /v1/currencies, don't hardcode.

Verifying IPN Signature — Critical

NOWPayments signs each webhook with HMAC-SHA512 using your IPN key (separate from API key). Without signature verification, attacker can send fake finished status and get free goods.

import * as crypto from 'crypto';

function verifyIPNSignature(
  payload: string,         // raw request body, not parsed
  receivedSignature: string,
  ipnSecret: string
): boolean {
  const hmac = crypto.createHmac('sha512', ipnSecret);
  hmac.update(payload);
  const computedSignature = hmac.digest('hex');
  
  // Constant-time comparison — protection from 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 });
  }
);

Important: for HMAC verification you need raw body. If middleware express.json() already parsed — signature won't match due to JSON serialization differences. Use express.raw() for webhook endpoint.

Statuses and Idempotent Processing

NOWPayments sends webhook on each status change. Same statuses may arrive multiple times (retry on your server unavailability).

type PaymentStatus = 
  | 'waiting'      // waiting for payment
  | 'confirming'   // transaction found, waiting for confirmations
  | 'confirmed'    // confirmed
  | 'sending'      // NOWPayments converts and sends
  | 'partially_paid' // incomplete sum received
  | 'finished'     // successfully completed
  | 'failed'       // error
  | 'refunded'     // refund
  | 'expired';     // wait time expired

async function handlePaymentUpdate(data: IPNPayload): Promise<void> {
  // Idempotency: check if already processed
  const existing = await db.query(
    'SELECT status FROM payments WHERE nowpayments_id = $1',
    [data.payment_id]
  );
  
  if (existing.rows[0]?.status === 'finished') {
    return; // Already processed, ignore
  }
  
  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);
  }
}

Sandbox for Testing

NOWPayments provides sandbox: https://api-sandbox.nowpayments.io. Separate API keys, test transactions don't go to real networks. For local webhook testing — ngrok or Cloudflare Tunnel for public URL.

# Test via 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"}'

What's Worth Implementing Additionally

  • Polling as fallback: if webhook doesn't arrive within 30 minutes after payment creation — query /v1/payment/{id} yourself
  • Storing payment_id from NOWPayments in your orders table — needed for reconciliation
  • Log all raw webhook payloads — helps with debugging and disputes
  • Alert on partially_paid — requires manual decision: accept, request top-up, or refund