1. 在线服务访客名片接入流程
注意: 本文只对在线服务接入访客名片功能进行详细叙述。
接入在线服务的租户通常会有这么一个需求,就是希望客服在工作台界面能够看到来咨询问题的访客的个人信息,以便提供更好的服务。出于为客户的数据安全考虑,云客服不会获取接入方的客户信息,这就需要接入方提供一个页面或接口(即“访客名片”)来供云客服系统实时展示,而这样的一个页面或接口需要查询参数,比如类似userId这样的能唯一确定客户身份的信息。接入方如何将这些查询参数传递给云客服,以便云客服系统在调用接入方的“访客名片”时将查询参数透传给接入方系统,这就是本文档要解决的问题。
在线服务接入访客名片的工作有两个,分别为 :
- 访客端聊天窗数据埋点
- 客服端工作台访客信息展示
访客名片详细工程(Java语言) JavaDemo(PhP语言)phpdemo(C#语言)C#Demo
上述demo可以直接本地执行Application.java中的main函数
其他开发语言目前没有代码demo,请用户参考Java语言进行开发
注意:该文档所需公钥,需要收集租户id(提供聊天窗url),和当前文档链接,提工单给我们的同学生成
2. 访客端聊天窗数据埋点
埋点主要是构造带有用户信息的加密链接。
2.1 在线服务埋点链接
访问 https://robot-ccs.aliyun.com/scenemng.htm#/scenemng 页面,选择要构造加密信息的聊天窗 URL,以移动端的链接为例,以下就是一个标准的云客服在线服务聊天窗 URL:https://cschat-ccs.aliyun.com/index.htm?tntInstId=_0GBRgxU&scene=SCE00000023 。 如果以这个埋点链接直接连入在线客户,客服在工作台端看到的只能是匿名访客。我们需要对这个链接加些参数,才能让其变为一个带实名访客信息的聊天窗链接。
2.2 需要加哪些参数?
只需要加上两个 URL 参数,即可使一个匿名链接变为实名链接:
-
cinfo:将要传给访客名片的查询参数被加密后的内容。
-
key:非对称加密算法加密后的对称加密密钥。
最终的聊天窗 URL 需变成这样:https://cschat-ccs.aliyun.com/index.htm?tntInstId=_0GBRgxU&scene=SCE00000023&cinfo=xxxxx&key=yyyyy可以看到,这两个参数提到最多的词就是“加密”。处于安全考虑,这些必须以密文形式传递,否则将会给恶意攻击者可趁之机。接入方对加密技术不了解也没关系,后面会有示例代码。
2.3 cinfo里是什么内容?
cinfo 里明文的内容构造示例:
{
"extInfo": {" uid " : "11111"," customerInfo ": "11111" },
"userId":"12345",
"uname":"张三",
"browser":"chrome",
"network":"4G",
"appVersion":"1.0.0",
"os":"iOS",
"device":"iphone6 plus",
"resolution":"1920*1280",
"timestamp":当前时间的UNIX时间戳
}
最重要的 3 个字段:extInfo、userId、timestamp。请详细阅读下表中这 3 个字段的说明。
字段代码 | 字段名称 | 字段说明 |
---|---|---|
extInfo | 业务扩展信息 | 该字段是一个json对象,内容由接入方确定。用于存放接入方访客名片需要用到的查询参数,云客服系统会负责将这些查询参数透传给接入方的访客名片地址,格式形如:”extInfo”:{“key1”:”val1”,” “key2”:”val2” },其中的key-value均由接入方自己指定。云客服平台不关注其中的内容,只会透传给接入方的访客名片。该字段必须设置。 |
userId | 用户id | 访客在接入方系统中的唯一标识。该字段必须设置。 |
timestamp | 时间戳 | 当前时间的UNIX时间戳,增加该字段可以控制埋点链接的有效时间,提升安全性,(单位:毫秒)。在线工作台配置管理中可增加一个配置编码为ENTRY_URL_EXPIRE_TIME的配置项(单位:秒),当云客服系统接收到请求的时间减去该字段的值得到的时间跨度超过了配置的有效时间,则认为该加密链接已失效,云客服系统将拒绝此次访问。该字段虽为可选字段,但出于安全考虑,建议设置。 |
uname | 用户名称 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
browser | 浏览器 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
network | 网络 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
appVersion | 应用端版本 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
os | 操作系统 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
device | 设备 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
resolution | 分辨率 | 用于在在线工作台的环境信息tab中展示,可不传;不传则不展示。 |
2.4 如何生成最终的cinfo和key?
接下来将明文进行加密,以得到最终要拼接到聊天窗 URL 中的 cinfo 和 key 参数。
代码示例
public class Test {
//注意:该公钥仅供demo示范使用,用户公钥请参考文档第四部分,收集信息,联系开发同学获取。
private static String pub_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp5khaVZIP+x+n+ari3c1dcYRuNG7BUL0VVt1X2+KDcOpTHtfdHUQIzrHdbikZSyaCKyHLbAuLsU24gulCKmMGtjFGS6OiavUFFikVnVnOyEzcnhSPBiJNdlUo5TNYOVN1SUCutJUxvibQ1za6IcnHf4okgPTgcrXVHyG5ntCbE9ippKLe7q0z0TUIkRxesbKZiQPBDOgBukJUiFMk95zqCdESCe6kCSp2GdIojyVAelU11JIcAm/4OjCCFMz6Jcnse7rdScxRsoMHU89tDmXG9mo3PhUXyfQJzyESlotKek99eHPkSr7EW/SBj3xMc+ysrBZd+4tFOZJIRIlFf/eSQIDAQAB";
//还原出RSA公钥对象
private static PublicKey getPubKey() throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(pub_key));
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
return key;
}
public static void main(String[] args) throws Exception {
JSONObject extInfo = new JSONObject();
extInfo.put("userId", "123456");
extInfo.put("userName","张三");
JSONObject cinfo = new JSONObject();
cinfo.put("userId", "123456");
cinfo.put("extInfo", extInfo);
PublicKey publicKey = getPubKey();
Map<String, String> map = CustomerInfoCryptoUtil.encryptByPublicKey(cinfo.toString(), publicKey);
String uri = "&key=" + map.get("key") + "&cinfo=" + map.get("text");
}
}
目前加密方法只支持 Java 语言,加密所需的密钥请联系云客服的技术人员获取,加密所需的工具类 jar 包可以下载导入
在此处直接下载 jar包
2.5 生成最终的带实名访客信息的聊天窗URL
通过上一步得到了加密后的cinfo和key,将这两个参数拼接到原聊天窗 URL 中,即可得到最终的 URL(如 2.2 描述)。可以在服务配置=》热线高级配置=》聊天窗埋点测试进行测试
3. 客服端工作台访客信息展示
在工作台对访客信息展示一共有两种方案:
- 页面嵌入
- 外部接口调用
这两种方案不可以同时使用, 请选择一种。
3.1 页面嵌入方案
该方案需要租户自己提供一个访客名片的展示页面, 以iframe的形式嵌入到在线工作台右侧。工作流程如下:
- 提供访客名片页面的URL。
- 对该页面的URL中的params进行解密 (params由云客服系统构造)。
- 页面对params的内容进行展示。
- 利用postMessage函数方法, 回传userId和userName。
3.1.1 提供访客名片URL
编写一个简单的页面用于展示客户信息。并把URL填写到 服务配置->热线配置->热线高级配置 的输入框中。
3.1.2 params进行解密
客服工作台在加载租户提供的URL的时候, 会在URL中拼接三个参数: params, key, iframeKey
.
https://www.example.com/customerinfo.htm?params=***&key=***&iframeKey=***
params : AES对称性加密后的JSON字符串
key : RSA非对称性加密后的AES密钥
iframeKey : 服务记录TAB-ID (不用关心该字段含义)
解密params方法请参考本文: 第四节 解密。
解密后的结果是一个json数据, 该数据内容的参数含义如下:
名称 | 类型 | 可空 | 字段限制 | 描述 |
---|---|---|---|---|
extInfo | String | 是 | 2048 | 业务个性化信息,来源于聊天窗埋点的链接,示例:”extInfo”:{“key1”:”value1”,” key2”:”value2” },其中key值接入组件可自行定义。 |
sourceType | String | 否 | 32 | 在线页面嵌入时需要根据此参数判断展示类型,在线为纵向展示。 |
contactId | String | 是 | 64 | 服务记录ID,供接入方处理个性化业务与服务记录之间的关联关系, 例如:业务自有工单需要与云客服平台服务记录做关联的场景中。 |
timestamp | String | 否 | 32 | 生成事件Unix时间戳。 |
3.1.3 页面对params的内容进行展示
拿到解密后的params内容后,在iframe页面上渲染其内容信息, 展示给客服。
3.1.4 利用postMessage函数方法回传userId和userName
请将下面代码放到页面中,
function sendMessage(type, data) {
const params = {};
params.type = type;
params.data = data;
params.resource = 'csbridge';
params.iframeKey = '1'; // 从url参数获取,示例假设值为1
window.parent.postMessage(JSON.stringify(params), '*');
}
然后在步骤 3.1.2 的params解密后, 调用:
sendMessage('updateUserInfo', {"userId" : "TESTID123456789", "userName" : "王某某"});
3.1.5 更新嵌入的页面高度
本小节是为了样式调整, 不是必须的. 使用方法同上一节类似
示例
sendMessage('updateHeight', 300); //300是需要设定的高度
以上是接入访客名片的所有流程。
3.2 外部接口调用
使用这种方案的时候,不需要租户提供任何前端页面, 只需要提供一个接口,云客服会通过Post的方式调用这个接口来查询访客信息。
注意:
该接口必须支持跨域请求. 跨域解决方案可自行搜索CORS获取。
接口协议必须是https。
响应头中 Content-Type 为 application/json; charset=utf-8。
客户需要做的流程为:
- 提供客户查询接口URL。
- 对该URL收到的POST请求中,取得params和key参数并解密。
- 通过解密后的内容,查询该访客的详细信息返回。
3.2.1 提供客户查询接口URL
提供该URL,并配置到云客服工作台的组件配置 -> 访客名片URL。用下面的方式选择 外部请求。
3.2.2 取得params和key参数并解密
云客服会对该接口发送POST请求(跨域)。其中两个参数params和key会拼接在上一步配置的URL后面。 例如:
https://www.example.com/customerinfo.json?params=***&key=***
在取得这两个参数后,需要进行解密。
解密params方法请参考本文: 第四节 解密
解密之后的内容是一个JSON字符串, 各个字段意义如下(各个字段并不一定有,可以为空):
名称 | 类型 | 可空 | 字段限制 | 描述 |
---|---|---|---|---|
tel | String | 是 | 32 | 电话号码 |
extInfo | String | 是 | 2048 | 聊天窗埋点信息 |
userId | String | 是 | 64 | 租户CRM中的用户ID |
cardId | String | 是 | 32 | 用户证件号码 |
timestamp | String | 否 | 32 | Unix时间戳 |
3.2.3 通过解密后的内容查询用户信息并返回.
解密后的内容含有用户的关键信息,通过这个信息客户自行查询到相信信息, 并按照严格的格式返回。
返回的JSON数据中,userId为必填字段。云客服平台会以此数据作为客户的唯一标识,并保存在云客服平台服务记录中。该字段不用再schema中做字段信息的描述。
样例:
{
"message": "查询成功", //成功的消息
"success": true, //表示查询成功
"result": { //下面是自定义用户信息内容.
"telePhone": "11111111111",
"customerName": "招商银行苏州分行",
"userName": "test",
"email": "wangqy1@hundsun.com",
"userId": "0CE0540B-E351-4B83-B47F-72A2E8D2479E", //这个必须有!
"customerNo": "77777",
"account": "77777",
"mobile": "",
"schema": { //会按照schema的结构来展示用户信息
"properties": {
"customerName": {
"name": "客户名",
"type": "text"
},
"account": {
"name": "客户帐号",
"type": "text"
},
"userName": {
"name": "联系人",
"type": "text"
},
"telePhone": {
"name": "电话",
"type": "text"
},
"email": {
"name": "邮箱",
"type": "text"
}
}
}
}
}
4. 解密
4.1 解密步骤
- 从https请求URL中获取对应参数,分别是params, key。
- 将用户公钥还原成PublicKey类型。
- 调用CustomerInfoCryptoUtil.decryptByPublicKey(params, key, publicKey)方法进行解密。
4.2 maven依赖
<repository>
<id>central</id>
<url>http://mvn.cloud.alipay.com/nexus/content/groups/public</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<dependency>
<groupId>com.alipay.fc.csplatform</groupId>
<artifactId>fccsplatform-common-crypto</artifactId>
<version>1.0.0.20161108</version>
</dependency>
在此处也可以直接下载 jar包
4.3 Java-Demo
public class Test {
//注意:该公钥仅供demo示范使用,用户公钥请联系技术支持获取。
private static String pub_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp5khaVZIP+x+n+ari3c1dcYRuNG7BUL0VVt1X2+KDcOpTHtfdHUQIzrHdbikZSyaCKyHLbAuLsU24gulCKmMGtjFGS6OiavUFFikVnVnOyEzcnhSPBiJNdlUo5TNYOVN1SUCutJUxvibQ1za6IcnHf4okgPTgcrXVHyG5ntCbE9ippKLe7q0z0TUIkRxesbKZiQPBDOgBukJUiFMk95zqCdESCe6kCSp2GdIojyVAelU11JIcAm/4OjCCFMz6Jcnse7rdScxRsoMHU89tDmXG9mo3PhUXyfQJzyESlotKek99eHPkSr7EW/SBj3xMc+ysrBZd+4tFOZJIRIlFf/eSQIDAQAB";
//还原出RSA公钥对象
private static PublicKey getPubKey() throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(pub_key));
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
return key;
}
public static void main(String[] args) throws Exception {
String params = request.getParameter("params");
String key = request.getParameter("key");
PublicKey publicKey = getPubKey();
String cinfo = CustomerInfoCryptoUtil.decryptByPublicKey(params, key, publicKey);
JSONObject dataJsonObject = JSON.parseObject(cinfo);
//加验时间戳
Date requestDate = new Date(dataJsonObject.getLong("timestamp"));
Date now = new Date();
//...
//TODO 业务处理
}
}
5 常见问题
(1) data must start with zero
解决方式:使用URLEncode解密params参数和key参数。
CustomerInfoCryptoUtil.decryptByPublicKey(URLEncoder.encode(params, "UTF-8"), URLEncoder.encode(key,"UTF-8"), publicKey);
(2) 如果在按照文档对接成功,但是工作台无法显示,需要提供租户id,工作台客户接入时的控制台network中调用填写接口的response信息,然后提工单给我们。