后端对接
用户钱包变动回调
用户消费、获得收益、获得额外奖励、退款时调用
注意
消费订单不会重试,收益、额外奖励和退款订单如果调用失败会自动重试,商户端需要根据orderUid进行去重,避免数据异常。同一个 App 内 orderUid 唯一。
请求格式
请求方式: POST
编码格式: Content-Type: application/json;charset=UTF-8
| 参数 | 类型 | 说明 |
|---|---|---|
| appId | int | 商户 App ID |
| amount | long | 本次变动余额,消费类型为负数,其余类型为正数 |
| gameId | int | Combo Game 游戏 ID |
| orderUid | string | 订单唯一 ID,格式为 UUID 字符串,同一个 App 内唯一 |
| payload | string | 额外信息 JSON 格式化后的字符串,退款时为{"relatedOrderUid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}包含被退款的原订单 ID,其余类型为{} |
| roundUid | string | 本局游戏 UID,格式为 UUID 字符串,部分游戏可能为空字符串 |
| type | int | 订单类型,1 消费,2 收益,3 额外奖励,4 退款;商户消费订单返回时间较长时可能会导致本局已结束,此时会进行退款 |
| userId | string | 用户 ID |
| token | string | App 加载游戏时携带的商户提供的用户鉴权 token,不能为空 |
| ts | long | 当前时间戳毫秒 |
| sign | string | MD5 小写字符串 |
{
"amount": -1000,
"gameId": 1001,
"orderUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"payload": "{}",
"roundUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"token": "xxxx",
"type": 1,
"userId": "1234556",
"ts": 1750151429551,
"sign": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}{
"amount": 2000,
"gameId": 1001,
"orderUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"payload": "{}",
"roundUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"token": "xxxx",
"type": 2,
"userId": "1234556",
"ts": 1750151429551,
"sign": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}{
"amount": 2000,
"gameId": 1001,
"orderUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"payload": "{}",
"roundUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"token": "xxxx",
"type": 3,
"userId": "1234556",
"ts": 1750151429551,
"sign": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}{
"amount": 2000,
"gameId": 1001,
"orderUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"payload": "{\"relatedOrderUid\":\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"}",
"roundUid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"token": "xxxx",
"type": 4,
"userId": "1234556",
"ts": 1750151429551,
"sign": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}签名算法
sign = MD5(amount + appId + gameId + orderUid + payload + roundUid + token + ts + type + userId + appKey)
示例代码
public static String sign(long amount, int appId, int gameId, String orderUid, String payload, String roundUid, String token, long ts, int type, String userId, String appKey) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
String sourceString = String.format(Locale.CHINA, "%d%d%d%s%s%s%s%d%d%s%s", amount, appId, gameId, orderUid, payload, roundUid, token, ts, type, userId, appKey);
return bytesToHex(digest.digest(sourceString.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("加密失败");
}
return null;
}
private static final byte[] HEX_ARRAY = "0123456789abcdef".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}h := md5.New()
h.Write([]byte(fmt.Sprintf("%d%d%d%s%s%s%s%d%d%s%s", amount, appId, gameId, orderUid, payload, roundUid, token, ts, Type, userId, appKey)))
sign := hex.EncodeToString(h.Sum(nil))返回格式
编码格式: Content-Type: application/json;charset=UTF-8
| 字段 | 类型 | 说明 |
|---|---|---|
| code | int | 错误码,详见错误码页 |
| msg | string | 错误信息,若无错误可以传空字符串或者 OK |
| data | object | 数据体 |
| data.balance | long | 用户钱包余额 |
{
"code": 0,
"msg": "OK",
"data": {
"balance": 88888888
}
}Token 补充说明
- 游戏过程中一直使用 token 进行交互,用户 token 要保证在游戏过程中有效。如果需要强制让用户退出游戏 APP 服务器也可以直接让 token 失效;
- 由于有一些多人游戏,需要延迟开奖。也就是玩家在参与游戏后,需要等一段时间后才能知道结果。如果这用户离线或强制退出 APP,APP 服务器也不能马上回收 token,每次游戏方调用用户钱包变动回调后,商户方需要保持 token 有效最少 3 分钟,这样才能保证该用户在游戏结果后也能正常获得奖励。