14 KiB
14 KiB
Flutter App 通知与钱包余额对接
本文只定义 App 需要对接的 HTTP 接口和 IM 私有事件契约,不约束 Flutter 端内部状态管理实现。
通用 HTTP 约定
Base URL
https://{api_host}
本地开发:
http://127.0.0.1:13000
Headers
| Header | 必填 | 说明 |
|---|---|---|
Authorization: Bearer {access_token} |
是 | 登录后后端返回的 App 访问令牌 |
Content-Type: application/json |
POST 必填 | 请求体为 JSON |
X-App-Code |
否 | 多 App 包场景可传;通常由 gateway 按包名解析 |
X-App-Platform |
否 | android / ios,部分钱包商品接口使用 |
响应 Envelope
所有 /api/v1 业务接口使用统一响应:
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {}
}
失败时没有 data:
{
"code": "UNAUTHORIZED",
"message": "unauthorized",
"request_id": "req_xxx"
}
错误代码
| HTTP | code | message | 说明 |
|---|---|---|---|
| 200 | OK |
ok |
成功 |
| 400 | INVALID_JSON |
invalid json |
JSON 格式错误 |
| 400 | INVALID_ARGUMENT |
invalid argument |
参数缺失或非法 |
| 401 | UNAUTHORIZED |
unauthorized |
未登录、token 过期或 token 无效 |
| 403 | PROFILE_REQUIRED |
profile required |
用户资料未完成 |
| 403 | PERMISSION_DENIED |
permission denied |
无权限 |
| 404 | NOT_FOUND |
not found |
资源不存在 |
| 409 | INSUFFICIENT_BALANCE |
insufficient balance |
金币/余额不足 |
| 409 | LEDGER_CONFLICT |
conflict |
钱包幂等冲突 |
| 409 | IDEMPOTENCY_CONFLICT |
conflict |
command_id 重复但请求内容不同 |
| 409 | ROOM_CLOSED |
room closed |
房间已关闭 |
| 409 | CONFLICT |
conflict |
当前状态不允许操作 |
| 429 | RATE_LIMITED |
rate limited |
触发限流 |
| 500 | INTERNAL_ERROR |
internal error |
服务端内部错误 |
| 502 | UPSTREAM_ERROR |
upstream service error |
内部依赖不可用 |
App 处理建议:UNAUTHORIZED 重新登录;UPSTREAM_ERROR 可提示重试;INSUFFICIENT_BALANCE 引导充值;其它错误按页面语义提示。
1. 获取腾讯 IM 登录票据
GET /api/v1/im/usersig
Headers
| Header | 必填 |
|---|---|
Authorization |
是 |
Query 参数
无。
返回值 data
{
"sdk_app_id": 1400000000,
"user_id": "10001",
"user_sig": "xxx",
"expire_at_ms": 1710000000000,
"join_groups": [
{
"group_id": "hy_lalu_bc_g",
"type": "global_broadcast",
"region_id": 0
}
]
}
| 字段 | 类型 | 说明 |
|---|---|---|
sdk_app_id |
int64 | 腾讯云 IM AppID |
user_id |
string | 腾讯云 IM identifier,直接用于 IM 登录 |
user_sig |
string | 腾讯云 IM 登录票据 |
expire_at_ms |
int64 | 票据过期时间,UTC epoch ms |
join_groups |
array | App 登录 IM 后需要加入的服务端群 |
可能错误
UNAUTHORIZED、PROFILE_REQUIRED、PERMISSION_DENIED、INTERNAL_ERROR、UPSTREAM_ERROR。
2. 获取我的钱包余额
GET /api/v1/wallet/me/balances
Headers
| Header | 必填 |
|---|---|
Authorization |
是 |
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
asset_type |
string/repeated | 否 | 可重复传,也可逗号分隔;不传默认只返回 COIN |
asset_types |
string/repeated | 否 | 同 asset_type |
示例:
GET /api/v1/wallet/me/balances?asset_type=COIN&asset_type=DIAMOND&asset_type=HOST_SALARY_USD&asset_type=AGENCY_SALARY_USD&asset_type=BD_SALARY_USD&asset_type=ADMIN_SALARY_USD
返回值 data
{
"balances": [
{
"asset_type": "COIN",
"available_amount": 1123123,
"frozen_amount": 0,
"version": 18
}
]
}
| 字段 | 类型 | 说明 |
|---|---|---|
asset_type |
string | COIN / DIAMOND / HOST_SALARY_USD / AGENCY_SALARY_USD / BD_SALARY_USD / ADMIN_SALARY_USD / GIFT_POINT |
available_amount |
int64 | 可用余额 |
frozen_amount |
int64 | 冻结余额 |
version |
int64 | 该资产余额版本,用于和 IM 通知去重/防乱序 |
可能错误
UNAUTHORIZED、PROFILE_REQUIRED、INVALID_ARGUMENT、UPSTREAM_ERROR。
3. 获取钱包首页摘要
GET /api/v1/wallet/me/overview
Headers
| Header | 必填 |
|---|---|
Authorization |
是 |
Query 参数
无。
返回值 data
{
"balances": [
{
"asset_type": "COIN",
"available_amount": 1123123,
"frozen_amount": 0,
"version": 18
},
{
"asset_type": "DIAMOND",
"available_amount": 200,
"frozen_amount": 0,
"version": 4
},
{
"asset_type": "HOST_SALARY_USD",
"available_amount": 1500,
"frozen_amount": 0,
"version": 2
}
],
"feature_flags": {
"recharge_enabled": true,
"diamond_exchange_enabled": true
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
balances |
array | 钱包首页需要展示的资产余额 |
feature_flags.recharge_enabled |
bool | 是否允许充值 |
feature_flags.diamond_exchange_enabled |
bool | 是否允许钻石兑换 |
可能错误
UNAUTHORIZED、PROFILE_REQUIRED、UPSTREAM_ERROR。
4. 送礼
POST /api/v1/rooms/gift/send
Headers
| Header | 必填 |
|---|---|
Authorization |
是 |
Content-Type: application/json |
是 |
Body 参数
{
"room_id": "lalu_xxx",
"command_id": "gift_1700000000000_10001",
"target_type": "user",
"target_user_ids": [10002],
"gift_id": "car_001",
"gift_count": 1
}
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
room_id |
string | 是 | 房间 ID |
command_id |
string | 是 | 本次用户动作幂等键;HTTP 重试必须复用同一个值 |
target_type |
string | 否 | 当前支持 user;后续扩展 all_mic、all_room、couple |
target_user_ids |
int64[] | 是 | 收礼用户 ID;target_type=user 时必须且只能 1 个 |
target_user_id |
int64 | 否 | 单目标兼容字段;新代码优先用 target_user_ids |
gift_id |
string | 是 | 礼物 ID |
gift_count |
int32 | 是 | 礼物数量 |
返回值 data
{
"result": {
"applied": true,
"room_version": 14,
"server_time_ms": 1710000000000
},
"billing_receipt_id": "br_xxx",
"room_heat": 123456,
"gift_rank": [],
"room": {}
}
| 字段 | 类型 | 说明 |
|---|---|---|
result.applied |
bool | 本次命令是否实际落地;幂等重放可能为 false |
result.room_version |
int64 | 房间版本 |
result.server_time_ms |
int64 | 服务端时间 |
billing_receipt_id |
string | 钱包扣费回执 |
room_heat |
int64 | 房间热度 |
gift_rank |
array | 房间礼物榜快照 |
room |
object | 房间快照 |
送礼成功不代表 App 必须从该响应里更新钱包余额。金币余额以 WalletBalanceChanged IM 私有事件或 /wallet/me/balances 刷新结果为准。
可能错误
UNAUTHORIZED、PROFILE_REQUIRED、INVALID_ARGUMENT、INSUFFICIENT_BALANCE、ROOM_CLOSED、LEDGER_CONFLICT、IDEMPOTENCY_CONFLICT、UPSTREAM_ERROR、INTERNAL_ERROR。
IM 私有事件:钱包余额变更
事件名称
WalletBalanceChanged
IM 通道
腾讯云 IM C2C 自定义消息。
| 字段 | 值 |
|---|---|
MsgType |
TIMCustomElem |
Desc |
WalletBalanceChanged |
Ext / extension |
wallet_notice |
To_Account |
当前登录用户的 IM identifier |
CloudCustomData |
与 TIMCustomElem.Data 相同的 JSON 字符串 |
Headers
无。IM 私有事件不是 HTTP 请求,没有 HTTP header。
参数 / Payload
TIMCustomElem.Data 是 JSON 字符串:
{
"event_type": "WalletBalanceChanged",
"event_id": "wev_xxx",
"app_code": "lalu",
"transaction_id": "tx_xxx",
"command_id": "gift_1700000000000_10001",
"user_id": "10001",
"asset_type": "COIN",
"available_delta": -9900,
"frozen_delta": 0,
"available_after": 1123123,
"frozen_after": 0,
"balance_version": 18,
"created_at_ms": 1710000000000,
"source_created_at_ms": 1710000000000,
"metadata": {}
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
event_type |
string | 是 | 固定 WalletBalanceChanged |
event_id |
string | 是 | 事件幂等键 |
app_code |
string | 是 | App 租户 |
transaction_id |
string | 是 | 钱包交易 ID |
command_id |
string | 是 | 触发该余额变化的业务命令 ID |
user_id |
string | 是 | 当前用户 ID 字符串 |
asset_type |
string | 是 | 资产类型 |
available_delta |
int64 | 是 | 可用余额变化量 |
frozen_delta |
int64 | 是 | 冻结余额变化量 |
available_after |
int64 | 是 | 变化后的可用余额 |
frozen_after |
int64 | 是 | 变化后的冻结余额 |
balance_version |
int64 | 是 | 该用户该资产的新版本 |
created_at_ms |
int64 | 是 | 钱包事件创建时间 |
source_created_at_ms |
int64 | 是 | 源 outbox 创建时间 |
metadata |
object | 否 | 业务附加信息,不作为余额展示依赖 |
返回值
无。该事件由服务端单向投递给 App,App 不需要回调后端确认。
错误代码
无 HTTP 错误代码。App 本地处理规则:
| 场景 | 处理 |
|---|---|
Desc 不是 WalletBalanceChanged |
忽略 |
Ext/extension 不是 wallet_notice |
忽略 |
| JSON 解析失败 | 忽略并记录客户端日志 |
event_id 已处理 |
忽略 |
本地 asset_type 的 version >= balance_version |
忽略 |
| 长时间未收到通知 | 调 GET /api/v1/wallet/me/balances 兜底刷新 |
App 最小对接顺序
- 登录后调用
GET /api/v1/im/usersig。 - 用返回的
user_id/user_sig/sdk_app_id登录腾讯云 IM。 - 调
GET /api/v1/wallet/me/balances拉取初始余额。 - 监听 C2C 自定义消息
WalletBalanceChanged。 - 送礼调用
POST /api/v1/rooms/gift/send。 - 送礼后的余额以 IM 私有事件或余额刷新接口为准。