数据签名规范
- 所有通信都使用MD5计算摘要作为签名数据
- 所有参数按照字段名的 ASCII 码从小到大排序后使用 QueryString 的格式(即key1=value1&key2=value2…)拼接而成,再直接拼接秘钥 key 做为签名串,签名结果需要转换为小写
注意以下重要规则
- 参数名ASCII码从小到大排序(字典序)
- 使用 QueryString 的格式(即key1=value1&key2=value2…) 拼接,之后直接连接key的值进行加密
- 不要再加入&key=这几个字符
- 空值不参与签名,参数名也不必提交
例如
accNo=123&bankCode=21002&firstName=john&lastName=tom&merchantNo=mer618218&orderAmt=1000&orderNo=1723595359882&payEmail=john.tom@gmail.com&payPhone=02012345678&productCode=11002b980d6f4c5c4485e9160d63155e22365
JAVA示例
import java.util.Map;
import java.util.TreeMap;
import java.util.HashMap;
import java.util.Map.Entry;
public class SignUtil {
public static String sign(Map<String, Object> paramMap, String signKey) {
try {
TreeMap<String, Object> sort = new TreeMap<>(paramMap);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : sort.entrySet()) {
String key = entry.getKey();
if ("sign".equals(key)) {
continue;
}
if (entry.getValue() == null || "".equals(entry.getValue())) {
continue;
}
String value = entry.getValue().toString();
sb.append(key).append("=").append(value).append("&");
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(signKey);
String verStr = SecureUtil.md5(sb.toString());
return verStr;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static void main(String[] args) {
String key = "b980d6f4c5c4485e9160d63155e22365";
Map<String, Object> map = new HashMap<>();
map.put("merchantNo", "mer618218");
map.put("orderNo", "2343243243432");
map.put("orderAmt", "10000");
map.put("firstName", "john");
map.put("lastName", "tom");
map.put("payEmail", "john.tom@gmail.com");
map.put("payPhone", "02012345678");
map.put("productCode", "11002");
map.put("notifyUrl", "http://www.xxx.com/notify");
String sign = sign(map, key);
map.put("sign", sign);
System.out.println("签名结果: " + sign);
}
}
NET示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static string Sign(Dictionary<string, object> paramMap, string signKey)
{
try
{
var sortedMap = paramMap
.Where(kvp => kvp.Key != "sign" && kvp.Value != null && kvp.Value.ToString() != "")
.OrderBy(kvp => kvp.Key)
.ToList();
var sb = new StringBuilder();
foreach (var entry in sortedMap)
{
string key = entry.Key;
string value = entry.Value.ToString();
sb.Append(key).Append("=").Append(value).Append("&");
}
if (sb.Length > 0)
{
sb.Length--; // Remove the last '&'
}
sb.Append(signKey);
using (var md5 = MD5.Create())
{
var hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()));
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
return "";
}
}
public static void Main()
{
string key = "b980d6f4c5c4485e9160d63155e22365";
var map = new Dictionary<string, object>
{
{ "merchantNo", "mer618218" },
{ "orderNo", "2343243243432" },
{ "orderAmt", "10000" },
{ "firstName", "john" },
{ "lastName", "tom" },
{ "payEmail", "john.tom@gmail.com" },
{ "payPhone", "02012345678" },
{ "productCode", "11002" },
{ "notifyUrl", "http://www.xxx.com/notify" }
};
string sign = Sign(map, key);
map["sign"] = sign;
}
}
PHP示例
<?php
function sign($paramMap, $signKey) {
try {
ksort($paramMap);
$sb = '';
foreach ($paramMap as $key => $value) {
if ($key === 'sign' || $value === null || $value === '') {
continue;
}
$sb .= $key . '=' . $value . '&';
}
$sb = rtrim($sb, '&');
$sb .= $signKey;
return md5($sb);
} catch (Exception $e) {
echo $e->getMessage();
return '';
}
}
$key = "b980d6f4c5c4485e9160d63155e22365";
$map = [
"merchantNo" => "mer618218",
"orderNo" => "2343243243432",
"orderAmt" => "10000",
"firstName" => "john",
"lastName" => "tom",
"payEmail" => "john.tom@gmail.com",
"payPhone" => "02012345678",
"productCode" => "11002",
"notifyUrl" => "http://www.xxx.com/notify"
];
$sign = sign($map, $key);
$map["sign"] = $sign;
?>