# 6. 支付
# 6.1 功能介绍
# 前端样式
用户点击商品,可选择微信支付或者支付宝支付,支付成功后,快手渠道服务端通知游戏发货,游戏发放商品。
如果该游戏使用快手渠道优惠券功能,则在支付下单时,默认选择该用户可使用的优惠券。
# 6.2 整体流程
# 6.3 开发指南
# 6.3.1 sdk支付接口
//使用Kwaisdk 支付
/**
* 调起客户端支付
* @parmas KwaiPayInfo 支付相关数据
* @params KwaiPayResultListener 支付结果回调
*/
KwaiSdk.pay(KwaiPayInfo info, KwaiPayResultListener payListener) {}
// 支付结果回调
public interface KwaiPayResultListener {
//支付完成(从三方支付页面返回时回调,不代表支付成功,游戏需要去服务端验证支付结果
public void onPaySucceed(DataSucceed succeed);
//支付失败 部分由于网络原因需要做二次验证
//支付失败的errorcode中,3002代表支付失败,3003代表支付取消
public void onPayFailed(DataFailed failed);
}
支付信息KwaiPayInfo对象包含:
参数 | 类型 | 说明 |
---|---|---|
roleId | String | 游戏角色id(参与签名) |
roleName | String | 游戏角色名称(参与签名) |
roleLevel | String | 游戏角色等级(参与签名) |
serverId | String | 游戏大区id(参与签名) |
serverName | String | 游戏大区名称(参与签名) |
productId | String | 购买产品id(参与签名) |
productName | String | 购买产品名称(参与签名) |
productNum | int | 产品数量(不参与签名) |
productDesc | String | 购买产品描述(参与签名) |
price | int | 要用户付多少钱(单位分)(参与签名) |
currencyType | String | 货币类型(CNY)(参与签名) |
notifyUrl | String | 游戏服务器接收支付成功通知地址(https)(参与签名) |
userIp | String | 用户当前下单ip(123.1.1.1)(参与签名) |
extension | String | 附加内容,通知时原封不动带回(参与签名) |
orderId | String | 订单ID 同一订单只有第一次有效(参与签名) |
sign | String | RSA签名结果(不参与签名)必须由游戏服务端计算得出 |
// 创建支付参数
KwaiPayInfo info=new KwaiPayInfo();
// 必填 角色ID 参与签名
info.roleId="2000034";
// 必填 角色名称 参与签名
info.roleName="roleName";
// 必填 角色等级 参与签名
info.roleLevel="1";
// 必填 服务器ID 参与签名
info.serverId="1";
// 必填 服务器名称 参与签名
info.serverName="1";
// 必填 产品ID 参与签名
info.productId="201";
// 必填 产品名称 参与签名
info.productName="30钻石";
// 必填 产品描述 参与签名
info.productDesc="30钻石";
// 必填 要支付的总价 单位为分 参与签名
info.price=price;
// 必填 货币类型 目前只支持CNY 参与签名
info.currencyType="CNY";
// 必填 回调地址 (!!必须为https) 参与签名
info.notifyUrl=
"https://c.kuaishoupay.com/rest/n/pay/notify/allin";
// 必填 用户IP 参与签名 微信H5支付需要,由游戏服务端获取,[请参考微信建议的获取方式](https://pay.weixin.qq.com/wiki/doc/api/H5
// .php?chapter=15_5)
info.userIp="202.189.1.92";
// 选填 扩展信息 参与签名
info.extension="{\"orderId\":3}";
// 必填 由游戏服务端计算得出 不参与签名 可以参考demo
info.sign="";
// 选填 订单号属于唯一值,同一订单号只可以使用一次 参与签名 如果填充了这个,则服务器签名时需要添加third_party_trade_no字段
info.orderId="AI10321312321321312";
// 调用支付
KwaiSdk.pay(info,callback);
WARNING
注意:
a.支付签名务必在服务端完成
b.确认签名字段包含 appId、channel 、sdkUserId,且值是从SDK获取,并与当前登录用户一致
c.支付回调需要使用HTTPS,证书需要公网可验证
d.订单号为单次流程标志,同一订单号只能完成一次支付。确保每次发起支付订单号不重复。一次性订单的订单号可一致(如首冲)
e.关于签名
整个支付流程共有两套签名文件 游戏生成的:私钥A,公钥A;渠道sdk生成的:私钥B,公钥B
A:下单时:游戏使用私钥A 计算签名,调用支付接口 -> 渠道sdk使用 公钥A 校验签名。完成下单确认
B:支付回调时:渠道sdk使用私钥B 计算签名,调用回调接口 -> 游戏根据公钥B 完成签名校验。
确认支付的有效性
可以参考demo 里的 PayTestHelper 这个类
# 6.3.2 下单签名
下单支付都在客户端进行。服务端需要计算客户端下单参数的签名sign
签名规则:
签名方式 RSA,算法 SHA512WithRSA,密钥长度 4096
签名规则: 排除空字段(null和"") 然后 升序排列 k=v&k1=v1…
签名串示例:k1=v1&k2=v2&k3=v3
注:
下单签名方式与回调一致
下单计算签名使用游戏自己的私钥
支付回调验签使用快手提供的公钥
WARNING
注: 签名/验签时请务必排除空字段
参与签名的字段:
app_id -> 快手分配给游戏的appid,必传非空,应从服务器读取,非客户端传入
channel_id -> sdk客户端初始化返回的,可通过客户端SDK的 KwaiSdk.getChannel() 接口获取,必传非空
game_id -->登录获取的game_id,即快手分配给用户的唯一标识,必传非空
role_id -> 游戏角色 id,必传非空
role_name -> 游戏角色名称,必传非空
role_level -> 游戏角色等级,必传非空
server_id -> 游戏大区 id,必传非空
server_name -> 游戏大区名称,必传非空
product_id -> 购买产品 id,必传非空
product_name -> 购买产品名称,必传非空
product_desc -> 购买产品描述,必传非空
money -> 要用户付多少钱(单位分),必传非空
currency_type -> 货币类型(目前只支持CNY),必传非空
notify_url -> 游戏服务器接收支付成功通知地址(https) ,必传非空
extension -> 附加内容,通知时原封不动带回(字符串长度最长支持500,超过自己处理),必传非空
user_ip-->用户当前外网ip(通过游戏服务器获取),必传非空(请参考微信建议的获取方式:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_5) ß
third_party_trade_no-->如客户端传入了orderId(订单号)则服务器计算签名时需要添加third_party_trade_no字段
签名串示例:
app_id=ks12345678910&channel_id=ks¤cy_type=CNY&extension={“orderId”:3}&game_id=12ab4db987491&money=1¬ify_url=https://game.com/notify&product_desc=30钻石&product_id=201&product_name=30钻石&role_id=2000034&role_level=1&role_name=Guest-2000034&server_id=1&server_name=1&user_ip=219.143.153.133
# 6.3.3 游戏服务器回调接口
支付完成后,allinpayserver会根据生成订单时参数中的notifyUrl通知游戏服务器订单支付结果。
接口地址:生成订单时的notifyUrl
请求方法:post
MediaType:application/x-www-form-urlencoded
请求参数:
字段名 | 是否必传 | 说明 |
---|---|---|
app_id | 必传 | 游戏id |
role_id | 必传 | 角色id |
server_id | 必传 | 服务器id |
product_id | 必传 | 产品id |
money | 必传 | 金额 |
extension | 非必传 | 附加内容 |
allin_trade_no | 必传 | 订单号 |
data | 非必传 | 通知回调的扩展信息 |
notify_detail | 非必传 | 订阅信息等额外通知信息 |
sign | 必传 | 签名(不参与签名,一定要验签,点击查看签名方法) |
TIP
*除了sign,其他非空参数均参与签名
# 通知规则
- 通知频率及间隔:时限24小时,每1分钟扫描并通知一次
- 游戏服务端需要返回 success 代表处理(发货)成功,否则sdk server会持续通知游戏服务端
- 如果业务服务端返回给快手支付系统的字符是success这7个字符,支付网关任务业务已经根据通知消息更新业务订单成功,支付网关不再发送通知。如果业务方处理Notify成功(比如更新对应业务订单状态成功)之后,response Body返回success,标识通知成功;(返回的success不带引号. 带引号一般是springMVC处理后的情况,要注意下不要这么做)
- 如果业务方处理Notify失败,请返回其他值
- 如果业务服务端返回给快手支付系统的字符不是success这7个字符,快手支付系统会不断重发通知,直到超过24小时。
- 对后台通知交互时,如果支付网关收到商户的应答不是成功或超时,支付网关认为通知失败,支付网关会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但支付网关不保证通知最终能成功
# 支付错误处理
- 微信支付IP 错误(如图) 处理方法参考微信 IP 获取。 客户端 IP 获取 (opens new window)
# 6.4 FAQ
# 6.4.1 支付错误码
code | 描述 |
---|---|
1000 | 无网络 |
1005 | 无法找到前台页面,Activity 异常 {未注册、onActivityResult被禁用、Activity拉起失败} |
1023 | 调用太频繁,请稍后重试(短时间内重复、多次调用) |
3002 | 支付失败 |
3003 | 支付取消 |
3004 | 传入参数错误,不能为空 |
3016 | 账号未登录 |
3024 | 开启未成年禁止支付,支付账号为未成年用户,支付失败 |
1 | 支付完成(从三方支付页面返回时回调,不代表支付成功,游戏需要去服务端验证支付结果) |
100200500/100202000 | 检查3.3.3 获取用户信息并校验token 接口地址是否与文档一致 |
# 6.4.2 服务器异常
如果出现返回错误message是服务器异常,可以联系快手的运营看是否商户号已经开通,开通之后才能进行正常支付。
# 6.4.3 签名错误
先对比sdk提供的demo中对应的签名字段是否正确,如果确认正确,检查进行签名的私钥是否和提供快手的公钥是否匹配。