下单签名
业务场景
- 为了防止游戏的订单信息,在客户端传递过程中被恶意篡改
- CP创单时,需要在服务端生成订单签名,传给游戏客户端
- SDK创单时,会按照相同的规则,在SDK服务端检验签名(不涉及http交互)
注意事项
- 对接参数和demo包里的示例参数,不要混用
- amount字段,参与签名的值是大于0的正数,单位是分
- 下单签名,必须使用服务端的appKey,不要使用客户端的signKey
- md5前的拼接串末尾,不能带肉眼无法识别的“换行符”或“回车符”
流程图
参数说明
字段 | 类型 | 必需 | 说明 |
openId | string | 是 | SDK用户唯一标识 |
orderNo | string | 是 | CP订单号 |
amount | int | 是 | 下单金额,单位:分, 600代表6元 |
serverId | string | 是 | 游戏区服ID |
extend | string | 是 | CP订单号之外的扩展信息,内容格式为json,长度不超过1000字符 |
appKey | string | 是 | SDK的服务端key |
extend特别说明
- extend内容格式,必须是json字符串,整个字段的值参与下单签名
- extend的值,必须包含notifyUrl的键值对,其它键值对由游戏侧自定义,长度不超过1000字符
- 客户端的notifyUrl值,传AES加密有效回调地址的密文地址空白时无需加密,密钥是服务端appKey
- 有效的回调地址,必须是http开头的,比如:
https://pay.game.cn/notify
和http://pay.game.com/notify
- 游戏客户端上报 sign 和 extend 的值,都必须从游戏服务端获取
签名规则
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":""}