Skip to content

Payment Processing Guide

Complete guide to processing payments with Nivatio.

Payment Flow Overview

graph TD
    A[Customer clicks Pay] --> B[Your app creates order]
    B --> C[Nivatio returns checkoutUrl]
    C --> D[Redirect to Checkout]
    D --> E[Customer connects wallet]
    E --> F[Customer approves payment]
    F --> G[Transaction submitted]
    G --> H[Wait for confirmations]
    H --> I[Payment succeeded webhook]
    I --> J[Fulfill order]

Creating a Payment Order

Basic Order

const order = await client.orders.create({
  amount: 100000,        // 100.000 NUSD (6 decimals)
  currency: 'NUSD',
  description: 'Premium Subscription - 1 Year',
  successUrl: 'https://yourapp.com/success',
  cancelUrl: 'https://yourapp.com/cancel'
});

Order with Metadata

Attach internal references to orders:

const order = await client.orders.create({
  amount: 100000,
  currency: 'NUSD',
  description: 'Order #12345',
  metadata: {
    internalOrderId: 'ord_12345',
    customerId: 'cust_789',
    planId: 'plan_premium_yearly'
  }
});

Redirecting to Checkout

Simple Redirect

window.location.href = order.checkoutUrl;

Opening in New Tab

window.open(order.checkoutUrl, '_blank');

Using JavaScript SDK (Embedded)

NivatioCheckout.open({
  orderId: order.id,
  onSuccess: (data) => {
    console.log('Payment successful!', data);
    // Redirect to success page
    window.location.href = '/success?orderId=' + order.id;
  },
  onCancel: () => {
    console.log('Payment cancelled');
    window.location.href = '/cancel';
  }
});

Handling Payment Status

app.post('/webhook', (req, res) => {
  const event = req.body;

  if (event.type === 'payment.succeeded') {
    const order = event.data;

    // Verify required confirmations
    if (order.confirmations >= 2) {
      fulfillOrder(order);
    }
  }

  res.json({ received: true });
});

Polling (Alternative)

// Poll order status every 3 seconds
const pollOrder = async (orderId) => {
  const order = await client.orders.retrieve(orderId);

  if (order.status === 'PAID') {
    fulfillOrder(order);
    return;
  }

  if (order.status === 'FAILED' || order.status === 'EXPIRED') {
    showError('Payment failed');
    return;
  }

  // Continue polling
  setTimeout(() => pollOrder(orderId), 3000);
};

pollOrder(order.id);

Confirmation Requirements

Nivatio waits for blockchain confirmations before marking payment as PAID.

Environment Required Confirmations Approx. Time
Sandbox 1 ~2 seconds
Production 2 ~30 seconds

Confirmation Cron

A background job runs every 5 seconds (dev) or 1 minute (prod) to check for confirmations.


On-Chain Verification (Optional)

For high-value orders, verify the transaction on-chain:

import { createPublicClient, http } from 'viem';
import { anvilLocal } from './chains';

const client = createPublicClient({
  chain: anvilLocal,
  transport: http()
});

// Verify transaction after webhook
const verifyOnChain = async (txHash) => {
  const receipt = await client.getTransactionReceipt({ hash: txHash });

  if (receipt.status === 'success') {
    console.log(`Confirmed with ${receipt.confirmations} blocks`);
    return true;
  }

  return false;
};

Common Scenarios

Subscription Payment

const subscriptionOrder = await client.orders.create({
  amount: 100000, // Annual subscription
  currency: 'NUSD',
  description: 'Premium Plan - Annual',
  metadata: {
    type: 'subscription',
    billingCycle: 'annual',
    userId: 'user_123'
  }
});

One-Time Purchase

const purchaseOrder = await client.orders.create({
  amount: 50000, // One-time payment
  currency: 'NUSD',
  description: 'Widget Pro License',
  metadata: {
    type: 'onetime',
    productId: 'widget_pro_license'
  }
});

Refund (Future)

// Note: Refunds are currently manual via dashboard
// Automated refunds coming in v2
console.log('Process refund via dashboard.nivat.io');

Error Handling

See Error Handling Guide for complete details.

Common payment errors:

Error Cause Solution
invalid_amount Amount not in 6-decimal format Use 100000 for 100.000 NUSD
insufficient_funds Customer has no NUSD Show faucet instructions
tx_failed Transaction reverted Ask customer to retry
order_expired Payment timeout Create new order

Testing Payments

Use sandbox environment for testing:

# Simulate successful payment (sandbox only)
curl -X POST https://sandbox.nivat.io/v1/sandbox/simulate-pay \
  -H "x-nivatio-internal-key: YOUR_INTERNAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orderId": "order_abc123"}'

Next Steps