服务端接入接口说明
如果您的游戏有自己的游戏服务器。并且游戏玩家数据由你的服务器保存和管理。则需要服务端进行接入:
模块 | 说明 | 接入建议 |
---|---|---|
用户系统 | 你需要将Jogos平台用户系统与您服务器的用户进行关联。则需要由服务端接入用户系统K | ☑️必要接入 |
内购系统 | 你的游戏有内购项目,则需要接入Jogos内购系统,以保障用户付费和数据安全 | ☑️内购游戏必要接入 |
接入用户系统
1 由客户端获取用户令牌
- 具体请先参阅查阅:
客户端接入用户SDK
文档。
2 概述
从jogos 获取用户token及publickey后,回传到游戏服务端,进行验证用获取用户信息,注意:每次验证令牌时都获取密钥publickey,因为它随时可能会改变。
3 验证逻辑
- 游戏开发者从jogos获取用户token回传到游戏开发者服务端。
- 由服务端获取publickey后(通过https://www.jogos.com/publicKey.json 获取)
- 根据Base64编码的publickey字符串获取RSA公钥。
- 根据RSA公钥进行JWT解密token。返回的令牌解码可以在 jwt.io 上进行测试。
- 解码后获取用户信息,与你的游戏服务器的用户经行绑定。
- 如解密不成功,则返回空的用户信息。
4 代码示例
工程中导入jwt:
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
</dependency>
java
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import com.alibaba.fastjson.JSONObject;
public class JwtUtils {
public static void main(String[] args) {
/**
* 从jogos sdk 获取publickey和用户token,传到服务端,如以下的publickey:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhyUrr/W8bSuC+HK8Rk++BDOGDefGMEBa9jekVwVE3oeqi7QbVzZPAuqb1K3GPJjDBfi44IWzb3w8Xa0P1ZeO2Cnbg1LltanlzFv/EcKseCIkOd8Qo78ARPfmlf4WP6MznYGKwNVGFh/s5Y6ar8QgWX1ttkcwYzHu/gUroO+nPOZkU6bfxHjRrJxk3lSQBZWfTSFd2JFwntq3h45UHymjQZiMtW47G2C4VTtTTt1Iz0VQ9rdtWie/+REqQYoFUm04Yns9jyG3TZzio9vsRrxESLQbBIRxto77cQNtEe9j/2EXNwQabRkiS6zo6i2TIN4O6uthWBVud5WXsBWdiyIOHQIDAQAB
-----END PUBLIC KEY-----
和用户token:
eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiIxOTY3NDIzNTM3NjE1NTgxMTg0Iiwic3ViIjoie1wicHJvZmlsZVBpY3R1cmVVcmxcIjpcImh0dHBzOi8vaW1hZ2Uuam9nb3NwcmUuY29tLzBlZTkyZjViZTRhMzQ4NzBhYjY0YWU1N2FjN2I1YmE0LmpwZ1wiLFwiZ2FtZUlkXCI6XCIxMjIwXCIsXCJ1c2VySWRcIjpcIjI1N1wiLFwidXNlcm5hbWVcIjpcIlBsYXllcjI1N1wifSIsImlzcyI6ImNvbTpqb2dvczpzZGsiLCJpYXQiOjE3NTc5MDUyOTgsImV4cCI6MTc2MTUwNTI5OH0.MKBab0XC1o5AYGosbq1l5m9xCLN1-4xfcr7_dHn-_C8Eh2moTuq9TbQutIajB3dViFW0e1KyIg7UhxNk00rdhT2b5UrvO23tyFsgg9FYyAyCZABURxHyI0lTW9V8YA9k4faycK_gCUMXH_IubseDMz1P7cYmPpo8WxJXZq3R-mL8OhhuKCn8DlpP5BVsd0_gYSTvDUD0gjdINLUNTVrZVkETcDVWW8OXQVzJxdTH0VVlDs4cGvIyko8TJ6g1Bvz4VWj4qy1XoQFfRBH8sgBl3oGJtUodhq3b4bOx4Cr_o-2tK54CetDdmmCPbtcbmmDcBqK5EQ51A2Kws-1cYtsFxg
注:每次验证令牌时都获取密钥publickey,因为它可能会更改
*/
String publickey ="您的publickey";
String token = "您的用户token";
System.out.println("publickey:["+publickey+"]");
System.out.println("token:["+token+"]");
Map<String, Object> userMap = verifyToken(token,publickey);
// 如果Token解析成功,userMap不为空
if (userMap != null) {
System.out.println("验证通过,用户信息为:");
System.out.println(JSONObject.toJSONString(userMap));
}else {
System.out.println("jwt验证不通过");
}
}
/**
* 验证token
* @param token
* @return
*/
public static Map<String, Object> verifyToken(String token,String publickey){
// 尝试解析Token,如果解析失败则直接返回null
Map<String, Object> map = null;
try {
map = parseToken(token,publickey);
} catch (Exception e) {
}
return map;
}
/**
* 解密token
* @param token
* @return
*/
public static Map<String,Object> parseToken(String token,String publickey){
//去除开头和结尾部分
if(publickey.startsWith("-----BEGIN PUBLIC KEY-----")) {
publickey = publickey.substring(27,publickey.length()-25);
}
Claims claims = (Claims) Jwts.parser()
.setSigningKey(getPublicKey(publickey))
.parse(token)
.getBody();
String parseToken = claims.getSubject();
if (parseToken != null) {
Map<String, Object> map = JSONObject.parseObject(parseToken, Map.class);
return map;
}
return null;
}
/**
* 根据Base64编码的字符串获取RSA公钥
* @param key Base64编码的公钥字符串
* @return 转换后的RSA公钥
* <p>
* 注意:该方法使用Base64解码器将输入的字符串解码为字节数组,然后使用KeyFactory和X509EncodedKeySpec
* 将字节数组转换为PublicKey对象。如果转换过程中发生异常,将抛出RuntimeException。
*/
private static PublicKey getPublicKey(String key) {
byte[] decode = Base64.getDecoder().decode(key);
PublicKey publicKey;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decode);
publicKey = keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new RuntimeException(e);
}
return publicKey;
}
}
接入内购系统
开始接入前,请先确认:
您当前使用是
www.jogospre.com
(测试环境)或者www.jogos.com
(正式环境);下文所提及的 POST 请求 URL 需要改成 对应地址 。
(例如测试环境到货 API 为:https://api.jogospre.com/api/gamepay/webhook/arrivedorder)确保您已经在开发者平台创建游戏应用,并在“游戏参数”中已经生成好您的游戏的 共享密钥。
确保您的游戏选项中,已勾选“使用应用内购买”模式,并输入您服务器接收 回调通知地址。
通信中采用 SHA-1 加密方式,具体用法如下:
javascriptprivate String sha1(String input) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hashInBytes = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : hashInBytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("SHA-1 algorithm is not available", e); } }
客户端前置接入:
- 由客户端创建订单 与 发起订单。
- 具体请参阅查阅:
客户端接入内购SDK
文档。
开始服务端接入:
1. 用户在游戏中购买商品成功后,Jogos 平台发送支付成功的通知
Jogos 平台会发送支付成功回调信息到您配置的 回调通知地址
请求头:
字段名 类型 可否为空 描述 Authorization string 否 验证头,sha1(body+秘钥),其中秘钥为该游戏共享秘钥,在游戏参数中设置 请求体包含以下字段:
字段名 类型 可否为空 描述 paytype String 否 通知类型 pay:支付成功通知;refund:退款成功通知 gameId int 否 游戏 ID orderId String 否 订单编号 arrivedStatus int 否 到货状态: 0 未到货,1 已到货 productId String 否 产品 ID createTime long 否 订单创建时间(Unix 时间戳) payTime long 否 订单支付成功时间(Unix 时间戳) userId int 否 用户 ID refundStatus int 是 退款状态,发生退款时不为空 0:退款中,1:退款成功; 2:退款失败;3:拒绝退款 refundTime long 是 退款时间(Unix 时间戳),发生退款时不为空 请求体示例:
javascript{ "gameId": 9968, "orderId": "zXhKYRy3genKKh9TgndBpu1gp5tBpA", "arrivedStatus": 0, "productId": "1002", "createTime": 1755657098, "payTime": 1755657115, "userId": 278, "refundStatus": null, "refundTime": null }
- 返回的 JSON 对象包含以下字段:(code:200 代表成功)
{ "code": "200", "msg": "Success" }
错误码:
305
必填参数为空40001
未找到对应的游戏41007
验证不通过500
系统异常
2. 游戏服务端发货完成后,需调用“到货状态更新”接口,将订单标识为已发货状态
请求 URL:[POST] https://api.jogos.com/api/gamepay/webhook/arrivedorder
请求头:
字段名 类型 可否为空 描述 Authorization string 否 验证头,sha1(body+秘钥),其中秘钥为该游戏共享秘钥,在游戏参数中设置 请求体:
字段名 类型 可否为空 描述 gameId int 否 游戏 ID orderId int 否 订单编号 请求体示例:
javascript{ "gameId": 9943, "orderId":"gHpGwweTlucXwcM6yIeSMcgKkhFmoO" }
返回的 JSON 对象包含以下字段:(code:200 代表成功)
{ "code": "200", "msg": "Success" }
错误码:
305
必填参数为空40001
未找到对应的游戏41007
验证不通过500
系统异常
3. 游戏服务端查询订单列表,同步订单及到货状态
请求 URL:[POST] https://api.jogos.com/api/gamepay/getOrders
请求头:
字段名 类型 可否为空 描述 Authorization string 否 验证头,sha1(body+秘钥),其中秘钥为该游戏共享秘钥,在游戏参数中设置 请求体:
字段名 类型 可否为空 描述 gameId int 否 游戏 ID pageNo int 否 页码 pageSize int 否 分页数 请求体示例 :
javascript{ "gameId": 9943, "pageNo":1, "pageSize":20 }
返回的 JSON 对象包含以下字段:(code:200 代表成功)
字段名 类型 可否为空 描述 code String 否 状态码,表示请求的结果 (例如: SUCCESS) message String 否 提示信息 (例如: 登出成功) total Integer 否 总数 pageSize Integer 否 每页数量 currentPage Integer 否 当前页 totalPage Integer 否 总页数 orderId String 否 订单编号 arrivedStatus Integer 否 到货状态;0 未到货,1 已到货 payTime long 否 支付时间(Unix 时间戳) productId String 否 产品 ID userId Integer 否 用户 id refundStatus Integer 是 退款状态,发生退款时不为空:0:退款中,1:退款成功,2 退款失败 ,3 拒绝退款 refundTime long 是 退款时间(Unix 时间戳),发生退款时不为空 json{ "code": "200", "msg": "Success", "page": { "total": 31, "totalPage": 7, "currentPage": 1, "pageSize": 5, "content": [ { "orderId": "gHpGwweTlucXwcM6yIeSMcgKkhFmoO", "arrivedStatus": 1, "payTime": 1112211221, "productId": "game_pro1", "userId": 278, "refundStatus": null, "refundTime": null } ] } }
错误码:
305
必填参数为空40001
未找到对应的游戏41007
验证不通过500
系统异常
4. 当用户发生退款时,向游戏服务端发送退款成功通知
- Jogos 平台会发送该用户申请退款成功的订单回调信息到您配置的 回调通知地址
- 请求体与第 1 条“支付成功通知”一样。
- 建议开发者根据退款订单的信息,酌情扣减或限制玩家之前充值所得的奖励道具。