西瓜SDK 支付通知接口

1. 文档概述

此文档是西瓜SDK支付通知接口接入文档。包括如下2个接口:

  1. 支付通知接口
  2. 二次查询验证订单接口

西瓜订单服务器在收到渠道的订单成功支付通知时,会主动调用支付通知URL通知游戏服务器订单支付信息。 游戏服务器在接收到支付通知时,应再次调用西瓜提供的订单查询接口,验证订单信息的一致性,否则一旦服务端密钥泄漏,就有可能遭受伪造订单通知攻击。
注意: 如游戏无支付要求,则无需接入。

1.1 文档结构

  1. 文档概述
  2. 支付通知接口
  3. 二次查询验证订单
  4. 服务端接入样例代码

2. 支付通知接口(通知游戏支付结果)

2.1 功能

发起方西瓜SDK服务端
接收方游戏服务器
接口类型HTTP POST Content-Type: application/json;charset=UTF-8
字符集编码UTF-8
安全机制签名
请求地址游戏方提供URL,西瓜SDK服务器主动调用
功能描述当接收到渠道的支付结果回调信息后,西瓜SDK服务端会对订单支付信息进行确认,确认后,西瓜SDK服务端会将订单支付结果信息推送到游戏服务器提供的支付订单回调地址。

2.2 支付通知参数

参数说明: 参数为一个JSON字符串

参数参数类型最大长度说明必须
typeString32接口类型,固定为notify-game
xgAppIdString64西瓜SDK分配给游戏的唯一ID 去西瓜Web控制台查看我的xgAppId
channelIdString32渠道ID
uidString128用户ID
zoneIdString32游戏区编号
serverIdString32游戏服编号
roleIdString32角色编号
roleNameString64角色名称
roleLevelString32角色等级
roleVipLevelString32角色VIP等级
currencyNameString64支付货币名称
productIdString64商品编号
productNameString64商品名称
productDescString128商品描述
productQuantityint10商品数量
productUnitPriceint10商品单价(单位分)
totalAmountint10总面额(单位分)
paidAmountint10总支付金额(单位分)
customInfoString2000游戏方创建订单时自定义字段,透传给游戏服务器
tsString14当前时间戳,秒级,如20150723150028 对应2015/7/23 15:00:28
gameTradeNoString64游戏订单号
signString40签名,签名算法参见签名章节,使用游戏服务端密钥
tradeNoString64西瓜订单号
paidTimeString14支付时间 yyyyMMddHHmmss
payStatusString1订单支付状态 1 支付成功 2 支付失败
payTypeString20订单支付类型, 内购没有此字段,网页支付:WebPay
extString2000 ios 支付订阅扩展参数

2.3 返回结果

游戏服务器处理完支付通知后,应返回如下格式的JSON字符串:

参数说明
code返回码,0代表成功。详情参考错误码章节
msg接口调用信息提示

2.4 通知样例

请求参数:
type=notify-game
xgAppId=2018
channelId=mi
uid=mi__3099245
zoneId=1
serverId=1
roleId=224455
roleName=八神
roleLevel=42
roleVipLevel=8
currencyName=CNY
productId=com.mygame.diamond600
productName=600钻石
productDesc=6元购买600钻石
productQuantity=600
totalAmount=600
paidAmount=600
customInfo=foo
gameTradeNo=20160325000001
tradeNo=31602f1000000001
paidTime=20150723145928
payStatus=1
ts=20150723150028 //当前时间戳
payType=WebPay
ext={"cancellationDate": "20160901201417","expiresDate": "20160901201417","isSandbox": true,"originalTradeNo": "016q2f1000303885"}

游戏服务端密钥: aca57f8a6c494a36a516e5c282c4db87去西瓜Web控制台查看我的游戏服务端密钥XgServerKey

则请求签名源串为: channelId=mi&currencyName=CNY&customInfo=foo&ext={"cancellationDate": "20160901201417","expiresDate": "20160901201417","isSandbox": true,"originalTradeNo": "016q2f1000303885"}&gameTradeNo=20160325000001&paidAmount=600&paidTime=20150723145928&payStatus=1&productDesc=6元购买600钻石&productId=com.mygame.diamond600&productName=600钻石&productQuantity=600&roleId=224455&roleLevel=42&roleName=八神&roleVipLevel=8&serverId=1&totalAmount=600&tradeNo=31602f1000000001&ts=20150723150028&type=notify-game&uid=mi__3099245&xgAppId=2018&zoneId=1
(注意:请动态拼接所有参数(方便以后添加新参数时自动加入签名),所有非空的参数都参与签名,值为空的参数不参与签名,按参数名字升序排列)

签名为:
60ebcd07edf4e0563c8632c53be5af6df07f3400

请求样例:
http://172.63.55.62:18888/moon/pay
POST Body:
{"type":"notify-game", "xgAppId":"2018", "channelId":"mi", "uid":"mi__3099245", "zoneId":"1", "serverId":"1", "roleId":"224455", "roleName":"八神", "roleLevel":"42", "roleVipLevel":"8", "currencyName":"CNY", "productId":"com.mygame.diamond600", "productName":"600钻石", "productDesc":"6元购买600钻石", "productQuantity":"600", "totalAmount":"600", "paidAmount":"600", "customInfo":"foo", "gameTradeNo":"20160325000001", "tradeNo":"31602f1000000001", "paidTime":"20150723145928", "payStatus":"1", "ts":"20150723150028", "ext":"{\"cancellationDate\": \"20160901201417\",\"expiresDate\": \"20160901201417\",\"isSandbox\": true,\"originalTradeNo\": \"016q2f1000303885\"}","sign":"4873560491111c3f719dada104a0b055e2531d8f"}

2.5 返回结果样例

{
    "code": "0",
    "msg": "success"
}

2.6 错误码

错误码 备注
0 成功
-1 签名失败
1 请求重发,表示游戏服前置机收到xg服务器通知,但是由于游戏服务器正在升级,不能处理响应,请求延后重新发送
2 重复订单,表示游戏服务器之前已收到了同样订单的通知,为避免因网络等原因导致道具或者游戏代币重复到账,建议游戏做订单排重
-2 xgAppId不存在
-3 channelId不存在
-4 区服不存在
-5 账号不存在
-6 订单号不存在
-98 请求参数疑似被篡改
-99 服务器内部错误

2.7 签名和验签

签名算法采用HmacSHA1

  1. 参数name按字母升序排序(区分大小写,大写排前面)(参与签名的参数可能由于版本更新增加,请游戏务必动态获取所有的参数)
  2. 按name1=value1&name2=value2&...拼接签名源串(值为空的参数和sign字段不参与签名),name和value不需要进行任何编码(如不需要URL Encode);
  3. 对最后拼接的签名源串进行HmacSHA1计算,得到签名。

2.8 游戏处理注意事项

为了避免被人网络截包篡改充值数据,需要游戏服务器收到充值通知后,按如下逻辑来实现校验充值数据,并到西瓜SDK服务器再次验证。

  1. 如遇游戏升级不能响应,则需返回 { "code": "1" };
  2. 先要进行验证签名,不通过则需返回 { "code": "-1" };
  3. 如果游戏保留了游戏订单但找不到对应订单时,则需返回 { "code": "-6" };
  4. 做订单排重,如果重复则需返回 { "code": "2" };
  5. 如果游戏保留了游戏订单,最好能够验证西瓜订单和游戏订单的一致性,如productId、productQuantity、paidAmount、uid、roleId,保证人、财、物是一致的,避免有人在网络截获包,同时密钥泄露时,冒充发送通知。如果验证不一致,返回 { "code": "-98" };
  6. 二次查询验证订单,如果订单不存在或者返回的订单信息一致性时,需返回 { "code": "-98" };
  7. 如果游戏系统发生内部异常,则需返回 { "code": "-99" }。

若以上7步都正常,则返回 { "code": "0" }。

3. 二次查询验证订单

3.1 功能

发起方: 游戏服务器
接收方: 西瓜SDK服务端
接口类型: HTTP GET
字符集编码: UTF-8
安全机制: 签名
请求地址:
http://p2.xgsdk.com/pay/verify-order/{xgAppId} 去西瓜WEB控制台查看我的xgAppId

其中xgAppId是西瓜SDK分配的游戏编号,如剑侠情缘是2018。 p2.xgsdk.com是金山云上的西瓜SDK服务器域名,接入不同的云环境有不同的域名,具体值请参考在西瓜Web控制台配置界面里的西瓜SDK接入参数。

功能描述: 用于游戏服务器验证收到的订单通知是否正确有效。

3.2 请求参数

参数说明:

参数名称参数类型最大长度说明必须
typeString32接口类型,固定为verify-order
tradeNoString32西瓜订单号
tsString14当前时间戳,秒级,如20150723150028对应2015/7/23 15:00:28
signString40签名,签名算法参见签名章节,使用游戏服务端密钥

3.3 返回结果

返回结果为JSON格式的字符串,分别有如下几个字段:

字段说明
code返回码,0代表成功。详情参考错误码章节
msg接口调用信息提示
data订单数据

data字段具体说明:

参数名称参数类型最大长度说明必须
typeString32接口类型,固定为verify-order
xgAppIdString64西瓜SDK分配给游戏的唯一ID 去西瓜Web控制台查看我的xgAppId
channelIdString32渠道ID
uidString128用户ID
zoneIdString32游戏区编号
serverIdString32游戏服编号
roleIdString32角色编号
roleNameString64角色名称
roleLevelString32角色等级
roleVipLevelString32角色VIP等级
currencyNameString64支付货币名称
productIdString64商品编号
productNameString64商品名称
productDescString128商品描述
productQuantityint10商品数量
productUnitPriceint10商品单价(单位分)
totalAmountint10总面额(单位分)
paidAmountint10总支付金额(单位分)
customInfoString2000游戏方创建订单时自定义字段,透传给游戏服务器
tsString14当前时间戳,秒级,如20150723150028 对应2015/7/23 15:00:28
gameTradeNoString64游戏订单号
signString40签名,签名算法参见签名章节,使用游戏服务端密钥
tradeNoString64西瓜订单号
paidTimeString14支付时间 yyyyMMddHHmmss
payStatusString1订单支付状态 1 支付成功 2 支付失败
payTypeString20订单支付类型, 内购没有此字段,网页支付:WebPay
extString2000 ios 支付订阅扩展参数

3.4 请求样例

请求参数:

tradeNo: 31602f1000000001

当前时间戳ts: 20150723150028

游戏服务端密钥: aca57f8a6c494a36a516e5c282c4db87 去西瓜Web控制台查看我的游戏服务端密钥XgServerKey

则请求签名源串为: tradeNo=2984456&ts=20150723150028&type=verify-order

签名为: 516b7da2faa4f1c27f70209eec32a29935b8f80d

请求样例:
http://p2.xgsdk.com/pay/verify-order/2018?tradeNo=31602f1000000001&ts=20150723150028&type=verify-order&sign=516b7da2faa4f1c27f70209eec32a29935b8f80d

3.5 返回值样例

响应签名源串为: channelId=mi&currencyName=CNY&customInfo=foo&ext={"cancellationDate": "20160901201417","expiresDate": "20160901201417","isSandbox": true,"originalTradeNo": "016q2f1000303885"}&gameTradeNo=20160325000001&paidAmount=600&paidTime=20150723145928&payStatus=1&productDesc=6元购买600钻石&productId=com.mygame.diamond600&productName=600钻石&productQuantity=600&roleId=224455&roleLevel=42&roleName=八神&roleVipLevel=8&serverId=1&totalAmount=600&tradeNo=31602f1000000001&ts=20150723150028&type=verify-order&uid=mi__3099245&xgAppId=2018&zoneId=1
(注意:请动态拼接所有参数(方便以后添加新参数时自动加入签名),所有非空的参数都参与签名,值为空的参数不参与签名,按参数名字升序排列)

游戏服务端密钥: aca57f8a6c494a36a516e5c282c4db87 去西瓜Web控制台查看我的游戏服务端密钥XgServerKey

签名为:
8a76ba82cf1dd26b91d6cc5d86162c57b8d521c1

最终返回为:

{
    "code": "0",
    "msg": "success",
    "data": {
        "type": "verify-order",
        "xgAppId": "2018",
        "channelId": "mi",
        "uid": "mi__3099245",
        "zoneId": "1",
        "serverId": "1",
        "roleId": "224455",
        "roleName": "八神",
        "roleLevel": "42",
        "roleVipLevel": "8",
        "currencyName": "CNY",
        "productId": "com.mygame.diamond600",
        "productName": "600钻石",
        "productDesc": "6元购买600钻石",
        "productQuantity": "600",
        "totalAmount": "600",
        "paidAmount": "600",
        "customInfo": "foo",
        "ts": "20150723150028",
        "gameTradeNo": "20160325000001",
        "sign": "9986011d474d1bb6cc927b0a1c7600aa074b7e89"
        "tradeNo": "31602f1000000001",
        "paidTime": "20150723145928",
        "payStatus": "1",
        "ext":"{\"cancellationDate\": \"20160901201417\",\"expiresDate\": \"20160901201417\",\"isSandbox\": true,\"originalTradeNo\": \"016q2f1000303885\"}"
    }
}

3.6 错误码

错误码 备注
0 成功
-1 签名失败
-6 订单号不存在
-99 西瓜SDK系统内部服务器错误

4. 服务端接入样例代码

西瓜SDK服务端接入样例代码(Java)