请求签名校验

1.签名前准备

假设所有发送或者接收到的参数为集合P,将集合P内的非空参数按照参数名的ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串S。 当value类型为JSON时,继续按照JSON key升序排序,将结果添加到排序后的字符串中。当value类型为JSON数组时,逐个获取元素,然后按照JSON key升序排序,将结果添加到排序后的字符串中。

特别注意以下重要规则:

  1. 参数名ASCII码从小到大排序(字典序)。

  2. 参数名区分大小写。

  3. 集合P内的空值null和空字符串(””)都不参与加签。

  4. 验证调用返回或 EtsyPay 主动通知签名时,传送的sign参数不参与签名,验证时将生成的签名与该sign值作校验。

  5. sign值区分大小写,统一使用小写。

2.加签

在字符串S最后拼接上key(EtsyPay给商户分配的密钥),得到字符串signTemp并对signTemp进行MD5运算,最终得到完成加签的值signValue。

3.加签示例(商户请求参数加签)

假设传送的参数如下:

params = {
        "tradeNo": "10012021010314463575400004",
        "merchantId": "153311",
        "customer": {
            "phone": "0818064342",
            "name": "jack"
        },
        "item": [{
            "id": 100114,
            "product": "test1",
            "amount": 5000
        },{
            "id": 100117,
            "product": "test2",
            "amount": 10000
        }]
}

商户密钥为:merchant-key。

首先按照最外层从小到大排序:

1."customer":{"phone": "0818064342","name": "jack"}
2."item":[{"id": 100114,"product": "test1","amount": 5000},{"id": 100117,"product": "test2","amount": 10000}]
3."merchantId":"153311"
4."tradeNo":"10012021010314463575400004"

对第1部分customer进行排序,其类型是JSON,按照JSON key进行升序排序得到字符串:

name=jack&phone=0818064342

对第2部分item进行排序,其类型是JSON数组,逐个获取元素,然后按照JSON key升序排序并拼接到最后得到字符串:

name=jack&phone=0818064342&amount=5000&id=100114&product=test1&amount=10000&id=100117&product=test2

将第3、4部分转换成key=value格式并拼接到最后得到字符串:

name=jack&phone=0818064342&amount=5000&id=100114&product=test1&amount=10000&id=100117&product=test2&merchantId=153311&tradeNo=10012021010314463575400004

生成签名

S = "name=jack&phone=0818064342&amount=5000&id=100114&product=test1&amount=10000&id=100117&product=test2&merchantId=153311&tradeNo=10012021010314463575400004";
获取签名(其中获取字符串md5值方法为:MD5() ):
signTemp = S + "merchant-key"
sign = MD5(signTemp) //加签后的值"e756a16616c82b0e9f63f83217165de9"

组装请求参数

params = {
        "tradeNo": "10012021010314463575400004",
        "merchantId": "153311",
        "customer": {
            "phone": "0818064342",
            "name": "jack"
        },
        "item": [{
            "id": 100114,
            "product": "test1",
            "amount": 5000
        },{
            "id": 100117,
            "product": "test2",
            "amount": 10000
        }],
        "sign": "e756a16616c82b0e9f63f83217165de9"
}

4.验签示例(商户接受 EtsyPay 服务端回调通知或请求响应)

注意:签名规则同上,sign参数不参与签名。 如回调请求为:

{
    "amount":"325000",
    "merchantId":"100011",
    "orderNo":"CTP92523920220104002031",
    "payState":"00",
    "returnCode":"200",
    "returnMsg":"success",
    "sign":"eb610f4e17a1f3041c10b5b4d258bef6",
    "tradeNo":"10012021010323203164700003",
    "type":1
}

则需验签的参数如下:

{
    "amount":"325000",
    "merchantId":"100011",
    "orderNo":"CTP92523920220104002031",
    "payState":"00",
    "returnCode":"200",
    "returnMsg":"success",
    "tradeNo":"10012021010323203164700003",
    "type":1
}

商户密钥: key = merchant-key

对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:

S = "amount=325000&merchantId=100011&orderNo=CTP92523920220104002031&payState=00&returnCode=200&returnMsg=success&tradeNo=10012021010323203164700003&type=1";
获取签名(其中获取字符串md5值方法为:MD5() ):
signTemp= S + "merchant-key"
newSign = MD5(signTemp)//加签后的值"eb610f4e17a1f3041c10b5b4d258bef6"

比对签名 判断响应或者请求参数sign是否等于newSign,是则验签通过。

5.签名代码样例

public static String createSign(JSONObject jsonObject, String merchantKey){
        try {
            List<String> keyValuePairs = new ArrayList<>();
            Map<String, Object> sortedMap = new TreeMap<>(jsonObject);

            for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
    
                if (value instanceof JSONObject) {
                    JSONObject innerJsonObject = (JSONObject) value;
                    String innerString = convertToString(innerJsonObject);
                    keyValuePairs.add(innerString);
                } else if (value instanceof JSONArray) {
                    JSONArray jsonArray = (JSONArray) value;
                    List array = new ArrayList();
                    for (Object item : jsonArray) {
                        if (item instanceof JSONObject) {
                            JSONObject innerJsonObject = (JSONObject) item;
                            String innerString = convertToString(innerJsonObject);
                            keyValuePairs.add(innerString);
                        } else {
                            array.add(item);
                        }
                    }
    
                    if (array.size() > 0) {
                        Object[] items = array.toArray();
                        Arrays.sort(items);
                        String result = StringUtils.join(items, ",");
                        if (StringUtils.isNotEmpty(result)) {
                            keyValuePairs.add(key + "=" + result);
                        }
                    }
                } else {
                    String stringValue = Objects.toString(value, "");
                    if (StringUtils.isNotEmpty(stringValue)) {
                        keyValuePairs.add(key + "=" + stringValue);
                    }
                }
            }

            String waitingSignStr = String.join("&", keyValuePairs);
            sign = MD5Util.getMD5String(waitingSignStr + merchantKey);//MD5加密
            return sign;
        } catch (Exception e) {
            return null;
        }
    }
    

Last updated