下单签名

业务场景

  1. 为了防止游戏的订单信息,在客户端传递过程中被恶意篡改
  2. CP创单时,需要在服务端生成订单签名,传给游戏客户端
  3. SDK创单时,会按照相同的规则,在SDK服务端检验签名(不涉及http交互)

注意事项

  1. 对接参数和demo包里的示例参数,不要混用
  2. amount字段,参与签名的值是大于0的正数,单位是分
  3. 下单签名,必须使用服务端的appKey,不要使用客户端的signKey
  4. md5前的拼接串末尾,不能带肉眼无法识别的“换行符”或“回车符”

流程图

参数说明

字段类型必需说明
openIdstringSDK用户唯一标识
orderNostringCP订单号
amountint下单金额,单位:分, 600代表6元
serverIdstring游戏区服ID
extendstringCP订单号之外的扩展信息,内容格式为json,长度不超过1000字符
appKeystringSDK的服务端key

extend特别说明

  1. extend内容格式,必须是json字符串,整个字段的值参与下单签名
  2. extend的值,必须包含notifyUrl的键值对,其它键值对由游戏侧自定义,长度不超过1000字符
  3. 客户端的notifyUrl值,传AES加密有效回调地址的密文地址空白时无需加密,密钥是服务端appKey
  4. 有效的回调地址,必须是http开头的,比如:https://pay.game.cn/notifyhttp://pay.game.com/notify
  5. 游戏客户端上报 signextend 的值,都必须从游戏服务端获取

签名规则

String sign = md5("amount|extend|openId|orderNo|serverId|appKey");

  顺序固定,appKey拼接在末尾,参数间用英文竖线分隔,生成md5值字母小写

调试示例

import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import org.apache.commons.lang3.StringUtils;

public class OrderExample {

    public static String getOrderSign(Long amount, String orderNo, String openId, String serverId, String extend, String appKey) {
        String delimiter = "|";
        String jointStr = String.join(delimiter, amount.toString(), extend, openId, orderNo, serverId, appKey);
        System.out.println(String.format("签名前的拼接字符串:%s", jointStr));
        String sign = SecureUtil.md5(jointStr);
        System.out.println(String.format("客户端要的下单签名:%s%s", sign, "\r\n"));
        return sign;
    }

    public static String getAesUrl(String notifyUrl, String areaId, String appKey) {
        System.out.println(String.format("参与签名的extend值:%s", getExtend(notifyUrl, areaId)));
        String cipherUrl = StringUtils.EMPTY;
        if (StringUtils.isNotEmpty(notifyUrl) && notifyUrl.startsWith("http")) {
            cipherUrl = SecureUtil.aes(appKey.getBytes()).encryptHex(notifyUrl);
        }
        System.out.println(String.format("客户端要的extend值:%s", getExtend(cipherUrl, areaId)));
        return cipherUrl;
    }

    public static String getExtend(String url, String areaId) {
        JSONObject extJson = new JSONObject();
        extJson.set("notifyUrl", url);
        extJson.set("areaId", areaId);
        return extJson.toString();
    }

    public static void main(String[] args) {
        String appKey = "AaBbCcDdEeFfGgHh";
        Long amount = 600L;
        String orderNo = "4012250_1731407616710_998";
        String openId = "12345678912345678912345";
        String serverId = "4012250";

        String notifyUrl = "http://pay.test.com/yiwan/notify";
        String areaId = "8_3,9_1$9";
        String extend1 = getExtend(notifyUrl, areaId);

        getOrderSign(amount, orderNo, openId, serverId, extend1, appKey);

        getAesUrl(notifyUrl, areaId, appKey);
    }
}

notifyUrl值是有效地址时

签名前的拼接字符串:600|{"areaId":"8_3,9_1$9","notifyUrl":"http://pay.test.com/yiwan/notify"}|12345678912345678912345|4012250_1731407616710_998|4012250|AaBbCcDdEeFfGgHh
客户端要的下单签名:d25faed4d920a9329fafabfadb6337c9

参与签名的extend值:{"areaId":"8_3,9_1$9","notifyUrl":"http://pay.test.com/yiwan/notify"}
客户端要的extend值:{"areaId":"8_3,9_1$9","notifyUrl":"82df1fae9c8a5c50adb07ca533fab0af216cf67af32b24548f557ae71073b7630ef6f9c73b606b7f4ff16913e7b9c2b6"}

  notifyUrl值是空白地址时

签名前的拼接字符串:600|{"areaId":"8_3,9_1$9","notifyUrl":""}|12345678912345678912345|4012250_1731407616710_998|4012250|AaBbCcDdEeFfGgHh
客户端要的下单签名:cb340c9f3244d64a16190d087344015e

参与签名的extend值:{"areaId":"8_3,9_1$9","notifyUrl":""}
客户端要的extend值:{"areaId":"8_3,9_1$9","notifyUrl":""}