手机网络游戏SDK集成指南

小米游戏中心手机网络游戏SDK接入包括有服务器和客户端两部分。

下载最新SDK,请访问:手机游戏SDK错误代码技术FAQ运营FAQ

有任何技术问题,欢迎到游戏开发者论坛吐槽,寻求帮助。

赶快适配小米平板吧! 小米平板接入最佳实践

在继续看文档之前,建议您先把随本文档一起分发的两个Demo程序安装到手机上,(SDK_Demo_OfflineGame.apk和SDK_Demo_OnlineGame.apk,上面SDK中已包含),这两个程序完整演示了小米游戏SDK的工作流程,有助于您快速理解我们SDK支付的整个流程。

修订记录

1 网络游戏流程概述

网络游戏开发者需要管理用户登录,并且需要记录用户登录状态,用户每次启动游戏时,必须调用 miLogin()来判断用户会话是否超时,在游戏过程中如果需要充值可以调用 miUniPayOnline()。 注意:在调用miLogin()函数之前,需要1)在开发者站申请并绑定有效的appId/appKey、2)配置应用内支付模块。

1.1网游用户开户及登录流程

登陆流程

注意:

1.2 网游用户使用米币充值流程

充值流程

2 SDK调用方法

对于开发者来说,只需要按以下操作引入代码即可完成网络游戏支付流程。

2.1初始化

在小米开发者创建应用并获取 AppId 、AppKey和AppSecretKey,创建应用时packageName必须以“.mi”为后缀。

将 SDK 包中的 MiGameCenterSDKService.apk 放到应用工程的的 assets 目录下, SDK 的 jar 包放到工程的 libs 下,在 buildpath 中引用,然后对 SDK 进行初始化。

danjiSDK

注意: 需要检查下面的一致性,如果不一致会导致调用登录和其它 SDK 接口失败

1.游戏的包名是否与提供给小米后台数据配置的包名一致;

2.AppId与 AppKey是否与申请的一致。

然后在Application.onCreate中调用以下初始化方法

MiAppInfo appInfo = new MiAppInfo();
appInfo.setAppId("请申请获得");
appInfo.setAppKey("请申请获得"); 
appInfo.setAppType(MiGameType.online); // 网游
MiCommplatform.Init( this, appInfo );

SDK 所需要的权限

<uses-permission android:name="android.permission.GET_TASKS"  />
<uses-permission android:name="com.xiaomi.sdk.permission.PAYMENT" />

2.2 小米账户登录调用代码

//可以通过实现OnLoginProcessListener接口来捕获登录结果
MiCommplatform.getInstance().miLogin( context, new OnLoginProcessListener(){
    @Override
    publicvoidfinishLoginProcess( intcode,MiAccountInfoarg1) {
        switch( code ) {
        case MiErrorCode.MI_XIAOMI_GAMECENTER_SUCCESS: 
            // 登陆成功
            //获取用户的登陆后的 UID(即用户唯一标识)
            long uid = arg1.getUid();
            //获取用户的登陆的 Session(请参考 3.3用户session验证接口) 
            String session = arg1.getSessionId();//若没有登录返回 null
            //请开发者完成将uid和session提交给开发者自己服务器进行session验证
            break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_LOGIN_FAIL: 
            // 登陆失败
            break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_CANCEL:
            // 取消登录 
            break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_ACTION_EXECUTED:
            // 登录操作正在进行中 
            break;
        default :
            // 登录失败
            break;
    } 
 }

2.3 虚拟币充值调用

MiBuyInfoOnline online = new MiBuyInfoOnline();
online.setCpOrderId(UUID.randomUUID().toString()); //订单号唯一(不为空)
online.setCpUserInfo( “cpUserInfo ” ); //此参数在用户支付成功后会透传给CP的服务器 
online.setMiBi( 10 ); //必须是大于1的整数, 10代表10米币,即10元人民币(不为空)

//用户信息※必填※
Bundle mBundle = new Bundle();
mBundle.putString( GameInfoField.GAME_USER_BALANCE, "1000" );  //用户余额
mBundle.putString( GameInfoField.GAME_USER_GAMER_VIP, "vip0" );  //vip 等级
mBundle.putString( GameInfoField.GAME_USER_LV, "20" );          //角色等级
mBundle.putString( GameInfoField.GAME_USER_PARTY_NAME, "猎人" );  //工会,帮派
mBundle.putString( GameInfoField.GAME_USER_ROLE_NAME, "meteor" ); //角色名称
mBundle.putString( GameInfoField.GAME_USER_ROLEID, "123456" );   //角色id
mBundle.putString( GameInfoField.GAME_USER_SERVER_NAME, "峡谷" );  //所在服务器
MiCommplatform.getInstance().miUniPayOnline(activity, online, mBundle
    new OnPayProcessListener(){
    @Override
    public void finishPayProcess( int code ) { 
        switch( code ) {
        case MiErrorCode.MI_XIAOMI_GAMECENTER_SUCCESS: 
             // 购买成功
             break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_PAY_CANCEL:
             // 取消购买 
             break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_PAY_FAILURE: 
             // 购买失败
             break;
        case MiErrorCode.MI_XIAOMI_GAMECENTER_ERROR_ACTION_EXECUTED:
             //操作正在进行中 
             break;
        default :
             // 购买失败
             break; 
        }
    } 
});

参数说明:

参数名称 用途 备注
cpOrderId 开发方订单号 必填参数。20~100字符以内,要求必须由开发者的业务服务器生成,因订单支付成功后游戏平台服务器会直接将支付结果通知给开发者的业务服务器,通知参数的cpOrderId是重要信息。
cpUserInfo 给网游透传参数 必填参数。用于透传用户信息,当用户支付成功后我们会将此参数透传给开发者业务服务器(不能为null或“”)
mibi 购买米币的数量 必填参数。且数量是int型,即最少只能购买1米币对应的虚拟币.

3 服务器接口

请开发者以小米游戏中心服务器通知的结果为准为用户进行虚拟币充值。

3.1 订单支付结果通知接口

流程说明:

在订单支付成功后,小米游戏平台服务器会将支付结果通知给开发者预先提供的服务器上。若开发者所提供的服务器地址不可用,在一定时间段内游戏平台服务器会按照周期进行轮训(前10次,每分钟通知1次;10次后每小时通知1次)。

具体流程如下:

1.1.6.1.1

注:由于是异步通知模型,(3)和(4)不一定是按序号产生。因此(4)和(5)需要进行轮询处理或者使用接口进行支付结果查询。

相比后面提到的开发者主动查询订单的模式,我们推荐使用此模式。

接口及参数说明:

接口地址:请随《游戏SDK申请表》一起提交

请求方法:GET

请求参数:

参数名称 重要性 说明
appId 必须 游戏ID
cpOrderId 必须 开发商订单ID
cpUserInfo 可选 开发商透传信息
uid 必须 用户ID
orderId 必须 游戏平台订单ID
orderStatus 必须 订单状态,TRADE_SUCCESS 代表成功
payFee 必须 支付金额,单位为分,即0.01 米币。
productCode 必须 商品代码
productName 必须 商品名称
productCount 必须 商品数量
payTime 必须 支付时间,格式 yyyy-MM-dd HH:mm:ss
orderConsumeType 可选 订单类型:10:普通订单11:直充直消订单
partnerGiftConsume 必选 使用游戏券金额 (如果订单使用游戏券则有,long型),如果有则参与签名
signature 必须 签名,签名方法见后面说明

返回参数说明:

参数名称 重要性 说明
errcode 必须 状态码,
200 成功
1506 cpOrderId 错误
1515 appId 错误
1516 uid 错误
1525 signature 错误
3515 订单信息不一致,用于和 CP 的订单校验
errMsg 可选 错误信息

注意:对于同一个订单号的多次通知,开发商要自己保证只处理一次发货。

3.2 主动查询订单支付状态接口

流程说明:

此接口由小米游戏中心为开发者提供。

1.1.6.2.1

接口及参数说明:

接口地址:http://mis.migc.xiaomi.com/api/biz/service/queryOrder.do

请求方法:GET

请求参数说明:

名称 重要性 说明
appId 必须 游戏ID
cpOrderId 必须 开发商订单ID
uid 必须 用户ID
signature 必须 签名,签名方法见后面说明

正确返回参数说明:

参数名称 重要性 说明
appId 必须 游戏ID
cpOrderId 必须 开发商订单ID
cpUserInfo 可选 开发商透传信息
uid 必须 用户ID
orderId 必须 游戏平台订单ID
orderStatus 必须 订单状态,TRADE_SUCCESS代表成功WAIT_BUYER_PAY代表未支付REPEAT_PURCHASE订购关系已经存在
payFee 必须 支付金额,单位为分,即0.01 米币。
productCode 必须 商品代码
productName 必须 商品名称
productCount 必须 商品数量
payTime 必须 支付时间,格式 yyyy-MM-dd HH:mm:ss
orderConsumeType 可选 订单类型:10:普通订单11:直充直消订单
signature 必须 签名,签名方法见后面说明

错误返回参数说明:

参数名称 重要性 说明
errcode 必须 状态码1506 cpOrderId 错误1515 appId 错误1516 uid 错误1525 signature 错误
errMsg 可选 错误信息

3.3 用户session验证接口

流程说明:

此接口由小米游戏中心为开发者提供,用于验证登录账户的有效性。

注意:用户的唯一标识是通过SDK获得的uid,而不是 Session,Session 用于校验 登录验证的有效性,必须经过SDK、游戏中心服务器、开发者服务器进行三方验证,如果 Session失效,需要重新调用miLogin()进行登录。

1.1.6.3.1

接口及参数说明:

接口地址:http://mis.migc.xiaomi.com/api/biz/service/verifySession.do

请求方法:GET

请求参数说明:

参数名称 重要性 说明
appId 必须 游戏ID
session 必须 用户sessionID
uid 必须 用户ID
signature 必须 签名,签名方法见后面说明

返回参数说明:

参数名称 重要性 说明
errcode 必须 状态码200 验证正确1515 appId 错误1516 uid 错误1520 session 错误1525 signature 错误
errMsg 可选 错误信息

3.4接口格式说明:

输入参数: ?参数1=值1&参数2=值2&….&参数n=值n,如果遇到文本参数值,需要 根据情况对参数值做 UrlEncode。

返回参数:采用 json 格式,如:{“返回参数 1″:”返回值 1″,”返回参数 2″:”返回值 2″,….” 返回参数 n”:” 返回值 n”}

3.5签名方法说明:

3.5.1 生成带签名字符串

表中各参数按字母顺序排序(不包含 signature),如果第一个字母相同,按第二个字母排序,依次类推。排序后拼接成par1=val1&par2=val2&par3=val3的格式,所生成的字符串即为待签名的字符串。没有值的参数请不要参与签名。由于有些数据根据HTTP协议需求,需要进行URLencoding,这样接收方才可以

接收到正确的参数,但如果这个参数参与签名,那么待签名字符串必须是字符串原值而非URLencoding 的值。

3.5.2 签名算法

以AppSecretKey作为key,使用hmac-sha1带密钥(secret)的哈希算法对代签字符串进行签名计算,签名的结果由16进制表示。 4.FAQ

4.1APK打包及发布

需要注意,SDK 包是以 jar 包提供给开发者,此 jar 包本身已为混淆状态,您在混淆 自己游戏的 APK 包时,需要在 proguard.cfg 里加入,以避免二次混淆.

-keep public class com.xiaomi.gamecenter.sdk.ui.actlayout.ViewAliPayWeb$PayObject{*;} -keepclasseswithmembers class * {
public (...);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

4.2服务器签名函数

Hmac-SHA1 算法 java 实现参考:

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

    public class HmacSHA1Encryption {
    private static final String MAC_NAME = "HmacSHA1" ;
    private static final String ENCODING = "UTF-8";
    /**
     * 使用 HMAC-SHA1 签名方法对对 encryptText 进行签名
     * @param encryptText 被签名的字符串
     * @param encryptKey 密钥
     * @return 返回被加密后的字符串
     * @throws Exception
     */
    public static String HmacSHA1Encrypt( String encryptText, String encryptKey ) throws Exception{
        byte[] data = encryptKey.getBytes( ENCODING );
        SecretKey secretKey = new SecretKeySpec( data, MAC_NAME );
        Mac mac = Mac.getInstance ( MAC_NAME );
        mac.init( secretKey );
        byte[] text = encryptText.getBytes( ENCODING );
        byte[] digest = mac.doFinal( text );
        StringBuilder sBuilder = bytesToHexString ( digest );
        return sBuilder.toString();
    }
    /**
     * 转换成Hex
     * @param bytesArray
     */
    public static StringBuilder bytesToHexString( byte[] bytesArray ){
        if ( bytesArray == null ){
            return null;
        }
        StringBuilder sBuilder = new StringBuilder();
        for ( byte b : bytesArray ){
            String hv = String.format("%02x", b);
            sBuilder.append( hv );
        }
        return sBuilder;
    }
    /**
     * 使用 HMAC-SHA1 签名方法对对 encryptText 进行签名
     * @param encryptData 被签名的字符串
     * @param encryptKey 密钥
     * @return 返回被加密后的字符串
     * @throws Exception
     */
     public static String HmacSHA1Encrypt( byte[] encryptData, String encryptKey ) throws Exception{
         byte[] data = encryptKey.getBytes( ENCODING );
         SecretKey secretKey = new SecretKeySpec( data, MAC_NAME );
         Mac mac = Mac.getInstance ( MAC_NAME );
         mac.init( secretKey );
         byte[] digest = mac.doFinal( encryptData );
         StringBuilder sBuilder = bytesToHexString ( digest );
         return sBuilder.toString();
     }
}

其他参考