关于签名
ach-access-sign 生成说明
生成过程
-
构建签名内容:对
timestamp
、method
、requestPath
和body
进行 HMAC SHA256 加密,使用SecretKey
进行加密,并通过 Base64 编码输出。 -
排序规则:
requestPath
中的参数和body
中的内容需遵循相同排序规则。- 排序顺序为:
int
→float/double
→string
→list & object
。 - 相同类型的数据按字典序排序,
list
和object
依据在序列中的位置进行排序。 - 对于嵌套结构,采用一致的递归排序规则。
- 空值(如
null
和''
)以及空数组[]
和空字典{}
不参与签名。
-
示例:
- 输入示例:
{{“x”: 1, “y”: 2}, 1, 3, 2, -4, 1.1, “xxxxx”, “yyyy”, “jscx”, 0, “sss”, {“z”: 2, “x”: 1, “a”: “”}}
- 排序后:
{-4, 0, 1, 2, 3, 1.1, “jscx”, “sss”, “xxxxx”, “yyyy”, {“x”: 1, “y”: 2}, {“x”: 1, “z”: 2}}
- 输入示例:
注意事项
- 确保传输中数列的数据排序与内容无关。
- 如果
path
和body
中都有参数,分别排序后拼接为timestamp + method + requestPath + body
。
示例签名
timestamp = 1538054050234
method = GET
requestPath = /api/v1/crypto/order?order_no=sdf23?token=ETH
body
为空- 签名内容为:
"1538054050234" + "GET" + "/api/v1/crypto/order?token=ETH?order_no=sdf23"
时间戳格式
timestamp
使用 ISO 格式的 Unix 时间戳(毫秒),示例:1538054050231
。
其他说明
method
为请求方法,必须大写。requestPath
大小写敏感,若以/
结尾仍需保留。body
为请求主体字符串,若无主体可省略。- 任何空参数均被过滤,不参与签名。
- 使用 HMAC SHA256 和
SecretKey
对哈希字符串签名,最后以 Base64 格式编码。
代码示例
import base64
import hmac
import json
import requests
from datetime import datetime
from typing import Dict, Any, List, Union, Type
# 签名
class SignatureUtility:
def generate_signature(self, secret_key: str, message: str) -> str:
"""Generate the HMAC SHA256 signature for a given message."""
signature = hmac.new(
secret_key.encode(), message.encode(), digestmod="sha256"
).digest()
return base64.b64encode(signature).decode()
def verify_signature(
self, secret_key: str, message: str, received_signature: str
) -> bool:
"""Verify the received signature against the computed one."""
computed_signature = self.generate_signature(secret_key, message)
return hmac.compare_digest(computed_signature, received_signature)
def clean_and_sort_dict(
self, data: Union[Dict[Any, Union[Dict, Any]], List[Union[Dict, Any]]]
) -> Union[Dict[Any, Union[Dict, Any]], List[Union[Dict, Any]]]:
if isinstance(data, dict):
sorted_dict = {}
for key, value in sorted(data.items()):
if isinstance(value, (dict, list)):
value = self.clean_and_sort_dict(value)
# Checking for non-empty values, including non-empty lists and non-empty dictionaries
if value or value == 0:
sorted_dict[key] = value
return (
sorted_dict if sorted_dict else None
) # Return None if the dictionary is empty
elif isinstance(data, list):
int_list = sorted([item for item in data if isinstance(item, int)])
float_list = sorted([item for item in data if isinstance(item, float)])
str_list = sorted([item for item in data if isinstance(item, str)])
complex_data_types = [
item for item in data if isinstance(item, (dict, list))
]
sorted_complex_data = [
self.clean_and_sort_dict(item) for item in complex_data_types
]
sorted_complex_data = [
item for item in sorted_complex_data if item
] # Filter out None values
result = int_list + float_list + str_list + sorted_complex_data
return result if result else None # Return None if the list is empty
#
def post_request(URL: str, body: Type):
sign_utility = SignatureUtility()
query_string = ""
method = "POST"
#
# 请求签名
body_str = ""
timestamp = str(int(datetime.utcnow().timestamp() * 1000))
req_cleaned_body = sign_utility.clean_and_sort_dict(body)
if req_cleaned_body is not None:
body_str = json.dumps(
req_cleaned_body,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
)
message = f"{timestamp}{method}{URL}{query_string}{body_str}"
print(message)
sign = sign_utility.generate_signature(secret_key=SECRET_KEY, message=message)
print(sign)
# POST REQUEST
headers = {
"ach-access-key": API_KEY,
"ach-access-sign": sign,
"ach-access-timestamp": timestamp,
}
response = requests.post(url=HOST + URL, headers=headers, json=body)
return response.text
API_KEY = "service000-local-apikey"
SECRET_KEY = "service000-local-secretkey"
HOST = "http://127.0.0.1:8072"
#
if __name__ == "__main__":
url = "/open/api/card/create"
body = {
"callbackUrl": "http://baidu.com",
"cardHolder": {
"address": {
"city": "string",
"country": "string",
"state": "string",
"street": "string",
"zipCode": "string"
},
"firstName": "string",
"lastName": "string"
},
"customerId": "user_id_123",
"deposit": "100",
"orderNo": "12165456165441",
"tagNameList": [
"string"
],
"vid": "vab_069af8a792ad"
}
response = post_request(url, body)
print(f"response: {response}")
Updated about 3 hours ago