《游戏用户支付模块》

接入简介

游戏服务端接入AnySDK的支付模块仅需要提供一个接收支付成功通知的接口,并按照AnySDK规定的格式响应处理结果即可。

完整支付流程图

支付流程

  1. 流程1:AnySDK Framework 向 AnySDK Server发起创建订单号请求。
  2. 流程2:AnySDK Server 返回订单号给 AnySDK Framework。
  3. 流程3:AnySDK Framework 调用 渠道 SDK 支付接口向 SDK-SERVER 请求支付。
  4. 流程4:支付成功后,SDK-SERVER 同步通知支付结果到客户端上的渠道SDK。
  5. 流程5:SDK-SERVER 异步通知支付结果到AnySDK Server上。
    注:这里务必在渠道后台(即 SDK-SERVER )填写 AnySDK 提供的地址(即 AnySDK Server)

  6. 流程6:AnySDK Server 针对不同渠道的验签和响应要求,返回渠道要求格式给 SDK-SERVER。

  7. 流程7:AnySDK Server 将支付结果按照统一格式通知到,游服提供的 GAME-SERVER地址。
  8. 流程8:GAME-SERVER 按照AnySDK的要求,进行验证并响应指定格式的数据给AnySDK Server。
    注:GAME-SERVER不是响应 okOK 的 , AnySDK会重复通知,将给游戏服务器带来压力

  9. 流程9:GAME-SERVER 进行相应的支付成功后逻辑处理。

以上流程中,开发者需要做

  1. 第一步:按照下面的接入细节在 GAME-SEVER 上搭建接收支付通知的服务,并提供服务地址给打包工具使用人员
  2. 第二步:打包工具使用人员将服务端人员提供的地址填写到下图所示的位置上 支付流程
  3. 第三步:在打包工具下图所示位置获取渠道通知地址,填写到渠道后台 。
    (注:每个渠道使用的渠道通知地址都是不一样的支付流程

接入细节

接收支付通知步骤

GAME-SERVER收到AnySDK-Server的通知后,需要做以下处理:

  1. 接收 AnySDK 支付通知参数
  2. 检查 AnySDK 支付通知服务 IP 白名单(可选)
  3. 按照下面的签名算法验证签名及检查金额是否合法 (重要)
  4. 成功响应及发放道具(失败响应及日志记录)

通知方式

POST(整个流程中使用的字符集是 UTF-8)

通知参数

参数 参数类型 说明
order_id string 订单号,AnySDK 产生的订单号
product_count string 要购买商品数量(暂不提供具体数量)
amount string 支付金额,单位元 值根据不同渠道的要求可能为浮点类型
下列渠道请不要使用此金额字段作为发放道具的依据,而应该使用 product_id 作为发放道具的依据:
    GooglePlay
    Apple appstore
    心动(非越狱)
pay_status string 支付状态,1 为成功,非1则为其他异常状态,游服请在成功的状态下发货
pay_time string 支付时间,YYYY-mm-dd HH:ii:ss 格式
user_id string 用户 ID,用户系统的用户 ID
order_type string 支付方式 详见 支付渠道标识表
game_user_id string 游戏内用户 ID,支付时传入的 Role_Id 参数
server_id string 服务器 ID,支付时传入的 Server_Id 参数
product_name string 商品名称,支付时传入的 Product_Name 参数
product_id string 商品 ID,支付时传入的 Product_Id 参数
下列渠道请使用此 product_id 作为发放道具的依据,而不要使用金额amount字段作为发放道具的依据:
    GooglePlay
    Apple appstore
    心动(非越狱)
channel_product_id string product_id 字段的值对应的渠道商品 ID,对应关系可以在 dev 后台 -> 游戏列表 -> 管理商品 页面进行配置。
private_data string 自定义参数,调用客户端支付函数时传入的EXT参数,透传给游戏服务器
channel_number string 渠道编号 渠道列表
sign string 通用签名串,通用验签参考签名算法
source string 渠道服务器通知 AnySDK 时请求的参数
enhanced_sign string 增强签名串,验签参考签名算法(有增强密钥的游戏有效)
channel_order_id string 渠道订单号,如果渠道通知过来的参数没有渠道订单号则为空。
game_id string 游戏 ID,AnySDK 服务端为游戏分配的唯一标识
plugin_id string 插件 ID,AnySDK 插件数字唯一标识 详见 支付渠道标识表
currency_type string 货币类型,只有企业版才有这个字段,通用版不会有这个字段

响应格式

GAME-SERVER收到通知服务后,需响应 okOK (不带任何空白字符、隐藏字符、控制字符的 2 个字节长度的纯字符串),表示已收到通知。
注意:如果响应其他值或者不响应,则被认为通知失败,AnySDK会尝试多次通知,将给GAME-SERVER带来不必要压力。

签名算法

增强签名算法:(必接)
1. 准备验签密钥 "增强密钥",(在 开发者后台 游戏列表 界面获取);
2. 接收到的参数按照字母升序排列,sign 与 enhanced_sign 参数不参与签名;
3. 排序后的参数值按顺序拼接成一个字符串str;
4. 对str做一次 md5 处理并转换成小写,得到的加密串 1;
5. 在加密串1末尾追加"增强密钥",做一次 md5 加密并转换成小写,得到的字符串就是签名 enhanced_sign 的值
通用签名算法:(选接)
1. 准备验签密钥"private_key",(在 开发者后台 游戏列表 界面获取);
2. 接收到的参数按照字母升序排列,sign 参数不参与签名,enhanced_sign 参数参与签名;
3. 排序后的参数值按顺序拼接成一个字符串str;
4. 对str做一次md5处理并转换成小写,得到的加密串 1;
5. 在加密串 1 末尾追加 "private_key",做一次 md5 加密并转换成小写,得到的字符串就是签名 sign 的值
注意:只有值参与签名,参数名不参与签名

例如收到的数据为: a=test&c=hello&b=2&sign=abc&enhanced_sign=def

增强签名:
string=test2hello; // 排序后将值拼接(sign和enhanced_sign不参与)
enhanced_sign=md5(md5(string)增强密钥); 

通用签名:
string=test2hellodef; // 排序后将值拼接(sign不参与)
enhanced_sign=md5(md5(string)private_key); 

验签 Demo

下面是一个PHP验签代码示例,更多服务端 Demo 托管在 GitHub,请前往自取--> Sample_Server

PHP验签示例:

/**
 * 支付通知验签demo
 */
$data = $_POST;
/**
 * 注意:$_POST数据如果服务器没有自动处理urldecode,请做一次urldecode(参考rfc1738标准)处理
 */
/**
  foreach ($data as $key => $value) {
  $data[$key] = urldecode($value);
  }
 * */
$privateKey = "481946CEC51BEDE79ED72391F42B4CAF";
$enhancedKey = 'OGM3ODFkNDRhYjUzYjM4ZmUzZjk';
//注意:如果没有增强密钥的游戏只需要通用验签即可,即只需要checkSign
//if (checkSign($data, $privateKey)) {
if (checkSign($data, $privateKey) && checkEnhancedSign($data, $enhancedKey)) {
        // @todo 验证成功,游戏服务器处理逻辑
        echo "ok";
} else {
        //@todo
        echo "failed";
}

/**
 * 通用验签
 * @param array $data 接收到的所有请求参数数组,通过$_POST可以获得。注意data数据如果服务器没有自动解析,请做一次urldecode(参考rfc1738标准)处理
 * @param array $privateKey AnySDK分配的游戏privateKey
 * @return bool
 */
function checkSign($data, $privateKey) {
        if (empty($data) || !isset($data['sign']) || empty($privateKey)) {
                return false;
        }
        $sign = $data['sign'];
        //sign 不参与签名
        unset($data['sign']);
        $_sign = getSign($data, $privateKey);
        if ($_sign != $sign) {
                return false;
        }
        return true;
}

/**
 * 增强验签
 * @param type $data
 * @param type $enhancedKey
 * @return boolean
 */
function checkEnhancedSign($data, $enhancedKey) {
        if (empty($data) || !isset($data['enhanced_sign']) || empty($enhancedKey)) {
                return false;
        }
        $enhancedSign = $data['enhanced_sign'];
        //sign及enhanced_sign 不参与签名
        unset($data['sign'], $data['enhanced_sign']);
        $_enhancedSign = getSign($data, $enhancedKey);
        if ($_enhancedSign != $enhancedSign) {
                return false;
        }
        return true;
}

/**
 * 计算签名
 * @param array $data
 * @param string $key
 * @return string
 */
function getSign($data, $key) {
        //数组按key升序排序
        ksort($data);
        //将数组中的值不加任何分隔符合并成字符串
        $string = implode('', $data);
        //做一次md5并转换成小写,末尾追加游戏的privateKey,最后再次做md5并转换成小写
        return strtolower(md5(strtolower(md5($string)) . $key));
}

接入后验证

游服按照上面规则接入后,可以前往 开发者后台模拟通知游服菜单进行验证。开发者可以填写需要通知的地址和通知的数据,AnySDK 会输出生成签名每个步骤的数据,开发者可以自己比对每一步的差异,及时调整验签算法:
模拟支付通知

注意事项

  1. AnySDK 支付通知到游戏服务器中的 amount 为人民币金额(单位:元),目前安卓 SDK 为用户实际支付金额,即渠道平台实际通知金额或渠道币严格比例转后的金额,如有特殊 SDK 支付通知无法获取支付的实际人民币金额,将从 AnySDK 订单中通知给游戏服务器,请注意游戏服务器接收支付通知后金额的验证。
  2. 如游戏服务器有需要对 AnySDK 支付通知服务进行 IP 验证或加入防火墙,可随时关注 AnySDK 支付通知服务 IP 列表;建议游戏服务器支付通知验证时加入 IP 验证。

其他支付相关

支付通知服务 IP 列表

211.151.20.126
211.151.20.127
117.121.57.82

重复通知规则

开发商服务器应做好接收到多次通知的准备,防止多次加钱。
同时,需要特别注意的是,回应 okOK不带任何空白字符、隐藏字符、控制字符的 2 个字节长度的纯字符串)表示应用已经正常接到消息,无需继续发送通知,它不表示订单成功与否,或者处理成功与否。
对于重复的通知, 可能发现订单已经成功处理完毕,无需继续处理,也要返回 okOK不带任何空白字符、隐藏字符、控制字符的 2 个字节长度的纯字符串),否则,AnySDK 支付通知服务会认为未成功通知,会继续发送通知。

重复通知间隔为:2 分钟,10 分钟,10 分钟,1 小时,2 小时,6 小时,15 小时共 7 次,直到中间有收到接收成功的反馈消息,或 7 次发送完,结束周期性发送。

后台订单状态说明

成功:AnySDK 服务端与渠道服务端验签成功(跟通知游服结果无关)。
失败:渠道服务端通知过来的订单状态为失败。
充值中
1. 收到渠道的通知,但是验签错误,一般是由于 SDK 参数配置错误。
2. 没有收到渠道的通知,可能是以下几种原因:

   1. 用户并没有实际支付,也就是点开支付界面就关掉;
   2. 渠道后台配置的支付通知地址并不是正确的 AnySDK 的地址;
   3. 渠道出问题没通知或延迟通知;