⚙️
API Explorer
  • Overview
  • API Reference
    • 交易
      • Sessiontoken
      • customer
      • Payment methods
      • Confirm charge
      • Checkout payment
      • Local payment
    • 授权交易完成/撤销
      • Authorization
    • 拒付
    • 退款
    • 物流
    • 交易查询
    • 网关绑定支付通道查询
    • 结算明细查询
    • 风控-来源网址查询
  • Webhook
    • 概述
    • 交易事件
    • 预授权事件
    • 退款事件
    • 拒付事件
    • 支付结果跳转
  • 附录
    • 数据签名过程
    • 测试账号信息
    • 常见错误码
    • 常见问题
    • 旧版API文档
Powered by GitBook
On this page
  • 1,准备
  • 2,通用签名规则
  • 数据定义
  • 通用签名步骤
  • 4,对请求数据签名
  • 5,对结果数据签名(接口响应,webHook通知)
  • 6,签名代码示例
  1. 附录

数据签名过程

签名以及验证

Previous支付结果跳转Next测试账号信息

Last updated 2 years ago

1,准备

  • 涉及接口:适用本文档(API Explorer)的所有接口

  • 申请签名key:商户需要申请签名key,用于数据签名和结果验签,签名key请妥善保管勿对外泄露

  • 签名与验证:不建议在web前端存储和进行数据签名验签,推荐在服务端进行签名和响应结果的验签

  • 签名计算:

2,通用签名规则

数据定义

header参数:指httpRequest header里的参数,如 或

path参数:指 API 文档,URL中带有占位符的为path参数;在接口文档描述中,该参数在path栏位

例如:https://host/V2022-03/payment_methods/{customerPaymentMethodId}

该URL中 customerPaymentMethodId 带有占位符

https://host/V2022-03/payment_methods/pm_1526760521989763072

此时pm_1526760521989763072为参数

query参数:指直接跟在请求地址后方的key=value数据(GET方式表单提交、url后追加的参数如:url?key1=val1&key2=val2)

body参数:httpRequest body中的json对象

通用签名步骤

1,将header参数:gateway-no request-id request-time 字段按ascii排序,取其非空值累加,得到数据H

2,将path参数按照参数名的ascii排序,取其值累加得到数据P

3,将query参数按照参数名的ascii排序,取其值累加得到数据Q

4,将body中的数据转为字符串(json对象转为字符串),此为数据B

注意:webhook 请求头中的header参数包含:gateway-no request-id request-time version,比较其他接口多一个 version 字段,则数据H的获取方式:

将header参数:gateway-no request-id request-time version 字段按ascii排序,取其非空值累加,得到数据H

//bodyJson为接口发送在body中的json对象数据,转为字符串,即为上述的数据B
JSON.stringify(bodyJson);
//jackson
new ObjectMapper().writeValueAsString(bodyJson)

此步骤特别注意:上述代码为转换字符串示意,一定要确保发送的bodyJson(http发送时一般会将json对象序列化)与转换后的字符串一致

5,以上四项,如数据项存在内容(字符长度大于0),则使用点号连接,得到待签名数据H.P.Q.B

6,采用商户key,对待签名数据进行HMAC-SHA256签名:HMAC-SHA256(H.P.Q.B , key),得到签名结果16进制字符串signValue(不区分大小写)

7,将signValue设置到HttpRequest header的sign字段中,发送该请求

4,对请求数据签名

# post请求示例
curl -X POST "https://api.asiabill.com/V2022-03/refund" 
-H 'Content-Type: application/json' 
-H 'request-id:123456'
-H 'request-time:1646648307486'
-H 'gateway-no:1000001'
-d '{"refundReason":"test refund","tradeNo":"2021212123123123"}'

对于以上请求,生成签名过程如下:

1,得到header参数的ascii顺序值的字符串H:10000011234561646648307486

2,path参数没有,略过该步骤

3,query参数没有,略过该步骤

4,body的json内容转字符串为{"refundReason":"test refund","tradeNo":"2021212123123123"}

5,上述各项非空数据,点号连接:10000011234561646648307486.{"refundReason":"test refund","tradeNo":"2021212123123123"}

6,使用商户key,比如12345678,进行hmac-sha256签名,得到签名结果:

8eb28572747479aedf3cbc4b59a70b5be180841a527449149ef52d480e12951b

7,将上述签名结果,放入header的sign-info字段中即可

5,对结果数据签名(接口响应,webHook通知)

发送请求,服务端处理后,会在http响应header中原样返回gateway-no response-id response-time version 等信息,同时使用上述签名规则,对响应header和body进行签名,签名结果放在响应header的sign-info字段。客户端收到结果后,推荐对结果数据进行签名验证

签名验证步骤

  1. 读取请求响应header,解析得到gateway-no request-id request-time字段的值,并按照上述签名顺序将存在的值累加,得到字符串H

  2. 读取响应body,得到json字符串B

  3. 使用点号连接,得到待签名数据H.B

  4. 使用商户key,进行hmac-sha256签名得到结果checkSignValue

  5. 比较响应header中的sign的值是否与4中的checkSignValue相等,如不相等则数据异常,或验证流程未匹配上

注意:webhook 请求头中的header参数包含:gateway-no request-id request-time version,比较其他接口多一个 version 字段,则数据H的获取方式:

将header参数:gateway-no request-id request-time version 字段按ascii排序,取其非空值累加,得到数据H

6,签名代码示例

// 生成签名,HmacSha256后转为16进制字符串,javascript类似
// 引入crypto-js
import CryptoJS from 'crypto-js/crypto-js'
// 按照签名数据规则拼接数据
let sign_info = "";
// 使用key进行签名
let hash = CryptoJS.HmacSHA256(sign_info, key);
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import java.nio.charset.StandardCharsets;

/**
 * 生成签名,HmacSha256后转为16进制字符串
 */
class HmacSha256Util {
	public static void main(String[] args) throws Exception {
		String key = "12345678";
		//由请求参数按规则拼接而来,这里是示例数据
		String signInfo = "1220000145508010711647341103179.{\"refundReason\":\"test refund\",\"tradeNo\":\"2021212123123123\"}";
		//密钥
		SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		//初始化mac
		mac.init(secretKey);
		//执行消息摘要
		byte[] digest = mac.doFinal(signInfo.getBytes(StandardCharsets.UTF_8));
		//转为十六进制的字符串
		String signValue =  new HexBinaryAdapter().marshal(digest);
		//得到签名值:7981DD89443E82C2CC0596702A86AA0FC03C77EA5818DF5BB6EE9B03BD465656
		//校验时不区分大小写
		System.out.println(signValue);
	}
}
// 由请求参数按规则拼接而来,这里是示例数据
$sign_str = '1220000145508010711647341103179.{\"refundReason\":\"test refund\",\"tradeNo\":\"2021212123123123\"}';

// 密钥
$sign_key = '12345678';

// HmacSha256后转为16进制字符串
hash_hmac('sha256', $sign_str, $sign_key);

https://www.asiabill.com/developers/sign/check.html
Request Header
Response Header