# GHOME Google 开发手册 for Android 国际版

更新日期:2024.05.15

# 版本更新说明

# 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. 开发环境要求

# 登录支持说明

  • 目前支持 G 家登录:游客、邮箱
  • 目前支持三方登录:google、facebook、twitter、line、apple
  • 支持通过 G 家后台配置动态设置选择其中某几个登录
  • !!!提示!!! 游戏接入 SDK 时,若需要使用三方登录,需配置 G 家后台三方登录渠道参数

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

  • !!!提示!!!下载 sdk 后参考接入指南
  • 根据接入指南将所选的 .ar 文件放在游戏项目的libs文件夹下
  • 项目build.gradle文件参考build.gradle文件
  • 项目根目录的build.gradle文件参考app_build.gradle文件
  • 项目根目录下的gradle.properties参考gradle.properties文件

# 3.2. 增加google配置

  • 获取地址参考官方文档(配置 Google API 控制台项目):https://developers.google.com/identity/sign-in/android/start-integrating?hl=zh-cn#configure_a_console_name_project

  • 在manifest中增加如下配置

    <meta-data android:name="com.google.android.gms.games.APP_ID"
              android:value="请填写google appId" />
    
    1
    2

# 3.3. 增加facebook配置

  • 获取地址参考官方文档(获取应用编号及获取客户端口令):https://developers.facebook.com/docs/android/getting-started#

  • 在manifest中增加如下配置

    <meta-data 
        android:name="com.facebook.sdk.ApplicationId"
        android:value="fb+填写facebook appId"/>   
    <meta-data 
        android:name="com.facebook.sdk.ClientToken"
        android:value="填写facebook_client_token"/>
    
    1
    2
    3
    4
    5
    6

# 3.4. 增加twitter配置

  • 在manifest中增加如下配置

    <meta-data 
        android:name="twitterConsumerKey"
        android:value="配置twitter分配的API Key对应的值"/>   
    <meta-data 
        android:name="twitterConsumerSecret"
        android:value="配置twitter分配的API secret Key对应的值"/>
    
    1
    2
    3
    4
    5
    6

# 3.5. 增加line配置

  • 在manifest中增加如下配置

    <meta-data
          android:name="lineAppId"
          android:value="配置line分配的CHANNEL_ID的值" />
    
    1
    2
    3

# 3.6. 地区api host自定义说明

注意:

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

配置项说明:

  • ghomesdkHostApiBiz 为业务服务端接口地址(主要包含认证业务,或除支付以外的业务接口)
  • ghomesdkHostApiPay 为业务服务端接口地址(主要包含支付业务接口)
  • ghomesdkHostApiLog 为日志收集服务端接口地址
  • ghomesdkHostApiSupplement 为退款补缴业务地址
  • ghomesdkHostApiPic 为邮箱登录图验地址
  • 所有配置项的key value可以独立配置,即例如只配置了 ghomesdkHostApiLog 使用北美,则SDK效果是:业务数据在新加坡,日志在北美。

地区列表说明:

  • 新加坡(默认):无需配置
  • 北美地区:ghomesdkHostApiBiz="咨询GhomeSDK产品或服务端负责人" ghomesdkHostApiPay="咨询GhomeSDK产品或服务端负责人" ghomesdkHostApiLog="reportsk-gg.web.sdo.com"
  • 其他地区:咨询GhomeSDK产品或服务端负责人

重要说明:

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

<!-- 样例为默认值(新加坡)-->
<meta-data
   android:name="ghomesdkHostApiBiz"
   android:value="abroad-sin.shengqugames-corp.com" />
<meta-data
   android:name="ghomesdkHostApiPay"
   android:value="abroad-sin.shengqugames-corp.com" />
<meta-data
   android:name="ghomesdkHostApiLog"
   android:value="reportsk-gg.web.sdo.com" />
<meta-data
   android:name="ghomesdkHostApiSupplement"
   android:value="abroad-sin-wpay.shengqugames-corp.com" />
<meta-data
   android:name="ghomesdkHostApiPic"
   android:value="abroad-sin-wlogin.shengqugames-corp.com" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

更多枚举信息,点这里

# 4. 基础功能(必接)

# 4.0. 客户端主Activity中需要接入的生命周期

  • 代码示例:
import com.ghome.sdk.GHome;

@Override
protected void onCreate() {
  super.onCreate();
   GHome.getInstance().create(this);
}

@Override
protected void onResume() {
  super.onResume();
  GHome.getInstance().resume(this);

@Override
protected void onStart() {
  super.onStart();
  GHome.getInstance().start(this);
}

@Override
protected void onRestart() {
  super.onRestart();
  GHome.getInstance().restart(this);
}

@Override
protected void onPause() {
  super.onPause();
  GHome.getInstance().pause(this);
}

@Override
protected void onStop() {
  super.onStop();
  GHome.getInstance().stop(this);
}

@Override
protected void onDestroy() {
  super.onDestroy();
  GHome.getInstance().destroy(this, null);
}

@Override
protected void onNewIntent(Intent intent) {
  super.onNewIntent(intent);
  GHome.getInstance().onNewIntent(this, intent);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 4.1. SDK初始化接口

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

  • 接口:

    initial(final Activity activity, final String gameId, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

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

  • 常量定义:

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

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

  • 代码示例:

    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{
          // 初始化失败
      }
    
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 4.2. SDK销毁接口

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

  • 接口:

    destroy(final Activity activity,final Callback callback);

  • 参数说明:

    activity: Activity对象。

  • 注意事项:

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

  • 代码示例:

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

# 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);
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

# 4.4. SDK数据上报

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

  • 注意事项:

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

  • 代码示例:

    @Override
    protected void onResume() {
        super.onResume();
        GHome.getInstance().resume(this);
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        GHome.getInstance().pause(this);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 4.5. 获取SDK版本号

游戏客户端通过该方法获取sdk版本号。

  • 代码示例:

      String sdkVerison = GHome.getSdkVersionName();
    
    1

# 5. 账号功能(必接)

# 5.1. 账号体系说明

  • 注册&登录原理

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

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

    3. 游戏如果有服务器,需要把客户端获取到ticket票据发送到“登录票据验证接口”【参考服务端文档的认证描述部分点击跳转 (opens new window)】进行验证,然后把返回的用户信息(用户id和账号)保存到游戏服务器;

  • 注册&登录时线图
    Android3

# 5.2. 客户端登录接口

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

  • 接口:

    login(final Activity activity, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 注意事项:

    通常情况游戏不需要提示用户登录结果,无论是登录失败还是登录成功,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是否绑定(只有游客才能绑定,如果是空则代表已绑定)
          // loginType是本次登录的登录类型; 0:游客 588:邮箱 502:google 503:FaceBook 504:Apple 505:Twitter 506:Line
          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("loginType=" + data.get("loginType"));
          //
          // 登录成功,进入游戏界面,游戏开发者补充逻辑。
        } else if (code == ErrorCode.ERROR_CODE_BIRTH_NOT_CHOOSE) {
           //未收集年龄信息,登录失败,如果游戏需要,可在此进行特出处理
    
        } else if(code == Constants.ERROR_NO_INIT || code == Constants.ERROR_INIT_ERROR){
          // 可在此处再次调用初始化接口
    
          // mGHome.initial(final Activity activity, final String gameId, final Callback callback);
        }else{
          // 登录失败
      }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

# 5.3 客户端游客绑定接口

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

  • 接口:

    GHome.getInstance().bindAccount(final Activity activity, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 代码示例:

    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 {
                // 游客绑定失败
            }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 5.4. 客户端退出登录接口

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

  • 接口:

    logout(final Activity activity, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 注意事项:

    暂无

  • 代码示例:

    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 {
              // 退出登录失败
            }
        }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 5.5 客户端获取区服信息

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

  • 接口:

    queryAreaInfo(final Activity activity,final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 代码示例:

    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 {
                    // 获取区服信息失败
                    }
                }
          });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    • JSON格式说明:
    {
      "message":[
      {
      "area_code":"1",  // 开发商后台配置的区服ID
      "name":"area1",    // 开发商后台配置的区服名称
      "notify_url":"http:\/\/test.hps.sdo.com\/mork\/notifygame"    // 充值发货地址
      },
      ...
      ]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 5.6 客户端获取商品信息

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

  • 接口:

    queryProductInfo(final Activity activity,final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 代码示例:

    GHome.getInstance().queryProductInfo(DemoActivity.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 {
                          // 获取商品信息失败
                        }
                    }
            });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • JSON格式说明:

    {
      "message":[
      {
      "item_name":"test1",  // 商品名称
      "money":"5"    // 商品价格
      "product_code":"test.id"    // 商品id
      "type":1   // 商品类型
      },
      ...
      ]
    } 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

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

  • 接口

    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));
                }
    
            }
        });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 5.8. 客户端扫码登录 PC 端

  • 接口

    scanQRCode(Activity activity, Callback callback);

  • 参数说明

    activity:Activity对象

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

  • 注意事项

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

  • 代码示例

    GHome.getInstance().scanQRCode(DemoActivity.this, new Callback() {
            @Override
            public void callback(int code, String message, Map<String, String> data) {
                if (code == ErrorCode.ERROR_NO_ERROR) {
                    // 登录 pc 端成功
                
                    } else {
                    // 登录 pc 端失败
                  }
            }
        });
    
    
    //在游戏客户端主Activity中接入
    //添加申请权限回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          GHome.getInstance().onRequestPermissionsResult(this, requestCode, permissions, grantResults);
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

# 5.9. 客户端注销账号

  • 接口

    logoff(Activity activity, Callback callback);

  • 参数说明

    activity:Activity对象

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

  • 注意事项

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

  • 代码示例

    GHome.getInstance().logoff(DemoActivity.this, new Callback() {
            @Override
            public void callback(int code, String message, Map<String, String> data) {
                if (code == ErrorCode.ERROR_NO_ERROR) {
                    // 注销成功,游戏做退出操作
                
                    } else {
                    // 注销失败
                  }
            }
        });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 6. 支付功能(必接)

# 6.1. 支付原理说明

  • 支付原理

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

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

    3. 第三方支付成功收费以后通知SDK服务器,再由SDK服务器通知游戏服务器发货至游戏,需要游戏提供“订单通知接口”进行接收【参考服务端文档的支付描述部分点击跳转 (opens new window)】;

  • 支付时线图

    Android4

# 6.2. 客户端支付接口

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

  • 接口:

    pay(final Activity activity, final String orderId, final String areaId, final String productId,final String extend, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

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

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

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

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

  • 注意事项:

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

  • 代码示例:

    GHome.getInstance().pay(this, "orderId", "areaId", "productId", new Callback() {
      @Override
      public void callback(int code, String message, Map<String, String> data) {
        if (code == ErrorCode.ERROR_NO_ERROR) {
          // 支付成功,游戏客户端可以在这里查询游戏服务器是否收到发货通知
          // 如果游戏服务器已经收到发货通知可以向用户发放道具
        }
          //6.3.0.230804版本 G 家对待缴数据做显示处理,游戏端可忽略该错误处理
         else if(code == ErrorCode.ERROR_HAVE_PAYPREPARE{
          // 支付暂未成功,这种状态存在待补缴订单,游戏端需将待补缴订单显示给用户
          // 用户对待补缴订单进行逐条补缴完成后方可继续支付购买商品
          // data为透传数据,参考格式说明
         } 
         //6.3.0.230804版本新增
         else if(code == ErrorCode.ERROR_CHANGE_LOGIN){
          // 该错误是用户在待缴支付界面选择换账号登录,游戏端可做对应处理
         } 
        else {
          // 支付暂未成功,这种状态可能是用户未完成支付,也可能是尚未收到渠道的发货通知,可做相应的友好提示
          // 游戏客户端可以不做处理,用户下次登录时再做查询
        }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    • 格式说明:
      {
        ext = , 
      gameOrderId = MS010178040012221122161456000001, 
      orderType = 1, 
      areaId = 1, 
      productId = test0000001, 
      supplementOrderList= [{
          "payOrderNo": "MP010178040015230419190027000001",//在补单支付时候orderId传入该字段
          "gameOrderId": "MS010178040012221122161456000001",
          "appMid": "10516778",
          "areaId": "1",//在补单支付时候areaId传入该字段
          "payChannelCode": "google",
          "priceAmount": "135",
          "priceLocale": "JPY",
          "productId": "test0000001",//在补单支付时候productId传入该字段
          "productName": "test0000001",
          "refundTime": "2023-08-14 16:25:43",
          "payTime": "2023-08-04 10:56:00"
        },
        {
          "payOrderNo": "MP010178040015230419190027000001",
          "gameOrderId": "MS010178040012221122161456000001",
          "appMid": "10516778",
          "areaId": "1",
          "payChannelCode": "google",
          "priceAmount": "135",
          "priceLocale": "JPY",
          "productId": "test0000001",
          "productName": "test0000001",
          "refundTime": "2023-08-14 16:25:43",
          "payTime": "2023-08-04 10:56:00"
        }]
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

# 6.3. 客户端获取待补缴订单列表接口

游戏客户端可以在登录完成后任意地方调用该接口。这将获取待缴订单的数据列表,游戏可对列表数据进行逐一补缴。

  • 接口:

    querySupplementOrderList(final Activity activity, final Callback callback);

  • 参数说明:

    activity: Activity对象。

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

  • 代码示例:

    GHome.getInstance().querySupplementOrderList(this, new Callback() {
      @Override
      public void callback(int code, String message, Map<String, String> data) {
        if (code == ErrorCode.ERROR_NO_ERROR) {
          // 获取待补缴订单列表成功
          String supplementOrderList = data.get("supplementOrderList")
          // 游戏端需将待补缴订单显示给用户
          // data为透传数据,参考格式说明
        } 
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 待补缴订单json示例

    [{
          "payOrderNo": "MP010178040015230419190027000001",
          "gameOrderId": "MS010178040012221122161456000001",
          "appMid": "10516778",
          "areaId": "1",
          "payChannelCode": "google",
          "priceAmount": "135",
          "priceLocale": "JPY",
          "productId": "test0000001",
          "productName": "test0000001",
          "refundTime": "2023-08-14 16:25:43",
          "payTime": "2023-08-04 10:56:00"
      },
      {
          "payOrderNo": "MP010178040015230419190027000001",
          "gameOrderId": "MS010178040012221122161456000001",
          "appMid": "10516778",
          "areaId": "1",
          "payChannelCode": "google",
          "priceAmount": "135",
          "priceLocale": "JPY",
          "productId": "test0000001",
          "productName": "test0000001",
          "refundTime": "2023-08-14 16:25:43",
          "payTime": "2023-08-04 10:56:00"
      }]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    字段说明:

    payOrderNo:G家支付订单号,在补单支付的时候作为orderId传入

    gameOrderId:G家商城订单号

    appMid:游戏帐号ID

    areaId:区ID

    payChannelCode:支付渠道,枚举:="google"、="mycard"、="onestore",【注意大小写一致,请勿修改】

    priceAmount:商品金额

    priceLocale:支付货币

    productId:商品ID

# 6.4. 客户端补缴接口

游戏客户端可以在支付失败后展示补缴订单后再进行补缴的地方调用该接口。这将打开支付界面,引导玩家补缴该订单。用户结束补缴操作后,SDK会回调游戏客户端,告知补缴结果是否已经成功。

  • 接口:

    pay(final Activity activity, final String orderId, final String areaId, final String productId,final int orderType,final String extend, final Callback callback);

  • 参数说明:

    activity: Activity对象。

    orderId: 必传,待补缴订单列表中payOrderNo字段

    areaId: 必传,待补缴订单列表中areaId字段

    productId: 必传,待补缴订单列表中productId字段

    orderType: 必传,该字段代表当前为补缴订单,默认传2

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

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

  • 代码示例:

    GHome.getInstance().pay(this, "orderId", "areaId", "productId", 2,  new Callback() {
      @Override
      public void callback(int code, String message, Map<String, String> data) {
        if (code == ErrorCode.ERROR_NO_ERROR) {
          // 补缴成功,游戏客户端可从待补缴列表中删除本次补缴订单,进行其他补缴
        } else {
          // 补缴失败
        }
      }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# 6.5 客户端获取谷歌商店商品信息

游戏客户端可以调用获取商店商品信息接口得到对应商品id在谷歌后台中具体商品信息。

  • !!!提示!!! 该接口只为游戏提供检查商品id是否在对应谷歌商店正常配置,若游戏需要获取商品列表或进行币种适配,请自行实现。
  • 接口:

    getProductMsgInfo(final Activity activity,final List productIdList, final Callback callback);

  • 参数说明:

    activity: Activity对象。

    productIdList: 必传,在手游直通车平台上设置的商品的ID集合。

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

  • 代码示例:

    List productIdList = new ArrayList();
    productIdList.add("test0000001");
    productIdList.add("test0000002");
    productIdList.add("test0000003");
    
    GHome.getInstance().getProductMsgInfo(DemoActivity.this, productIdList,
             new Callback() {
    
                    @Override
                    public void callback(int code, String errMsg, Map<String, String> data) {
                          if (code == ErrorCode.ERROR_NO_ERROR) {
                          // 获取谷歌商品信息成功
                          // 该json为谷歌商品信息透传,祥见12. 附录B:查询谷歌商品信息透传 12.1. 谷歌商品
                          String json = data.get("data");
                          // Gson gson = new Gson();
                          // List<com.android.billingclient.api.ProductDetails> productDetailsList = gson.fromJson(data.get("data"), new TypeToken<List<com.android.billingclient.api.ProductDetails>>() {}.getType());
                        } else {
                          // 获取谷歌商品信息失败
                        }
                    }
            });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

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

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

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

  • 接口:

    loginArea(final Activity activity, final String areaId);

  • 参数说明:

    activity: Activity对象。

    areaId: 用户进入的区号。

    handler: 回调接口

  • 注意事项:

    暂无

  • 代码示例:

GHome.getInstance().loginArea(this, "1");
1

# 8.分享功能

# 8.1. 配置分享相关的AndroidManifest.xml 配置项:

<!-- GShare start -->
        <!-- provider 方式分享 authorities 配置为:com.facebook.app.FacebookContentProvider 后拼接 facebookookId -->
        <provider
            android:name="com.facebook.FacebookContentProvider"
            android:authorities="com.facebook.app.FacebookContentProvider拼接facebookookId"
            android:exported="true" />

<!-- GShare end -->
1
2
3
4
5
6
7
8

# 8.2. 分享相关生命周期接口(游戏主activity中接入)

@Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
        //gshare
      GShare.init(this);
    }

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //gshare
       GShare.onActivityResult(requestCode, resultCode, data);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.3. 分享接口入参说明

分享类型:

GShareChannel channel = GShareChannel.GHShareChannelSystem;

public enum GShareChannel
{
    GHShareChannelSystem,//系统分享
    GHShareChannelFacebook,//facebook分享
    GHShareChannelMessenger,//messenger分享
    GHShareChannelWhatsApp;//whatsapp分享
}
1
2
3
4
5
6
7
8
9

# 8.4. 分享文字(facebook不支持纯文本分享)

  • 接口:

    shareTextWithChannel(GShareChannel channel, String shareText, GShareListener listener);

  • 参数说明:

    channel 必传,GShareChannel对象。

    shareText 必传,需要分享的文本内容。

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

  • 代码示例:

GShare.shareTextWithChannel(channel, "测试分享纯文本", new GShareListener() {
                    @Override
                    public void onResult(GShareChannel channel, GShareResultCode code, String message) {
                        if (code == GShareResultCode.GShareResultSuccess) {
                            Log.d("DemoActivity", "分享成功");
                        } else {
                            Log.d("DemoActivity", "分享失败 = " + message);
                        }
                    }
                });
1
2
3
4
5
6
7
8
9
10

# 8.5. 分享链接

  • 接口:

    shareUrlWithChannel(GShareChannel channel, String shareText,String shareUrl, GShareListener listener);

  • 参数说明:

    channel 必传,GShareChannel对象。

    shareText 非必传,分享链接的说明,没有则传null。

    shareUrl 必传,需要分享的链接。

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

  • 代码示例:

GShare.shareUrlWithChannel(channel, "测试分享链接", "http://www.baidu.com",new GShareListener() {
                    @Override
                    public void onResult(GShareChannel channel, GShareResultCode code, String message) {
                        if (code == GShareResultCode.GShareResultSuccess) {
                            Log.d("DemoActivity", "分享成功");
                        } else {
                            Log.d("DemoActivity", "分享失败 = " + message);
                        }
                    }
                });
1
2
3
4
5
6
7
8
9
10

# 8.6. 分享图片

  • 接口:

    shareImageWithChannel(GShareChannel channel, String imagePath, GShareListener listener);

  • 参数说明:

    channel 必传,GShareChannel对象。

    imagePath 必传,需要分享的图片地址。

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

  • 代码示例:

GShare.shareImageWithChannel(channel, this.getExternalFilesDir(null).toString() + "/" + "GShare.jpg" ,new GShareListener() {
                    @Override
                    public void onResult(GShareChannel channel, GShareResultCode code, String message) {
                        if (code == GShareResultCode.GShareResultSuccess) {
                            Log.d("DemoActivity", "分享成功");
                        } else {
                            Log.d("DemoActivity", "分享失败 = " + message);
                        }
                    }
                });
1
2
3
4
5
6
7
8
9
10

# 9.本地定时通知功能

  • 1.通知 id 重复则会替换之前的通知消息
  • 2.本地通知依赖app实现,杀掉 app 进程后会收不到推送通知
  • 3.需要在进程杀死的情况下正常收到通知消息需要游戏方自主保活

# 9.1. 本地定时通知相关生命周期接口

@Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
        //gpush
      GPush.init(this, "gameId(791000***)");
    }

@Override
    protected void onNewIntent(Intent intent){
        super.onNewIntent(intent);
        //gpush通知点击后收到的通知内容
        String param = intent.getStringExtra("param");
    }

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         //gpush通知权限
        GPush.onRequestPermissionResult(requestCode, permissions, grantResults);
    }   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 9.2 调用SDK的判断本地通知权限是否打开方法

游戏可调用该接口检测通知权限是否已打开(当游戏targetversion>=33 且 Android 版本>=13 ,申请动态通知权限。了解更多 (opens new window)

  • 接口:

    GPush.isHavaNotification(Activity activity,boolean isShowGHomeDialog);

  • 参数说明:

    activity 必传,Activity对象(动态权限要求,传入 Activity 对象)。

    isShowGHomeDialog 必传,传入 true 代表显示 G 家通知权限授权框,false 代表不需要显示 G 家权限授权框,游戏端需要自己显示权限授权。

  • 代码示例:

        //若isShowGHomeDialog传入 true,游戏方调用后不需处理其他。
        GPush.isHavaNotification(DemoPushActivity.this, true);

        //若isShowGHomeDialog传入 false, 该接口返回 false 后游戏方需自行显示授权弹框跳转设置界面
        if (!GPush.isHavaNotification(DemoPushActivity.this, false)) {
           //游戏自行处理权限授权弹框
           //显示弹框。
           //打开设置 10.3 调用SDK的打开系统通知配置设置GPush.openNotificationSettingsForApp(Context context);
        }else{
          //无需处理
        } 
1
2
3
4
5
6
7
8
9
10
11

# 9.3 调用SDK的打开系统通知配置设置

游戏可调用该接口打开手机系统通知配置设置界面(该接口配合10.2接口返回未开启通知权限使用)

  • 接口:

    GPush.openNotificationSettingsForApp(Context context);

# 9.4 本地定时通知接口入参说明

通知参数:

NotifyObject obj =  new NotifyObject();

public class NotifyObject
{
   public Integer type;//(必传)通知id,若id为重复,默认为更新该id的通知
   public Class<? extends Activity> activityClass;//(必传)通知消息跳转类
   @DrawableRes
   public int icon;//(必传)通知icon
   public Long firstTime;//(必传)通知时间(时间戳-毫秒级)


   public String title;//通知标题
   public String subText;//通知副标题
   public String content;//通知内容
   public String param;//通知消息点击后传递的数据
   public boolean showBigText;//是否显示超大文本内容通知
   public boolean showBigLarge;//是否显示超大图片通知
   @DrawableRes
   public int largeIcon;//通知图片
   @DrawableRes
   public int bigLarge;//大图片通知模式中显示图片
   public boolean isRepeat;//是否重复通知
   public Long intervalTime;//重复通知间隔时间。不填默认为间隔一天
   public int importanceType = 4;//通知消息重要程度:
    0.关闭通知,
    1.开启通知,不弹出,无提示音,状态栏不显示,
    2.开启通知,不弹出,无提示音,状态栏显示,
    3.开启通知,不会弹出,发出提示音,状态栏显示,
    4.开启通知,会弹出,发出提示音,状态栏显示

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 10.5 调用SDK的发送定时通知方法

游戏可调用该接口发送本地通知(若通知权限授权用户未通过,手机无通知权限,该接口listener返回false,该推送通知创建失败)

  • 接口:

    GPush.notifyByAlarm(Context context, Map<Integer, NotifyObject> notifyObjects, GPushListener listener);

  • 参数说明:

    context 必传,Context对象。

    notifyObjects 必传,推送通知的具体内容。

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

  • 代码示例:

    try {
      Map<Integer, NotifyObject> notifyObjects = new HashMap<>();
                    NotifyObject obj = new NotifyObject();
                    obj.type = notificationId;//必填,通知id
                    obj.title = titleStr;//通知标题
                    obj.content = contentStr;//通知内容
                    obj.activityClass = DemoActivity.class;//必填,通知点击后跳转事件类,若无特殊跳转,请传入游戏主activity类
                    obj.icon = R.drawable.ghome_ic_launcher;//必填,通知标题icon,若无特殊,请传入游戏使用icon
                    obj.largeIcon = R.drawable.ghome_push_icon;//选填,通知消息类所使用的图片
                    obj.param = "";//选填,点击后跳转事件类带入的事件参数,如gameId等等。。
                    obj.showBigLarge = true;//选填,是否使用大图片类型通知消息,该类型可展示大图片通知内容类型通知
                    obj.bigLarge = R.drawable.ghome_push_icon;//选填,采用大图片通知类型所使用的图片
                    obj.showBigText = true;//选填,是否使用大文本类型通知消息,该类型可展示超长通知内容类型通知

                    //如下可选择多种模式通知
                    //定义立即发送通知消息
                    long now = System.currentTimeMillis();
                    SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date = new Date(now);
                    obj.firstTime = now;//必填,请传入首次通知时间,该时间为时间戳-毫秒级)
                    obj.subText = smf.format(date);//选填,通知消息栏显示的推送到达时间
                    notifyObjects.put(obj.type, obj);

                    //定义某一时间发送通知消息
                    SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Calendar calendar = Calendar.getInstance();
                    calendar.set(yearStr, monthStr, dayStr, hourStr, minuteStr, 0);//传入年月日时分
                    Date date = new Date(calendar.getTimeInMillis());
                    obj.firstTime = calendar.getTimeInMillis();//必填,请传入首次通知时间,该时间为时间戳-毫秒级)
                    obj.subText = smf.format(date);//选填,通知消息栏显示的推送到达时间
                    notifyObjects.put(obj.type, obj);

                    //定义当前时间多久后发送通知消息
                    Long inter = 60 * 1000;//60秒
                    long now = System.currentTimeMillis();
                    SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date = new Date(now + inter);
                    obj.firstTime = now + inter;//必填,请传入首次通知时间,该时间为时间戳-毫秒级)
                    obj.subText = smf.format(date);//选填,通知消息栏显示的推送到达时间
                    notifyObjects.put(obj.type, obj);

                    //定义重复通知,若采用此模式,请传入下方参数
                    obj.isRepeat = true;//开启重复通知
                    obj.intervalTime = 0 ;//重复通知间隔时间,若开启重复通知,不传入此项或传入0,默认为间隔一天
                    //设置重复闹钟的开启时间
                    SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Calendar calendar = Calendar.getInstance();
                    calendar.set(Calendar.HOUR_OF_DAY, hourStr);//传入时
                    calendar.set(Calendar.MINUTE, minuteStr);//传入分
                    calendar.set(Calendar.SECOND, 0);
                    Date date = new Date(calendar.getTimeInMillis());
                    obj.firstTime = calendar.getTimeInMillis();//必填,请传入首次通知时间,该时间为时间戳-毫秒级)
                    obj.subText = smf.format(date);//选填,通知消息栏显示的推送到达时间
                    notifyObjects.put(obj.type, obj);

                    //定义某时间后开始间隔多久发送通知消息
                    long interval[] = {5*60*1000, 10*60*1000, 30*60*1000, 60*60*1000};//5分钟后,10分钟后,30分钟后,1小时后发送通知
                    SimpleDateFormat smf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    long now = System.currentTimeMillis();
                    for (long inter : interval) {
                        Date date = new Date(now + inter);
                        NotifyObject obj = new NotifyObject();
                        obj.type = notificationId++;//通知id
                        obj.title = titleStr;
                        obj.subText = smf.format(date);
                        obj.content = contentStr;
                        obj.firstTime = now + inter;
                        obj.activityClass = DemoActivity.class;
                        obj.icon = R.drawable.ghome_ic_launcher;
                        obj.largeIcon = R.drawable.ghome_push_icon;
                        obj.param = obj.toString();
                        notifyObjects.put(obj.type, obj);
                    }

                    GPush.notifyByAlarm(DemoPushActivity.this, notifyObjects, new GPushListener() {
                        @Override
                        public void onResult(int code, String message, NotifyObject obj) {
                            if (code == 0) {
                                Log.e(TAG, "通知已设定");
                            } else {
                                Log.e(TAG, "demo错误:" + message);
                            }
                        }
                    });
          } catch (Exception e) {
                    Log.e(TAG, "demo错误:" + e.getMessage());
                    ToastUtil.showMessage(DemoPushActivity.this, "demo错误:" + e.getMessage());
          }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# 10.6 调用SDK的取消单个通知接口

游戏可调用该接口取消单个通知

  • 接口:

    GPush.clearOneNotifyMsgAndAlarm(Context context, int id, boolean isDeleteAlarm, boolean isDeleteNotityMsg);

  • 参数说明:

    context 必传,Context对象。

    id 必传,需要取消的通知id。

    isDeleteAlarm: 是否删除后续通知(该字段为取消还未发送的通知)

    isDeleteNotityMsg 是否删除已发通知(该字段为取消已经发送且到达通知栏的通知)

  • 代码示例:

    GPush.clearOneNotifyMsgAndAlarm(DemoPushActivity.this,1, true, false);
1

# 10.7 调用SDK的取消所有通知接口

游戏可调用该接口取消所有通知

  • 接口:

    GPush.clearAllNotifyMsgAndAlarm(Context context, boolean isDeleteAlarm, boolean isDeleteNotityMsg);

  • 参数说明:

    context 必传,Context对象。

    isDeleteAlarm: 是否删除后续通知(该字段为取消还未发送的通知)

    isDeleteNotityMsg 是否删除已发通知(该字段为取消已经发送且到达通知栏的通知)

  • 代码示例:

    GPush.clearAllNotifyMsgAndAlarm(DemoPushActivity.this, true, false);
1

# 12. 附录A:常见错误码

错误码 描述
0 成功
-10869543 shakehand失败
-10869560 没有可用的网络连接
-10869540 没有初始化
-10869541 初始化失败
-10869544 没有登录
-10869500 登录失败
-10869501 登录被取消
-10869505 正在进行的付款操作
-10869509 支付取消
-10869510 支付失败
-10310522 支付失败,有待补缴订单
-10869511 请勿重复登录
-10869512 绑定账户取消
-10869513 绑定账户失败
-10869517 没有可用的绑定渠道
-10869515 注销失败

# 12. 附录B:查询谷歌商品信息透传

# 12.1 谷歌商品

[
    {
        "zza": "{"productId":"test0000001","type":"inapp","title":"测试商品 (G家手游国际版Demo)","name":"测试商品","description":"用于功能测试","localizedIn":["zh-CN"],"skuDetailsToken":"AEuhp4LeDG-KRQRXgdRTsNFl9LRrtQdV6qZGFhpJ_Nhd9-Bw3gvpO3QKg6GlwYQ3WJ8=","oneTimePurchaseOfferDetails":{"priceAmountMicros":132060961,"priceCurrencyCode":"JPY","formattedPrice":"JP¥132"}}",
        "zzb": {
            "nameValuePairs": {
                "description": "用于功能测试",
                "localizedIn": {
                    "values": [
                        "zh-CN"
                    ]
                },
                "name": "测试商品",
                "oneTimePurchaseOfferDetails": {
                    "nameValuePairs": {
                        "formattedPrice": "JP¥132",
                        "priceAmountMicros": 132060961,
                        "priceCurrencyCode": "JPY"
                    }
                },
                "productId": "test0000001",
                "skuDetailsToken": "AEuhp4LeDG-KRQRXgdRTsNFl9LRrtQdV6qZGFhpJ_Nhd9-Bw3gvpO3QKg6GlwYQ3WJ8=",
                "title": "测试商品 (G家手游国际版Demo)",
                "type": "inapp"
            }
        },
        "zzc": "test0000001",
        "zzd": "inapp",
        "zze": "测试商品 (G家手游国际版Demo)",
        "zzf": "测试商品",
        "zzg": "用于功能测试",
        "zzh": "AEuhp4LeDG-KRQRXgdRTsNFl9LRrtQdV6qZGFhpJ_Nhd9-Bw3gvpO3QKg6GlwYQ3WJ8=",
        "zzi": "",
        "zzk": [
            {
                "zza": "JP¥132",
                "zzb": 132060961,
                "zzc": "JPY",
                "zzd": "",
                "zze": "",
                "zzf": []
            }
        ]
    },
    {
        "zza": "{"productId":"test0000002","type":"inapp","title":"测试商品2 (G家手游国际版Demo)","name":"测试商品2","description":"测试商品2","localizedIn":["zh-CN"],"skuDetailsToken":"AEuhp4I-Ok865UdB8oJeQQdhZQpILoWubzfNXrwzggABIxPDF59T77g2MsUzhM3sxa8=","oneTimePurchaseOfferDetails":{"priceAmountMicros":10564876,"priceCurrencyCode":"JPY","formattedPrice":"JP¥11"}}",
        "zzb": {
            "nameValuePairs": {
                "description": "测试商品2",
                "localizedIn": {
                    "values": [
                        "zh-CN"
                    ]
                },
                "name": "测试商品2",
                "oneTimePurchaseOfferDetails": {
                    "nameValuePairs": {
                        "formattedPrice": "JP¥11",
                        "priceAmountMicros": 10564876,
                        "priceCurrencyCode": "JPY"
                    }
                },
                "productId": "test0000002",
                "skuDetailsToken": "AEuhp4I-Ok865UdB8oJeQQdhZQpILoWubzfNXrwzggABIxPDF59T77g2MsUzhM3sxa8=",
                "title": "测试商品2 (G家手游国际版Demo)",
                "type": "inapp"
            }
        },
        "zzc": "test0000002",
        "zzd": "inapp",
        "zze": "测试商品2 (G家手游国际版Demo)",
        "zzf": "测试商品2",
        "zzg": "测试商品2",
        "zzh": "AEuhp4I-Ok865UdB8oJeQQdhZQpILoWubzfNXrwzggABIxPDF59T77g2MsUzhM3sxa8=",
        "zzi": "",
        "zzk": [
            {
                "zza": "JP¥11",
                "zzb": 10564876,
                "zzc": "JPY",
                "zzd": "",
                "zze": "",
                "zzf": []
            }
        ]
    },
    {
        "zza": "{"productId":"test0000003","type":"inapp","title":"测试商品3 (G家手游国际版Demo)","name":"测试商品3","description":"测试商品3","localizedIn":["zh-CN"],"skuDetailsToken":"AEuhp4LBgKZXC_tLAyR6FfEy650TqPRTZ3QL3xHoSxO0fJyNGBooDWeP1FFAeAPH5aA=","oneTimePurchaseOfferDetails":{"priceAmountMicros":660304809,"priceCurrencyCode":"JPY","formattedPrice":"JP¥660"}}",
        "zzb": {
            "nameValuePairs": {
                "description": "测试商品3",
                "localizedIn": {
                    "values": [
                        "zh-CN"
                    ]
                },
                "name": "测试商品3",
                "oneTimePurchaseOfferDetails": {
                    "nameValuePairs": {
                        "formattedPrice": "JP¥660",
                        "priceAmountMicros": 660304809,
                        "priceCurrencyCode": "JPY"
                    }
                },
                "productId": "test0000003",
                "skuDetailsToken": "AEuhp4LBgKZXC_tLAyR6FfEy650TqPRTZ3QL3xHoSxO0fJyNGBooDWeP1FFAeAPH5aA=",
                "title": "测试商品3 (G家手游国际版Demo)",
                "type": "inapp"
            }
        },
        "zzc": "test0000003",
        "zzd": "inapp",
        "zze": "测试商品3 (G家手游国际版Demo)",
        "zzf": "测试商品3",
        "zzg": "测试商品3",
        "zzh": "AEuhp4LBgKZXC_tLAyR6FfEy650TqPRTZ3QL3xHoSxO0fJyNGBooDWeP1FFAeAPH5aA=",
        "zzi": "",
        "zzk": [
            {
                "zza": "JP¥660",
                "zzb": 660304809,
                "zzc": "JPY",
                "zzd": "",
                "zze": "",
                "zzf": []
            }
        ]
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

# 13. 常见错误问题解决

# 接入问题

Last Updated: 6/18/2024, 2:15:26 AM