Skip to content

支付签名规范 ​

前言 ​

本文为 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 ​

text
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 ​

text
signature = to_hex( hmac_sha256( session_key, signData ) )

密钥为该用户的 session_key,由 auth.code2session 在登录流程中返回。session_key 直接使用其 Base64 字符串本身作为 HMAC 密钥,不要做 Base64 解码。

用户态签名的完整算法规范、密钥处理方式的常见误区,以及 session_key 的有效性与失效处理,见开放数据校验与解密第三、四节。

注意:待签名消息与支付态签名完全相同,都是同一个 signData 字符串。请使用同一个字符串变量计算两次 HMAC,不要分别序列化两遍。

四、两个签名的职责划分 ​

项用户态签名 signature支付态签名 paySig
密钥session_keypay_sign_key
密钥来源auth.code2session 返回SUD OpenPaaS 后台生成
密钥粒度每个登录用户一个每个游戏一个(全局)
有效期随会话失效长期有效,直至后台轮换
证明的事实该笔订单来自这个已登录用户该商品与价格由 Game SVR 授权
算法HMAC-SHA256HMAC-SHA256
待签名原文同一个 signData 字符串同一个 signData 字符串

五、验签规则 ​

SUD OP SVR 收到下单请求后:

  1. 取传输的 signData 原始字符串,不做任何重新序列化。
  2. 用该用户会话的 session_key 复算 signature。
  3. 用该游戏的 pay_sign_key 复算 paySig。
  4. 两个签名都必须一致,任一不一致即拒绝下单。

验签通过不等于业务校验通过。 验签只证明报文未被篡改,productId、unitPrice、currency 是否与运营后台的道具配置一致属于独立的业务校验;发货前 Game SVR 还需对回调报文做三向比对,见虚拟支付配置 - 道具直购第 5 节。

六、安全规范 ​

  1. 密钥不出服务端:session_key 与 pay_sign_key 严禁下发到客户端、写入前端代码或可被前端读取的配置,也不要输出到日志与错误信息中。建议使用 KMS 或密文配置中心托管。
  2. 签名必须在服务端生成:客户端不得参与 signData 的拼装或签名计算,只负责原样透传三个产物。
  3. 启动时校验密钥已配置:HMAC 对空密钥不会报错,会产出一个格式合法的 64 位十六进制串。因此密钥缺失不会在签名阶段暴露,只会在验签阶段表现为「签名一直不对」。Game SVR 应在启动或首次使用时断言两个密钥非空。
  4. 签名比较使用恒定时间比较:在 Game SVR 自身需要校验签名的场合(如处理发货回调),使用 hmac.Equal、crypto.timingSafeEqual 等恒定时间比较函数,不要用普通字符串相等判断。
  5. 订单号唯一与发货幂等:outTradeNo 由 Game SVR 生成并保证全局唯一。发货回调可能重复到达,需要注意保证幂等。

本文规范的是支付下单参数的签名。用户态签名的通用规范见开放数据校验与解密;HTTP 报文层的加密与签名见服务端 API 签名加密。三者是独立机制,请勿混用。