# 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&currency_type=CNY&extension={“orderId”:3}&game_id=12ab4db987491&money=1&notify_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小时。
  • 对后台通知交互时,如果支付网关收到商户的应答不是成功或超时,支付网关认为通知失败,支付网关会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但支付网关不保证通知最终能成功

# 支付错误处理

# 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中对应的签名字段是否正确,如果确认正确,检查进行签名的私钥是否和提供快手的公钥是否匹配。