
Component | Responsibility |
Client (Sender/Receiver) | Initiates send/claim requests for red packets; displays red packet card UI. |
Tencent Cloud Chat | Delivers messages, updates status, stores claim records, pushes claim notifications. |
Red Packet System | Manages red packet lifecycle: creation, validation, opening, amount calculation. |
Payment System | Handles funds: deduction, transfer, and refund operations. |
Field | Type | Description |
BusinessID | String | Message type identifier. Always set to "red_packet" for red packet messages. |
packet_id | String | Unique identifier generated by the red packet system after a successful deduction. Written into the custom message payload via REST API to bind the Chat message to the red packet. |
money_amount | Number | Red packet amount (used for one-on-one red packets). |
best_wishes | String | Greeting message, for example: "Wishing you prosperity and good luck." |
cover | String | URL for the red packet cover image. |
status | String | Red packet status: "Unopened" (not claimed) "Opened"(claimed / partially claimed) "None left" (fully claimed) |
quantity | Number | (Group red packet only) Number of red packets available. |
total | Number | (Group red packet only) Total amount across all red packets. |
random_amount | Boolean | (Group red packet only) Indicates whether the red packet uses random amounts ("Luckiest Draw" mode). |
{"BusinessID": "red_packet","money_amount": 1.00,"best_wishes": "Happy New Year!","cover": "https://example.com/cover.png","status": "Unopened","packet_id": "rp_20260301_abc123"}
{"BusinessID": "red_packet","total": 2.00,"quantity": 3,"random_amount": true,"best_wishes": "Happy New Year!","cover": "https://example.com/cover.png","status": "Unopened","packet_id": "rp_20260301_xyz789"}
Key | Value | Description |
{userID} | {amount},{timestamp} | Each claim record is stored as a key-value pair. |
{"user_daniel": "1.40,1709312400","user_anson": "0.60,1709312580"}
MsgBody) after it has been sent. In the red packet scenario, this is used to update the status field on the red packet card (e.g., transitioning from "Unopened" to "Opened" or "None left"), ensuring all clients see the latest status.From_Account + To_Account + MsgKey.key = userID, value = amount,timestamp. Extensions are synchronized to all clients in real time.From_Account + To_Account + MsgKey.GroupId + MsgSeq.SupportMessageExtension to 1 when sending the message.Capability | Use Case (Red Packet Scenario) | C2C Locating Parameters | Group Locating Parameters |
Message Modification | Update red packet status (Unopened → Opened → None left) | From_Account + To_Account + MsgKey | GroupId + MsgSeq |
Message Extension | Store claim records (key=userID, value=amount,timestamp) | From_Account + To_Account + MsgKey | GroupId + MsgSeq |
packet_id → send message → persist the returned MsgKey / MsgSeq. If sending fails, you can retry (idempotently) or refund/rollback according to your business rules."None left" first and a later request may overwrite it back to "Opened". Server-side serialization ensures strict, irreversible state transitions ("Unopened" → "Opened" → "None left").status field is for UI display only and should be treated as untrusted. The red packet system is the source of truth for claimability, remaining amount, expiration, etc. When a user taps a red packet, the client should query the red packet system using packet_id and render based on the latest response. The message status is a best-effort snapshot.
packet_id.From_Account set to the sender user ID. This guarantees the message is sent only after funds are deducted.// POST https://console.tim.qq.com/v4/openim/sendmsg{"SyncOtherMachine": 1,"From_Account": "sender_userid","To_Account": "receiver_userid","MsgRandom": 1287657,"MsgBody": [{"MsgType": "TIMCustomElem","MsgContent": {"Data": "{\\"BusinessID\\":\\"red_packet\\",\\"money_amount\\":1.00,\\"best_wishes\\":\\"Happy New Year!\\",\\"cover\\":\\"https://example.com/cover.png\\",\\"status\\":\\"Unopened\\",\\"packet_id\\":\\"rp_20260301_abc123\\"}"}}],"SupportMessageExtension": 1}


userID, value = amount,timestamp.status from "Unopened" to "Opened" via Message Modification REST API.{"From_Account": "sender_userid","To_Account": "receiver_userid","MsgKey": "msg_key_from_send_response","MsgBody": [{"MsgType": "TIMCustomElem","MsgContent": {"Data": "{\\"BusinessID\\":\\"red_packet\\",\\"money_amount\\":1.00,\\"best_wishes\\":\\"Happy New Year!\\",\\"cover\\":\\"https://example.com/cover.png\\",\\"status\\":\\"Opened\\",\\"packet_id\\":\\"rp_20260301_abc123\\"}"}}]}
{"From_Account": "sender_userid","To_Account": "receiver_userid","MsgKey": "msg_key_from_send_response","OperateType": 1,"ExtensionList": [{"Key": "user_anson","Value": "0.60,1709312580"}]}



From_Account set to sender user ID.// POST https://console.tim.qq.com/v4/group_open_http_svc/send_group_msg{"GroupId": "group_id","From_Account": "sender_userid","Random": 1287657,"MsgBody": [{"MsgType": "TIMCustomElem","MsgContent": {"Data": "{\\"BusinessID\\":\\"red_packet\\",\\"total\\":2.00,\\"quantity\\":3,\\"random_amount\\":true,\\"best_wishes\\":\\"Happy New Year!\\",\\"cover\\":\\"https://example.com/cover.png\\",\\"status\\":\\"Unopened\\",\\"packet_id\\":\\"rp_20260301_xyz789\\"}"}}],"SupportMessageExtension": 1}
status to "None left" via Modify Group Message REST API.GroupId + MsgSeq parameters. Status transitions include "None left".status to "None left":{"GroupId": "group_id","MsgSeq": 67890,"MsgBody": [{"MsgType": "TIMCustomElem","MsgContent": {"Data": "{\\"BusinessID\\":\\"red_packet\\",\\"total\\":2.00,\\"quantity\\":3,\\"random_amount\\":true,\\"best_wishes\\":\\"Happy New Year!\\",\\"cover\\":\\"https://example.com/cover.png\\",\\"status\\":\\"None left\\",\\"packet_id\\":\\"rp_20260301_xyz789\\"}"}}]}
{"GroupId": "group_id","MsgSeq": 67890,"OperateType": 1,"ExtensionList": [{"Key": "user_daniel","Value": "1.40,1709312400"},{"Key": "user_anson","Value": "0.10,1709312580"}]}

userID. After retrieving the userID list from extension data, the client should call the Batch Get Group Member Info API to fetch avatars, nicknames, etc., for UI rendering.getGroupMembersInfo (Java / Swift) / Objective-C / C++), passing the extracted userID list to retrieve group member avatars (faceUrl), nicknames (nickName), etc., for the claim list UI.
quantity)."Unopened" (not claimed) → "Opened" (partially claimed) → "None left" (all claimed).API | Invocation Timing | Description |
Deduction API | Send red packet | Deduct funds from sender's balance to the fund management account. |
Transfer API | Claim red packet | Transfer funds from the fund management account to the claimant. |
Refund API | Refund on expiry (24h) | Return unclaimed funds to the sender after 24 hours. |
Fund Flow API | Query/Audit | Retrieve transaction records for reconciliation and audits. |


Category | Item | Description |
General Rules | 24-hour expiry refund | Funds not claimed within 24 hours are automatically refunded to the sender's account. |
| One claim per user | Each user can only claim the same red packet once; the system must validate before opening. |
| Status consistency | Keep red packet status synchronized across all clients using the message modification API. |
Group Red Packet Specifics | Maximum claimants | Group red packets support up to 300 claimants (limited by message extension data capacity). |
| Concurrent claiming | The red packet system must safely handle concurrent claim requests. Use distributed locks or atomic operations to prevent over-claiming. |
| Random amount algorithm | In random mode, fairness and a minimum amount per packet must be ensured. "Double mean" algorithm is commonly used. |
| Luckiest Draw | The highest claimant is marked as "Luckiest Draw" in the details page. |
Exception Handling | Deduction failure | Do not create the red packet or send a message; notify the sender of insufficient balance. |
| Transfer failure | Retry transfer. If failures persist, mark for manual reconciliation and do not update message status. |
| Message modification failure | Retry with exponential backoff. Status may be temporarily inconsistent, but funds are already processed. |
| Open red packet network timeout | Use idempotent design—identical packet_id + userid open requests must return the same result. |
Security Best Practices | Server-side validation | All red packet operations (send, open, query) must be validated on the server. Never trust client-submitted amounts. |
| Idempotency design | Use packet_id + userid as the idempotency key when opening a red packet. |
| Rate limiting | Apply rate limiting to open red packet requests to prevent abuse. |
| Audit logs | Log all payment operations (deduction, transfer, refund) for reconciliation and dispute handling. |
Feedback