支付签名规范
前言
本文为 Game SVR 研发团队规范创建支付订单时两个签名的计算方式。玩家发起购买时,Game SVR 需要构造支付原串 signData,并基于同一份原串计算出两个签名:
- 支付态签名
paySig:证明该商品与价格由 Game SVR 授权,客户端无法伪造。 - 用户态签名
signature:证明该笔订单来自这个已登录用户,把订单与会话绑定。
两个签名的算法与待签名原文完全相同,唯一区别是密钥。三个产物(signData、paySig、signature)由 Game SVR 返回给游戏客户端,由客户端原样透传给 SUD SDK 发起支付。
本文只规范签名的计算与校验。下单接口的请求参数、响应结构与错误码,以及客户端如何把三个产物传给 SDK,见 sud.requestVirtualPayment;完整链路与轮询编排见支付流程。
本文的
signData是业务数据层面的签名对象,与服务端 API 签名加密的 HTTP 报文层加密签名是两套独立机制,请勿混用。
一、待签名串 signData 构造规范
signData 是一个 JSON 字符串,它既是两个签名的输入,也是需要随签名一起传输的字段。签名针对的是这个字符串的字节序列,因此构造规则必须逐字节对齐。本节是全文重点,验签失败绝大多数源于此。
1.1 字段定义与顺序
| 参数名 | 必填 | 类型 | 说明 |
|---|---|---|---|
| mode | 是 | string | 支付类型,道具直购固定为 goods |
| env | 是 | number | 环境配置,0-正式环境,1-沙箱环境 |
| productId | 是 | string | 商品 ID,须与运营后台道具配置一致 |
| quantity | 是 | number | 购买数量 |
| unitPrice | 是 | number | 商品单价,单位:分 |
| currency | 是 | string | 币种,如 CNY |
| outTradeNo | 是 | string | 业务订单号,由 Game SVR 生成,须全局唯一 |
| attach | 否 | string | 透传数据,发货回调时原样返回 |
1.2 字节一致性要求
用于计算签名的字符串,必须与最终传输的
signData字段值是完全相同的字节。
signData需原格式传递(包括其中的空格与换行),并且建议由 Game SVR 下发,不要由客户端拼装。严禁「先签名,再把 JSON 反序列化后重新序列化」——任何重新序列化都可能改变空格、键顺序或转义形式,导致验签失败。正确做法是:序列化一次得到字符串,用同一个字符串变量计算两个签名并传输。
二、支付态签名 paySig
paySig = to_hex( hmac_sha256( pay_sign_key, signData ) )| 项 | 取值 |
|---|---|
| 算法 | HMAC-SHA256 |
| 密钥 | pay_sign_key,在 SUD OpenPaaS 后台生成,每个游戏一个 |
| 待签名消息 | 第一节构造的 signData 字符串的 UTF-8 字节 |
| 输出编码 | 小写十六进制字符串 |
pay_sign_key 直接以其 UTF-8 字节作为 HMAC 密钥,不做任何编解码转换。密钥的生成与获取方式见支付密钥。
客户端在任何情况下都不应接触 pay_sign_key —— 这是支付态签名不可伪造的前提。一旦泄露,客户端即可自行构造任意商品与价格的合法签名。
三、用户态签名 signature
signature = to_hex( hmac_sha256( session_key, signData ) )密钥为该用户的 session_key,由 auth.code2session 在登录流程中返回。session_key 直接使用其 Base64 字符串本身作为 HMAC 密钥,不要做 Base64 解码。
用户态签名的完整算法规范、密钥处理方式的常见误区,以及 session_key 的有效性与失效处理,见开放数据校验与解密第三、四节。
注意:待签名消息与支付态签名完全相同,都是同一个 signData 字符串。请使用同一个字符串变量计算两次 HMAC,不要分别序列化两遍。
四、两个签名的职责划分
| 项 | 用户态签名 signature | 支付态签名 paySig |
|---|---|---|
| 密钥 | session_key | pay_sign_key |
| 密钥来源 | auth.code2session 返回 | SUD OpenPaaS 后台生成 |
| 密钥粒度 | 每个登录用户一个 | 每个游戏一个(全局) |
| 有效期 | 随会话失效 | 长期有效,直至后台轮换 |
| 证明的事实 | 该笔订单来自这个已登录用户 | 该商品与价格由 Game SVR 授权 |
| 算法 | HMAC-SHA256 | HMAC-SHA256 |
| 待签名原文 | 同一个 signData 字符串 | 同一个 signData 字符串 |
五、验签规则
SUD OP SVR 收到下单请求后:
- 取传输的
signData原始字符串,不做任何重新序列化。 - 用该用户会话的
session_key复算signature。 - 用该游戏的
pay_sign_key复算paySig。 - 两个签名都必须一致,任一不一致即拒绝下单。
验签通过不等于业务校验通过。 验签只证明报文未被篡改,productId、unitPrice、currency 是否与运营后台的道具配置一致属于独立的业务校验;发货前 Game SVR 还需对回调报文做三向比对,见虚拟支付配置 - 道具直购第 5 节。
六、安全规范
- 密钥不出服务端:
session_key与pay_sign_key严禁下发到客户端、写入前端代码或可被前端读取的配置,也不要输出到日志与错误信息中。建议使用 KMS 或密文配置中心托管。 - 签名必须在服务端生成:客户端不得参与
signData的拼装或签名计算,只负责原样透传三个产物。 - 启动时校验密钥已配置:HMAC 对空密钥不会报错,会产出一个格式合法的 64 位十六进制串。因此密钥缺失不会在签名阶段暴露,只会在验签阶段表现为「签名一直不对」。Game SVR 应在启动或首次使用时断言两个密钥非空。
- 签名比较使用恒定时间比较:在 Game SVR 自身需要校验签名的场合(如处理发货回调),使用
hmac.Equal、crypto.timingSafeEqual等恒定时间比较函数,不要用普通字符串相等判断。 - 订单号唯一与发货幂等:
outTradeNo由 Game SVR 生成并保证全局唯一。发货回调可能重复到达,需要注意保证幂等。
本文规范的是支付下单参数的签名。用户态签名的通用规范见开放数据校验与解密;HTTP 报文层的加密与签名见服务端 API 签名加密。三者是独立机制,请勿混用。
