Red Packet Use Case Implementation
Scenario Description
Users send red packets (real currency, virtual currency, points, etc.) to specified friends in one-to-one chat/group chat conversations. Senders and receivers can clearly perceive whether the red packet has been claimed (unclaimed, claimed).
Effect Example

Red Packet Message Flow
1. Preparation
Before getting started, please ensure you have created an application and completed client SDK integration.
2. Define Red Packet Message
2.1 Define Red Packet Message Type
You can create red packet type messages (such as RedPacketMessage
) through IMLib SDK's custom messages. Message content structure should be defined according to business requirements, ensuring consistency across platforms (Android / iOS / Web).
Android / iOS custom message class references:
- Android CustomRedPacketMessage.java
- iOS CustomRedPacketMessage.h
- iOS CustomRedPacketMessage.m
- Server SDK RedPacketMessage.java
Web Sample Code:
// 1. RongIMLib.registerMessageType must be called before connect, otherwise may cause abnormal message receiving behavior.
// 2. Try to register all custom messages in your application uniformly at once, and only call registration once for the same type of message for easier management.
const messageType = 'app:red_packet' // Message type
const isPersited = true // Whether to store
const isCounted = true // Whether to count
const searchProps = [] // Search fields, no need to set for Web, search field values should be in range (-Math.pow(2, 64), Math.pow(2, 64)) and integers when set to numbers
const isStatusMessage = false // Whether it's a status message. Status messages are not stored, not counted, and can only be received when receiver is online.
const PersonMessage = RongIMLib.registerMessageType(messageType, isPersited, isCounted, searchProps, isStatusMessage)
2.2 Define Red Packet Message Display Template
If you're using IMKit SDK, you must create corresponding message display templates, otherwise SDK cannot properly display this type of message.
Android / iOS custom red packet message display template reference classes:
- Android CustomRedPacketMessageItemProvider.java
- iOS CustomRedPacketMessageCell.h
- iOS CustomRedPacketMessageCell.m
Web IMKit Custom Message Style Sample Code
// Construct IMKit initialization parameters
const customMessage = {
// Regular message display
userMessage: {
// key is the messageType of custom message, returned elements currently don't support setting class, you can set inline styles if needed.
'app:red_packet': (message) => {
const content = message.content;
return `<div style='padding: 0.5em 0.8333em;'>Red packet from ${content.name}</div>`;
}
},
// Notification message display
notifyMessage: {
// key is the messageType of custom message, returned elements currently don't support setting class, you can set inline styles if needed.
'app:red_packet': (message) => {
const content = message.content
const string = `<div>Red packet from ${content.name}</div>`
return string;
}
},
// Last message display in conversation
lastMessage:{
// key is the messageType of custom message, returned elements currently don't support setting class, you can set inline styles if needed.
'app:red_packet': (message) => {
const content = message.content;
return `[Red Packet]`;
},
}
};
// Special note: This init is only for demonstrating custom message settings, applications don't need to initialize multiple times
imkit.init({
customMessage:customMessage
});
3. Register and Receive Red Packet Messages
- Android
- iOS
- Web
// Register custom message type, register after SDK initialization
ArrayList<Class<? extends MessageContent>> myMessages = new ArrayList<>();
myMessages.add(CustomRedPacketMessage.class);
RongCoreClient.registerMessageType(myMessages);
// Receive message example: Set receive message listener, automatically callback when receiving messages. If you're using IMLib SDK, please call RongCoreClient's addOnReceiveMessageListener method
IMCenter.addOnReceiveMessageListener(
new io.rong.imlib.listener.OnReceiveMessageWrapperListener() {
@Override
public boolean onReceivedMessage(Message message, ReceivedProfile profile) {
int left = profile.getLeft();
boolean isOffline = profile.isOffline();
boolean hasPackage = profile.hasPackage();
}
});
// Returned `Message` entity reference information below, can use `objectName` or `content` to distinguish message types:
{
"conversationType": "PRIVATE",
"targetId": "userid3453",
"messageId": 70,
"channelId": "",
"messageDirection": "RECEIVE",
"senderUserId": "userid3453",
"receivedStatus": "io.rong.imlib.model.Message$ReceivedStatus @560f848",
"sentStatus": "SENT",
"receivedTime": 1739428279001,
"sentTime": 1739428279158,
"objectName": "app:red_packet",
"content": {
"content": "Red packet message"
},
"extra": "",
"readReceiptInfo": "io.rong.imlib.model.ReadReceiptInfo @b8d3c06",
"messageConfig": {
"disablePushTitle": false,
"pushTitle": "",
"pushContent": "",
"pushData": "null",
"templateId": "",
"forceShowDetailContent": false,
"iOSConfig": null,
"androidConfig": null,
"harmonyConfig": null
},
"canIncludeExpansion": false,
"expansionDic": null,
"expansionDicEx": null,
"mayHasMoreMessagesBefore": false,
"UId": "CKVO-0J6T-GM26-D3E6",
"disableUpdateLastMessage": "false",
"directedUsers": "0"
}
// Register custom message after SDK initialization, before connecting to IM
[[RCCoreClient sharedCoreClient] registerMessageType:[CustomRedPacketMessage class]];
// Register receive message listener, IMLib users set delegate for [RCCoreClient sharedCoreClient]
[RCIM sharedRCIM].receiveMessageDelegate = self;
// IMKit delegate callback:
- (void)onRCIMReceiveMessage:(RCMessage *)message left:(int)left;
// IMLib delegate callback
- (void)onReceived:(RCMessage *)message left:(int)nLeft object:(nullable id)object;
// Retrieved custom message related content
_isOffline = false
_canIncludeExpansion = false
_hasChanged = false
_disableUpdateLastMessage = false
_conversationType = 1
_targetId = @"123"
_channelId = @""
_messageId = 17
_messageDirection = 2
_senderUserId = @"123"
_receivedStatusInfo RCReceivedStatusInfo * 0x3025eb5c0
_sentStatus = 30
_receivedTime = 1740566955000
_sentTime = 1740566955056
_objectName = @"app:red_packet"
_content CustomRedPacketMessage * 0x300691b80
_RCMessageContent RCMessageContent
_senderUserInfo
_mentionedInfo
_auditInfo
_destructDuration = 0
_extra
_rawJSONData
_contentBaseKeys
_content = @"Red packet"
_extra = @""
_messageUId = @"CL87-G08C-6L85-E222"
_readReceiptInfo
_groupReadReceiptInfoV2
_messageConfig RCMessageConfig * 0x30279cee0
_messagePushConfig RCMessagePushConfig * 0x300693110
_directedUserIds
_destructTime = 0
_expansionDicEx
// Set message listening
const Events = RongIMLib.Events
RongIMLib.addEventListener(Events.MESSAGES, (evt) => {
console.log(evt.messages)
})
// Register custom message
const PersonMessage = RongIMLib.registerMessageType('app:red_packet', true, true, [], false)
// Send custom message
// Build custom message to send
const message = new PersonMessage({ name: 'someone', age: 18 })
// Receive this custom message example
// Call addEventListener to set message receive listener. All received messages will callback in this interface method.
// Returns IAReceivedMessage type message array, for type reference: https://doc.rongcloud.cn/apidoc/im-web/latest/zh_CN/interfaces/IMessagesEvent.html
const Events = RongIMLib.Events
const listener = (evt) => {
console.log(evt.messages)
};
RongIMLib.addEventListener(Events.MESSAGES, listener)
// Returned `Message` entity reference information below, can use `messageType` to distinguish message types:
{
// Can filter gift messages for business layer processing based on messageType
"messageType": "app:red_packet",
"channelId": "",
"content": {
"content": "Red packet message"
},
"senderUserId": "user01",
"targetId": "chart01",
"conversationType": 4,
"sentTime": 1743414376795,
"receivedTime": 0,
"messageUId": "Message unique value",
"messageDirection": 1,
"isPersited": true,
"isCounted": true,
"isMentioned": false,
"disableNotification": false,
"isStatusMessage": false,
"canIncludeExpansion": false,
"expansion": null,
"receivedStatus": 0,
"receivedStatusInfo": {
"isRead": false,
"isListened": false,
"isDownload": false,
"isRetrieved": false
},
"messageId": 8488190336485840000,
"sentStatus": 30,
"isOffLineMessage": false
}
4. Extension Area Display (Send Red Packet Entry)
4.1 Set Red Packet Extension Panel Plugin
If you're using IMLib SDK, you need to implement the send red packet entry yourself. If you're using IMKit SDK, you can add custom plugins to IMKit's extension panel.
Android Implementation Process
Custom plugins need to implement the IPluginModule
interface class. You can refer to IPluginModule.java
in IMKit source code and specific implementation classes.
Here we use implementing custom plugin RedPacketPlugin
example class as an example.
iOS Implementation Process
Insert corresponding red packet extension plugin icon in chat page's viewDidLoad
method
[self.chatSessionInputBarControl.pluginBoardView insertItem:[UIImage imageNamed:@"redPacket"] highlightedImage:[UIImage imageNamed:@"redPacket"] title:@"Send Red Packet" tag:20080];
Web IMKit doesn't have extension panel concept
4.2 Configure Extension Panel Plugin
Sample Code
- Android
- iOS
- Web
// 1. Inherit `DefaultExtensionConfig`, create custom extension panel configuration class `MyExtensionConfig`, override `getPluginModules()` method.
public class MyExtensionConfig extends DefaultExtensionConfig {
@Override
public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType, String targetId) {
List<IPluginModule> pluginModules = super.getPluginModules(conversationType,targetId);
// Add red packet extension item
pluginModules.add(new RedPacketPlugin());
return pluginModules;
}
}
// 2. After SDK initialization, call `setExtensionConfig` method to set custom input configuration. SDK will display extension panel according to this configuration.
RongExtensionManager.getInstance().setExtensionConfig(new MyExtensionConfig());
// Override plugin click method in chat page to implement red packet sending related business logic:
// Handle extension item click event
- (void)pluginBoardView:(RCPluginBoardView *)pluginBoardView clickedItemWithTag:(NSInteger)tag {
// Red packet extension plugin tag
if (tag == 20080) {
// Business layer implements red packet sending related logic
}else {
[super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
}
}
// Web IMKit not supported
5. Send Red Packet Message After Successful Payment
After user payment succeeds, you need to call RongCloud's send message method to send red packet message and set message as extensible. Choose which platform to send this type of message from according to business logic.
- Android
- iOS
- Web
// Build red packet message
CustomRedPacketMessage redPacketMessage = CustomRedPacketMessage.obtain("0.01");
io.rong.imlib.model.Message message =
io.rong.imlib.model.Message.obtain(targetId, conversationType, redPacketMessage);
// Set message as extensible
message.setCanIncludeExpansion(true);
HashMap<String, String> redInfo = new HashMap<>();
redInfo.put("open","false");
redInfo.put("count","1");
redInfo.put("amount","0.01");
message.setExpansion(redInfo);
// Send message, if you're using IMLib SDK, please use RongCoreClient's sendMessage method
IMCenter.getInstance().sendMessage(message, null, null, null);
// Build red packet message
CustomRedPacketMessage *message = [CustomRedPacketMessage messageWithContent:@"0,01"];
RCMessage *msg = [[RCMessage alloc] initWithType:self.conversationType targetId:self.targetId direction:MessageDirection_SEND content:message];
// Set message as extensible
msg.canIncludeExpansion = YES;
msg.expansionDic = @{@"open":@"false",@"count":@"1",@"amount":@"0.01"};
// Send message, if you're using IMLib SDK, please use RCCoreClient's sendMessage method
[[RCIM sharedRCIM] sendMessage:msg pushContent:@"Red packet is here" pushData:nil successBlock:^(RCMessage *successMessage) {
} errorBlock:^(RCErrorCode nErrorCode, RCMessage *errorMessage) {
}];
// Build custom red packet message to send
const message = new PersonMessage({ key1: 'value1', key2: 'value2' })
// Send message
RongIMLib.sendMessage({
conversationType: RongIMLib.ConversationType.PRIVATE,
targetId: '<targetId>'
}, message).then(res => {
if (res.code === 0) {
console.log(res.code, res.data)
} else {
console.log(res.code)
}
})
Server Sample Code
- Server SDK in Java
- Server SDK in PHP
- Server SDK in GO
/**
* Send group gray bar message - targeted users (up to 1000 users per request)
*/
String[] targetIds = {"groupId"};
RedPacketMessage redPacketMessage =new RedPacketMessage("Red packet message");
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("open", "false");
hashMap.put("count", "1");
hashMap.put("amount", "2");
GroupMessage groupMessage = new GroupMessage()
.setSenderId("fromId")
.setIsIncludeSender(1)
.setTargetId(targetIds) // Group ID
.setContent(redPacketMessage)
.setObjectName(txtMessage.getType())
.setExpansion(true)
.setExtraContent(hashMap);
ResponseResult redPackeReslut = group.send(groupMessage);
System.out.println("group info Notify message result: " + redPackeReslut.toString());
require "./../../RongCloud.php";
define("APPKEY", '');
define('APPSECRET','');
use RongCloud\RongCloud;
use RongCloud\Lib\Utils;
/**
* Group message sending
*/
function send()
{
$RongSDK = new RongCloud(APPKEY,APPSECRET);
$message = [
'senderId'=> 'Vu-oC0_LQ6kgPqltm_zYtI',// Sender ID
'targetId'=> ['php group1'],// Group ID
"objectName"=>'app:red_packet',// Message type Text
'content'=>json_encode(['content'=>'Red packet message'])// Message Body
'expansion'=>true,
'extraContent' => ['open' => 'false', 'count' => '2', 'amount' => '3'],// Custom message extension content, JSON structure, set in Key, Value format
];
$Result = $RongSDK->getMessage()->Group()->send($message);
Utils::dump("Group message sending",$Result);
}
send();
func TestRongCloud_GroupSend(t *testing.T) {
rc := NewRongCloud(
os.Getenv("APP_KEY"),
os.Getenv("APP_SECRET"),
REGION_BJ,
)
msg := TXTMsg{
Content: "Red packet message",
Extra: "helloExtra",
}
// Create a map containing multiple key-value pairs
dataMap := map[string]string{
"open": "false",
"count": "1",
"amount": "2",
}
// Convert map to JSON format
data, err := json.Marshal(dataMap)
err := rc.GroupSend(
"7Szq13MKRVortoknTAk7W8",
[]string{"CFtiYbXNQNYtSr7rzUfHco"},
"app:red_packet",
&msg,
"",
"",
1,
0,
WithMsgExpansion(true),
WithMsgExtraContent(data),
)
t.Log(err)
}
6. Open Red Packet and Update Red Packet Extension
After user clicks to open red packet, red packet message status needs to change to "opened". At this time, you can update this message's extension information through Message Extension Listener updateMessageExpansion
, set opening user ID and mark as opened status in extension, and change local display message style.
- Android
- iOS
- Web
// messageUid is the original red packet message unique ID.
RongIMClient.getInstance()
.updateMessageExpansion(
redInfo,
messageUid,
new RongIMClient.OperationCallback() {
@Override
public void onSuccess() {
// Update sender handles UI data refresh after updating extension here
IMCenter.getInstance().refreshMessage(currentMessage);
}
@Override
public void onError(RongIMClient.ErrorCode errorCode) {
Toast.makeText(
getApplicationContext(),
"Setting failed, ErrorCode : " + errorCode.getValue(),
Toast.LENGTH_LONG)
.show();
}
});
// Traverse conversation page data source
for (RCMessageModel *model in self.conversationDataRepository) {
if (model.messageUId == red packet messageUId) {
// Update red packet extension
[[RCCoreClient sharedCoreClient] updateMessageExpansion:dict messageUId:model.messageUId success:^{
NSUInteger row = [self.conversationDataRepository indexOfObject:model];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
dispatch_async(dispatch_get_main_queue(), ^{
// Update corresponding UI
[self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[indexPath]];
});
} error:^(RCErrorCode status) {
}];
}
}
// Update red packet message extension fields
RongIMLib.updateMessageExpansion({ key1: 'val1', key2: 'val2' }, message).then(res => {
if (res.code === 0) {
console.log(res.code, 'Update successful')
} else {
console.log(res.code, res.msg)
}
})
Server Sample Code
- Server SDK in Java
- Server SDK in PHP
- Server SDK in GO
/**
*
* Set Message Extension
*
*/
ExpansionModel msg = new ExpansionModel();
msg.setMsgUID("BS45-NPH4-HV87-10LM");
msg.setUserId("WNYZbMqpH");
msg.setTargetId("tjw3zbMrU");
msg.setConversationType(1);
HashMap<String, String> kv = new HashMap<String, String>();
kv.put("type1", "1");
kv.put("type2", "2");
kv.put("type3", "3");
kv.put("type4", "4");
msg.setExtraKeyVal(kv);
msg.setIsSyncSender(1);
ResponseResult result = expansion.set(msg);
System.out.println("set expansion: " + result.toString());
/**
* Message module two-person message instance
*/
require "./../../RongCloud.php";
define("APPKEY", '');
define('APPSECRET', '');
use RongCloud\RongCloud;
use RongCloud\Lib\Utils;
/**
* Two-person message sending
*/
function set()
{
// Connect to the Singapore Data Center
// RongCloud::$apiUrl = ['http://api.sg-light-api.com/'];
$RongSDK = new RongCloud(APPKEY, APPSECRET);
$message = [
'msgUID' => 'BS45-NPH4-HV87-10LM', // Message unique identifier, the server can obtain it through the full message routing function.
'userId' => 'WNYZbMqpH', // Need to set the extended message delivery user Id.
'targetId' => 'tjw3zbMrU', // Target ID, depending on the conversationType, could be a user ID or a group ID.
'conversationType' => '1', // Conversation type, one-on-one chat is 1, group chat is 3, only supports single chat and group chat types.
'extraKeyVal' => ['type1' => '1', 'type2' => '2', 'type3' => '3', 'type4' => '4',],// Custom message extension content, JSON structure, set in Key, Value format
'isSyncSender' => 0 // Whether the sender accepts the terminal user's online status, 0 indicates not accepting, 1 indicates accepting, default is 0 not accepting
];
$res = $RongSDK->getMessage()->Expansion()->set($message);
Utils::dump("Two-person message sending", $res);
}
set();
func TestRongCloud_MessageExpansionSet(t *testing.T) {
data, err := json.Marshal(map[string]string{"type": "3"})
if err != nil {
t.Log("marshal err", err)
return
}
rc := NewRongCloud(
os.Getenv("APP_KEY"),
os.Getenv("APP_SECRET"),
REGION_BJ,
)
if err := rc.MessageExpansionSet("C16R-VBGG-1IE5-SD0C",
"u01",
"3",
"testExp0309",
string(data),
1,
); err != nil {
t.Error(err)
return
}
t.Log("do UGMessageGet suc")
}
7. Sender Updates Red Packet Message Extension
Red packet message senders can globally set message extension listeners and handle corresponding processing when receiving message extension update callbacks. It's recommended to call client service API to get latest red packet claim information.
- Android
- iOS
- Web
// Receiver Android sample code
RongIMClient.getInstance().setMessageExpansionListener(new RongIMClient.MessageExpansionListener() {
@Override
public void onMessageExpansionUpdate(Map<String, String> expansion, Message message) {
if (message.getContent() instanceof CustomRedPacketMessage){
IMCenter.getInstance().refreshMessage(message); // Refresh original message
// Other custom processing
}
}
});
// Receiver iOS sample code:
[RCCoreClient sharedCoreClient].messageExpansionDelegate = self;
// Message extension information change callback
- (void)messageExpansionDidUpdate:(NSDictionary<NSString *,NSString *> *)expansionDic message:(RCMessage *)message {
for (int i = 0; i < self.conversationDataRepository.count; i++) {
RCMessageModel *model = self.conversationDataRepository[i];
if ([model.messageUId isEqualToString:message.messageUId]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[indexPath]];
});
break;
}
}
}
// Web receiver receives message extension notification:
RongIMLib.addEventListener(RongIMLib.Events.EXPANSION, ({ updatedExpansion, deletedExpansion }) => {
console.log('Extension info update:', updatedExpansion);
console.log('Extension info delete:', deletedExpansion);
})
8. Post Red Packet Claim Operations
After opening red packet, if you need to display "XX claimed the red packet", the server can send a group targeted message to red packet sender and claimer, or monitor message extension information to display corresponding user information and claim status. Red packet sender can monitor message extension information, and after receiving monitoring, actively get latest claim status from business client.
- Android
- iOS
- Web
// Insert gray bar message
InformationNotificationMessage informationNotificationMessage = InformationNotificationMessage.obtain("You claimed the red packet sent by xxx");
ConversationType conversationType = ConversationType.PRIVATE;
String targetId = "user1";
String senderUserId = "Simulated sender ID";
ReceivedStatus receivedStatus = new ReceivedStatus(0x1);
String sentTime = System.currentTimeMillis();
IMCenter.getInstance().insertIncomingMessage(conversationType, targetId, senderUserId, receivedStatus, informationNotificationMessage, sentTime, new RongIMClient.ResultCallback<Message>() {
/**
* Success callback
* @param message Inserted message
*/
@Override
public void onSuccess(Message message) {
}
/**
* Failure callback
* @param errorCode Error code
*/
@Override
public void onError(RongIMClient.ErrorCode errorCode) {
}
});
RCInformationNotificationMessage *warningMsg =
[RCInformationNotificationMessage
notificationWithMessage:@"You claimed the red packet sent by xxx" extra:nil];
RCMessage *savedMsg = [[RCCoreClient sharedCoreClient]
insertOutgoingMessage:self.conversationType targetId:self.targetId
sentStatus:SentStatus_SENT content:warningMsg];
// If using IMKit, call this method in chat page to insert message in UI
[self appendAndDisplayMessage:savedMsg];
// Web message listener receives messages, display UI as needed
const Events = RongIMLib.Events
const listener = (evt) => {
console.log(evt.messages)
};
RongIMLib.addEventListener(Events.MESSAGES, listener)