Appearance
客户不在 APP 时的消息推送
客户问完就退出 APP 了,客服十分钟后才回。要让他收到系统推送,需要你的服务端参与。
这件事为什么要你参与
给 APP 发系统推送需要推送证书:iOS 的 APNs 证书、Android 各家厂商的推送通道。这些都在你手里。
所以分工是:合从发离线消息通知,你负责推送。客户离线期间客服发了新消息,合从把这条消息的信息发到你指定的地址,你的服务端查到这位客户的推送 token,用你自己的推送通道发出去。
和未读红点的分工
推送要你的服务端参与,SDK 内置的未读消息与红点不用:客户回到 APP 时 SDK 查一次未读数,你把数字画在自己的客服入口上就行。
| 什么时候触达客户 | 要你做什么 | |
|---|---|---|
| 系统推送 | 客户完全退出 APP 也能收到 | 服务端接回调 + 用你自己的推送通道发 |
| 未读红点 | 客户下次打开 APP 时看到 | 在入口上画一个数字 |
红点管「客户回来了」,推送管「客户没回来」。推送要拉服务端同事一起做,先只接红点也可以。
配置回调地址
在工作台的 APP 渠道详情页,左侧选择 离线消息通知,在 回调地址 里填你的服务端接收地址。页面上方的 启用离线消息通知 开关默认开着,填好地址保存就生效;以后不想再收,清空地址再保存。
签名密钥(选填) 可以留空。填了之后合从会对报文签名,你的服务端可以据此确认请求确实来自合从,见下面的验真。
什么时候会收到
三个条件同时成立时,合从发出一次通知请求:
- 这位客户已经离开会话 —— 退出 APP、切到后台、或者关掉了客服页面
- 发消息的是人工客服
- 消息不是内部备注
机器人的回复、自动回复都不会触发。客户还在 APP 里看着聊天页时也不会。
直接用 HecongChatView 的话,前后台联动必须接
合从靠 APP 的前后台事件判断客户是否已离开。整页打开或用 Fragment / ViewController 嵌入时,这件事已经内置;直接用 HecongChatView 时要你转发宿主的前后台回调,否则合从会一直认为客户在线,离线消息通知一条都不会发。做法见把聊天嵌进自己的页面。
你会收到什么
一个 POST 请求,Content-Type: application/json,报文结构:
json
{
"event": "offline_message",
"tenantId": "…",
"channelId": "…",
"dialogId": "…",
"customerId": "…",
"anonymousId": "…",
"externalUserId": "…",
"agentNickname": "小美",
"message": {
"id": "…",
"contentType": "text",
"text": "您的订单已经发出了",
"createdAt": 1755500000000
},
"ts": 1755500000100
}| 字段 | 说明 |
|---|---|
anonymousId | 合从给这台设备的稳定标识,客户没登录时靠它认人 |
externalUserId | 你通过 identify 传的会员 ID,客户登录过才有值 |
agentNickname | 客服的对外昵称,可以直接写进推送文案 |
message.contentType | 消息类型,text 之外还有图片、视频、语音等 |
message.text | 文本内容,只给前 500 个字符。非文本消息这里是 null,按 contentType 出兜底文案(例如「[图片]」) |
message.createdAt | 消息时间,毫秒时间戳 |
anonymousId 和 externalUserId 两个都会给,不是二选一。客户登录过的话两个同时有值。
怎么找到该推给谁
你的服务端要能从报文里的身份找到设备的推送 token,分两种情况:
客户登录过:用 externalUserId,那就是你自己的会员 ID,直接查你的库。
客户没登录:只能用 anonymousId。这个值要提前存下来 —— 在 APP 里监听 onAnonymousIdChanged 回调,拿到值之后连同这台设备的推送 token 一起报给你的服务端:
kotlin
// Android:在你的 HecongChatListener 实现里
override fun onAnonymousIdChanged(anonymousId: String) {
reportToMyServer(anonymousId, myPushToken)
}swift
// iOS:在你的 HecongChatDelegate 实现里
func hecongChatDidChangeAnonymousId(_ anonymousId: String) {
reportToMyServer(anonymousId, myPushToken)
}不要用你自己生成的设备 ID 去对照
anonymousId 由合从维护,可能和你在配置里传的 deviceId 不同。以回调里给到的值为准。
验证请求确实来自合从
配了密钥的话,请求头里会带一个签名:
X-Hecong-Signature: sha256=<十六进制签名>签名是用你填的密钥对请求体原始字符串做 HMAC-SHA256。你的服务端用同样的方法算一遍,比对一致再处理。
用解析后的对象重新序列化再算签名会对不上,要用原始的请求体字节。
送达与失败
每条消息只发一次通知请求,不重试。
你的服务端返回什么状态码都算送到了,包括 4xx。合从不消费响应内容。连续失败 5 次(超时、连不上、5xx)之后,这个渠道的离线消息通知会暂停 5 分钟再恢复。
所以你的服务端要做到:快速返回,把推送逻辑丢进自己的队列异步处理,不要在这个请求里同步等推送结果。
怎么自测
工作台里没有测试发送按钮,验证要走一遍真实链路:
- 真机打开 APP,进客服页面发一条消息,让对话建立起来
- 把 APP 切到后台或直接杀掉进程
- 在工作台里用人工客服身份回一条消息
- 看你的服务端有没有收到通知请求
收不到的话,先对照什么时候会收到的三个条件,再看接入问题排查。
下一步
- 未读消息与红点 —— APP 开着时的提醒
- 把聊天嵌进自己的页面 —— 直接用 HecongChatView 时前后台联动的接法