GHOME SDK 开发手册 for Android国际版


1. 前言

本文用于指导游戏开发商接入SDK,文中包含客户端的接入说明,以及服务器接口的介绍;

2. SDK接入流程概述

  • 搭建SDK开发环境

  • 初始化SDK

  • 初始化成功之后可以调用账号想关功能相关接口

  • 用户在进行购买时,调用支付功能相关接口

  • 其他功能接口,可根据业务需要适时调用

  • 销毁SDK

2.1. 接入前必备

  • 在模块(应用级)Gradle 文件中,添加 Google 服务插件: apply plugin: 'com.google.gms.google-services' 将上述固定配置放在build.gradle中(apply plugin: 'com.android.application',开头一般为该内容的gradle)

  • 若使用包含firebase版本sdk,下载 Firebase Android 配置文件 (google-services.json),然后将其添加到您的应用,放在模块(应用级)根目录中。 申请地址:https://console.firebase.google.com/

3. 开发环境要求

3.1. 集成SDK文件到游戏工程

  • 将libs中的所有 .aar 文件放在游戏项目的libs文件夹下
  • 项目build.gradle文件参考build.gradle文件
  • 项目根目录的build.gradle文件参考app_build.gradle文件
  • 项目根目录下的gradle.properties参考gradle.properties文件

3.2. 增加google配置

3.3. 增加支付配置

  • 在values目录下 string.xml 中增加

    谷歌支付
    <string name="gl_google_pay_key">请填写谷歌支付key</string>
    onestore支付
    <string name="onestore_pay_id">请填写onestore支付key</string>

3.4. 增加facebook配置

  • 在values目录下 string.xml 中增加

    <string name="facebook_app_id">请填写facebook appId</string>
    <string name="fb_login_protocol_scheme">请填写fb_login_protocol_scheme</string>
    <string name="facebook_client_token">请填写facebook_client_token</string>
  • 获取地址参考官方文档(获取应用编号及获取客户端口令):https://developers.facebook.com/docs/android/getting-started#

  • 在manifest中增加 string.xml 中增加

    <meta-data 
      android:name="com.facebook.sdk.ApplicationId"
      android:value="@string/facebook_app_id"/>   
    <meta-data 
      android:name="com.facebook.sdk.ClientToken"
      android:value="@string/facebook_client_token"/>

3.5. 增加twitter配置

  • 在values中增加 string.xml 中增加

    <string name="com.twitter.sdk.android.CONSUMER_KEY">配置twitter分配的API Key对应的值</string>
    <string name="com.twitter.sdk.android.CONSUMER_SECRET">配置twitter分配的API secret Key对应的值</string>

3.6. 增加line配置

  • 在values中增加 string.xml 中增加

    <string name="line_app_id">配置line分配的游戏ID的值</string>
    <integer name="line_channle_id">配置line分配的渠道ID对应的值</integer>
  • 在manifest中增加配置
  • 在游戏登录Activity中增加如下两条meta-data配置

    <activity
      android:name="游戏登录activity">
    
      <meta-data
          android:name="android.app.trident_lib"
          android:value="trident" />
      <meta-data
          android:name="android.app.trident_modules_resource_id"
          android:resource="@array/trident_modules" />
    </activity>
  • 在manifest中application节点下增加如下配置

    <meta-data
      android:name="jp.line.sdk.ChannelId"
      android:value="@integer/line_channle_id" />
    <meta-data
      android:name="jp.line.sdk.AuthScheme"
      android:value="line.游戏分配的id(line_app_id的值)" />
    <meta-data
      android:name="jp.line.sdk.Phase"
      android:value="REAL" />
    <meta-data
      android:name="com.linecorp.trident.sdk.Phase"
      android:value="ReleasePhase" />

3.7. 地区api host自定义说明

  • 不配置,则 sdk 内置默认新加坡地区的 api host
  • 如果游戏APP需要分地区实现区域化运营需求,可在manifest中增加配置

配置项说明:

  • ghomesdkHostApiBiz 为业务服务端接口地址
  • ghomesdkHostApiLog 为日志收集服务端接口地址
  • 两个配置key value可以独立配置,即例如只配置了 ghomesdkHostApiLog 使用北美,则SDK效果是:业务数据在新加坡,日志在北美。

地区列表说明:

  • 新加坡(默认):无需配置
  • 北美地区:ghomesdkHostApiBiz="暂未部署" ghomesdkHostApiLog="reportsk-gg.web.sdo.com"

重要说明:地区配置游戏方研发必须和游戏产品负责人、运营等相关人员确认,并与ghome产品负责人、服务端研发、运维沟通后,选择所需的 api host

<meta-data
   android:name="ghomesdkHostApiBiz"
   android:value="xxxxxxx" />
<meta-data
   android:name="ghomesdkHostApiLog"
   android:value="reportsk-gg.web.sdo.com" />

4. 基础功能(必接)

4.1. SDK初始化接口

游戏客户端需要在游戏主Activity的onCreate()方法中调用该接口,在调用其他API前需先调用此接口对SDK进行初始化(create生命周期接口,必须在initialize接口之前调用)。否则可能无法正常使用后续的接口。

  • 接口:

    initial(final Activity activity, final String gameId, final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    gameId: 游戏在手游直通车平台上申请得到的ID。

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 常量定义:

    public static final int ERROR_NO_ERROR= 0;
  • 注意事项:

    如果没有特别说明,在初始化接口成功之前调用其他接口都有可能出现错误,请确保初始化接口成功之后再调用其他接口。

  • 代码示例:

    GHome.getInstance().initialize(this, "1000", new Callback(){
    
    @Override
    public void callback(int code, String message, Map<String, String> data) {
      // success
      if(code == ErrorCode.ERROR_NO_ERROR){
        // 初始化成功的回调,无需处理
      }else{
        // 初始化失败,无需处理
    }
    
    });

4.2. SDK销毁接口

游戏客户端需要在游戏退出之前调用该接口。

  • 接口:

    destroy(final Activity activity,final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

  • 注意事项:

    在游戏主Activity的onDestroy接口中调用。

  • 代码示例:

    GHome.getInstance().destroy(this,null);//一般不需要回调,一定成功

4.3. SDK支付回调

游戏客户端需要在onActivityResult实现该方法。

  • 注意事项:

    建议在主Activity的onActivityResult接口中调用。

  • 代码示例:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (GHome.getInstance().handleActivityResult(requestCode, resultCode, data)) {
          return;
      }
      super.onActivityResult(requestCode, resultCode, data);
    
    }

    4.4. SDK数据上报

游戏客户端需要在onResume及onPause实现该方法。

  • 注意事项:

    在主Activity的onResume及onPause接口中调用。

  • 代码示例:

    @Override
    protected void onResume() {
      super.onResume();
      //若包含line登录 需参考8. line解除授权回调(若有line登录必接)
      GHome.getInstance().resume(this,null);
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      GHome.getInstance().pause(this);
    }

5. 账号功能(必接)

5.1. 账号体系说明

  • 注册&登录原理

    1) 在游戏登录界面打开SDK注册&登录界面;

    2) 用户完成注册&登录之后,SDK把用户id和ticket票据返回游戏;

    3) 游戏如果有服务器,需要把客户端获取到ticket票据发送到“登录票据验证接口”(参考5.4节)进行验证,然后把返回的用户信息(用户id和账号)保存到游戏服务器;

  • 注册&登录时线图
    Android3

5.2. 客户端登录接口

游戏客户端可以调用登录接口为玩家提供登录功能。若用户第一次登录,将显示登录和注册的界面。若用户上次使用账号成功登录过,则下一次登录会用该账号自动登录。

  • 接口:

    login(final Activity activity, final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 注意事项:

    通常情况游戏不需要提示用户登录结果,无论是登录失败还是登录成功,SDK都有Toast提示。

  • 代码示例:

    GHome.getInstance().login(this, new Callback() {
    
    @Override
    public void callback(int code, String message, Map<String, String> data) {
      // 登陆结果的回调。
      if (code == ErrorCode.ERROR_NO_ERROR) {
        // 如果游戏需要,可以在登录成功后,通过data获取到以下信息。
        // userid是该玩家在我们平台的用户id号;
        // ticket是本次登录得到的票据,用来给游戏服务器向平台服务器验证本次登陆是否合法。
        // isGuest是否是游客,isBind是否绑定(只有游客才能绑定,如果是空则代表已绑定)
        // accountType是本次登录的登录渠道名称;
        System.out.println("userid=" + data.get("userid"));
        System.out.println("ticket=" + data.get("ticket"));
        System.out.println("isGuest=" + data.get("isGuest")==null?false:data.get("isGuest").equals("true"));
        System.out.println("isBind=" + data.get("isBind")==null?true:data.get("isBind").equals("true"));
        System.out.println("accountType=" + data.get("accountType"));
        //
        // 登录成功,进入游戏界面,游戏开发者补充逻辑。
      }else if(code == Constants.ERROR_NO_INIT || code == Constants.ERROR_INIT_ERROR){
        // 可在此处再次调用初始化接口
    
        // mGHome.initial(final Activity activity, final String gameId, final ResultHandler handler);
      }else{
        // 登录失败无需处理
    }
    
    }
    
    });

    5.3 客户端游客绑定接口

    游戏客户端可以调用游客绑定接口为玩家提供游客绑定功能。

  • 接口:

    GHome.getInstance().bindAccount(activity,, final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 代码示例:

    GHome.getInstance().bindAccount(this, new Callback() {
    
      @Override
      public void callback(int code, String errMsg, Map<String, String> data) {
          if (code == ErrorCode.ERROR_NO_ERROR) {
              // 游客绑定成功,无需处理
          } else {
              // 游客绑定失败,无需处理
          }

5.4. 客户端注销接口

游戏客户端可以在游戏内需要注销账号的地方调用该接口,执行成功将删除自动登录的记录信息,玩家下次登录将不再自动登录,而是显示出登录界面。

  • 接口:

    logout(final Activity activity, final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

  • 注意事项:

    暂无

  • 代码示例:

    GHome.getInstance().logout(this, new Callback() {
      @Override
      public void callback(int code, String message, Map<String, String> data) {
          if (code == ErrorCode.ERROR_NO_ERROR) {
            // 注销成功,游戏做退出操作
          } else {
            // 注销失败,无需处理
          }
      }
    });

5.5 客户端获取区服信息

游戏客户端可以调用获取区服信息接口得到当前游戏id配置下区服信息。在游戏已初始化状态下可用。

  • 接口:

    queryAreaInfo(final Activity activity,final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 代码示例:

    GHome.getInstance().queryAreaInfo(this,
        new Callback() {
              @Override
              public void callback(int code, String errMsg, Map<String, String> data) {
                  if (code == ErrorCode.ERROR_NO_ERROR) {
                  // 获取区服信息成功
                  String json = data.get("data"));
                  } else {
                  // 获取区服信息失败
                  }
              }
        });
    • JSON格式说明:
    {
    "message":[
    {
    "area_code":"1",  // 开发商后台配置的区服ID
    "name":"area1",    // 开发商后台配置的区服名称
    "notify_url":"http:\/\/test.hps.sdo.com\/mork\/notifygame"    // 充值发货地址
    },
    ...
    ]
    }

5.6 客户端获取商品信息

游戏客户端可以调用获取商品信息接口得到当前游戏id配置下商品信息。获取支付商品列表

  • 接口:

    queryProductInfo(final Activity activity,final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 代码示例:

    GHome.getInstance().queryProductInfo(DemoActivity.this,
           new ResultHandler() {
    
                  @Override
                  public void handleResult(int code, String errMsg, Map<String, String> data) {
                        if (code == ErrorCode.ERROR_NO_ERROR) {
                        // 获取商品信息成功
                        String json = data.get("data"));
                      } else {
                        // 获取商品信息失败
                      }
                  }
          });
  • JSON格式说明:

    {
    "message":[
    {
    "item_name":"test1",  // 商品名称
    "money":"5"    // 商品价格
    "product_code":"test.id"    // 商品id
    "type":1   // 商品类型
    },
    ...
    ]
    } 

5.7. 服务器登录票据验证接口

  • 用法说明

    为了保证游戏服务器的安全性,游戏服务器在登记用户登录信息之前需要到国际版服务器做一个验证

  • 接口地址

    https://abroad.sdo.com/fh/open/ticket

  • 传值方式

    GET

  • 参数列表

    appid: 游戏对应的APPID

    timestamp: 精确到秒的unix时间戳

    sequence: 不重复的请求序列号(全局唯一)

    ticket_id: 从客户端登录接口返回的ticket字符串;

    sign: 签名串(参考:“附录A:服务器端签名算法”,签名原始串示例:appid=xxx&sequence=xxx&ticket_id=xxx & timestamp=xxxappsecretkey)

  • 返回结果

    返回结果按json编码,数据格式为:

    {
    code:0, #返回状态,0为成功,1为失败,2  签名错误7 osap商户不存在,3001 票据不存在或超过有效期或被反复验证,1003 票据申请的appId和验证的APPID不一致
    msg:"ok", #错误信息
    data:{
      userid:123456, #平台用户id,纯数字,无类型。
      token:"40E00263-16A7-8CFA-D0E8-C951D683EA24", #平台TOKEN,36位字符串,暂时不用
      phone:"+86-139****6893" #手机帐号,带国际区号,可用于显示帐号
      invitation_code:"34343"  #邀请码
    }
    }

5.8. 客户端获取一次性登录票据接口

  • 接口

    getTicket(Activity activity, String appId, String areaId, Callback callback);

  • 参数说明

    activity:Activity对象

    appId:需要使用该票据完成登录的应用ID

    areaId:需要使用该票据完成登录的应用区号,如果该应用没有区的概念可以传"0"

    callback:回调对象,用来回调执行结果,详情参考代码示例

  • 注意事项

    必须在登录成功后才使用该接口

  • 代码示例

    GHome.getInstance().getTicket(DemoActivity.this, "1234", "1", new Callback() {
          @Override
          public void callback(int code, String message, Map<String, String> data) {
              if (code == ErrorCode.ERROR_NO_ERROR & data != null) {
                  ToastUtil.showMessage(DemoActivity.this, getDemoToastMsg("ticket = "
                          + data.get("ticket")));
              } else {
                  ToastUtil.showMessage(DemoActivity.this, getDemoToastMsg("get ticket fail, code = "
                          + code + ", msg = " + message));
              }
    
          }
      });

6. 支付功能(必接)

6.1. 支付原理说明

  • 支付原理

    1) 游戏客户端发起支付请求;

    2) SDK服务器收到请求,并发送至第三方支付;

    3) 第三方支付成功收费以后通知SDK服务器,再由SDK服务器通知游戏服务器发货至游戏,需要游戏提供“订单通知接口”进行接受(参考6.3节);

  • 支付时线图

    Android4

6.2. 客户端支付接口

游戏客户端可以在购买道具的地方调用该接口。这将打开支付界面,引导玩家支付该订单。用户结束支付操作后,SDK会回调游戏客户端,告知支付结果是否已经成功。

  • 接口:

    pay(final Activity activity, final String orderId, final String areaId, final String productId,final String extend, final ResultHandler handler);

  • 参数说明:

    activity: Activity对象。

    orderId: 必传,游戏订单号。如果游戏需要记录订单号,可以传入唯一的字符串来标识此订单。

    areaId: 必传,在手游直通车平台上设置的区服的ID

    productId: 必传,在手游直通车平台上设置的商品的ID

    extend:扩展字段,若当前游戏不需要可传空

    handler: 回调对象,用来回调执行结果,详情请参考代码事例。

  • 注意事项:

    由于可能发生网络延迟等等原因,有时候客户端返回的支付结果可能与实际情况有出入,所以最终订单是否支付成功,建议以平台服务器端的通知为准,客户端的通知结果仅作为参考。

  • 代码示例:

    GHome.getInstance().pay(this, "game order id", "1", "p001", new Callback() {
    @Override
    public void callback(int code, String message, Map<String, String> data) {
      if (code == ErrorCode.ERROR_NO_ERROR) {
        // 支付成功,游戏客户端可以在这里查询游戏服务器是否收到发货通知
        // 如果游戏服务器已经收到发货通知可以向用户发放道具
      } else {
        // 支付暂未成功,这种状态可能是用户未完成支付,也可能是尚未收到渠道的发货通知,可做相应的友好提示
        // 游戏客户端可以不做处理,用户下次登录时再做查询
      }
    }
    });

6.3. 服务器订单通知接口

  • 用法说明

    用于将用户支付结果通知给游戏。

  • 接口地址

    接口地址需要游戏提供,暂不支持配置,手工导入

  • 传值方式

    POST

  • 参数列表

    orderNo: 服务器的订单号

    userId:用户ID

    gameOrderNo: 游戏的订单号

    product: 产品ID(暂不支持配置,手工导入)

    extend:游戏发送过来的扩展信息,原格式返回

    sign:签名串(参考“附录A:服务器端签名算法”)

    time:到账时间

  • 参数示例

    orderNo=791000012PP016140210105937000001&userId=18178&gameOrderNo=NONE&product=com.winggod.jingzhuan&extend=NONE&time=1392004960&sign=ad08d9e2d7b7df6603bc431392d1c707
  • 返回字符串:success,表示成功

    其他表示失败。

7. 数据统计上报接口(必接)

7.1. 客户端上报游戏区接口

游戏客户端在用户选择游戏区之后需要调用该接口,否则数据报表将无法按区提供。

  • 接口:

    loginArea(final Activity activity, final String areaId);

  • 参数说明:

    activity: Activity对象。

    areaId: 用户进入的区号。

    handler: 回调接口

  • 注意事项:

    暂无

  • 代码示例:
GHome.getInstance().loginArea(this, "1");

8. line解除授权回调(若有line登录必接)

游戏客户端在包含line登录的情况下,接入该方法,做解除授权后的操作处理。

  • 代码示例:

    @Override
    protected void onResume() {
      super.onResume();
      GHome.getInstance().resume(this,mResultHandler);
      GHome.getInstance().checkLineAuth(this, new IGHomeApi.Callback2() {
          @Override
          public void callback(int code, String message, String data) {
              if (mResultHandler != null)
                  mResultHandler.handleResult(code, message, null);
          }
      });
    }
    
    private ResultHandler mResultHandler = new ResultHandler() {
    
      @Override
      public void handleResult(int code, String errMsg, Map<String, String> data) {
          if (code == Constants.LINE_BIND_CANCEL) {
              Log.d(TAG, "Line授权取消");
              //此处进行line授权取消后的操作(调用登录logout方法或其他)
          }
      }
    };

9. 附录A:服务器端签名算法

  • 参数说明:

    1) timestamp为调用接口时的unix时间戳(精确到秒)

    2) sequence为请求序列号(游戏服务器端需要保证两次调用接口生成的sequence不重复)

  • 签名算法:

    1) 调用方首先需要将请求的参数根据参数的key(ASCII码值)进行升序排序

    2) 将排序好的接口请求参数和参数值按key=val&key2=val2…这样得格式拼装成一个字符串,并在最后加上与APPID对应的APPKEY

    3) 对上述拼接好的字符串进行md5编码,获得最终的签名串

  • 代码示例(PHP):

    public function sign ($params, $secret_key) { // $params数组必须包含timestamp
    $in = ksort($params);
    $pairs = array();
    foreach($in as $k => $v){
      $pair[] = $k. '=' .$v;
    }
    $str = implode('&', $pair); // 拼接字符串
    $str = $str.$secret_key; // 把APPKEY补充到最后
    return md5($str);
    }

10. 附录B:特殊说明

  • 订单重发机制:

    平台对所有发货状态非“success”的订单,采取统一的重发机制。具体的重发策略为:每60秒重发1次,最大重发次数为60次。

    对尝试重发后,最终失败的订单,我们会隔天通过报表机制发现,由平台方补发货,游戏方不需要参与。

  • 发货重复处理机制:

    基于订单重发机制,可能导致游戏方重复收到相同orderNo的发货通知,此时要求游戏检查orderNo实际到货情况,避免重复向玩家发放游戏币等虚拟货品。建议对一个月内的orderNo做唯一性检查。

    针对已发货成功的订单,要求游戏方收到重复通知时,请务必返回success,状态为“success”的订单,将不会继续触发重发机制。

  • SDK常用类所在包:

    import com.ghomesdk.international.api.GHome;
    import com.ghomesdk.international.api.Constants;
    import com.ghomesdk.google.api.IGHomeApi.Callback;

11. 附录C:常见错误码

错误码 描述
0 成功
-10869543 shakehand失败
-10869560 没有可用的网络连接
-10869540 没有初始化
-10869541 初始化失败
-10869544 没有登录
-10869500 登录失败
-10869501 登录被取消
-10869505 正在进行的付款操作
-10869509 支付取消
-10869512 绑定账户取消
-10869513 绑定账户失败
-10869515 注销失败

12. 常见错误问题解决

接入问题

  • 接入后编译提示jdk版本错误

    若targetSdkVersion为31或以上,编译时提示jdk版本错误,分两种解决方式:
    1.将本地jdk版本升至jdk11,重新编译即可
    2.将targetSdkVersion降为30,重新编译即可

GOOGLE相关问题

  • 登录及支付错误问题

    登陆前确保游戏运营和G家后台已添加游戏参数配置
    国内版手机确保VPN及google框架正常可运行
    确保上传了带有签名的APK文件;
    确保设备上安装了带有签名的APK,而不是调试版;
    确保在测试中添加了测试人员;
    确保设备登录的账号属于测试账号;
    确保测试账号激活了测试链接(最容易忽略);
    确保设备上的应用版本号和版本名称与Google开发者后台上传的APK一致;
    确保商品状态为Active。
  • apk包登录正常,aab包登录错误

    与.apk不同,当上传.aab(应用程序捆绑)时,谷歌现在强迫签署该文件,并且它默认这样做。当谷歌签署它时,它使用SHA1,你只能通过访问你的谷歌游戏控制台帐户在签名部分看到:(在谷歌搜索 "谷歌游戏控制台 - 播放应用程序签名")
    在这里你可以看到谷歌使用的SHA1:这个SHA1是你必须放在Firebase配置中的(不是你在aab文件中的私人签名的SHA1,而是谷歌签名的SHA1)。
  • Google支付遇到“无法购买您要买的商品?”问题

    保证 versionCode 和版本号与你上传的apk的包的一样。
    保证后台和你传入的购买商品的 id 一致。
    确保你所使用的账号是在测试人员里。
    应用签名和google后台上传保持一致。
  • Google支付其他问题

    谷歌支付 10001 谷歌商店最好只保留一个账号
    谷歌支付 1002 谷歌商店不存在当前账号
    谷歌支付 1003 谷歌配置gl_google_pay_key错误
    谷歌支付无弹框 请检查谷歌商店是否支持付费,具体请打开谷歌商店,检查付费栏目是否有数据

FACEBOOK相关问题

  • 登录错误问题

    执行查询错误:添加测试人员账号
    提示hashKey错误:fb密钥散列错误,重新生成
    通过谷歌商店下载fb登录错误:发布谷歌商店后下载的包是经过谷歌二次签名的,需要解析谷歌后台配置的签名文件重新生成密钥,配置到facebook后台,可通过:
    openssl dgst -sha1 -binary <GooglePlay的发布证书>.der | openssl base64 重新生成密钥
  • facebook密钥生成

    可参考官方文档(为应用提供开发和发布密钥散列):https://developers.facebook.com/docs/facebook-login/android