API 签名

签名参数

参数名参数说明备注
timestamp⼗三位时间戳1699261493465
httpMethod请求方式 GET/POST请求方式须大写
requestPath请求路径不包含域名
bodyString请求参数GET:为空
POST:请求参数

第一步:生成签名字符串

签名字符串固定为:timestamp + httpMethod + requestPath + bodyString

其中 requestPath、bodyString 中的参数,依据参数名使⽤字典顺序排序,去掉空值。

POST 请求

如:创建订单接口

  • timestamp:1699261493465
  • httpMethod:POST
  • requestPath:/open/api/v4/merchant/trade/create
  • bodyString:
{
    "side": "BUY",
    "cryptoCurrency": "USDT",
    "address": "0xef17748b259a133a581e236ebc97edce3b50aaaf",
    "network": "TRX",
    "fiatCurrency": "USD",
    "amount": "100",
    "depositType": 2,
    "payWayCode": "10001",
    "alpha2": "US",
    "redirectUrl": "",
    "callbackUrl": "http://payment.jyoumoney.com/alchemyRamp/pay/callback?tradeNo=DZ02207091800356504"
}bo'dbo'dd

(1)bodyString 参数排序

{"address":"0xef17748b259a133a581e236ebc97edce3b50aaaf","alpha2":"US","amount":"100","callbackUrl":"http://payment.jyoumoney.com/alchemyRamp/pay/callback?tradeNo=DZ02207091800356304","cryptoCurrency":"USDT","depositType":2,"fiatCurrency":"USD","network":"TRX","payWayCode":"10001","side":"BUY"}

(2)拼接签名字符串:

1699261493465POST/open/api/v4/merchant/trade/create{"address":"0xef17748b259a133a581e236ebc97edce3b50aaaf","alpha2":"US","amount":"100","callbackUrl":"http://payment.jyoumoney.com/alchemyRamp/pay/callback?tradeNo=DZ02207091800356304","cryptoCurrency":"USDT","depositType":2,"fiatCurrency":"USD","network":"TRX","payWayCode":"10001","side":"BUY"}

GET 请求

如:查询订单状态接口

  • timestamp:1699261493465
  • httpMethod:GET
  • requestPath:/open/api/v4/merchant/query/trade?orderNo=1028577684629876736&side=BUY&[email protected]
  • bodyString:

(1)requestPath 参数排序

[email protected]&orderNo=1028577684629876736&side=BUY

(2)拼接签名字符串

1699261493465GET/open/api/v4/merchant/query/[email protected]&orderNo=1028577684629876736&side=BUY

第二步:生成您的签名

public class AchSign {


    public static String apiSign(String timestamp, String method, String path, Map<String, String> paramMap, String secretkey) throws NoSuchAlgorithmException, InvalidKeyException {
        String content = timestamp + method.toUpperCase() + path + getJsonBody(paramMap);
        Base64.Encoder base = Base64.getEncoder();
        String signVal = base.encodeToString(sha256(content.getBytes(StandardCharsets.UTF_8), secretkey.getBytes(StandardCharsets.UTF_8)));
        return signVal;
    }

    public static byte[] sha256(byte[] message, byte[] secret) throws NoSuchAlgorithmException, InvalidKeyException {
        Mac sha256_HMAC = Mac.getInstance("HmacSha256");
        SecretKeySpec secretKey = new SecretKeySpec(secret, "HmacSha256");
        sha256_HMAC.init(secretKey);
        return sha256_HMAC.doFinal(message);
    }


    private static String getJsonBody(Map<String,String> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return "";
        }
        parameters = removeEmptyKeys(parameters);
        parameters = (Map) sortObject(parameters);
        return JSON.toJSONString(parameters);
    }
    

    private static Map removeEmptyKeys(Map map) {
        if (map.isEmpty()) {
            return map;
        }
        Map retMap = new HashMap();
        Iterator<Map.Entry> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            if (entry.getValue() != null && !entry.getValue().equals("")) {
                retMap.put(entry.getKey(), entry.getValue());
            }
        }
        return retMap;
    }

    private static Object sortObject(Object obj) {
        if (obj instanceof Map) {
            return sortMap((Map) obj);
        } else if (obj instanceof List) {
            sortList((List) obj);
            return obj;
        }
        return null;
    }

    private static Map sortMap(Map map) {
        if (map.isEmpty()) {
            return null;
        }
        SortedMap<String, Object> sortedMap = new TreeMap<>(removeEmptyKeys(map));
        for (String sortKey : sortedMap.keySet()) {
            if (sortedMap.get(sortKey) instanceof Map) {
                sortedMap.put(sortKey, sortMap((Map) sortedMap.get(sortKey)));
            } else if (sortedMap.get(sortKey) instanceof List) {
                sortedMap.put(sortKey, sortList((List) sortedMap.get(sortKey)));
            }
        }
        return sortedMap;
    }

    private static List sortList(List list) {
        if (list.isEmpty()) {
            return null;
        }
        List objectList = new ArrayList();

        List intList = new ArrayList();
        List floatList = new ArrayList();
        List stringList = new ArrayList();

        List jsonArray = new ArrayList();
        for (Object obj : list) {
            if (obj instanceof Map || obj instanceof List) {
                jsonArray.add(obj);
            } else if (obj instanceof Integer) {
                intList.add(obj);
            } else if (obj instanceof BigDecimal) {
                floatList.add(obj);
            } else if (obj instanceof String) {
                stringList.add(obj);
            } else {
                intList.add(obj);
            }
        }

        Collections.sort(intList);
        Collections.sort(floatList);
        Collections.sort(stringList);

        objectList.addAll(intList);
        objectList.addAll(floatList);
        objectList.addAll(stringList);
        objectList.addAll(jsonArray);

        list.clear();
        list.addAll(objectList);


        List retList = new ArrayList();

        for (Object obj : list) {
            if (obj instanceof Map) {
                retList.add(sortMap((Map) obj));
            } else if (obj instanceof List) {
                retList.add(sortList((List) obj));
            } else {
                retList.add(obj);
            }
        }
        return retList;
    }

    public static void main(String[] args) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis());
        String method = "";
        String path = "";
        Map map = new hashMap();
        String secretkey = "";
        String sign = apiSign(timestamp, method, path, map, secretkey);
        System.out.println(timestamp);
        System.out.println(sign);
        System.out.println(URLEncoder.encode(sign));

    }
}
<?php

function generateSignature($timestamp, $httpMethod, $requestPath,$bodyString, $secretKey) {
    // Concatenate parameters for signature string
    $signatureString = $timestamp . $httpMethod . $requestPath . $bodyString ;

    echo "\n"."signatureString:".$signatureString;
    // Generate HMAC SHA256 signature using the secret key
    $signature = base64_encode(hash_hmac('sha256', $signatureString, $secretKey, true));

    return $signature;
}

//Sort the parameters in lexicographical order and return json String
function getStringToSign($params) {
    ksort($params);
    $json =  json_encode($params);
    return stripslashes($json);
}


// Example for On Ramp
$onRampHttpMethod = "POST";
$onRampRequestPath = "/open/api/v4/merchant/trade/create";
$appId= "f83Is2y7L425rxl8";
$onRampSecretKey = "4Yn8RkxDXN71Q3p0";
$timestamp = strval(time() * 1000);
echo "timestamp:".$timestamp;

//request parameters
$paramsToSign = array(
  'address' => 'TSx82tWNWe5Ns6t3w94Ye3Gt6E5KeHSoP8',
  'alpha2' => 'US',
  'amount' => '100',
  'callbackUrl' => 'http://payment.jyoumoney.com/alchemyRamp/pay/callback?tradeNo=DZ02207091800356304',
  'cryptoCurrency' => 'USDT',
  'depositType' => '2',
  'network' => 'TRX',
  'fiatCurrency' => 'USD',
  'payWayCode' => '10001',
  'side' => 'BUY',
);



$rawDataToSign = getStringToSign($paramsToSign);
$onRampSignature = generateSignature($timestamp, $onRampHttpMethod, $onRampRequestPath, $rawDataToSign,$onRampSecretKey);

echo "\n"."On Ramp Signature: " . $onRampSignature . PHP_EOL;

?>

import base64
import hashlib
import hmac
import json
from urllib.parse import urlparse, urlencode

def api_sign(timestamp, method, request_url, body, secretkey):
    content = timestamp + method.upper() + get_path(request_url) + get_json_body(body)
    print(content)
    key = secretkey.encode('utf-8')
    message = content.encode('utf-8')
    signature = hmac.new(key, message, hashlib.sha256).digest()
    signature_b64 = base64.b64encode(signature).decode('utf-8')
    return signature_b64


def sha256(message, secret):
    return hmac.new(secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).digest()


def get_path(request_url):
    parsed_url = urlparse(request_url)
    path = parsed_url.path
    params = dict(parsed_url.query)
    if not params:
        return path
    sorted_params = {k: params[k] for k in sorted(params)}
    query_string = urlencode(sorted_params)
    return f'{path}?{query_string}'


def get_json_body(body):
    try:
        json_data = json.loads(body)
    except (json.JSONDecodeError, TypeError):
        json_data = {}
    if not json_data:
        return ''
    json_data = remove_empty_keys(json_data)
    return json.dumps(sort_object(json_data), separators=(',', ':'))


def parse_path(request_url):
    parsed_url = urlparse(request_url)
    path = parsed_url.path
    params = dict(parsed_url.query)
    return path, params


def is_json(json_string):
    try:
        json.loads(json_string)
        return True
    except (json.JSONDecodeError, TypeError):
        return False


def remove_empty_keys(data):
    return {k: v for k, v in data.items() if v}


def sort_object(data):
    if isinstance(data, dict):
        return {k: sort_object(v) for k, v in sorted(data.items())}
    elif isinstance(data, list):
        data.sort(key=sort_object)
        return [sort_object(item) for item in data]
    else:
        return data
      
if __name__ == '__main__':
  timestamp = ""
  request_method = ""
  request_url = ""
  req_body = {
        "XXX": "XXXXX"
  }
  sign = api_sign(timestamp, method, request_url,get_json_body(json.dumps(req_body)), appSecret)
  print(sign)


import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"testing"
	"time"
)


func PostRequest(url string, headers map[string]string, bodyData map[string]string) ([]byte, error) {
	requestBody, err := json.Marshal(bodyData)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
	if err != nil {
		return nil, err
	}

	for key, value := range headers {
		req.Header.Set(key, value)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return responseBody, nil
}

func APISign(timestamp string, method string, requestURL string, body string, secretKey string) (string, error) {
	content := timestamp + strings.ToUpper(method) + getPath(requestURL) + getJSONBody(body)
	signature, err := calculateHMACSHA256([]byte(content), []byte(secretKey))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(signature), nil
}

func calculateHMACSHA256(message []byte, secret []byte) ([]byte, error) {
	hash := hmac.New(sha256.New, secret)
	_, err := hash.Write(message)
	if err != nil {
		return nil, err
	}
	return hash.Sum(nil), nil
}

func getPath(requestURL string) string {
	u, err := url.Parse(requestURL)
	if err != nil {
		return ""
	}
	path := u.Path
	query := u.Query()
	query.Del("")
	keys := make([]string, 0, len(query))
	for key := range query {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	var encodedParams []string
	for _, key := range keys {
		encodedParams = append(encodedParams, fmt.Sprintf("%s=%s", key, query.Get(key)))
	}
	paramsString := strings.Join(encodedParams, "&")
	if paramsString != "" {
		return path + "?" + paramsString
	}
	return path
}

func getJSONBody(body string) string {
	if body == "" {
		return "" 
	}

	var data interface{}
	err := json.Unmarshal([]byte(body), &data)
	if err != nil {
		return "" 
	}

	
	if dataMap, ok := data.(map[string]interface{}); ok && len(dataMap) == 0 {
		return "" 
	}

	removeEmptyKeys(data)
	sortedData := sortObject(data)

	jsonBytes, err := json.Marshal(sortedData)
	if err != nil {
		return ""
	}
	return string(jsonBytes)
}


func removeEmptyKeys(data interface{}) {
	switch value := data.(type) {
	case map[string]interface{}:
		for key, v := range value {
			if v == nil || v == "" {
				delete(value, key)
			} else {
				removeEmptyKeys(v)
			}
		}
	case []interface{}:
		for _, v := range value {
			removeEmptyKeys(v)
		}
	}
}

func sortObject(data interface{}) interface{} {
	switch value := data.(type) {
	case map[string]interface{}:
		keys := make([]string, 0, len(value))
		for k := range value {
			keys = append(keys, k)
		}
		sort.Strings(keys)

		sortedMap := make(map[string]interface{})
		for _, k := range keys {
			v := sortObject(value[k])
			sortedMap[k] = v
		}
		return sortedMap

	case []interface{}:
		for i, v := range value {
			value[i] = sortObject(v)
		}
		return value
	default:
		return value
	}
}

func main() {
	timestamp := fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
	method := "XX"
	requestURL := "XXXXX"
	req_body := map[string]string{
		"XXX": "XXXXX",
	}

	dataType, _ := json.Marshal(req_body)
	dataString := string(dataType)

	appId := "XXXXX"
	secretKey := "XXXXX"

	sign, err := APISign(timestamp, method, requestURL, dataString, secretKey)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return
	}

	headers := map[string]string{
		"appId":     appId,
		"timestamp": timestamp,
		"sign":      sign,
	}

	response, err := PostRequest(requestURL, headers, req_body)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println(string(response))
	}

const crypto = require('crypto');
const url = require('url');

function apiSign(timestamp, method, requestUrl, body, secretkey) {
  const content = timestamp + method.toUpperCase() + getPath(requestUrl) + getJsonBody(body);
  const signVal = crypto.createHmac('sha256', secretkey)
    .update(content, 'utf8')
    .digest('base64');
  
  return signVal;
}

function sha256(message, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(message);
  return hmac.digest();
}

function getPath(requestUrl) {
  const uri = new URL(requestUrl);
  const path = uri.pathname;
  const params = Array.from(uri.searchParams.entries());

  if (params.length === 0) {
    return path;
  } else {
    const sortedParams = [...params].sort(([aKey], [bKey]) => aKey.localeCompare(bKey));
    const queryString = sortedParams.map(([key, value]) => `${key}=${value}`).join('&');
    return `${path}?${queryString}`;
  }
}

function getJsonBody(body) {
  let map;

  try {
    map = JSON.parse(body);
  } catch (error) {
    map = {};
  }

  if (Object.keys(map).length === 0) {
    return '';
  }

  map = removeEmptyKeys(map);
  map = sortObject(map);

  return JSON.stringify(map);
}

function parsePath(requestUrl) {
  const uri = new URL(requestUrl);
  const path = uri.pathname;
  const params = Object.fromEntries(uri.searchParams.entries());

  return { path, params };
}

function isJson(jsonString) {
  if (!jsonString || jsonString === '') {
    return false;
  }

  try {
    JSON.parse(jsonString);
    return true;
  } catch (error) {
    return false;
  }
}

function removeEmptyKeys(map) {
  const retMap = {};

  for (const [key, value] of Object.entries(map)) {
    if (value !== null && value !== '') {
      retMap[key] = value;
    }
  }

  return retMap;
}

function sortObject(obj) {
  if (typeof obj === 'object') {
    if (Array.isArray(obj)) {
      return sortList(obj);
    } else {
      return sortMap(obj);
    }
  }

  return obj;
}

function sortMap(map) {
  const sortedMap = new Map(Object.entries(removeEmptyKeys(map)).sort(([aKey], [bKey]) => aKey.localeCompare(bKey)));

  for (const [key, value] of sortedMap.entries()) {
    if (typeof value === 'object') {
      sortedMap.set(key, sortObject(value));
    }
  }

  return Object.fromEntries(sortedMap.entries());
}

function sortList(list) {
  const objectList = [];
  const intList = [];
  const floatList = [];
  const stringList = [];
  const jsonArray = [];

  for (const item of list) {
    if (typeof item === 'object') {
      jsonArray.push(item);
    } else if (Number.isInteger(item)) {
      intList.push(item);
    } else if (typeof item === 'number') {
      floatList.push(item);
    } else if (typeof item === 'string') {
      stringList.push(item);
    } else {
      intList.push(item);
    }
  }

  intList.sort((a, b) => a - b);
  floatList.sort((a, b) => a - b);
  stringList.sort();

  objectList.push(...intList, ...floatList, ...stringList, ...jsonArray);
  list.length = 0;
  list.push(...objectList);

  const retList = [];

  for (const item of list) {
    if (typeof item === 'object') {
      retList.push(sortObject(item));
    } else {
      retList.push(item);
    }
  }

  return retList;
}

const timestamp = String(Date.now());
const method = 'XXXXX';
const requestUrl = 'XXXXX';
const body = '';
const secretkey = 'XXXXX';
const sign = apiSign(timestamp, method, requestUrl, body, secretkey);
console.log(timestamp);
console.log(sign);
console.log(encodeURIComponent(sign));