15 KiB
RedPacket 后端接口说明
本文档按当前 internal/api/redpacket.go 与 internal/rpc/redpacket/* 实现整理。红包服务已经从独立 Gin 服务迁移为 OpenIM 标准 RPC 服务:
- HTTP 入口在
internal/api网关,路由前缀为/redpacket - 网关通过
pbredpacket.RedPacketClient调用internal/rpc/redpacket - RPC 服务使用 MongoDB 存储,通过
pkg/common/storage/controller.RedPacketDatabase聚合 DAO - 服务注册名为
redPacket,配置文件为config/openim-rpc-redpacket.yml
1. 基础约定
1.1 Base URL
网关地址由 openim-api 部署决定,例如:
http://127.0.0.1:10002
红包接口统一挂在:
/redpacket
1.2 鉴权
当前 internal/api/router.go 的 Whitelist 未包含 /redpacket/*,因此所有红包 HTTP 接口默认都需要登录 token。
请求头:
token: <OpenIM user token>
operationID: <request id>
RPC 层不信任请求体中的 user_id。当前登录用户统一从 mcontext.GetOpUserID(ctx) 读取。
1.3 请求字段命名
HTTP 请求建议使用 snake_case。网关使用 a2r.ParseRequestNotCheck 解析到 protobuf 请求对象。
示例:
- HTTP:
packet_id - protobuf Go 字段:
PacketID
1.4 响应格式
网关使用 apiresp.GinSuccess / apiresp.GinError 包装响应。不同 OpenIM 版本的外层字段可能略有差异,下面示例重点展示 data 内容。
成功示意:
{
"errCode": 0,
"errMsg": "",
"data": {}
}
失败示意:
{
"errCode": 1001,
"errMsg": "packet_id is required"
}
2. 接口总览
用户侧接口:
POST /redpacket/create_orderPOST /redpacket/created_callbackPOST /redpacket/detailPOST /redpacket/issue_claim_signPOST /redpacket/claim_resultPOST /redpacket/wallet_bind/challengePOST /redpacket/wallet_bind/confirmPOST /redpacket/wallet_bind/detail
管理员接口:
POST /redpacket/admin/set_signerPOST /redpacket/admin/set_tokenPOST /redpacket/admin/set_expiryPOST /redpacket/admin/set_allow_all_tokensPOST /redpacket/admin/set_native_token_enabledPOST /redpacket/admin/parse_tx_events
3. 用户侧接口
3.1 创建红包业务单
POST /redpacket/create_order
gRPC: CreateOrder(CreateOrderReq) returns (CreateOrderResp)
链上创建红包前调用,服务端创建一条 PENDING 业务记录并返回 biz_id。
请求示例:
{
"chain_type": "EVM",
"chain_id": 1,
"contract_address": "0xA1f42567559aBA5Ff0aac84cdE1AaF1F9DbB888F",
"creator_wallet": "0x1111111111111111111111111111111111111111",
"group_id": "g001",
"scope_type": "GROUP",
"receiver_user_id": "",
"receiver_user_ids": [],
"packet_type": 1,
"token": "0x2222222222222222222222222222222222222222",
"total_amount": "1000000000000000000",
"total_shares": 10,
"expiry_at": 0,
"remark": "happy new year"
}
字段说明:
chain_type: 必填,当前支持EVM、TRONchain_id: 可选;EVM client 可用时为空会使用配置的 chainIDcontract_address: 可选;EVM/TRON client 可用时为空会使用配置地址creator_wallet: 必填,发红包钱包地址scope_type:GROUP、DIRECT、PUBLIC;空值默认PUBLICgroup_id:scope_type=GROUP时必填receiver_user_id/receiver_user_ids:scope_type=DIRECT时至少一个非空packet_type:0固定红包,1拼手气红包,2转账total_amount: 链上最小单位十进制字符串total_shares: 总份数expiry_at: Unix 秒;0表示使用合约默认过期
成功响应 data:
{
"biz_id": "f8a0f87e-d9cb-4d4a-8350-7bd43ab2e9a4"
}
服务端写入:
- collection:
red_packet - status:
PENDING - creatorUserID: 来自登录上下文,不来自请求体
3.2 创建交易回写
POST /redpacket/created_callback
gRPC: CreatedCallback(CreatedCallbackReq) returns (CreatedCallbackResp)
链上创建交易确认后调用,用于把 biz_id 与链上 packet_id / tx_hash 绑定。
请求示例:
{
"biz_id": "f8a0f87e-d9cb-4d4a-8350-7bd43ab2e9a4",
"tx_hash": "0xabc123...",
"packet_id": "10001",
"group_id": "g001",
"scope_type": "GROUP",
"receiver_user_id": "",
"receiver_user_ids": []
}
成功响应 data:
{}
服务端逻辑:
biz_id与tx_hash必填- 如果链客户端可用,会解析交易 receipt 中的
PacketCreated - 解析成功后校验 creator、packetType、token、amount、shares、expiry 是否与业务单一致
- 如果链客户端不可用或解析失败,但请求提供了
packet_id,会使用 fallback - 成功后更新
red_packet.status=ACTIVE
3.3 查询红包详情
POST /redpacket/detail
gRPC: GetDetail(GetDetailReq) returns (GetDetailResp)
请求示例:
{
"packet_id": "10001"
}
成功响应 data:
{
"record": {
"biz_id": "f8a0f87e-d9cb-4d4a-8350-7bd43ab2e9a4",
"chain_type": "EVM",
"packet_id": "10001",
"chain_id": 1,
"contract_address": "0xA1f42567559aBA5Ff0aac84cdE1AaF1F9DbB888F",
"creator_user_id": "u1001",
"creator_wallet": "0x1111111111111111111111111111111111111111",
"group_id": "g001",
"scope_type": "GROUP",
"receiver_user_id": "",
"receiver_user_ids": [],
"packet_type": 1,
"token": "0x2222222222222222222222222222222222222222",
"total_amount": "1000000000000000000",
"total_shares": 10,
"claimed_amount": "123456789",
"claimed_shares": 1,
"expiry_at": 0,
"tx_hash": "0xabc123...",
"status": "ACTIVE",
"created_at": 1777000000,
"updated_at": 1777000060
},
"claims": [
{
"packet_id": "10001",
"user_id": "u2002",
"claimer_wallet": "0x3333333333333333333333333333333333333333",
"auth_nonce": "328840239847239847",
"claim_tx_hash": "0xdef456...",
"claimed_amount": "123456789",
"block_number": 1234567,
"status": "CONFIRMED",
"created_at": 1777000100,
"updated_at": 1777000100
}
]
}
说明:
created_at/updated_at为 Unix 秒claims按 Mongo 查询返回,DAO 层按created_at desc排序
3.4 申请领取签名
POST /redpacket/issue_claim_sign
gRPC: IssueClaimSign(IssueClaimSignReq) returns (IssueClaimSignResp)
请求示例:
{
"packet_id": "10001",
"claimer": "0x3333333333333333333333333333333333333333",
"random_seed": "0"
}
成功响应 data:
{
"auth_nonce": "328840239847239847",
"deadline": 1777012345,
"signature": "0x7b1e...a2",
"random_seed": "8888812345"
}
校验逻辑:
- 当前用户必须存在:
mcontext.GetOpUserID(ctx) != "" packet_id与claimer必填- 红包必须存在且
status=ACTIVE - 未过期、未退款
- 当前用户与
claimer钱包必须有ACTIVE绑定 - 同一用户 / 同一钱包不能重复领取
- 固定红包和拼手气红包要求
group_id存在 - 转账红包要求当前用户为
receiver_user_id
签名逻辑:
- EVM client 可用时调用
getSignMessage(packetId, claimer, authNonce, randomSeed, deadline)获取 digest - 使用
chain.signerPrivateKey裸签 digest v从 0/1 调整为 27/28- 如果 signer 私钥未配置,当前代码会返回 placeholder 签名,仅适合本地调试
3.5 领取结果回写
POST /redpacket/claim_result
gRPC: ClaimResult(ClaimResultReq) returns (ClaimResultResp)
请求示例:
{
"packet_id": "10001",
"claimer": "0x3333333333333333333333333333333333333333",
"tx_hash": "0xdef456..."
}
成功响应 data:
{}
服务端逻辑:
- 先保存一条
PENDINGclaim - 若能立即解析
PacketClaimed事件,则更新为CONFIRMED - 成功解析后会累计
claimed_amount/claimed_shares - 红包领完时状态变为
COMPLETED - 如果 receipt 暂不可用,保持
PENDING,等待 indexer 补偿
3.6 发起钱包绑定挑战
POST /redpacket/wallet_bind/challenge
gRPC: IssueWalletBindChallenge(IssueWalletBindChallengeReq)
请求示例:
{
"chain_type": "EVM",
"chain_id": 1,
"wallet_address": "0x3333333333333333333333333333333333333333",
"domain": "redpacket.example.com",
"uri": "https://redpacket.example.com/wallet-bind"
}
成功响应 data:
{
"challenge_id": "1f7d9b0d-7b43-4d84-bb11-65f2ecf7e321",
"user_id": "u2002",
"chain_type": "EVM",
"chain_id": 1,
"wallet": "0x3333333333333333333333333333333333333333",
"protocol": "siwe-eip4361",
"sign_method": "personal_sign",
"nonce": "7b7d8d48-9db6-4e95-9daa-40e9517a2a85",
"message": "redpacket.example.com wants you to sign in with your Ethereum account:\n...",
"issued_at": "2026-04-30T03:00:00Z",
"expires_at": "2026-04-30T03:10:00Z"
}
说明:
- EVM 使用
siwe-eip4361+personal_sign - TRON 使用
tron-signmessagev2+signMessageV2 - challenge 有效期为 10 分钟
3.7 确认钱包绑定
POST /redpacket/wallet_bind/confirm
gRPC: ConfirmWalletBind(ConfirmWalletBindReq)
请求示例:
{
"challenge_id": "1f7d9b0d-7b43-4d84-bb11-65f2ecf7e321",
"signature": "0x8f..."
}
成功响应 data:
{
"user_id": "u2002",
"chain_type": "EVM",
"chain_id": 1,
"wallet_address": "0x3333333333333333333333333333333333333333",
"status": "ACTIVE",
"verified_at": "2026-04-30T03:01:00Z"
}
当前限制:
- EVM 验签已实现
- TRON 验签当前返回
TRON wallet binding verification is not implemented yet
3.8 查询钱包绑定
POST /redpacket/wallet_bind/detail
gRPC: GetWalletBinding(GetWalletBindingReq)
请求示例:
{
"chain_type": "EVM",
"wallet_address": "0x3333333333333333333333333333333333333333"
}
成功响应 data:
{
"user_id": "u2002",
"chain_type": "EVM",
"chain_id": 1,
"wallet_address": "0x3333333333333333333333333333333333333333",
"status": "ACTIVE",
"challenge_id": "1f7d9b0d-7b43-4d84-bb11-65f2ecf7e321",
"verified_at": "2026-04-30T03:01:00Z"
}
4. 管理员接口
4.1 设置 signer
POST /redpacket/admin/set_signer
gRPC: SetSigner(SetSignerReq)
请求:
{
"signer_address": "0x4444444444444444444444444444444444444444"
}
响应:
{
"message": "signer address updated successfully"
}
4.2 设置 token 白名单
POST /redpacket/admin/set_token
gRPC: SetToken(SetTokenReq)
请求:
{
"token_address": "0x2222222222222222222222222222222222222222",
"allowed": true,
"min_amount": "1000000"
}
响应:
{
"message": "token configuration updated"
}
4.3 设置默认过期时间
POST /redpacket/admin/set_expiry
gRPC: SetExpiry(SetExpiryReq)
请求:
{
"expiry_seconds": 86400
}
4.4 设置是否允许所有 token
POST /redpacket/admin/set_allow_all_tokens
gRPC: SetAllowAllTokens(SetAllowAllTokensReq)
请求:
{
"allow_all": false
}
4.5 设置原生币开关
POST /redpacket/admin/set_native_token_enabled
gRPC: SetNativeTokenEnabled(SetNativeTokenEnabledReq)
请求:
{
"enabled": true
}
4.6 解析交易事件
POST /redpacket/admin/parse_tx_events
gRPC: ParseTxEvents(ParseTxEventsReq)
请求:
{
"tx_hash": "0xabc123...",
"chain": "eth"
}
EVM 响应:
{
"chain": "eth",
"tx_hash": "0xabc123...",
"events": [
{
"name": "PacketCreated",
"data": {
"packetId": "10001",
"creator": "0x1111111111111111111111111111111111111111"
}
}
]
}
TRON 当前响应:
{
"chain": "tron",
"tx_hash": "7d9e...txid",
"note": "TRON event parsing not fully implemented in this version"
}
4.7 管理接口当前行为边界
- EVM admin 接口当前为 mock,仅记录日志并返回 message,不发链上交易。
- TRON admin 接口会调用
SendAdminTransaction(...)尝试发链上交易。 - 管理接口目前没有单独管理员校验,默认只依赖 API 网关 token。生产建议补管理员鉴权与审计。
5. 业务状态
红包状态:
PENDING: 已创建业务单,尚未确认链上创建ACTIVE: 链上创建已确认,可领取COMPLETED: 已领取完成REFUNDED: 已退款
领取状态:
PENDING: 已提交领取 txHash,receipt 尚未解析或未确认CONFIRMED: 已解析PacketClaimedFAILED: 预留失败状态,当前逻辑仅用于重复领取判断时放行失败记录
钱包绑定 challenge 状态:
PENDINGVERIFIEDFAILEDEXPIRED
钱包绑定状态:
ACTIVE
6. 常见错误
op user id is empty: 缺少 token 或 token 未正确注入上下文unsupported chain_type:chain_type不是EVM或TRONpacket_id is required: 缺少红包链上 IDwallet is not bound to user: 当前用户未绑定该领取钱包user already claimed: 当前用户已领取already claimed: 当前钱包已领取packet is not active: 红包尚未激活或已经完成/退款packet is expired: 红包已过期TRON wallet binding verification is not implemented yet: 当前未实现 TRON 绑定验签
7. 前端推荐调用顺序
创建红包:
POST /redpacket/create_order- 钱包发起
createFixedPacket/createRandomPacket/createTransfer - 从
PacketCreated解析packetId POST /redpacket/created_callbackPOST /redpacket/detail刷新状态
绑定钱包:
POST /redpacket/wallet_bind/challenge- 钱包按
sign_method签名message POST /redpacket/wallet_bind/confirmPOST /redpacket/wallet_bind/detail
领取红包:
POST /redpacket/detailPOST /redpacket/issue_claim_sign- 钱包调用链上
claim(packetId, authNonce, randomSeed, deadline, signature) - 可选:
POST /redpacket/claim_result POST /redpacket/detail刷新状态
8. 存储与索引
Mongo collections:
red_packetred_packet_claimred_packet_claim_authred_packet_refundwallet_binding_challengewallet_binding
主要索引:
red_packet.biz_id唯一red_packet.packet_idred_packet.group_idred_packet_claim.claim_tx_hash唯一red_packet_claim.packet_id + user_idred_packet_claim.packet_id + claimer_walletred_packet_claim_auth.auth_nonce唯一wallet_binding_challenge.challenge_id唯一wallet_binding.user_id + chain_type + wallet_address唯一
9. 配置文件
config/openim-rpc-redpacket.yml:
rpc:
registerIP: ""
listenIP: 0.0.0.0
autoSetPorts: false
ports: [10560]
prometheus:
enable: false
ports: [12560]
chain:
rpcURL: ""
contractAddress: ""
chainID: 0
signerPrivateKey: ""
configAdminPrivateKey: ""
tron:
fullNodeURL: ""
contractBase58: ""
ownerBase58: ""
privateKeyHex: ""
feeLimit: 100000000
indexer:
pollInterval: 5
chain.rpcURL 为空时 EVM client 初始化会失败并降级;tron.fullNodeURL 为空时 TRON client 不启用。服务会继续启动。