CRM Docking Scheme
# Function overview
After configuring the CRM docking function, when customers access, the agent system will call the enterprise API to query user information and store it in the CRM. The queried customer information will be used as conditions for customer grouping and routing strategies. The value of this function is as follows:
- Improve customer information: For third-party channels, the API param passing is not supported. You can use the CRM docking function to call the enterprise business system API to query customer information when customers access. For example, for the WhatsApp channel, more customer information can be queried using the customer's mobile number through the enterprise API calling
- Improve security: Desktop and mobile sites support passing user information via links, but this scheme has lower security. After upgrading the CRM docking function, querying user information through the server is more secure
# Docking process description
This function requires the enterprise developers to provide an API for querying customer information, which the agent system can call to query customer information. The detailed process is as follows:
- The enterprise provides an API for querying customer information according to the specifications of the agent system API
- Add docking schemes, configure valid channels, encryption keys, and query customer information API in Livechat Agent - Docking Setting - Docking Customer Information
- When customers access, read the CRM docking scheme configured for the channel, and query customer information through the API configured in the CRM docking scheme using the mobile no., email, and partnerid, and synchronize the query results to the agent system CRM
# CRM docking API specifications
# ● API description
When Sobot is calling the enterprise API, it will perform AES encryption on the request content. The enterprise also needs to encrypt the returned data in the response.
API type: Callback API
API function: Query user information through「Query User Information API URL」+「partnerid/Mobile No./Email」, and enter the queried user information into the CRM.
Request method: POST
Request API legality verification:
1. When requesting the enterprise API, the header will include a timestamp (server time timestamp) and a sign (signature). 2. The sign generation rule is MD5 (companyId + timestamp + secretKey + parameter), parameter (request param after AES encryption) 3. The enterprise verifies the legitimacy of the request based on the params passed in the header
Timestamp conversion tool:
http://tool.chinaz.com/Tools/unixtime.aspx
Example of parameter encryption:
For example, query condition email = "55612609866@163.com"; partnerid = "partnerId"; tel = "55612609866"; secretKey="secretKey".
parameter = AESUtils.dcodes("{"email":"55612609866@163.com","partnerid":"partnerId","tel":"55612609866"}",secretKey) is 1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132.
sign signature generation example:
For example, companyId = "1"; timestamp="1569397773"; secretKey="secretKey";parameter="1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132".
sign = Md5("11569397773secretKey1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132") is 868b2fe841ca486e7344b0c15aac1e76.
Param description:
Param | Type | Required | Description |
---|---|---|---|
parameter | String | Request param after AES encryption |
Request example:
The「User Information API URL」needs to be configured by the customer. For example, https://xxxxxxx.xxxxxx.com/sobot/geUserInfo
POST https://xxxxxxx.xxxxxx.com/sobot/geUserInfo HTTP/1.1
Content-Type: application/json
timestamp: 1569397773
sign: 868b2fe841ca486e7344b0c15aac1e76
{
"parameter": "1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132" // Request param after AES encryption
}
2
3
4
5
6
7
8
9
Return param encryption:
1. After receiving the request, the enterprise first verifies the sign and then decrypts the parameter. 2. The enterprise uses the secretKey (such as secretKey) agreed upon with the Sobot agent system to decrypt the received parameter field and obtain the original request data.
Example of parameter decryption:
For example, the received parameter = "1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132".
Decryption example: AESUtils.ecodes("1D6061B2D3E630351669C2A7FA96681E7D2F340484225A0EEF7D0065FF8D1FA4F42FDBCF8841CFA8F92C44026739C74D0A95AABEA0553274A0E393B891DAACD8EEF0A18B45A61F1807FF8EADD9D76132",secretKey)
Decryption result:
{"email":"55612609866@163.com","partnerid":"partnerId","tel":"55612609866"}
Example of data encryption:
For example, the queried customer information is {"uname":"su0012","realname":"su0012","email":"55612609866@163.com","tel":"55612609866","remark":"Regular customer, interested in premium services.","customer_fields":"{\"4a069df0fd4d49298041240bf15e47c8\":\"123\"}","is_vip":"1","vip_level":"","user_label":"0fd208364e3a4c67954027680002a89e"}
data = AESUtils.dcodes("{"uname":"su0012","realname":"su0012","email":"55612609866@163.com","tel":"55612609866","remark":"Regular customer, interested in premium services.","customer_fields":"{\"4a069df0fd4d49298041240bf15e47c8\":\"123\"}","is_vip":"1","vip_level":"","user_label":"0fd208364e3a4c67954027680002a89e"}",secretKey)
Encryption result: 8DD8EF1E018180C11E287594A1751B095B44EC2029160C96D58305BD536A9DCBB09591D1D708B22E39A041FE06C149C0A7E10381B67770BF97872B9C125847050A9EAE9990323AFFDCD06DC5EC5736FF007545D07099B761DD66BF6268E91EB5BF67907EE9509D24BA3F95E6F7BAE77D575E762612E58D866B00DFA3587B113F73593866A4026EB88A78996FF71A894705024F4DD743D794129CFBA7A14BE410F1C7D87A80F9CFAFF877A53115E1D35C4D8C14A19EA66C52ABD7240B3CA28B301808802FF21B2F072E006F0408C807BF94CFA2109515746DF9697B6597228A7E0B3E7C6907D3EFBBF79F14040FD2450EE9122B813AD0B47BA2DFC0DA406956FB90361A8073E19832AF1C49E6F9CA37F863376417D472CE6AC02D2CAA34BCA3B4DDCF3DEF3C61922AC6DEE6F107F42F44
Return example:
{
"data":"8DD8EF1E018180C11E287594A1751B095B44EC2029160C96D58305BD536A9DCBB09591D1D708B22E39A041FE06C149C0A7E10381B67770BF97872B9C125847050A9EAE9990323AFFDCD06DC5EC5736FF007545D07099B761DD66BF6268E91EB5BF67907EE9509D24BA3F95E6F7BAE77D575E762612E58D866B00DFA3587B113F73593866A4026EB88A78996FF71A894705024F4DD743D794129CFBA7A14BE410F1C7D87A80F9CFAFF877A53115E1D35C4D8C14A19EA66C52ABD7240B3CA28B301808802FF21B2F072E006F0408C807BF94CFA2109515746DF9697B6597228A7E0B3E7C6907D3EFBBF79F14040FD2450EE9122B813AD0B47BA2DFC0DA406956FB90361A8073E19832AF1C49E6F9CA37F863376417D472CE6AC02D2CAA34BCA3B4DDCF3DEF3C61922AC6DEE6F107F42F44"
}
2
3
Example of return when customer information is not found:
{
"data":""
}
2
3
# ● User identity and profile fields
Param | Type | Required | Description |
---|---|---|---|
uname | String | No | Nickname |
realname | String | No | Real name |
String | No | Email account | |
tel | String | No | Phone no. or telephone no. |
remark | String | No | Note |
customer_fields | String | No | Custom field (controlled by the backend) |
is_vip | Number | No | VIP customer or not 0: General customer, 1: VIP customer General customer by default |
vip_level | String | No | The id corresponding to the VIP level (id value is obtained by the above 'Query Customer Fixed Field Info' API.) If it is passed in as a general customer field, VIP level passed in will be invalid |
user_label | String | No | The id corresponding to the customer tag (id value is obtained by the above 'Query Customer Fixed Field Info' API) |
# ● AES symmetric encryption and decryption tool class
/**
* AES symmetric encryption and decryption tool class
*/
public class AESUtils {
private static final Logger log = LoggerFactory.getLogger(AESUtils.class);
/**
* <p>Discription:[encryption]</p>
* @param content Plaintext JSON string converted using JSON.toJSONString(Map<String, String> map)
* @param key Encryption and Decryption Rules, the key provided by the Visitor System"
* @return String decryption
*/
public static String ecodes(String content, String key) {
if (content == null || content.length() < 1) {
return null;
}
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] byteRresult = cipher.doFinal(byteContent);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteRresult.length; i++) {
String hex = Integer.toHexString(byteRresult[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* <p>Discription:[decryption]</p>
* @param content ciphertext
* @param key Encryption and Decryption Rules
* @return String plaintext
*/
public static String dcodes(String content, String key) {
if (content == null || content.length() < 1) {
return null;
}
if (content.trim().length() < 19) {
return content;
}
byte[] byteRresult = new byte[content.length() / 2];
for (int i = 0; i < content.length() / 2; i++) {
int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
byteRresult[i] = (byte) (high * 16 + low);
}
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] result = cipher.doFinal(byteRresult);
return new String(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99