Quick Start
Create your first payment order in a few minutes.
Prerequisites
- A HashNut merchant account with an Access Key ID and Secret Key
- A deployed Splitter Address (split contract address)
Both are produced in one shot by One-Key Setup.
Create a Payment Order
Go and Java have official SDKs that handle signing, URL assembly, and error handling for you. Python and Node.js have no SDK yet, so you sign requests yourself following Authentication — the samples below are complete and runnable.
// go get github.com/nuttybounty/hashnut-sdk-go/v4
package main
import (
"fmt"
"log"
hashnut "github.com/nuttybounty/hashnut-sdk-go/v4"
"github.com/nuttybounty/hashnut-sdk-go/v4/model"
)
func main() {
client := hashnut.NewClient("your-secret-key", false) // second argument is always false
order, err := client.CreateOrder(&model.CreateOrderRequest{
AccessKeyID: "your-access-key-id",
MerchantOrderID: "aaf..-8",
BlockChain: "ETH",
TokenSymbol: "usdt",
Amount: "0.05",
SplitterAddress: "0xYourSplitterAddress",
Subject: "Test Product",
ExpireDuration: 60000, // seconds
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Order ID: %s\n", order.PayOrderID)
fmt.Printf("Receipt Address: %s\n", order.ReceiptAddress)
fmt.Printf("Payment Page: %s\n", order.PayURL)
}// Maven coordinates and repository config: see the Java SDK page
import io.hashnut.client.HashNutClientImpl;
import io.hashnut.service.HashNutServiceImpl;
import io.hashnut.model.request.CreateOrderRequest;
import io.hashnut.model.response.CreateOrderResponse;
public class CreateOrder {
public static void main(String[] args) throws Exception {
var client = new HashNutClientImpl("your-secret-key", false); // second argument is always false
var service = new HashNutServiceImpl(client);
CreateOrderResponse resp = service.createOrder(new CreateOrderRequest.Builder()
.withAccessKeyId("your-access-key-id")
.withMerchantOrderId("aaf..-8")
.withBlockChain("ETH")
.withTokenSymbol("usdt")
.withAmount("0.05")
.withSplitterAddress("0xYourSplitterAddress")
.withSubject("Test Product")
.withExpireDuration(60000L) // seconds
.build());
System.out.println("Order ID: " + resp.getData().getPayOrderId());
System.out.println("Receipt Address: " + resp.getData().getReceiptAddress());
System.out.println("Payment Page: " + resp.getData().getPayUrl());
}
}import hmac, hashlib, base64, json, time, uuid
from urllib.parse import urlencode
import requests
SECRET_KEY = "your-secret-key"
BASE_URL = "https://defi.hashnut.io/api/v4.0.0"
PAY_PAGE = "https://defi.hashnut.io/pay"
# The signed string and the sent bytes must be identical, so serialize once into
# `body` and send that exact string below via data=body.
body = json.dumps({
"accessKeyId": "your-access-key-id",
"merchantOrderId": "aaf..-8",
"blockChain": "ETH",
"tokenSymbol": "usdt",
"amount": "0.05",
"splitterAddress": "0xYourSplitterAddress",
"subject": "Test Product",
"expireDuration": 60000,
}, separators=(",", ":"))
# signing string = uuid + timestamp + body, HMAC-SHA256 then base64
req_uuid = str(uuid.uuid4()) # must be unique per request; the server rejects replays
timestamp = str(int(time.time() * 1000)) # Unix milliseconds
message = req_uuid + timestamp + body
signature = base64.b64encode(
hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).digest()
).decode()
resp = requests.post(
f"{BASE_URL}/api/orders/create",
headers={
"Content-Type": "application/json",
"hashnut-request-uuid": req_uuid,
"hashnut-request-timestamp": timestamp,
"hashnut-request-sign": signature,
},
data=body,
)
order = resp.json()["data"]
# The response carries no payUrl — build the payment page URL yourself
# (the official SDKs already do this for you)
pay_url = PAY_PAGE + "?" + urlencode({
"payOrderId": order["payOrderId"],
"merchantOrderId": order["merchantOrderId"],
"accessSign": order["accessSign"],
"blockChain": order["blockChain"],
"payApiVersion": "v4",
})
print("Order ID: ", order["payOrderId"])
print("Receipt Address:", order["receiptAddress"])
print("Payment Page: ", pay_url)const crypto = require("crypto");
const SECRET_KEY = "your-secret-key";
const BASE_URL = "https://defi.hashnut.io/api/v4.0.0";
const PAY_PAGE = "https://defi.hashnut.io/pay";
// The signed string and the sent bytes must be identical, so serialize once into
// `body` and send that exact string as the request body below.
const body = JSON.stringify({
accessKeyId: "your-access-key-id",
merchantOrderId: "aaf..-8",
blockChain: "ETH",
tokenSymbol: "usdt",
amount: "0.05",
splitterAddress: "0xYourSplitterAddress",
subject: "Test Product",
expireDuration: 60000,
});
// signing string = uuid + timestamp + body, HMAC-SHA256 then base64
const uuid = crypto.randomUUID(); // must be unique per request; the server rejects replays
const timestamp = Date.now().toString(); // Unix milliseconds
const message = uuid + timestamp + body;
const signature = crypto
.createHmac("sha256", SECRET_KEY)
.update(message)
.digest("base64");
fetch(`${BASE_URL}/api/orders/create`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"hashnut-request-uuid": uuid,
"hashnut-request-timestamp": timestamp,
"hashnut-request-sign": signature,
},
body,
})
.then((res) => res.json())
.then(({ data }) => {
// The response carries no payUrl — build the payment page URL yourself
// (the official SDKs already do this for you)
const payUrl = `${PAY_PAGE}?${new URLSearchParams({
payOrderId: data.payOrderId,
merchantOrderId: data.merchantOrderId,
accessSign: data.accessSign,
blockChain: data.blockChain,
payApiVersion: "v4",
})}`;
console.log("Order ID: ", data.payOrderId);
console.log("Receipt Address:", data.receiptAddress);
console.log("Payment Page: ", payUrl);
});WARNING
Three things trip people up when signing by hand:
- The signed body must be byte-for-byte identical to what you send. Do not sign one serialization and then re-serialize when sending — a single difference in key order or whitespace fails verification. The samples above build
bodyonce and send it verbatim. hashnut-request-uuidmust differ on every request. The server deduplicates onaccessKey + uuidand rejects a repeated uuid as a replay.hashnut-request-timestampis in Unix milliseconds and must be within ±5 minutes of server time. A skewed machine clock is the most common cause of this error.
Full signing rules: Authentication.
Response
{
"code": 0,
"msg": "success",
"data": {
"merchantAddress" : "0x17a...42",
"merchantChannel" : "default",
"blockChain" : "ETH",
"tokenSymbol" : "usdt",
"createChannel" : 1,
"merchantOrderId" : "aaf..-8",
"payOrderId" : "01K...H8",
"tokenAddress" : "0xdac17f958d2ee523a2206206994597c13d831ec7",
"receiptAddress" : "0x5...c4",
"amount" : 0.05,
"state" : 0,
"accessSign" : "AA377....F5",
"rate" : 80,
"obtainAmount" : 0.0496,
"platformFee" : 4.0E-4,
"expireDuration" : "60000",
"underPaid" : false,
"paidAmount" : 0.0,
"createTime" : 1785753243948
}
}code is 0 on success; otherwise read msg. state 0 means awaiting payment — see Order States for the full set. For every field in data, see Create Order.
NOTE
The server serializes Java long / Long fields as JSON strings (so JavaScript does not lose precision above 2^53). Fields like expireDuration, confirmCount, and chainId come back as "600", not 600. When parsing the JSON yourself, read them as strings or use a type that accepts both (json.Number in Go, string | number in TypeScript). The official SDKs already handle this.
Payment page URL
Once the order is created, build the payment page URL like this:
https://defi.hashnut.io/pay?accessSign=${accessSign}&merchantOrderId=${merchantOrderId}&payOrderId=${payOrderId}&blockChain=${blockChain}&payApiVersion=v4The official SDKs already do this for you — just read order.PayURL (Go) or resp.getData().getPayUrl() (Java). When building it yourself, URL-encode the parameters: merchantOrderId is a value you supply, and a space or & in it would break the URL.
Next Steps
- Fund Flow — how funds are claimed and withdrawn
- Information Flow — how the order state advances and how you get notified
- Notifications — receive webhook callbacks on order state changes
- Query Order — poll order status and transaction details
- Go SDK / Java SDK — full method list and installation