GHOME SDK 开发手册 for Windows


1. 前言

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

2. SDK接入流程概述

  • 搭建SDK开发环境

  • 初始化SDK

  • 初始化成功,调用账号功能相关接口

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

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

  • 销毁SDK

3. 开发环境要求

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

  • 将压缩包中的文件解压到游戏工程目录下的指定目录中,推荐使用“sdo”目录。
  • 在调用代码中增加头文件引用(#include "ghome-sdk.h")
  • 加载ghomesdk.dll文件(loadlibrary("xxx\sdo\ghomesdk.dll"))。

  • 注意事项:

    所有接口中的字符串参数均为UTF-8格式,请注意编码格式。

  • 代码示例:
HMODULE hModule = LoadLibraryA("sdo\\ghomesdk.dll");
if (hModule == NULL)
{
  printf("load library failed.");
  return -1;
}

4. 基础功能(必接)

4.1. SDK初始化接口

在调用其他API前需先调用此接口对SDK进行初始化。否则可能无法正常使用后续的接口。

  • 接口:

    int __stdcall initial(const char appId, const char appVersion);

  • 参数说明:

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

    appVersion: 游戏版本号。

  • 注意事项:

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

  • 代码示例:
initial pInitial = (initial)GetProcAddress(hModule, "initial");

int result = pInitial("100", "1.0.1");
if (result != ERROR_CODE_SUCCESS)
{
  printf("initial failed result code %d\n", result);
}

4.2. SDK销毁接口

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

  • 接口:

    int __stdcall destroy();

  • 参数说明:

  • 注意事项:

  • 代码示例:
destroy pDestroy = (destroy)GetProcAddress(hModule, "destroy");

int result = pDestroy();
if (result != ERROR_CODE_SUCCESS)
{
  printf("initial failed result code %d\n", result);
}

5. 账号功能(必接)

5.1. 账号体系说明

  • 注册&登录原理

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

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

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

  • 注册&登录时线图
    Android3

5.2. 客户端获取登录二维码接口

游戏客户端可以调用该接口获取登录二维码url,然后在游戏客户端中展示二维码。

  • 接口:

    int __stdcall getLoginQRCode(const char* areaId, GetLoginQRCodeCallback callback);

  • 参数说明:

    areaId: 游戏区号,如果没有区号可以传空字符串“”。

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

  • 注意事项:

  • 代码示例:
getLoginQRCode pGetLoginQRCode = (getLoginQRCode)GetProcAddress(hModule, "getLoginQRCode");
loopLoginQRCode pLoopLoginQRCode = (loopLoginQRCode)GetProcAddress(hModule, "loopLoginQRCode");

void  __stdcall LoopLoginQRCodeCallback_(int code, const char* msg, const char* userid, const char* ticket, const char* extend)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    // 登录成功
    printf("login success userid[%s] ticket[%s]", userid, ticket);
  }
  else if (code == ERROR_CODE_QRCODE_EXPIRED)
  {
    // 二维码已失效,应该重新调用getLoginQRCode重新获取二维码
  }
  else if (code == ERROR_CODE_LOOP_CANCELED)
  {
    // 已通过cancelLoopLoginQRCode取消等待
  }
}

void __stdcall GetLoginQRCodeCallback_(int code, const char* msg, const char* url, const char* qrCode)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    // 当获取二维码成功之后,游戏客户端需要显示二维码,并调用loopLoginQRCode检查二维码登录是否成功
    // loopLoginQRCode接口内部会循环检测,直到登录成功,或者二维码失效
    int result = pLoopLoginQRCode(qrCode, "1", LoopLoginQRCodeCallback_);
    if (result != ERROR_CODE_SUCCESS)
    {
      printf("loopLoginQRCode failed result code %d\n", result);
    }
  }
  else
  {
    // 如果二维码获取失败,游戏客户端需要处理错误,并尝试重试,但是不能在这里直接调用获取二维码接口,否则可能会导致死循环
  }
}

int result = pGetLoginQRCode("1", GetLoginQRCodeCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  // 如果二维码获取失败,游戏客户端需要处理错误,并尝试重试,但是不能在这里直接调用获取二维码接口,否则可能会导致死循环
  printf("getLoginQRCode failed result code %d\n", result);
}

5.3. 客户端二维码登录接口

游戏客户端在调用完getLoginQRCode之后需要调用该接口完成登陆。

  • 接口:

    int __stdcall loopLoginQRCode(const char qrCode, const char areaId, LoopLoginQRCodeCallback callback);

  • 参数说明:

    qrCode: 二维码识别码,使用getLoginQRCode返回的qrCode。 areaId: 游戏区号,如果没有区号可以传空字符串“”。

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

  • 注意事项:

  • 代码示例:
getLoginQRCode pGetLoginQRCode = (getLoginQRCode)GetProcAddress(hModule, "getLoginQRCode");
loopLoginQRCode pLoopLoginQRCode = (loopLoginQRCode)GetProcAddress(hModule, "loopLoginQRCode");

void  __stdcall LoopLoginQRCodeCallback_(int code, const char* msg, const char* userid, const char* ticket, const char* extend)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    // 登录成功
    printf("login success userid[%s] ticket[%s]", userid, ticket);
  }
  else if (code == ERROR_CODE_QRCODE_EXPIRED)
  {
    // 二维码已失效,应该重新调用getLoginQRCode重新获取二维码
  }
  else if (code == ERROR_CODE_LOOP_CANCELED)
  {
    // 已通过cancelLoopLoginQRCode取消等待
  }
}

void __stdcall GetLoginQRCodeCallback_(int code, const char* msg, const char* url, const char* qrCode)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    // 当获取二维码成功之后,游戏客户端需要显示二维码,并调用loopLoginQRCode检查二维码登录是否成功
    // loopLoginQRCode接口内部会循环检测,直到登录成功,或者二维码失效
    int result = pLoopLoginQRCode(qrCode, "1", LoopLoginQRCodeCallback_);
    if (result != ERROR_CODE_SUCCESS)
    {
      printf("loopLoginQRCode failed result code %d\n", result);
    }
  }
  else
  {
    // 如果二维码获取失败,游戏客户端需要处理错误,并尝试重试,但是不能在这里直接调用获取二维码接口,否则可能会导致死循环
  }
}

int result = pGetLoginQRCode("1", GetLoginQRCodeCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  // 如果二维码获取失败,游戏客户端需要处理错误,并尝试重试,但是不能在这里直接调用获取二维码接口,否则可能会导致死循环
  printf("getLoginQRCode failed result code %d\n", result);
}

5.4. 客户端注销接口

游戏客户端可以在游戏内需要注销账号的地方调用该接口。

  • 接口:

    int __stdcall logout(LogoutCallback callback);

  • 参数说明:

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

  • 注意事项:

  • 代码示例:
logout pLogout = (logout)GetProcAddress(hModule, "logout");

void __stdcall LogoutCallback_(int code, const char* msg)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    printf("logout success\n");
  }
}

int result = pLogout(LogoutCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("logout failed result code %d\n", result);
}

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

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

  • 接口:

    int __stdcall loginArea(const char* areaId);

  • 参数说明:

    areaId: 用户进入的区号。

  • 注意事项:

    如果登录前已经选区,那么应该在登录接口中传递区号,不应该使用该接口。

  • 代码示例:
loginArea pLoginArea = (loginArea)GetProcAddress(hModule, "loginArea");

int result = pLoginArea("1");
if (result != ERROR_CODE_SUCCESS)
{
  printf("loginArea failed result code %d\n", result);
}

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

  • 用法说明

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

  • 接口地址

    https://mservice.sdo.com/v1/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"  #邀请码
  }
}

6. 支付功能(必接)

6.1. 支付原理说明

  • 支付原理

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

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

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

  • 支付时线图

    Android4

6.2. 客户端支付获取二维码接口

游戏客户端可以在购买道具的地方调用该接口获取支付二维码。

  • 接口:

    int __stdcall getPayQRCode(const char orderId, const char areaId, const char productId, const char extend, GetPayQRCodeCallback callback);

  • 参数说明:

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

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

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

    extend: 可选,扩展参数,游戏可用于自行扩展,平台发货通知时将原样返回给游戏服务器。

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

  • 注意事项:

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

  • 代码示例:
getPayQRCode pGetPayQRCode = (getPayQRCode)GetProcAddress(hModule, "getPayQRCode");
loopPayQRCode pLoopPayQRCode = (loopPayQRCode)GetProcAddress(hModule, "loopPayQRCode");

void __stdcall LoopPayQRCodeCallback_(int code, const char* msg, const char* orderId, const char* productId, const char* extend, const char* qrExtend)
{
  // qrExtend 移动端扫码时传入的extend参数
  if (code == ERROR_CODE_SUCCESS)
  {
    // 支付成功
    printf("pay success orderId[%s] productId[%s] extend[%s]", orderId, productId, extend);
  }
  else if (code == ERROR_CODE_QRCODE_EXPIRED)
  {
    // 二维码已失效
  }
  else if (code == ERROR_CODE_LOOP_CANCELED)
  {
    // 已通过cancelLoopPayQRCode取消等待
  }
}

void __stdcall GetPayQRCodeCallback_(int code, const char* msg, const char* url, const char* qrCode)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    int result = pLoopPayQRCode(qrCode, "1", LoopPayQRCodeCallback_);
    if (result != ERROR_CODE_SUCCESS)
    {
      printf("loopPayQRCode failed result code %d\n", result);
    }
  }
}

int result = pGetPayQRCode("orderid", "1", "productid", "extend", GetPayQRCodeCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("loopLoginQRCode failed result code %d\n", result);
}

6.3. 客户端二维码支付接口

游戏客户端在调用完getLoginQRCode之后需要调用该接口完成登陆。

  • 接口:

    int __stdcall loopPayQRCode(const char qrCode, const char areaId, LoopPayQRCodeCallback callback);

  • 参数说明:

    qrCode: 二维码识别码,使用getPayQRCode返回的qrCode。 areaId: 游戏区号,该参数必传。

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

  • 注意事项:

  • 代码示例:
getPayQRCode pGetPayQRCode = (getPayQRCode)GetProcAddress(hModule, "getPayQRCode");
loopPayQRCode pLoopPayQRCode = (loopPayQRCode)GetProcAddress(hModule, "loopPayQRCode");

void __stdcall LoopPayQRCodeCallback_(int code, const char* msg, const char* orderId, const char* productId, const char* extend, const char* qrExtend)
{
  // qrExtend 移动端扫码时传入的extend参数
  if (code == ERROR_CODE_SUCCESS)
  {
    // 支付成功
    printf("pay success orderId[%s] productId[%s] extend[%s]", orderId, productId, extend);
  }
  else if (code == ERROR_CODE_QRCODE_EXPIRED)
  {
    // 二维码已失效
  }
  else if (code == ERROR_CODE_LOOP_CANCELED)
  {
    // 已通过cancelLoopPayQRCode取消等待
  }
}

void __stdcall GetPayQRCodeCallback_(int code, const char* msg, const char* url, const char* qrCode)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    int result = pLoopPayQRCode(qrCode, "1", LoopPayQRCodeCallback_);
    if (result != ERROR_CODE_SUCCESS)
    {
      printf("loopPayQRCode failed result code %d\n", result);
    }
  }
}

int result = pGetPayQRCode("orderid", "1", "productid", "extend", GetPayQRCodeCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("loopLoginQRCode failed result code %d\n", result);
}

6.4. 服务器订单通知接口

  • 用法说明

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

  • 接口地址

    接口地址需要游戏提供,可以在开发商后台“我的游戏 > 区服管理”中配置。

  • 传值方式

    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. 客户端获取游戏区服列表接口

游戏客户端可以调用该接口获取游戏在G家平台登记的区服配置。

  • 接口:

    int __stdcall getAreaConfig(GetAreaConfigCallback callback);

  • 参数说明:

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

  • 注意事项:

    返回参数格式见JSON格式说明

  • 代码示例:
void __stdcall GetAreaConfigCallback_(int code, const char* msg, const char* data)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    printf("area list %s\n", data);
  }
}

getAreaConfig pGetAreaConfig = (getAreaConfig)GetProcAddress(hModule, "getAreaConfig");

int result = pGetAreaConfig(GetAreaConfigCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("getAreaConfig failed result code %d\n", result);
}
  • JSON格式说明:
{
  "message":[
  {
    "area_code":"1",  // 开发商后台配置的区服ID
    "name":"area1"    // 开发商后台配置的区服名称
  },
  ...
  ]
}

7.2. 客户端获取游戏商品列表接口

游戏客户端可以调用该接口获取游戏在G家平台登记的商品配置。

  • 接口:

    int __stdcall getProductConfig(GetProductConfigCallback callback);

  • 参数说明:

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

  • 注意事项:

    返回参数格式见JSON格式说明

  • 代码示例:
void __stdcall GetProductConfigCallback_(int code, const char* msg, const char* data)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    printf("product list %s\n", data);
  }
}

getProductConfig pGetProductConfig = (getProductConfig)GetProcAddress(hModule, "getProductConfig");

int result = pGetProductConfig(GetProductConfigCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("getProductConfig failed result code %d\n", result);
}
  • JSON格式说明:
{
  "message":[
  {
    "product_code":"com.product.gold100",  // 开发商后台配置的产品ID
    "item_name":"100 Gold",       // 开发商后台配置的产品名称
    "money":"10.00",          // 开发商后台配置的产品金额
    "type":0              // 0:Android游戏,1:IOS游戏,2:Android和IOS游戏
  },
  ...
  ]
}

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

游戏客户端在接入其他需要登录G家平台的应用时(如:客服、论坛等。),可以调用该接口获取一个一次性小票提供个应用完成登录。

  • 接口:

    int __stdcall getTicket(const char appId, const char areaId, GetTicketCallback callback);

  • 参数说明:

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

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

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

  • 注意事项:

    必须在登录成功之后才可以使用该接口。

  • 代码示例:
void __stdcall GetTicketCallback_(int code, const char* msg, const char* ticket)
{
  if (code == ERROR_CODE_SUCCESS)
  {
    printf("getTicket success ticket[%s]\n", ticket);
  }
}

getTicket pGetTicket = (getTicket)GetProcAddress(hModule, "getTicket");

int result = pGetTicket("1000", "1", GetTicketCallback_);
if (result != ERROR_CODE_SUCCESS)
{
  printf("getTicket failed result code %d\n", result);
}

7.4. 客户端提交角色信息接口

渠道要求游戏在运行过程中提交一些用于运营需要的扩展数据,以便双方对游戏内容进行更深度的运营。游戏在接入时请将代码示例中的玩家数据改为真实数据。本接口需在以下两种情况下调用:

  1. 玩家登陆成功后角色信息确定之后调用。

  2. 角色等级发生变化的时候调用。
  • 接口: int __stdcall reportRoleInfo(const char roleId, const char roleName, const char roleLevel, const char areaId, const char* areaName);
  • 参数说明:

    roleId: 当前登录的玩家角色ID。

    roleName: 当前登录的玩家角色名。

    roleLevel: 当前登录的玩家角色等级。

    areaId: 当前登录的游戏区服ID。

    areaName: 当前登录的游戏区服名称。

  • 代码示例:
  reportRoleInfo pReportRoleInfo = (reportRoleInfo)GetProcAddress(hModule, "reportRoleInfo");

  result = pReportRoleInfo("R0010","令狐一冲","99","1","游戏一区-逍遥谷");
  if (result != ERROR_CODE_SUCCESS)
  {
    printf("reportRoleInfo failed result code %d\n", result);
  }

8. 附录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);
}

9. 附录B:特殊说明

  • 订单重发机制:

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

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

  • 发货重复处理机制:

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

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