Skip to main content

Your First Payment

A complete step-by-step tutorial to create and process your first HashNut payment.

Overview

In this tutorial, you'll:

  1. Set up your development environment
  2. Create a payment order
  3. Process the payment
  4. Handle the webhook notification

Time: ~15 minutes
Difficulty: Beginner

Prerequisites

  • Installation completed
  • ✅ API credentials from HashNut Dashboard
  • ✅ Test wallet with test tokens
  • ✅ Development environment set up

Step 1: Initialize the Client

import { HashNutClient, HashNutService } from '@hashnut/sdk';

const secretKey = process.env.HASHNUT_SECRET_KEY!;
const accessKeyId = process.env.HASHNUT_ACCESS_KEY_ID!;
const testMode = true; // true for testnet

const client = new HashNutClient(secretKey, testMode);
const service = new HashNutService(client);

Step 2: Create a Payment Order

import { CreatePayOrderRequest } from '@hashnut/sdk';

// Create order
const request = new CreatePayOrderRequest.Builder()
.withAccessKeyId(accessKeyId)
.withMerchantOrderId(`order-${Date.now()}`)
.withChainCode('erc20')
.withCoinCode('usdt')
.withAmount('0.01')
.build();

const response = await service.createPayOrder(request);
const order = response.data;

console.log('Order created:', order.payOrderId);
console.log('Payment URL:', order.paymentUrl);

Step 3: Redirect Customer to Payment Page

// In your frontend
window.location.href = order.paymentUrl;

Or in a React component:

<button onClick={() => window.open(order.paymentUrl, '_blank')}>
Pay Now
</button>

Step 4: Customer Completes Payment

The customer will:

  1. See the payment page with order details
  2. Connect their wallet
  3. Approve the payment transaction
  4. Wait for blockchain confirmation

Step 5: Handle Webhook Notification

// Express.js webhook endpoint
import express from 'express';
const app = express();

app.post('/api/webhook', express.json(), async (req, res) => {
try {
// Parse webhook data
const data = req.body;
const { payOrderId, merchantOrderId, state } = data;

// Query order status to get latest details
import { QueryOrderRequest } from '@hashnut/sdk';

const queryRequest = new QueryOrderRequest.Builder()
.withPayOrderId(payOrderId)
.withMerchantOrderId(merchantOrderId)
.withAccessSign(data.accessSign)
.build();

const queryResponse = await service.queryOrder(queryRequest);
const order = queryResponse.data;

// Handle payment success
if (order.state === 4) {
console.log('Payment successful!');
// Update your database
await updateOrderStatus(merchantOrderId, 'paid');
// Fulfill order
await fulfillOrder(merchantOrderId);
}

res.send('success');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('failed');
}
});

Step 6: Test the Complete Flow

Local Testing Setup

  1. Use ngrok for local webhook testing:
# Install ngrok
npm install -g ngrok

# Expose local server
ngrok http 3000

# Use the HTTPS URL as your webhook URL
# https://abc123.ngrok.io/api/webhook
  1. Create a test order:
import { CreatePayOrderRequest } from '@hashnut/sdk';

const request = new CreatePayOrderRequest.Builder()
.withAccessKeyId(accessKeyId)
.withMerchantOrderId('test-order-1')
.withChainCode('erc20')
.withCoinCode('usdt')
.withAmount('0.01')
.build();

const response = await service.createPayOrder(request);
const order = response.data;
  1. Complete payment:

    • Open payment URL
    • Connect wallet
    • Complete payment with test tokens
  2. Verify webhook:

    • Check your server logs
    • Verify order status updated
    • Confirm payment processed

Complete Example

Here's a complete working example:

import express from 'express';
import { HashNutClient, HashNutService, CreatePayOrderRequest, QueryOrderRequest } from '@hashnut/sdk';

const app = express();
app.use(express.json());

const secretKey = process.env.HASHNUT_SECRET_KEY!;
const accessKeyId = process.env.HASHNUT_ACCESS_KEY_ID!;
const testMode = true;

const client = new HashNutClient(secretKey, testMode);
const service = new HashNutService(client);

// Create order endpoint
app.post('/api/create-order', async (req, res) => {
try {
const request = new CreatePayOrderRequest.Builder()
.withAccessKeyId(accessKeyId)
.withMerchantOrderId(`order-${Date.now()}`)
.withChainCode('erc20')
.withCoinCode('usdt')
.withAmount('0.01')
.build();

const response = await service.createPayOrder(request);
res.json({ paymentUrl: response.data.paymentUrl });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Webhook endpoint
app.post('/api/webhook', express.json(), async (req, res) => {
try {
const data = req.body;
const queryRequest = new QueryOrderRequest.Builder()
.withPayOrderId(data.payOrderId)
.withMerchantOrderId(data.merchantOrderId)
.withAccessSign(data.accessSign)
.build();

const queryResponse = await service.queryOrder(queryRequest);
const order = queryResponse.data;

if (order.state === 4) {
// Payment successful - update your database
console.log('Payment successful:', order.payOrderId);
}

res.send('success');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('failed');
}
});

app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});

Troubleshooting

Common Issues

Issue: "Invalid signature"

  • ✅ Check apiKey is correct
  • ✅ Verify request body is stringified correctly
  • ✅ Ensure all required headers are present

Issue: "Order not found"

  • ✅ Verify payOrderId and merchantOrderId are correct
  • ✅ Check you're using the correct environment (testnet vs production)

Issue: Webhook not received

  • ✅ Check webhook URL is accessible (use ngrok for local testing)
  • ✅ Verify webhook URL uses HTTPS
  • ✅ Check server logs for errors

Next Steps


Congratulations! 🎉 You've created your first HashNut payment. Explore the full documentation to unlock more features!