Your First Payment
A complete step-by-step tutorial to create and process your first HashNut payment.
Overview
In this tutorial, you'll:
- Set up your development environment
- Create a payment order
- Process the payment
- 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
- JavaScript/TypeScript
- Python
- Java
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);
from hashnut import HashNutClient, HashNutService
import os
secret_key = os.getenv('HASHNUT_SECRET_KEY')
access_key_id = os.getenv('HASHNUT_ACCESS_KEY_ID')
test_mode = True # True for testnet
client = HashNutClient(secret_key, test_mode)
service = HashNutService(client)
import io.hashnut.client.HashNutClient;
import io.hashnut.client.HashNutClientImpl;
import io.hashnut.service.HashNutService;
import io.hashnut.service.HashNutServiceImpl;
String secretKey = System.getenv("HASHNUT_SECRET_KEY");
String accessKeyId = System.getenv("HASHNUT_ACCESS_KEY_ID");
boolean testMode = true; // true for testnet
HashNutClient hashnutClient = new HashNutClientImpl(secretKey, testMode);
HashNutService hashNutService = new HashNutServiceImpl(hashnutClient);
Step 2: Create a Payment Order
- JavaScript/TypeScript
- Python
- Java
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);
from hashnut import CreatePayOrderRequest
from decimal import Decimal
import time
# Create order
request = CreatePayOrderRequest.Builder() \
.with_access_key_id(access_key_id) \
.with_merchant_order_id(f'order-{int(time.time())}') \
.with_chain_code('erc20') \
.with_coin_code('usdt') \
.with_amount(Decimal('0.01')) \
.build()
response = service.create_pay_order(request)
order = response.data
print(f'Order created: {order.pay_order_id}')
print(f'Payment URL: {order.payment_url}')
import io.hashnut.model.request.CreatePayOrderRequest;
import io.hashnut.model.response.CreatePayOrderResponse;
import java.math.BigDecimal;
// Create order
CreatePayOrderRequest request = new CreatePayOrderRequest.Builder()
.withAccessKeyId(accessKeyId)
.withMerchantOrderId("order-" + System.currentTimeMillis())
.withChainCode("erc20")
.withCoinCode("usdt")
.withAmount(new BigDecimal("0.01"))
.build();
CreatePayOrderResponse response = hashNutService.createPayOrder(request);
HashNutOrder order = response.getData();
System.out.println("Order created: " + order.getPayOrderId());
System.out.println("Payment URL: " + order.getPaymentUrl());
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:
- See the payment page with order details
- Connect their wallet
- Approve the payment transaction
- Wait for blockchain confirmation
Step 5: Handle Webhook Notification
- JavaScript/TypeScript
- Python
- Java
// 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');
}
});
# Flask webhook endpoint
from flask import Flask, request
@app.route('/api/webhook', methods=['POST'])
def webhook():
try:
# Parse webhook data
data = request.get_json()
pay_order_id = data['payOrderId']
merchant_order_id = data['merchantOrderId']
state = data['state']
# Query order status
from hashnut import QueryOrderRequest
query_request = QueryOrderRequest.Builder() \
.with_pay_order_id(pay_order_id) \
.with_merchant_order_id(merchant_order_id) \
.with_access_sign(data['accessSign']) \
.build()
query_response = service.query_order(query_request)
order = query_response.data
# Handle payment success
if order.state == 4:
print('Payment successful!')
# Update database
update_order_status(merchant_order_id, 'paid')
# Fulfill order
fulfill_order(merchant_order_id)
return 'success'
except Exception as e:
print(f'Webhook error: {e}')
return 'failed', 500
// Spring Boot webhook endpoint
@PostMapping("/api/webhook")
public ResponseEntity<String> webhook(@RequestBody WebhookPayload payload) {
try {
// Query order status
import io.hashnut.model.request.QueryOrderRequest;
import io.hashnut.model.response.QueryOrderResponse;
QueryOrderRequest queryRequest = new QueryOrderRequest.Builder()
.withPayOrderId(payload.getPayOrderId())
.withMerchantOrderId(payload.getMerchantOrderId())
.withAccessSign(payload.getAccessSign())
.build();
QueryOrderResponse queryResponse = hashNutService.queryOrder(queryRequest);
HashNutOrder order = queryResponse.getData();
// Handle payment success
if (order.getState() == 4) {
System.out.println("Payment successful!");
// Update database
orderService.updateStatus(payload.getMerchantOrderId(), "paid");
// Fulfill order
orderService.fulfill(payload.getMerchantOrderId());
}
return ResponseEntity.ok("success");
} catch (Exception e) {
System.err.println("Webhook error: " + e.getMessage());
return ResponseEntity.status(500).body("failed");
}
}
Step 6: Test the Complete Flow
Local Testing Setup
- 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
- 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;
-
Complete payment:
- Open payment URL
- Connect wallet
- Complete payment with test tokens
-
Verify webhook:
- Check your server logs
- Verify order status updated
- Confirm payment processed
Complete Example
Here's a complete working example:
- JavaScript/TypeScript
- Python
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');
});
from flask import Flask, request, jsonify
from hashnut import HashNutClient, HashNutService, CreatePayOrderRequest, QueryOrderRequest
from decimal import Decimal
import os
import time
app = Flask(__name__)
secret_key = os.getenv('HASHNUT_SECRET_KEY')
access_key_id = os.getenv('HASHNUT_ACCESS_KEY_ID')
test_mode = True
client = HashNutClient(secret_key, test_mode)
service = HashNutService(client)
# Create order endpoint
@app.route('/api/create-order', methods=['POST'])
def create_order():
try:
request_obj = CreatePayOrderRequest.Builder() \
.with_access_key_id(access_key_id) \
.with_merchant_order_id(f'order-{int(time.time())}') \
.with_chain_code('erc20') \
.with_coin_code('usdt') \
.with_amount(Decimal('0.01')) \
.build()
response = service.create_pay_order(request_obj)
return jsonify({'payment_url': response.data.payment_url})
except Exception as e:
return jsonify({'error': str(e)}), 500
# Webhook endpoint
@app.route('/api/webhook', methods=['POST'])
def webhook():
try:
data = request.get_json()
query_request = QueryOrderRequest.Builder() \
.with_pay_order_id(data['payOrderId']) \
.with_merchant_order_id(data['merchantOrderId']) \
.with_access_sign(data['accessSign']) \
.build()
query_response = service.query_order(query_request)
order = query_response.data
if order.state == 4:
print(f'Payment successful: {order.pay_order_id}')
return 'success'
except Exception as e:
print(f'Webhook error: {e}')
return 'failed', 500
if __name__ == '__main__':
app.run(port=3000)
Troubleshooting
Common Issues
Issue: "Invalid signature"
- ✅ Check
apiKeyis correct - ✅ Verify request body is stringified correctly
- ✅ Ensure all required headers are present
Issue: "Order not found"
- ✅ Verify
payOrderIdandmerchantOrderIdare 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
- Order Management: Learn advanced order features
- Webhook Integration: Deep dive into webhooks
- Error Handling: Handle errors gracefully
Congratulations! 🎉 You've created your first HashNut payment. Explore the full documentation to unlock more features!