网游支付SDK客户端文档

1. 申请支付接口

登录4399手机开放平台http://open.4399.cn,点击 [新建游戏] 填写相关信息完成游戏创建, 进入游戏详细页面,选择 [支付SDK] - [配置管理] 填写配置并提交申请。

平台会将所提交申请,先审核成为 测试模式 测试模式 主要是服务器端回调接口联调,该模式下 客户端 调用支付接口直接模拟充值,无充值界面。 测试模式 联调成功后,告知平台开通 正式模式 后进行RMB充值测试。

2. 环境搭建

下载SDK包

SDK下载地址: 点击这里

关联类库项目

假设现在你的工程目录名字叫project,下面将具体介绍如何将SDK接入project中

① 将 m4399OperateSDK 导入到eclipse中

② 右键点击 m4399OperateSDK 工程名→Properties Android

③ 勾选 Is Library →OK

④ 右键点击 project 工程名→ Properties → Add

⑤ 在弹出的对话框中点选资源工程 m4399OperateSDK →OK

若游戏仅支持部分指令集,需要在引入资源工程后将m4399OperateSDK\lib\目录下未使用的指令集文件夹删除。

如游戏仅支持arm6(armeabi),即可将其余的x86、arm64-v8a、armeabi-v7a文件夹删除。

包名要求

联运游戏apk包名必须以  m4399 结尾,如 com.xxx.xxx.m4399

配置Androidmanifest.xml

添加SDK所需权限

    <!-- Common permission -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- SMS pay permission -->
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!-- YouYiFu permission -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

注册SDK相关Activity&Service,注意必须放入<application>元素区块内

        <!--6.0系统授权辅助Actiivty-->
        <activity
            android:name="cn.m4399.common.permission.AuthActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:multiprocess="false"
            android:theme="@style/m4399ActivityTheme"/>


        <!-- For 4399 recharging SDK. 请不要在此处修改RechargeActivity的方向设置,因为某些2。3的机型启动Activity总是先启动
            竖屏,然后强制转换成横屏,这会导致潜在问题. -->
        <!-- activity的配置不能少于orientation|screenSize|keyboardHidden,这些配置是为了防止Activity被系统或第三方界面强  
            拉成竖屏时,发生重建而加入的。SDK的Activity支持横屏或竖屏,但不支持横竖屏切换,否则会包初始化问题 -->
        <!-- For 4399 recharging SDK. -->
        <activity
            android:name="cn.m4399.recharge.ui.activity.RechargeActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:theme="@style/m4399TranslucentFullscreenActivityTheme"
            android:screenOrientation="behind">
        </activity>

        <!-- For 4399 Operation SDK -->
         <activity
            android:name="cn.m4399.operate.ui.activity.LoginActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/m4399TranslucentFullscreenActivityTheme"/>
        <activity
            android:name="cn.m4399.operate.ui.activity.UserCenterActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/m4399TranslucentFullscreenActivityTheme"
            android:hardwareAccelerated="false">
            <intent-filter>
                <action android:name="cn.m4399.sdk.action.USER_CENTER"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity
            android:name="cn.m4399.operate.ui.activity.CustomWebActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/m4399TranslucentFullscreenActivityTheme"/>

        <activity
            android:name="cn.m4399.operate.ui.activity.CommonActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/m4399ActivityTheme">
        </activity>
        <activity
            android:name="cn.m4399.operate.ui.activity.GetActivationCodeActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/android:Theme.Translucent.NoTitleBar.Fullscreen">
        </activity>
        <activity
            android:name="cn.m4399.operate.ui.activity.CaptchaActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="behind"
            android:theme="@style/android:Theme.Translucent.NoTitleBar.Fullscreen">
        </activity>


        <!--------以下为第三方支付SDK Activity&Service配置------------>
        <activity
            android:name="com.alipay.sdk.app.H5PayActivity"
            android:configChanges="orientation|keyboardHidden|navigation|screenSize"
            android:exported="false"
            android:screenOrientation="behind"
            android:windowSoftInputMode="adjustResize|stateHidden"/>
        <!-- For YouYiFu -->
        <activity
            android:name="com.arcsoft.hpay100.HPaySdkActivity"
            android:configChanges="keyboardHidden|screenSize|orientation"
            android:screenOrientation="behind"
            android:theme="@style/android:Theme.Translucent.NoTitleBar.Fullscreen">
        </activity>
        <activity
            android:name="com.arcsoft.hpay100.web.HPayWebActivity"
            android:configChanges="keyboardHidden|screenSize|orientation"
            android:screenOrientation="behind"
            android:theme="@style/hpay_custom_confim_dialog">
        </activity>
        <activity
            android:name="com.arcsoft.hpay100.web.HPayWebFullActivity"
            android:configChanges="keyboardHidden|screenSize|orientation"
            android:screenOrientation="behind">
        </activity>

兼容7.0+ 文件访问权限变更 android:authorities="游戏包名.operate.FileProvider"

 <provider
            android:name="cn.m4399.operate.OpeFileProvider"
            android:authorities="{APP_PACKAGE_NAME}.operate.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/m4399_ope_file_paths" />
</provider>

注:

1.第三方支付SDK的Activity需在AndroidManifest.xml中强制配置横竖屏,请游戏方根据游戏的横竖屏要求手工配置landscape|portrait

游戏Activity的配置不能少于orientation|screenSize|keyboardHidden这三项,这些配置是为了防止Activity被系统或第三方界面强 拉成竖屏时,发生重建而加入的。因为Activity重建有可能会因为某些初始化不全,发生crash。

2.SDK的Activity支持横屏或竖屏,但不支持横竖屏切换,缺少orientation|screenSize|keyboardHidden有可能发生初始化问题。

3.关于渠道开关:接入4399SDK的游戏,又希望在多个渠道投放apk,可以打开Manifest中的开关,并且联系运营使用专用打包工具打入渠道标识,但不需要游戏再针对每个渠道单独打包;如果不需渠道标识,请勿打开开关,否则会增加SDK初始化时间。

3. 代码实现

初始化SDK

初始化推荐在游戏初始化过程中进行,析构函数则在游戏退出前执行。

mOpeCenter = OperateCenter.getInstance();
mOpeConfig = new OperateCenterConfig.Builder(this)
    .setGameKey("GAME_KEY")     //设置GameKey
    .setDebugEnabled(false)     //设置DEBUG模式,用于接入过程中开关日志输出,发布前必须设置为false或删除该行。默认为false。
    .setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)  //设置横竖屏方向,默认为横屏,现支持横竖屏,和180度旋转
    .setSupportExcess(true)     //设置服务端是否支持处理超出部分金额,默认为false
    .setPopLogoStyle(PopLogoStyle.POPLOGOSTYLE_ONE) //设置悬浮窗样式,现有四种可选
    .setPopWinPosition(PopWinPosition.POS_LEFT) //设置悬浮窗默认显示位置,现有四种可选
    .setSMEnable(true)    //设置数美开关,默认值为true,false为关闭状态
    .build();
mOpeCenter.setConfig(mOpeConfig);
mOpeCenter.init(activity, new OperateCenter.OnInitGloabListener() {
    // 初始化结束执行后回调
    @Override
    public void onInitFinished(boolean isLogin, User userInfo) {
        assert(isLogin == mOpeCenter.isLogin());
    }

    // 注销帐号的回调, 包括个人中心里的注销和logout()注销方式
    // fromUserCenter区分是否是从悬浮窗-个人中心("4399游戏助手页面")注销的,若是则为true,不是为false
    @Override
    public void onUserAccountLogout(boolean fromUserCenter, int resultCode) {
    }

 // 切换帐号的回调 
 //fromUserCenter区分是否是从"4399游戏助手页面"切换的,若是则为true,不是为false
    @Override
    public void onSwitchUserAccountFinished(boolean fromUserCenter,User userInfo) {

    }
});
是否支持处理超出部分金额也可单独设置
mOpeCenter.setSupportExcess(support);

能否支持处理超出部分金额指在使用SDK充值时,由于用户选择的充值渠道不同,可能造成实际充值金额超出游戏下单时传入的金额。如果游戏服务端能够正确处理超出部分的金额,则本接口传入true。如果无法支持处理超出部分的金额,则传入false,SDK将会根据传入金额自动隐藏无法满足充值金额的渠道(例:开发者设置SupportExcess为false,充值时传入7元,此时4399一卡通中无7元面额的充值卡,此时4399一卡通的充值渠道将自动隐藏)。SupportExcess默认为false。

注:代码中MainActivity为当前Activity.下文的mOpeCenter指OperateCenter实例,通过getInstance()静态方法获得。

用户登录接口

用户在触发登录时,调用该接口,如果SDK内已包含未注销的用户凭证,将自动返回用户信息。如需强制调出登录界面,请使用【用户切换】接口。

mOpeCenter.login(MainActivity.this, new OnLoginFinishedListener() {

    @Override
    public void onLoginFinished(boolean success, int resultCode, User userInfo)
    {
        //登录结束后的游戏逻辑
    }
});

SDK会自动识别用户手机中是否安装了新版的4399游戏盒1.4.1以上版本,如果已安装,自动跳转至游戏盒授权登录。如果未安装,则弹出Web版4399统一登录界面。 在登录成功后,监听器返回的User类型的用户信息中将包含State登录凭证,该信息可用于游戏服务端进行用户信息二次验证。

注:登录后如果未注销,登录状态将一直保持直至登录凭证过期或失效(若用户修改平台账户密码,所有游戏授权凭证将失效,需重新登录)。建议游戏在初始化完成后调用登录状态查询接口查询用户当前登录状态。

获取用户信息

在SDK处于登录状态时,可通过该接口获取当前用户的信息(UID、用户名、昵称、登录凭证)。

注意:SDK返回的uid是字符串类型,无需进行类型转换。如有转换整型情况,请直接转换为long类型。强烈建议使用新注册账号进行测试,以免转型后长度溢出问题

User user = mOpeCenter.getCurrentAccount();

User类内部方法含义说明

/**     * Description: 获取UID     */
public String getUid() {
    if (SDKEnvironment.getInstance().getGameInfo() != null && SDKEnvironment.getInstance().getGameInfo().fromGuangZhou())
       return suid;
    return uid;
}

/**     * Description: 若是广州的SDK则返回suid,若不是返回空字符串     */
protected String getSuid() {
    return suid;
}

/**     * Description: 获取用户名     */
public String getName() {
    return name;
}

/**     * Description: 获取昵称     */
public String getNick() {
    return nick;
}

/**     * Description: 获取登录凭证     */
public String getState() {
    return state;
}

/**     * Description: 是否有激活码     */
public boolean isActivated() {
    return activated;
}

/**     * Description: 是否通过实名认证(需要服务端打开开关,主要用于SDK内部校验) true:通过,false:不通过     */
public boolean isIdChecked() {
    return idChecked;
}

/**     * Description: 是否通过实名认证(开发者判断是否实名认证使用这个字段) true:通过,false:不通过     */
public boolean isIdCheckedReal() {
    return idCheckedReal;
}

/**     * Description: 开启 1 已绑定手机 2 未绑定 未开启 3 已绑定手机 4 未绑定     */
public int getPhoneBound() {
    return phoneBound;
}

/**     * Description: vip_state 0没有VIP  1有VIP但是不弹窗  2有VIP并且弹窗     */
public int getVipState() {
    return vipState;
}

/**     * Description: 是否成年 0 没填,1 小于8岁,2 小于18岁,3 大于18岁,4 假身份     */
public int getIdCardState() {
    return vipState;
}

实名认证

当用户需要实名认证时,可调用本接口启动实名认证界面。

mOpeCenter.nameAuthentication(this, new OperateCenter.NameAuthSuccessListener() {
    @Override
    public void onAuthSuccess(int idCardState) {
	// idCardState :0:没有身份认证; 1:小于8岁; 2:大于8岁小于18岁;3:大于18岁;4;假身份(格式符合要求的身份证)
        Toast.makeText(MainActivity.this, "name auth success , idCardState: " + idCardState, Toast.LENGTH_SHORT).show();
    }
});

账号切换

当用户需要注销当前登录状态,且同时弹出登录界面时,使用本接口。本接口的监听器类型与【用户登录】接口相同。

mOpeCenter.switchAccount(MainActivity.this, new OnLoginFinishedListener() {

    @Override
    public void onLoginFinished(boolean success, int resultCode, User userInfo)
    {
        //用户账号切换结束后的游戏逻辑
    }
});

跳转到游戏圈帖子详情页面。

/**
 * 打开游戏盒游戏圈指定帖子,若没有游戏圈toast提示用户,若没有下载游戏盒则提示下载
 *
 * @param activity
 * @param id 帖子id
 */
public void showGameCircleDetail(Activity activity,int id) {
    mAccountManager.showGameCircleDetail(activity,id);
}

用户注销

当用户需要注销当前登录状态时,使用本接口,注销回调在初始化接口回调中统一处理。

mOpeCenter.logout();

游戏关闭

如果游戏已经配置游戏圈, 则在关闭前,SDK会弹出对话框询问“退出游戏”还是“前往游戏圈,如果还没有配置,SDK弹框会提示“退出游戏”还是“留在游戏”。

mOpeCenter.shouldQuitGame(MainActivity.this, new OnQuitGameListener() {

    @Override
    public void onQuitGame(boolean shouldQuit) {
        // 点击“退出游戏”时,shouldQuit为true,游戏处理自己的退出业务逻辑
        // 点击“前往游戏圈”时,shouldQuit为false,SDK会进入游戏圈或者下载
        //  游戏盒子界面,游戏可以不做处理。
        // 点击“留在游戏”时,shouldQuit为false,SDK和游戏都不做任何处理
        // 点击右上角的关闭图标,shouldQuit为false,SDK和游戏都不做任何处理
    }
});

登录状态查询

查询当前客户端是否有账号登录

boolean isLogin = mOpeCenter.isLogin();

设置服务器ID

当游戏有分服时,在用户选择角色进入分服时,请务必立即通过本接口设置所在服的ID。如果无分服,则可不设置。

//serverid为不超过10位的数字字符串
mOpeCenter.setServer("SERVER_ID");

充值接口

当用户需要充值时,可调用本接口启动充值中心界面。

特别注意:mark为游戏方订单号,最大长度32位,由包含字母或数字组成的唯一字符串,该字段不可为空,不可重复

mOpeCenter.recharge(MainActivity.this,
                je,             //充值金额(元)
                mark,           //游戏方订单号
                productName,    //商品名称
                new OnRechargeFinishedListener() {

                    @Override
                    public void onRechargeFinished(
                        boolean success, int resultCode,
                        String msg)
                    {
                        if(success){
                            //请求游戏服,获取充值结果
                        }else{
                            //充值失败逻辑
                        }
                    }
                });

je充值金额:整型数字,4399充值中心仅支持整数金额充值,最小充值金额1元,最大不超过50000元。 

mark订单号:最大长度32位,支持大小写字母、数字、‘|’(竖线)、‘-’(中划线)、‘_’(下划线),该字段不可为空,不可重复。 

productName商品名称:最长不超过8个字符(尽量不要传入特殊字符,以免造成充值异常)。 如果传入商品名,充值中心将直接显示该商品名称,如果充值金额大于下单时传入的je时,将显示商品名+XXX游戏币,相关游戏币的兑换比例在接入时提供给运营人员配置。如果未传入商品名,则直接显示XXX游戏币。

绑定手机

/*
*返回值:
*resultCode:
* -3:用户取消绑定
* 0:用户未登录(无需绑定) 
* 1:已绑定,无需再次绑定(无需绑定) 
* 2:绑定成功
* 3:已绑定,游戏关闭绑定功能(无需绑定) 
* 4:未绑定,游戏关闭绑定功能(无需绑定) 
*msg:绑定成功或失败的描述信息
**/ 
public void onBindPhoneClicked(View view) {
    mOpeCenter.bindPhone(this, new OnPhoneBindResultListener() {
        @Override
        public void onPhoneBindResult(int resultCode, String msg) {
        Log.v(TAG, "bindPhone resultCode=" + resultCode+",msg="+msg);
        Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
        }
    });
}

检查手机绑定状态

/*
*该接口为异步接口
*返回值:
*resultCode:
* -2:非网络问题的检查绑定状态失败
* -1:网络异常的检查绑定状态失败
* 0:用户未登录
* 1:游戏开启绑定手机功能,用户已绑定手机号
* 2:游戏开启绑定手机功能,用户未绑定手机号
* 3:游戏未开启绑定手机功能,用户已绑定手机号
* 4:游戏未开启绑定手机功能,用户未绑定手机号
*msg:查询结果的描述信息
**/ 
    public void onCheckBindPhoneClicked(View view) {
        mOpeCenter.checkBindPhoneState(new OnCheckPhoneBindStateListener() {
            @Override
            public void onCheckPhoneBindState(int resultCode, String msg) {
                Log.v(TAG, "check bindPhone resultCode=" + resultCode+",msg="+msg);
                Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
            }
        });
    }

测试模式

何为测试模式?
游戏申请支付接口,平台会先将该申请设置为测试模式。测试模式需要使用平台指定的账号进行登录,方可测试充值接口,
测试模式调用充值接口不会弹出充值界面,直接模拟充值,主要是与服务器端进行联调,客户端会根据服务器端返回的状态进行显示成功还是异常。

测试模式联调成功后,平台会开通正式模式,进行RMB测试。

获取状态信息

工具接口,用于将回调函数中的resultCode解析为中文的说明(充值接口recharge的resultCode对应的中文是回调中的msg)。

String resultMessage = OperateCenter.getResultMsg(resultCode);

获取SDK版本号

mOpeCenter.getVersion();

析构

游戏退出时调用本接口,释放SDK资源以及保存相关数据。

mOpeCenter.destroy();

检查更新

SDK包含自动更新模块,SDK将检查后台是否有新版本游戏上线,如果有,则显示更新内容,并提示用户升级。 

该升级为增量升级,后台在提交新版游戏时自动制作差分包,更新时用户只需下载APK文件中新旧版本有差异的部分。

相关更新内容和版本提交事宜,请联系4399相关运营对接人员。 

4399SDK的增量升级为全自动增量更新(无需操作,默认初始化完成)

代码混淆

如果您的应用使用了混淆, 请不混淆SDK代码,可以在proguard.cfg文件里追加以下配置。

-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**

-dontwarn com.arcsoft.hpay100.**
-keep class com.arcsoft.hpay100.**{*;}
-keep class cn.m4399.operate.** {*;}
-keep class cn.m4399.recharge.** {*;}
-dontwarn cn.m4399.operate.**
-dontwarn cn.m4399.recharge.**
-keepclassmembers class cn.m4399.recharge.R$* {*;}
-dontskipnonpubliclibraryclassmembers
-keepclassmembers class cn.m4399.recharge.R$* {*;}
-dontwarn android.net.**
-keep class android.net.SSLCertificateSocketFactory{*;}

4. 注意事项

1.游戏打包的targetSdkVersion如果高于23,必须使用高于v24.0.0.0的SDK,最好使用最新版本,否则Android 7.0+手机在安装更新包时,会出现文件访问权限问题,而导致游戏崩溃。

2.配置FileProvider时,请务必保证”android:authorities“格式,建议变更后打一个旧版本的包测试下安装(此时会是全量更新)。

3.打包环境 targetSdkVersion >= 26 时,在 Android 8.0+手机上有安装权限问题,导致不能正常安装应用,需要在AndroidManifest中显示地声明权限 android:name="android.permission.REQUEST_INSTALL_PACKAGES"。