Skip to content

Query Order ​

Query the current state of a payment order and its supplement records.

Authentication ​

Query endpoints do not use header-based HMAC signing. They authenticate with the accessSign field in the request body:

accessSign = HMACSHA256(secretKey, payOrderId and merchantOrderId sorted case-insensitively, then concatenated)

The result is an uppercase hexadecimal string (64 characters).

IMPORTANT

You never need to compute accessSign yourself — the Create Order response returns it as data.accessSign. Persist it alongside payOrderId and echo it back on later query and confirm calls. The official SDKs work exactly this way.

Query Order Status ​

Endpoint ​

POST /v4.0.0/api/orders/query

Request ​

json
{
  "payOrderId" : "01K...H8",
  "merchantOrderId" : "aaf...-8",
  "accessSign" : "AA377...F5"
}
FieldTypeRequiredDescription
payOrderIdstringYesHashNut-generated order ID (26-character ULID)
merchantOrderIdstringYesYour order identifier
accessSignstringYesThe accessSign returned by Create Order, echoed verbatim

WARNING

All three fields are required — the server reads all three keys directly and errors if any is missing.

Response ​

json
{
  "code" : 0,
  "msg" : "success",
  "ui" : null,
  "version" : null,
  "count" : 0,
  "data" : {
    "merchantAddress" : "0x17a7...042",
    "merchantChannel" : "default",
    "blockChain" : "ETH",
    "tokenSymbol" : "usdt",
    "createChannel" : 1,
    "accessChannel" : 0,
    "merchantOrderId" : "aaff...-8",
    "payOrderId" : "01KZ...H8",
    "tokenAddress" : "0xdac17f958d2ee523a2206206994597c13d831ec7",
    "receiptAddress" : "0x58b1...2c4",
    "amount" : 0.05,
    "state" : 1,
    "accessSign" : "AA3777...6F5",
    "payTxId" : "0x4a96...2a0",
    "rate" : 80,
    "obtainAmount" : 0.0496,
    "platformFee" : 4.0E-4,
    "expireDuration" : "60000",
    "underPaid" : true,
    "paidAmount" : 0.03,
    "supplementCount" : 1,
    "errorCode" : "failed",
    "errorMsg" : "amount not enough",
    "createTime" : 1785753243948,
    "supplements" : [ {
      "supplementId" : "01KZ...3",
      "payOrderId" : "01K..H8",
      "supplementAmount" : 0.02,
      "paidAmount" : 0.0,
      "underPaid" : false,
      "receiptAddress" : "0x58...c4",
      "state" : 0,
      "reason" : "amount not enough",
      "createTime" : 1785753770316
    }, {
      "supplementId" : "01KZ..BWT",
      "payOrderId" : "01K..8",
      "supplementAmount" : 0.04,
      "paidAmount" : 0.02,
      "underPaid" : true,
      "payTxId" : "0x269...f7",
      "receiptAddress" : "0x58b..c4",
      "state" : 1,
      "reason" : "amount not enough",
      "errorCode" : "failed",
      "errorMsg" : "amount not enough",
      "createTime" : 1785753722283
    } ]
  }
}

Response Fields ​

FieldTypeDescription
codeinteger0 for success, non-zero for error
msgstringHuman-readable message
data.merchantAddressstringMerchant address that deployed the split contract
data.merchantChannelstringMerchant order channel; returns the default value default when the request leaves it empty
data.blockChainstringBlockchain name (e.g. ETH, TRON, BSC, POLYGON)
data.tokenSymbolstringToken symbol (e.g. usdt, usdc)
data.createChannelintegerCreation channel; 0 = created from the merchant dashboard, 1 = created via api key
data.accessChannelintegerDefaults to 0
data.merchantOrderIdstringThe merchant order ID you supplied when creating the order
data.payOrderIdstringUnique order ID generated by HashNut (26-character ULID)
data.tokenAddressstringContract address of the order's payment token, e.g. the USDT contract address
data.receiptAddressstringReceipt address of the order; the customer sends the token here to pay
data.amountnumberAmount due on the order
data.stateintegerOrder state (0 = INIT). See Order States
data.accessSignstringCredential required by later query / confirm calls — persist it
data.payTxIdstringTransaction ID of the first payment against this order — persist it
data.rateintegerFee rate; divide by rateBase for the actual ratio
data.obtainAmountnumberAmount the merchant receives after the platform fee
data.platformFeenumberPlatform fee; only charged when the merchant withdraws
data.expireDurationstringExpiration in seconds
data.underPaidboolWhether the order was underpaid. false while the customer has not paid; true once they pay less than the amount due, and it stays true after they top the order up in full
data.paidAmountnumberAmount the customer has paid so far
data.supplementCountintegerNumber of unpaid supplement records
data.errorCodestringError code
data.errorMsgstringError message
data.createTimedateOrder creation time
data.supplementsobjectArraySupplement records. Empty when the customer has not paid at all, or paid in full

NOTE

The example above is an underpaid order, shown so the supplement records are visible. When the customer has not paid, or has paid in full, supplements is an empty array.

Supplement Record ​

FieldTypeDescription
supplementIdstringSupplement record ID
payOrderIdstringID of the order that needs topping up
supplementAmountnumberAmount still to be topped up
paidAmountnumberAmount already topped up
underPaidboolWhether the top-up covered the full amount
payTxIdstringTransaction ID that paid this supplement record
receiptAddressstringReceipt address of this supplement record
stateintegerSupplement state (0 = INIT). See Order States
reasonstringWhy the top-up is needed
errorCodestringError code
errorMsgstringError message
createTimedateSupplement record creation time

NOTE

confirmCount, expireDuration, chainId, and eip712ChainId come back as JSON strings, not numbers — the server serializes Java long / Long as strings. The Go SDK reads them as json.Number, which accepts either form.

Query Supplements ​

Underpaid orders let the customer top up the difference; each top-up is a separate record.

Endpoint ​

POST /v4.0.0/api/orders/supplements          # all records, returns an array
POST /v4.0.0/api/orders/supplements/latest   # most recent only, returns a single object (null if none)

Request ​

Both endpoints take the same body — only payOrderId:

json
{
  "payOrderId" : "01KZ...H8"
}

WARNING

These two endpoints verify neither accessSign nor a header signature — a well-formed payOrderId is enough to read the data. Do not expose payOrderId on your own public pages or anywhere reachable from your frontend.

Response ​

json
{
  "code" : 0,
  "msg" : "success",
  "data" : [ {
    "supplementId" : "01KZ...3",
    "payOrderId" : "01K..H8",
    "supplementAmount" : 0.02,
    "paidAmount" : 0.0,
    "underPaid" : false,
    "receiptAddress" : "0x58...c4",
    "state" : 0,
    "reason" : "amount not enough",
    "createTime" : 1785753770316
  } ]
}
FieldTypeDescription
supplementIdstringSupplement record ID
payOrderIdstringID of the order that needs topping up
supplementAmountnumberAmount still to be topped up
paidAmountnumberAmount already topped up
underPaidboolWhether the top-up covered the full amount
payTxIdstringTransaction ID that paid this supplement record
receiptAddressstringReceipt address of this supplement record
stateintegerSupplement state (0 = INIT). See Order States
reasonstringWhy the top-up is needed
errorCodestringError code
errorMsgstringError message
createTimedateSupplement record creation time

TIP

Most integrations only need to poll Query Order Status and fetch supplement detail when supplementCount is greater than zero. Better still, subscribe to Notifications instead of polling.