Skip to content

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
// 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)
}
java
// 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());
    }
}
python
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)
javascript
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:

  1. 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 body once and send it verbatim.
  2. hashnut-request-uuid must differ on every request. The server deduplicates on accessKey + uuid and rejects a repeated uuid as a replay.
  3. hashnut-request-timestamp is 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 ​

json
{
  "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=v4

The 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 ​