后端接入文档
1 服务器域名地址
sdk服务器使用域名地址
api-sdk-gameplus.meetsocial.com
以下文档域名均以$MAIN_HOST代替,注:Content-Type: application/json
2 游戏服务器获取用户信息
在sdk登陆回调成功后,使用返回数据UserInfo中的token字段向sdk服务器发送身份校验请求,游戏服务器直接使用身份校验返回的用户信息。
使用token获取用户信息curl示例
curl -X GET "https://$MAIN_HOST/auth/myProfile" -H "Authorization: token" -v
接口返回数据对象
| 字段 | 描述 | 类型 |
|---|---|---|
| code | 状态码,200以外均为异常 | int |
| error | 错误类型 | stirng |
| message | 错误描述 | string |
| data | UserProfile对象 | object |
UserProfile对象
| 字段 | 描述 | 类型 |
|---|---|---|
| id | 平台应用下用户的唯一id | long |
| name | 唯一用户账户名 | string |
| isGuest | 是否游客(未绑定第三方账号为游客) | boolean |
| agreementChecked | 是否同意协议 | boolean |
3 支付成功回调验证
使用sdk分配的secret密钥进行数据验签,按md5算法进行签名计算。签名相同即认为数据可信。
sdk服务器进行回调请求的超时时长为5秒,超时或其他情况导致回调失败都会进行重试。服务器将以1分钟为间隔重试10次,如果十次请求全部失败,则该订单视为掉单,需人工处理。
游戏服务器收到回调后,只需向 SDK 服务器返回 HTTP 状态码 200 OK,并在响应 Body 中原样返回 JSON:{"result":"success"}(注意:该 JSON 外层不能有任何其他包裹内容,例如额外字段、嵌套结构或前后拼接文本),SDK 服务器即认为回调成功并终止重试(若响应中 result 值不是 “success”,则一律认为回调失败)
另外,如重复收到同一笔支付回调(重复通知),也需要返回 HTTP 状态码 200 OK,并在响应 Body 中原样返回 JSON:{"result":"success"}(同样不能有任何外层包裹内容)。
回调数据验签。
- 获取请求body中的json数据,进行反序列化处理。
- 读取json中signOrder字段,该字段中为签名字段名排列顺序。
- 将signOrder中字段值转化为字符串以&相隔连接,字段中不存在null值,类型一般为字符串和数字。
- 在最后拼接上&和secret,secret为应用申请时生成的secret密钥。
- 对连接的字符串进行md5散列摘要计算,并对结果进行base64编码。
- 比较base64编码结果与json中的sign字段值是否相同,相同说明内容未被篡改可以信任。
| 响应内容 | 描述 |
|---|---|
| {"result":"success"}或者{"result":"failure"} | {"result":"success"}且HTTP状态码为200 OK表示处理订单成功,SDK服务端方收到响应success后不会再通知给cp方。failure失败(也可返回其他错误信息,收到{"result":"success"}以外的字符串后都会多次重复通知)。 |
java代码示例
@PostMapping("fakeServerResponse")//使用Post请求
@ApiOperation(value = "接收服务器订单支付回调", notes = "订单支付成功向游戏的回调数据")
public Map fakeServerResponse(HttpServletRequest request) throws Throwable {
// 服务器将对回调地址发送回调请求,被通知服务返回json: {"result":"success"} 且HTTP状态码为`200 OK`即认为回调成功
// 服务器五秒内会主动断开连接,超时即认为失败
// 失败后服务器会进行重试,间隔1分钟重试十次,之后将不进行通知
// 获取数据
byte[] bytes = new byte[request.getContentLength()];
request.getInputStream().read(bytes);
String data = new String(bytes, request.getCharacterEncoding());
Map map = objectMapper.readValue(data, Map.class);
// 使用md5和secret进行数据校验,secret为控制台应用中生成的密钥
// 获取数据签名的字段列
ArrayList<String> signOrder = (ArrayList<String>) map.get("signOrder");
Long appId = (Long) map.get("appId");
String sign = (String) map.get("sign");
String secret = "*****";
MessageDigest md5 = MessageDigest.getInstance("MD5");
StringBuilder stringBuilder = new StringBuilder();
// 拼接校验数据和secret
for (String s : signOrder) {
Object o = map.get(s);
stringBuilder.append(o.toString()).append("&");
}
stringBuilder.append(secret);
String signData = stringBuilder.toString();
String s = Base64.encodeBase64String(md5.digest(signData.getBytes()));
// 验证摘要值相同
assert s.equals(sign);
HashMap<String, Object> result = new HashMap<>();
// 向sdk服务器返回成功响应
result.put("result", "success");
return result;
}
回调数据示例
{
"event": "orderPayed",
"orderId": 100000000000000001,
"appId": 100000000000000002,
"createTime": "2026-01-01 12:00:00",
"productType": "GOOGLE",
"productCode": "item_01",
"originOrderId": "GPA.xxxx-xxxx-xxxx-xxxxx",
"originInfo": "{\"purchaseTimeMillis\":\"1700000000000\",\"purchaseState\":0,\"consumptionState\":0,\"developerPayload\":\"\",\"orderId\":\"GPA.xxxx-xxxx-xxxx-xxxxx\",\"purchaseType\":0,\"acknowledgementState\":1,\"kind\":\"androidpublisher#productPurchase\",\"obfuscatedExternalAccountId\":\"100000000000000001\",\"obfuscatedExternalProfileId\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\"regionCode\":\"US\",\"productId\":\"item_01\"}",
"customInfo": "{\"amount\":1,\"developerPayload\":\"{\\\"characterId\\\":\\\"100000000000000003\\\",\\\"gameServer\\\":\\\"1\\\",\\\"orderId\\\":\\\"G20260101120000001\\\",\\\"extra\\\":\\\"any_custom_data\\\"}\",\"productId\":\"item_01\",\"productName\":\"示例商品\",\"roleInfo\":{\"roleId\":\"100000000001\",\"roleLevel\":\"1\",\"roleName\":\"PlayerName\",\"serverId\":\"1\",\"serverName\":\"server_name\",\"vipLevel\":\"0\"}}",
"signOrder": ["event", "orderId", "productType", "productCode", "originOrderId", "originInfo", "customInfo"],
"sign": "eHh4eHh4eHh4eHh4eHh4eA=="
}
回调数据字段
| 字段 | 描述 | 类型 |
|---|---|---|
| signOrder | 签名字段顺序列表 | ArrayList<String> |
| productType | 商品类型,应用平台 | String |
| productCode | 各平台配置的应用内唯一编码 | String |
| originOrderId | 第三方平台生成的原始订单号 | String |
| originInfo | 第三方原始订单回调数据 | json string |
| orderId | 订单唯一编号 | Long |
| event | 固定值orderPayed | String |
| customInfo | 用户上传的订单数据 | String |
| createTime | 创建时间 | String |
| appId | 应用唯一编号 | Long |
| sign | 签名 | String |
customInfo参数说明:
| 字段 | 描述 | 类型 |
|---|---|---|
| amount | 商品数量 | int |
| developerPayload | CP自定义透传字段,内容由客户端在发起支付时传入,SDK不做处理原样透传,可用于业务扩展校验 | String |
| productType | 商品类型 | String |
| productId | 商品ID | String |
| productName | 商品名称 | String |
| roleInfo | 角色信息 | Object |
developerPayload 为CP自定义透传字段,需要客户端在调用SDK支付接口时主动传入。SDK不会对该字段做任何处理,支付成功后会原样回调给CP服务器。CP可利用该字段传递自定义业务数据(如内部订单号、角色信息等),以便在回调时进行业务关联和校验。
客户端传入方式请参考对应平台的接入文档:
roleInfo参数说明:
| 字段 | 描述 | 类型 |
|---|---|---|
| roleId | 角色ID | String |
| roleName | 角色名 | String |
| roleLevel | 角色等级 | String |
| serverName | 服务器名称 | String |
| vipLevel | vip等级 | String |