服务端签名
2020-02-12 17:17 更新
目标
签名方案只验证 服务端签名前的支付数据 和 tt.pay 发送到字节跳动后端的支付数据 的一致性(因而,具体字段有哪些,以及字段的值是否正确并不由签名验证)
生成签名规则
Key 筛选
去掉请求参数中的字节类型字段(如文件、字节流)、sign 与 risk_info 字段、value 为空的字段(某些接口请求参数中特殊标明不参与签名的字段也需要去掉)。
K-V 排序和拼接
把筛选后的 k-v 集合按照 key 的 ASCII 码升序排序,例如:
a, a1, abc, abcd, abce, abd, b1, ba
k-v 按照 key=value 的格式链接,并且按照 key 排序使用 & 连接成一个字符串,生成待签名字符串
MD5 加签
MD5(待签名字符串 + app_secret); // 待签名字符串 + app_secret 是字符串直接拼接,待签名字符串在前,中间没有连接符。
完整流程
例如下面是我们待验签的 orderInfo:
{
app_id: '800000000001',
merchant_id: '1900000001',
timestamp: 1570694312,
sign_type: 'MD5',
out_order_no: '201900000000000001',
total_amount: 1,
product_code: 'pay',
payment_type: 'direct',
trade_type: 'H5',
version: '2.0',
currency: 'CNY',
subject: '测试订单',
body: '测试订单',
uid: '0000000000000001',
trade_time: 1570585744,
valid_time: 300,
notify_url: '',
risk_info: '{"ip":"120.230.0.0"}',
wx_type: 'MWEB',
wx_url: 'https://wx.tenpay.com/xxx',
alipay_url: 'app_id=2019000000000006&biz_content=xxxx'
}
则待签名字符串 unsigned_str 为:
alipay_url=app_id=2019000000000006&biz_content=xxxx&app_id=800000000001&body=测试订单¤cy=CNY&merchant_id=1900000001&out_order_no=201900000000000001&payment_type=direct&product_code=pay&sign_type=MD5&subject=测试订单×tamp=1570694312&total_amount=1&trade_time=1570585744&trade_type=H5&uid=0000000000000001&valid_time=300&version=2.0&wx_type=MWEB&wx_url=https://wx.tenpay.com/xxx
若你的 app_secret 是 'a',那么带入上述 unsigned_str,最终 MD5(unsigned_str + app_secret) 后签名字符串 signed_str 应为:
0f1e3358a9898d7c4c6c23740251808a
验证:你可以在你的验签方法里用上面的 orderInfo 和 app_secret 数据测试,验证与我们给出的 unsigned_str 和 signed_str 是否一致保持一致:通过上述方式生成签名 sign 之后,将该 sign 字段挂在 orderInfo 上并最终透传至前端 tt.pay,传递过程中请不要增删或修改任何字段(保持服务端签名前的支付数据和 tt.pay 发送到字节跳动后端的支付数据一致),否则会导致签名检验不通过
签名代码示例(以 Node 为例)
const crypto = require("crypto");
const createSign = (params, appSecret) => {
const paramKeys = Object.keys(params).sort();
let signStr = "";
paramKeys.forEach(key => {
let value = params[key];
// 空值,risk_info, sign 不参与签名
if (!value || ["risk_info", "sign"].indexOf(key) >= 0) {
return;
}
if (signStr.length > 0) {
signStr += "&";
}
if (typeof value === "object") {
value = JSON.stringify(value);
}
signStr += key + "=" + value;
});
signStr += `${appSecret}`;
console.log("signStr:", signStr);
const md5 = crypto.createHash("md5");
const sign = md5.update(signStr).digest("hex");
return sign;
};
签名验证失败排查方案
- 服务端:服务端生成签名方式可能有误,请参考上方「完整流程」中的 orderInfo 与 app_secret 代入你的签名方法,验证与我们给出的预期 unsigned_str 和 signed_str 是否一致,不一致将导致签名验证失败
- 小程序端:请保证带有 sign 签名的 orderInfo 由服务端返回且与 tt.pay 中传入的 orderInfo 完全一致,不一致将导致签名验证失败
- 反馈:若上面两者已经确认无误,但签名验证依然无法通过,请在开发者社区反馈,反馈中请提供线上报 CD0025 失败订单的 unsigned_str
各语言签名实现
可参考以下语言 Demo 中签名的实现
以上内容是否对您有帮助:
← 接入流程
更多建议: