# 支付类事件订阅
# 功能介绍
针对小游戏的后台状态变更事件,平台为其中一部分事件提供了自助订阅的能力。开发者订阅后可以通过消息通道收到对应的事件通知。
# 流程接入
开发者可以进入事件订阅页面,查看相关的订阅事件和对应的接入文档
按对应事件的文档说明接入完成,确保消息接收模块扩容了适当的容量。点击订阅,便可以进入模拟推送环节
点击模拟推送,此时会按对应事件规定的消息格式发送一条模拟的消息内容(消息内容相关字段为随机值)给开发者,若回包格式正确,则测试状态转为测试通过,再次点击订阅则完成订阅。完成订阅后5分钟左右,若有相关事件产生,则开始真实事件推送。
# 开发指引
订阅事件主动通知基于微信小游戏统一的消息推送能力,因此在订阅事件时,需要开发者服务器处理好消息推送的协议。
注意:MP配置消息回调地址只有1个,所有消息推送场景都会使用该通道,所以需要先根据MsgType
与Event
来判断消息类型,进行相应的处理。本文档只介绍订阅事件相关的消息处理协议,其他消息参考对应文档说明。对未测试验证完成的消息(<MsgType,Event>)建议开发者在现网增加一个白名单转发测试环境的开关,这样可以先把未测试完成的消息转回测试环境处理,方便快速开发和调试。
订阅事件处理流程:
1)检测到非“模拟推送”(IsMock为false),按真实业务逻辑进行处理
2)检测到当前为“模拟推送”(IsMock为true),则仅检查字段类型,不要执行其他逻辑
不管模拟推送还是非模拟推送,最后的回包均需要按对应的事件类型定义的回包格式返回。
# 接入消息推送能力
若之前已接入跳过该步骤。 接入文档可查阅:消息推送
# 消息协议
所有的订阅事件消息按如下格式,只是不同Event类型对应Payload消息结构不一样
# 请求参数
字段 | 类型 | 说明 |
---|---|---|
CreateTime | Number | 消息发送时间 |
MsgType | String | 消息类型,事件类通知固定为:event |
Event | String | 事件类型 (1)代币发货完成通知 minigame_coin_deliver_completed |
MiniGame | Object | 具体通知内容 |
MiniGame
字段 | 类型 | 说明 |
---|---|---|
IsMock | Bool | 是否是模拟推送 |
其余字段根据事件类型区分,详情请见下述文档 |
# 支付类订阅事件
# 支付类订阅事件签名算法说明
pay_event_sig
参数的签名算法,使用“mp-支付基础配置”中的AppKey对支付的请求进行签名,代表请求经过开发者服务端的支付模块发起。签名算法伪代码为:
pay_event_sig = to_hex(hmac_sha256(app_key, event + '&' + payload))
可以参考以下python示例中的calc_pay_event_sig
实现,其中:
event
为推送的事件类型,如:minigame_coin_deliver_completed
app_key
为当前支付环境(env参数)对应的AppKey,从“mp-支付基础配置”处获取payload
为本次推送的数据,对应minigame结构内的payload,参考具体推送的请求参数说明
以调用minigame_deliver_h5_pay_products
接口为例,签名计算如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" PayEventSig签名算法计算示例 """
import hmac
import hashlib
def calc_pay_event_sig(event, payload, appkey):
""" pay_event_sig签名算法
Args:
event - 事件类型 例如 minigame_coin_deliver_completed
payload - 事件对应包体,通知消息中的payload 例如{"OpenId":"to_user_openid","OutTradeNo":"xxxxxxx","WeChatPayInfo":{"MchOrderNo":"xxxxxxx","TransactionId":"xxxxxxx"},"Env":0,"CoinInfo":{"ZoneId":"1","TotalPrice":100,"BuyQuantity":1,"OrigPrice":100}}
appkey - 对应环境的AppKey
Returns:
支付请求签名pay_event_sig
"""
need_sign_msg = event + '&' + payload
pay_sig = hmac.new(key=appkey.encode('utf-8'), msg=need_sign_msg.encode('utf-8'),
digestmod=hashlib.sha256).hexdigest()
return pay_sig
# 游戏币发货完成事件(Event=minigame_coin_deliver_completed)
注意事项
游戏币的发货在米大师侧完成,充值完成后米大师会给虚拟游戏币账号增加相应比例的游戏币数量,增加完成后触发此事件的推送。若开发者需要更及时感知游戏币是否发货完成,可以订阅该事件。同时建议保持之前的定期查询游戏币账号余额的逻辑,通过接收推送和主动查询双向来保证更及时处理支付业务逻辑,不要以outTradeNo对应的下单时的道具信息直接发货,最终需以扣除游戏币成功做为依据。
若通过小游戏的前端版本变更给wx.requestMidasPayment增加了
outTradeNo
参数,则势必存在一个版本覆盖过程,也就是同时存在不传此参数和传了此参数的支付请求,开发者的事件订阅模块需要正确处理好此类事件:比如outTradeNo
关联不上业务单,则仅认为对应openid
发生了充值行为,可以去查询余额变动
wx.requestMidasPayment新增一个参数:outTradeNo
。对外文档暂时未说明,实际上已支持该参数。
字段 | 类型 | 说明 |
---|---|---|
outTradeNo | String | 业务订单号,每个订单号只能使用一次,重复使用会失败。开发者需要确保该订单号在对应游戏下的唯一性,平台会尽可能校验该唯一性约束,但极端情况下可能会跳过对该约束的校验。 要求32个字符内,只能是数字、大小写字母、符号| _-*@ 组成,不能以下划线(_)开头。建议每次调用wx.requestMidasPayment都换新的 outTradeNo 。若没有传入,则平台会自动填充一个,并以下划线开头 |
# 请求参数
字段 | 类型 | 说明 |
---|---|---|
CreateTime | Number | 消息发送时间 |
MsgType | String | 消息类型,事件类通知固定为:event |
Event | String | 事件类型 其中(1)代(1)代币发货完成通知 minigame_coin_deliver_completed |
MiniGame | Object | 具体通知内容 |
MiniGame
字段 | 类型 | 说明 |
---|---|---|
Payload | String | 携带的具体内容,因为需要使用支付密钥进行签名,因此格式固定为JSON序列化的结果,与消息推送时配置的格式类型无关。即哪怕配置了XML格式,此处的内容也仍然是JSON序列化的结果。 具体JSON定义见随后的 Payload 说明。 |
PayEventSig | String | 见下文支付请求签名算法说明(PayEventSig ) |
IsMock | Bool | 是否是模拟数据,模拟数据完全随机,仅用于校验格式。 |
Payload
字段 | 类型 | 说明 |
---|---|---|
OpenId | String | 用户openid |
OutTradeNo | String | 业务订单号 |
WeChatPayInfo | Object | 微信支付信息非微信支付渠道可能没有 |
Env | Number | 环境配置 0:现网环境(也叫正式环境) 1:沙箱环境 |
CoinInfo | Object | 游戏币信息 |
WeChatPayInfo
字段 | 类型 | 说明 |
---|---|---|
MchOrderNo | String | 微信支付商户单号 |
TransactionId | String | 交易单号(微信支付订单号) |
CoinInfo
字段 | 类型 | 说明 |
---|---|---|
ZoneId | String | 分区id |
ActualPrice | Number | 实际支付价格(分) |
BuyQuantity | Number | 购买数量 |
OrigPrice | Number | 原始价格(分) |
# 返回参数
字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
ErrCode | Number | 是 | 发送状态。0:成功,其他:失败 |
ErrMsg | String | 否 | 错误原因,用于调试。在errcode非0 的情况下可以返回 |
# XML格式示例
请求
<xml>
<CreateTime>1583202606</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[minigame_coin_deliver_completed]]></Event>
<MiniGame>
<Payload>{"OpenId":"to_user_openid","OutTradeNo":"xxxxxxx","WeChatPayInfo":{"MchOrderNo":"xxxxxxx","TransactionId":"xxxxxxx"},"Env":0,"CoinInfo":{"ZoneId":"1","TotalPrice":100,"BuyQuantity":1,"OrigPrice":100}}</Payload>
<PayEventSig>f749f67b751fa80f27ddc0b7c8d2821aeda162ea22b323cd64a2c8056c2736f0</PayEventSig>
<IsMock>true</IsMock>
</MiniGame>
</xml>
成功返回
<xml>
<ErrCode>0</ErrCode>
<ErrMsg>Success</ErrMsg>
</xml>
失败返回
<xml>
<ErrCode>99999</ErrCode>
<ErrMsg>internal error</ErrMsg>
</xml>
# JSON格式示例
请求
{
"CreateTime": 1583202606,
"MsgType": "event",
"Event": "minigame_coin_deliver_completed",
"MiniGame": {
"Payload": "{\"OpenId\":\"to_user_openid\",\"OutTradeNo\":\"xxxxxxx\",\"WeChatPayInfo\":{\"MchOrderNo\":\"xxxxxxx\",\"TransactionId\":\"xxxxxxx\"},\"Env\":0,\"CoinInfo\":{\"ZoneId\":\"1\",\"TotalPrice\":100,\"BuyQuantity\":1,\"OrigPrice\":100}}",
"PayEventSig": "f749f67b751fa80f27ddc0b7c8d2821aeda162ea22b323cd64a2c8056c2736f0"
}
}
成功返回
{"ErrCode":0,"ErrMsg":"Success"}
失败返回
{"ErrCode":99999,"ErrMsg":"internal error"}
# 退款成功事件(Event=minigame_pay_refund_succ_notify)
# 请求参数
字段 | 类型 | 说明 |
---|---|---|
CreateTime | Number | 消息发送时间 |
MsgType | String | 消息类型,事件类通知固定为:event |
Event | String | 事件类型 (2)退款成功通知 minigame_pay_refund_succ_notify |
MiniGame | Object | 具体通知内容 |
MiniGame
字段 | 类型 | 说明 |
---|---|---|
Payload | String | 携带的具体内容,因为需要使用支付密钥进行签名,因此格式固定为JSON序列化的结果,与消息推送时配置的格式类型无关。即哪怕配置了XML格式,此处的内容也仍然是JSON序列化的结果。 |
PayEventSig | String | 见下文支付请求签名算法说明(PayEventSig ) |
IsMock | Bool | 是否是模拟数据,模拟数据完全随机,仅用于校验格式。 |
Payload
字段 | 类型 | 说明 |
---|---|---|
RefundId | String | 小游戏退款单号 |
RefundAmount | Number | 退款金额,单位分 |
RefundSource | Number | 退款来源 1:平台 2:MP提交 3:API提交 |
Env | Number | 环境配置 0:现网环境(也叫正式环境) 1:沙箱环境 |
OutTradeNo | String | 业务订单号 |
WeChatPayInfo | Object | 退款单对应支付订单的微信支付信息非微信支付渠道可能没有 |
WeChatPayInfo
字段 | 类型 | 说明 |
---|---|---|
MchOrderNo | String | 微信支付商户单号 |
TransactionId | String | 交易单号(微信支付订单号) |
# 返回参数
字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
ErrCode | Number | 是 | 发送状态。0:成功,其他:失败 |
ErrMsg | String | 否 | 错误原因,用于调试。在errcode非0 的情况下可以返回 |
# XML格式示例
请求
<xml>
<CreateTime>1583202606</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[minigame_pay_refund_succ_notify]]></Event>
<MiniGame>
<Payload>{"RefundId":"refund_id","RefundAmount":100,"RefundSource":1,"Env":0, "WeChatPayInfo":{"MchOrderNo":"xxxxxxx","TransactionId":"xxxxxxx"}}</Payload>
<PayEventSig>f749f67b751fa80f27ddc0b7c8d2821aeda162ea22b323cd64a2c8056c2736f0</PayEventSig>
<IsMock>true</IsMock>
</MiniGame>
</xml>
成功返回
<xml>
<ErrCode>0</ErrCode>
<ErrMsg>Success</ErrMsg>
</xml>
失败返回
<xml>
<ErrCode>99999</ErrCode>
<ErrMsg>internal error</ErrMsg>
</xml>
# JSON格式示例
请求
{
"CreateTime": 1583202606,
"MsgType": "event",
"Event": "minigame_pay_refund_succ_notify",
"MiniGame": {
"Payload": "{\"RefundId\":\"refund_id\",\"RefundAmount\":100,\"RefundSource\":1,\"Env\":0,\"WeChatPayInfo\":{\"MchOrderNo\":\"xxxxxxx\",\"TransactionId\":\"xxxxxxx\"}}",
"PayEventSig": "f749f67b751fa80f27ddc0b7c8d2821aeda162ea22b323cd64a2c8056c2736f0"
}
}
成功返回
{"ErrCode":0,"ErrMsg":"Success"}
失败返回
{"ErrCode":99999,"ErrMsg":"internal error"}
# 注意事项
12小时内总共会发起13次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h,