多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### 1.筛选 将所有请求参数组合为一个集合,包括公共请求参数(消息通知无此项)和业务参数,但***不包括sign参数和值为NUll参数*** ### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#2%E5%BA%8F%E5%88%97%E5%8C%96%E5%8F%82%E6%95%B0%E5%B9%B6%E6%8B%BC%E6%8E%A5)2.序列化参数并拼接 将筛选后的集合序列化为JSON格式,并保证***所有层级的Key***按照ASCII码递增排序(字母升序排序),将序列化后参数使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串,在字符串最后拼接`&appSecret=`应用的秘钥 ### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#3%E7%AD%BE%E5%90%8D)3.签名 使用MD5算法对拼接后的字符串采用"UTF-8"格式生成文件摘要,并转为16进制字符串,把生成的字符串转为大写赋值给公共的请求参数`sign` ### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#4%E7%A4%BA%E4%BE%8B)4.示例 #### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#41-%E6%8F%90%E4%BA%A4%E8%AE%A2%E5%8D%95%E9%A2%84%E4%B8%8B%E5%8D%95)4.1 提交订单(预下单) * 接口名:dby.scm.order.submit * 版本号:v1 * 请求方式:POST * 请求数据类型: application/json * 业务请求参数: ~~~json { "orderRemark":"测试下单", "consigneeAddress":"安腾国际", "consigneeMobile":"15900000000", "consigneeName":"张三", "consigneeProvinceCode":"42", "consigneeTownCode":"420106010", "consigneeCountyCode":"420106", "consigneeCityCode":"4201", "skuInfos":[ //嵌套的key也需要排序 { "unitPrice":8000, "skuNum":1, "skuCode":"50180878441" }], "tradeNo":"1598510632214159360" } ~~~ #### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#42-%E5%85%AC%E5%85%B1query%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0)4.2 公共Query请求参数 ~~~ method=dby.scm.order.submit appKey=7knzxd30ob version=v1 timestamp=1669949608466 sign= ~~~ #### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#43-%E6%8E%92%E5%BA%8F%E5%90%8E%E7%9A%84%E5%8F%82%E6%95%B0)4.3 排序后的参数 ~~~json { "appKey":"7knzxd30ob", "consigneeAddress":"安腾国际", "consigneeCityCode":"4201", "consigneeCountyCode":"420106", "consigneeMobile":"15900000000", "consigneeName":"张三", "consigneeProvinceCode":"42", "consigneeTownCode":"420106010", "method":"dby.scm.order.submit", "orderRemark":"测试下单", "skuInfos":[ { "skuCode":"50180878441", "skuNum":1, "unitPrice":8000 } ], "timestamp":1669949608466, "tradeNo":"1598510632214159360", "version":"v1" } ~~~ #### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#44-%E6%8B%BC%E6%8E%A5%E4%B8%BA%E5%BE%85%E7%AD%BE%E5%90%8D%E5%AD%97%E7%AC%A6%E4%B8%B2)4.4 拼接为待签名字符串 ~~~ appKey=7knzxd30ob&consigneeAddress=安腾国际&consigneeCityCode=4201&consigneeCountyCode=420106&consigneeMobile=15900000000&consigneeName=张三&consigneeProvinceCode=42&consigneeTownCode=420106010&method=dby.scm.order.submit&orderRemark=测试下单&skuInfos=[{"skuCode":"50180878441","skuNum":1,"unitPrice":8000}]&timestamp=1669949608466&tradeNo=1598510632214159360&version=v1&appSecret=2077wuuyh88gfzf2vpv2s2gf1cqkkuro ~~~ #### 4.5 对待签名字符串进行md5签名后转换为16进制字符串并转为大写,赋值给公共Query参数`sign`,最终的请求为 ~~~ curl -XPOST -H "Content-type: application/json" -d '{     "consigneeAddress":"安腾国际",     "consigneeCityCode":"4201",     "consigneeCountyCode":"420106",     "consigneeMobile":"15900000000",     "consigneeName":"张三",     "consigneeProvinceCode":"42",     "consigneeTownCode":"420106010",     "orderRemark":"测试下单",     "skuInfos":[         {             "skuCode":"50180878441",             "skuNum":1,             "unitPrice":8000         }     ],     "tradeNo":"1598523628516773888" }' 'http://127.0.0.1/open/api?method=dby.scm.order.submit&appKey=7knzxd30ob&version=v1&timestamp=1669952706993&sign=7D2F11F449D7160D1684968A029583A6' ~~~ ### [](https://duobaoyu.com.cn/documentcenter?onlyFlag=414ce68bebb334fe46d941321863e22b#5-%E7%AD%BE%E5%90%8D%E5%8F%82%E8%80%83)5\. 签名参考 Java ~~~json public class SignUtil { /** * 签名参考 * * @param appKey 商户应用appKey * @param method 接口方法名称 * @param timestamp 13位格式时间戳,5分钟误差内 * @param version 版本号,现在固定为v1 * @param appSecret 商户应用appSecret * @param bizParam 业务参数,具体参数请查看接口对接文档 * @return 签名字符串 */ public static String sign(String appKey, String method, Long timestamp, String version, String appSecret, Object bizParam) { //参数排序 SortedMap signParam = new TreeMap<>(); //添加通用参数 signParam.put("appKey", appKey); signParam.put("method", method); signParam.put("timestamp", timestamp); signParam.put("version", version); //添加业务参数 if (ObjectUtil.isNotNull(bizParam)) { //对参数进行排序(a-z) //所有的层级都需要排序 String jsonStr = JSONObject.toJSONString(bizParam, SerializerFeature.MapSortField); JSONObject bizJson = JSONObject.parseObject(jsonStr, Feature.OrderedField); signParam.putAll(bizJson); } StringBuilder strBuilder = new StringBuilder(); boolean isFirst = true; for (Map.Entry entry : signParam.entrySet()) { //排除值为NULL的参数 if (entry.getKey() != null && entry.getValue() != null) { if (isFirst) { isFirst = false; } else { strBuilder.append("&"); } //拼接字符串 strBuilder.append(Convert.toStr(entry.getKey())).append("=").append(Convert.toStr(entry.getValue())); } } //末尾拼接appSecret strBuilder.append("&appSecret=" + appSecret); //生成待签名字符串 String signStr = strBuilder.toString(); //使用Md5算法生成摘要并转为16进制字符串 return new Digester(DigestAlgorithm.MD5).digestHex(signStr, CharsetUtil.UTF_8).toUpperCase(); } } ~~~ PHP ~~~json <?php /** * 签名参考 * * @param string appKey 商户应用appKey * @param string method 接口方法名称 * @param string timestamp 13位格式时间戳,5分钟误差内 * @param string version 版本号,现在固定为v1 * @param string appSecret 商户应用appSecret * @param array bizParam 业务参数,具体参数请查看接口对接文档 * @return 签名字符串 */ function sign($appKey, $method, $timestamp, $version, $appSecret, array $bizParam) { //添加通用参数 $commonParam = array(); $commonParam[‘appKey’] = $appKey; $commonParam[‘method’] = $method; $commonParam[‘timestamp’] = $timestamp; $commonParam[‘version’] = $version; //合并通用参数和业务参数数组 $paramArr = array_merge($commonParam, bizParam); //对所有层级参数递归排序(a-z) recursion_ksort(paramArr); //将参数按序拼接成字符串 $spliceStr = ‘&’; foreach ($paramArr as $key => $value) { if (!is_null($value)) { $spliceStr .= ‘&’ . $key . ‘=’ . (is_array($value) ? json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $value); } } $spliceStr = trim($spliceStr, ‘&’) . ‘&appSecret=’ . $appSecret; //生成待签名字符串,md5签名并转大写 $sign = strtoupper(md5($spliceStr)); return $sign; } /** * 关联数组递归排序 * @param array $arr * @param bool $removeNull * @return void */ function recursion_ksort(array &$arr,$removeNull = true) { $kstring = true; foreach ($arr as $k => &$v) { if (!is_string($k)) { $kstring = false; } if(is_null($v) && $removeNull){ unset($arr[$k]); } if (is_array($v)) { recursion_ksort($v,$removeNull); } } if ($kstring) { ksort($arr); } } ~~~