Appearance
签名规则 ¶
请求参数签名 ¶
注意
签名字符串中不能拥有 "
和 \
特殊符号,通常处理有嵌套对象的请求参数时,进行Json序列化会产生,如果不是在使用SDK
,需要我们手动替换为空字符串,然后再进行签名。
将发送的所有数据假定位为集合M,将集合内非空的参数值按照参数名ASCII码
从小到大排序,使用URL请求键值对参数方式进行拼接成字符串,类似key1=value1&key2=value2
这样的格式。需要注意如下原则:
- 将字符串中
"
和\
特殊符号替换为空字符串'' - 参数名ASCII码从小到大排序(字典序)
- 如果参数值为空不进行签名
- 参数名不区分大小写
- 如果是嵌套参数,将嵌套的参数按上面的规则进行排序,然后转换为
json字符串
,然后参与排序 - 如果是json字符串,
- 进行验签时,
sign
字段不参与签名,将生成的签名与该sign
值作校验 - 含有小数金额的数值不需要保留末尾的0,例如
1.10
需要设置为1.1
,1.00
需要设置为1
。
将上面生成的参数后追加上 sign=秘钥
(追加后格式为 key1=value1&key2=value2&key=秘钥
),然后将该字符串转换为大写,然后进行数据摘要算法计算出 sign
值,并将sign
值添加到请求中进行发送。
数据摘要支持
MD5
和HmacSHA256
两种,使用HmacSHA256
时,key值作为秘钥
Java样例(使用SDK) ¶
java
package org.dromara.daxpay.single.sdk.util;
import org.dromara.daxpay.single.sdk.param.channel.WeChatPayParam;
import org.dromara.daxpay.single.sdk.param.pay.PayParam;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.Map;
/**
* 参数签名测试类
* @author xxm
* @since 2024/2/7
*/
@Slf4j
public class PayParamSignTest {
/**
* 签名测试
*/
@Test
public void sign(){
PayParam param = new PayParam();
param.setClientIp("127.0.0.1");
param.setNotNotify(true);
param.setBizOrderNo("P0001");
param.setTitle("测试接口支付");
Map<String, String> map = PaySignUtil.toMap(param);
log.info("转换为有序MAP后的内容: {}",map);
String data = PaySignUtil.createLinkString(map);
log.info("将MAP拼接字符串, 并过滤掉特殊字符: {}",data);
String sign = "123456";
data += "&sign="+sign;
data = data.toUpperCase();
log.info("添加秘钥并转换为大写的字符串: {}",data);
log.info("MD5: {}",PaySignUtil.md5(data));
log.info("HmacSHA256: {}",PaySignUtil.hmacSha256(data,sign));
}
/**
* 多层嵌套对象签名测试
*/
@Test
public void sign2(){
PayParam param = new PayParam();
param.setClientIp("127.0.0.1");
param.setBizOrderNo("P0002");
param.setTitle("测试接口支付");
WeChatPayParam weChatPayParam = new WeChatPayParam();
weChatPayParam.setOpenId("6688812");
weChatPayParam.setAuthCode("123456");
param.setExtraParam(weChatPayParam);
Map<String, String> map = PaySignUtil.toMap(param);
log.info("转换为有序MAP后的内容: {}",map);
String data = PaySignUtil.createLinkString(map).replaceAll("\\\"","").replaceAll("\"","");
log.info("将MAP拼接字符串, 并过滤掉特殊字符: {}",data);
String sign = "123456";
data += "&sign="+sign;
data = data.toUpperCase();
log.info("添加秘钥并转换为大写的字符串: {}",data);
log.info("MD5: {}",PaySignUtil.md5(data));
log.info("HmacSHA256: {}",PaySignUtil.hmacSha256(data,sign));
}
}
输出:
shell
转换为有序MAP后的内容: {bizOrderNo=P0001, clientIp=127.0.0.1, notNotify=true, reqTime=1715579269, title=测试接口支付}
将MAP拼接字符串, 并过滤掉特殊字符: bizOrderNo=P0001&clientIp=127.0.0.1¬Notify=true&reqTime=1715579269&title=测试接口支付
添加秘钥并转换为大写的字符串: BIZORDERNO=P0001&CLIENTIP=127.0.0.1&NOTNOTIFY=TRUE&REQTIME=1715579269&TITLE=测试接口支付&SIGN=123456
MD5: 4b60845df556be3c0f9be8643cea3d36
HmacSHA256: 69c61e6c539ebee56ae2b6de16f59b4d6b4da9e6809738ec7f7049daad1f845b
-----------------------------------------------------------------------------------------------------------------------------------------
转换为有序MAP后的内容: {bizOrderNo=P0002, clientIp=127.0.0.1, extraParam={"authCode":"123456","openId":"6688812"}, reqTime=1715579300, title=测试接口支付}
将MAP拼接字符串, 并过滤掉特殊字符: bizOrderNo=P0002&clientIp=127.0.0.1&extraParam={authCode:123456,openId:6688812}&reqTime=1715579300&title=测试接口支付
添加秘钥并转换为大写的字符串: BIZORDERNO=P0002&CLIENTIP=127.0.0.1&EXTRAPARAM={AUTHCODE:123456,OPENID:6688812}&REQTIME=1715579300&TITLE=测试接口支付&SIGN=123456
MD5: 44d81601494e7d9bc453c08137326689
HmacSHA256: 471c3612ee8b177bfce2c7752323c8d5b92b5605558d4bc8906dcf276d3022d3
响应参数验签 ¶
注意
响应参数验签时,通常我们接口返回的类型会是公共响应参数<业务响应参数>
的格式,签名规则基本与请求参数验签一致。
通知消息验签 ¶
注意
通知消息验签时,规则与请求参数一致。