Skip to main content

Documentation Index

Fetch the complete documentation index at: https://neilyan.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Every API request to XPayLabs must include a cryptographic signature. This page details the exact signing algorithm, the purpose of each field, and common pitfalls.

Signing Algorithm

The sign field is computed as:
sign = HEX(HMAC-SHA256(JSON.stringify(data), merchant_token))
Where:
  • data is the request body payload (the data field of ReqPayload)
  • merchant_token is your configured secret token
  • HMAC-SHA256 produces a binary digest
  • HEX converts it to a lowercase hexadecimal string (64 characters)

Important: JSON Serialization

The data object must be serialized to JSON with no extra whitespace. Different JSON libraries may produce different output:
LibraryOutputValid?
JSON.stringify(obj){"amount":"100.00","symbol":"USDT","chain":"TRON"}✅ Yes
json.dumps(obj, separators=(",",":")){"amount":"100.00","symbol":"USDT","chain":"TRON"}✅ Yes
json.dumps(obj, indent=2)Formatted with spaces/newlines❌ No
The signature is computed over the data field only — not over the full ReqPayload envelope. The sign, timestamp, and nonce fields are excluded from signing.

Request Envelope

Each request must include these four fields as a JSON object:
{
  "sign": "a1b2c3d4e5f6...",
  "timestamp": 1717000000,
  "nonce": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "amount": "100.00",
    "symbol": "USDT",
    "chain": "TRON"
  }
}

Language Examples

Node.js

import crypto from "crypto";

function signRequest(data, token) {
  return crypto
    .createHmac("sha256", token)
    .update(JSON.stringify(data), "utf8")
    .digest("hex");
}

Python

import hmac, hashlib, json

def sign_request(data: dict, token: str) -> str:
    json_str = json.dumps(data, separators=(",", ":"), sort_keys=True)
    return hmac.new(token.encode(), json_str.encode(), hashlib.sha256).hexdigest()

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;

public static String signRequest(String dataJson, String token) throws Exception {
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(token.getBytes("UTF-8"), "HmacSHA256"));
    byte[] digest = mac.doFinal(dataJson.getBytes("UTF-8"));
    return bytesToHex(digest);
}

Go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
)

func SignRequest(data interface{}, token string) string {
    jsonBytes, _ := json.Marshal(data)  // compact JSON
    mac := hmac.New(sha256.New, []byte(token))
    mac.Write(jsonBytes)
    return hex.EncodeToString(mac.Sum(nil))
}

Verification

XPayLabs verifies each request by:
  1. Recomputing the HMAC-SHA256 signature from the data field and your stored merchant token.
  2. Checking that the computed signature matches the provided sign value.
  3. Validating that the timestamp is within 5 minutes of the server clock.
  4. Ensuring the nonce has not been used before.
If any check fails, the gateway returns a 401 Unauthorized response:
{
  "code": 401,
  "msg": "Sign verification failed",
  "data": null
}

GET Request Signing

For GET requests that require authentication (e.g., /v1/order/status/{orderId}, /v1/order/pay, /v1/order/getOrderStatus), the sign parameter is passed as a query string value. The signature is computed over the relevant parameters (typically orderId):
sign = HEX(HMAC-SHA256(orderId, merchant_token))