# 虚拟支付签名

# 支付请求签名(pay_sig)算法说明

pay_sig参数的签名算法,使用“mp-支付基础配置”中的AppKey对支付的请求进行签名 后台签名pay_sig代表请求经过开发者服务端的支付模块发起。签名算法伪代码为:

pay_sig = to_hex(hmac_sha256(app_key, uri + '&' + post_body))

可以参考以下python示例中的calc_pay_sig实现,其中: 如为后台api

  • uri为不带参数的API路径,如: /wxa/game/getbalance /wxa/game/pay /wxa/game/cancelpay /wxa/game/present
  • app_key为当前支付环境(env参数)对应的AppKey,从“mp-支付基础配置”处获取
  • post_body为该API(和uri对应)要求的原始http请求post的数据,参考具体接口的请求参数说明
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" pay_sig签名算法计算示例 """

import hmac
import hashlib
import json
import time

def calc_pay_sig(uri, post_body, appkey):
    """ pay_sig签名算法
      Args:
          uri       - 当前请求的支付API的uri部分,不带query_string
                      例如:/wxa/game/getbalance、/wxa/game/pay
          post_body - http POST的数据包体
          appkey    - 对应环境的AppKey
      Returns:
          支付请求签名pay_sig
    """
    need_sign_msg = uri + '&' + post_body
    pay_sig = hmac.new(key = appkey.encode('utf-8'), msg = need_sign_msg.encode('utf-8'),
                       digestmod=hashlib.sha256).hexdigest()
    return pay_sig

# uri,切记不可带参数,即去掉"?"及后面的部分
# 其他值如: /wxa/game/pay、/wxa/game/cancelpay、/wxa/game/present
uri = '/wxa/game/getbalance'

# 此处appkey为假设值,实际使用应根据支付环境(env参数)替换为对应的AppKey
appkey = "12345"

# 注意:JSON数据序列化结果,不同语言/版本结果可能不同
# 所以示例为了保证稳定性,直接用其中一个序列化的版本
# 实际使用时只需要保证,参与签名的post_body和真正发起http请求的一致即可
"""
# ts需要设置为当前unix timestap(秒级)
# 实际使用时可参考: int(time.time())
# 此处写死方便稳定复现算法
ts = 1668136271

# 不同接口要求的Post Body参数不一样,此处以getBalance接口为例(和uri对应)
post_body = json.dumps({
    "offer_id": "12345678",
    "openid": "oUrsfxxxxxxxxxx",
    "ts": ts,
    "zone_id": "1",
    "env": 0
})
"""
post_body = '{"offer_id": "12345678", "openid": "oUrsfxxxxxxxxxx", "ts": 1668136271, "zone_id": "1", "env": 0}'

# pay_sig签名计算(支付请求签名算法)
pay_sig = calc_pay_sig(uri, post_body, appkey)
print("pay_sig:", pay_sig)

# 若实际请求返回pay_sig签名不对,根据以下步骤排查:
# 1. 确认算法:uri、post_body、appkey写死以上参数,确保你的签名算法和示例calc_pay_sig结果完全一致
# 2. 确认参数:
#    - uri不可带参数(即"?"及后续部分全部舍去)
#    - post_body必须和真正发起HTTP请求的post body完全一致
#    - appkey必须是与请求中对应的环境匹配(env参数决定)
assert pay_sig == "11bac6388871d29c055c7d16fbe42e8d646855b666faf89b15c815218b1b23bd"

# 用户登录态签名(signature)签名算法

可以参考用户登录态签名,也可以参考随后python示例中calc_signature实现。

#!/usr/bin/python
# -*- coding: utf-8 -*-
""" pay_sig签名算法计算示例 """

import hmac
import hashlib
import json
import time

def calc_signature(post_body, session_key):
    """ 用户登录态signature签名算法
      Args:
          post_body   - http POST的数据包体
          session_key - 当前用户有效的session_key,参考auth.code2Session接口
      Returns:
          用户登录态签名signature
    """
    need_sign_msg = post_body
    signature = hmac.new(key = session_key.encode('utf-8'), msg = need_sign_msg.encode('utf-8'),
                       digestmod=hashlib.sha256).hexdigest()
    return signature

# 此处appkey为假设值,实际使用应根据支付环境(env参数)替换为对应的AppKey
appkey = "12345"

# 注意:JSON数据序列化结果,不同语言/版本结果可能不同
# 所以示例为了保证稳定性,直接用其中一个序列化的版本
# 实际使用时只需要保证,参与签名的post_body和真正发起http请求的一致即可
"""
# ts需要设置为当前unix timestap(秒级)
# 实际使用时可参考: int(time.time())
# 此处写死方便稳定复现算法
ts = 1668136271

# 不同接口要求的Post Body参数不一样,此处以getBalance接口为例(和uri对应)
post_body = json.dumps({
    "offer_id": "12345678",
    "openid": "oUrsfxxxxxxxxxx",
    "ts": ts,
    "zone_id": "1",
    "env": 0
})
"""
post_body = '{"offer_id": "12345678", "openid": "oUrsfxxxxxxxxxx", "ts": 1668136271, "zone_id": "1", "env": 0}'

# signature签名计算(用户登录态签名算法)
# session_key需要为当前用户有效session_key(参考auth.code2Session接口获取)
# 此处写死方便复现算法
session_key = "9hAb/NEYUlkaMBEsmFgzig=="
signature = calc_signature(post_body, session_key)
print("signature:", signature)

# 若实际请求返回signature签名不对,参考随后的“90010-signature签名错误问题排查思路”进行排查
assert signature == "42fe1d3341fb1c8bd6f5014ba735ab04eacc80a2deb3ab4669eab4700b5b6729"