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和账号)保存到游戏服务器;
- 注册&登录时线图
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家平台服务器做一个验证
-
接口地址
-
传值方式
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节);
-
支付时线图
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. 客户端提交角色信息接口
渠道要求游戏在运行过程中提交一些用于运营需要的扩展数据,以便双方对游戏内容进行更深度的运营。游戏在接入时请将代码示例中的玩家数据改为真实数据。本接口需在以下两种情况下调用:
-
玩家登陆成功后角色信息确定之后调用。
- 角色等级发生变化的时候调用。
- 接口: 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”的订单,将不会继续触发重发机制。