企业微信外部联系人回调事件

说明:

1) 下列"外部联系人" 和 "客户联系" 其实都是一个意思,都是指顾客,但是由于企业微信开发文档中叫"外部联系人",管理后台叫"客户联系", 为方便操作,故本文名称跟企业微信保持一致

2) 下列两段代码实例,默认使用者已经有PHP-SDK, 原生代码案例可以自行前往git下载PHP-SDK(地址见官方文档),ThinkPHP5.*版本案例,可以参考本人下载资源中的PHP-SDK,或者根据官方提供的sdk自行修改命名空间; 由于时间问题,原生代码部分由TP5版本代码修改而成,仅供参考代码,暂未实测; ThinkPHP5.*版本代码亲测有效,有异议欢迎提出讨论

1.作用

企业成员 添加/删除外部联系人 时,可在企业后台接收添加/删除的外部人数据,及时更新企业后台数据

2.运行原理

1) 开发者验证回调事件url有效性,验证通过后,可在企业微信管理员后台,配置回调所需的3个参数: 回调事件url, Token , EncodingAESKey

2) 企业微信管理后台给相关企业成员配置"客户联系"权限

3) 当企业成员(需要配置客户联系操作权限) 添加/删除外部联系人时,企业微信服务器会向外部联系人回调事件url 推送一段加密字符串(xml格式) ,具体事件格式可参照开发文档, 而且务必保证正确处理数据,

3.开发者操作步骤

1) 管理后台配置外部联系人回调事件url地址,并验证该url有效性

A) 先调用调试工具,验证回调事件url有效性,具体参见:

![验证回调事件url的详情图片](https://img-blog.csdnimg.cn/20190221220329870.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2R1cmluZ25vbmU=,size_16,color_FFFFFF,t_70)

B) 除了A)操作,还需要在该url中,返回解密后的加密消息内容,首先两点;

a) 调用PHP-SDK中的XMLparse类中的VerifyUrl方法(Verify方法可验证回调事件url); PHP-SDK可下载本人下载资源中的SDK(此SDK根据ThinkPHP5.0进行了命名空间的封装),或者参考企业微信开发文档自行编写

b) **坑一**: 在回调事件中必须 return 企业微信调试工具发送的Get请求结果(ps:企业微信文档中说的是正确响应,经和其技术沟通,并亲测为使用return关键字即可,示例代码如下:

$params = $_GET;

$obj = new XMLparse();

$callbackRes = $obj->VerifyUrl($params);//调用SDK中的VerifyUrl方法,返回值为解密后的消息,即步骤A)图中的 "测试消息123"

return $callbackRes;// 相当于直接返回明文消息: return"测试消息123";

)

c) **坑二**: 验证回调事件url有效性时,即调用verifyUrl方法时,必须先urldecode('echoStr参数'),否则会抛出异常; 若使用原生代码示例,务必自行添加urldecode,若使用框架可自行输出参数查看;(本人使用ThinkPHP5.*框架,ThinkPHp5.*框架中做了urldecode的处理,可以不必开发者手动urldecode;)

2) url通过验证后,添加/删除外部联系人时,会想该url推送指定格式的xml数据(加密字符)

A) **坑三**: 接收xml数据时,确保接收的数据事原生的,最好使用 $params = file_get_contents('php://input');(ps: 本人就是因为在框架配置了htmlspecialchars(html标签过滤函数)这个函数将<>转义了,导致SDK中的DOMDocument类的loadXML方法, 无法读取正确的xml数据,抛出异常,记得将数据恢复成xml格式数据,若使用了htmlspecialchars,记得htmlspecialchars_decode一次)

B) **坑四**: 因为涉及的字符串长度过长(400~750个字节),使用var_dump,echo,print_r均无法正常输出字符内容,建议使用将字符写入文件中(使用函数fopen(),fwrite(),或者file_put_content())

C) 具体代码过程参见下列两个版本,以ThinkPHP5.*版本为准,如有异议,欢迎提出讨论

3) 详情参见企业微信api开发文档,

A)文档地址:

https://work.weixin.qq.com/api/doc#90000/90135/90664

B) 接口调试工具地址:

https://work.weixin.qq.com/api/devtools/devtool.php

3.注意:

1) 上述有提及四个坑点,请各位开发者多留意 (可结合下列示例代码理解)

2) 必须先登录企业管理后台(管理员身份),配置回调事件的url相关的参数,并给相关企业成员配置"客户联系"权限

4.PHP原生代码示例 (使用前提,需要先获取SDK)

class External {

protected $_weworkConfig = [

'corpId' => '',//企业ID

];

protected $_externalCallbackEvent = [//回调事件参数

'url'=> 'http://www.test.com/External/callbackEvent',

'token' => '',

'encodingAESKey' => ''

];

protected $_callbackObj; // 回调事件对象(外部联系人添加/删除)

protected $_callbackErrorMsgArr = [//企业微信(添加/删除外部联系人)回调事件错误码+错误信息

'0'=> 'success',

'-40001'=> '签名验证错误',

'-40002'=> 'xml解析失败',

'-40003'=> 'sha加密生成签名失败',

'-40004'=> 'encodingAesKey 非法',

'-40005'=> 'corpid 校验错误',

'-40006'=> 'aes 加密失败',

'-40007'=> 'aes 解密失败',

'-40008'=> '解密后得到的buffer非法',

'-40009'=> 'base64加密失败',

'-40010'=> 'base64解密失败',

'-40011'=> '生成xml失败',

];

public function __construct() {

# 回调事件对象

$this->_callbackObj = new \weworkapi\callback\WXBizMsgCrypt($this->_externalCallbackEvent['token'], $this->_externalCallbackEvent['encodingAESKey'], $this->_weworkConfig['corpId']); //企业应用回调, 消息加密/解密类WXBizMsgCrypt

}

/**

* 验证(添加/删除外部联系人)回调事件url有效性

*/

public function verifyUrl($data) {

$params = $_GET;

$params2 = $_POST;

$params = array_merge($params,$params2); // get数据+post数据

try {

if (empty($params['msg_signature'])) {

throw new \Exception('msg_signature不得为空');

}

if (empty($params['timestamp'])) {

throw new \Exception('timestamp不得为空');

}

if (empty($params['nonce'])) {

throw new \Exception('nonce不得为空');

}

if (empty($params['echostr'])) {

throw new \Exception('echostr不得为空');

}

$sReplyEchoStr = "";

$verifyRes = $this->_callbackObj->VerifyURL($data['msg_signature'], $data['timestamp'], $data['nonce'], urldecode($data['echostr']),$sReplyEchoStr); // 此处需要urldecode($data['echoStr'])

0 !== $verifyRes && exception('errCode: '.$verifyRes .', errMsg: '.$this->_callbackErrorMsgArr[$verifyRes]);

return $sReplyEchoStr;

} catch (\Exception $ex) {

return ['errCode'=>'0084','errMsg'=>$ex->getMessage()];

}

return ['errCode'=>'0','errMsg'=>'success','data'=>$verifyRes];

}

/**

* 测试企业微信的外部联系人事件回调

*/

public function callbackEvent() {

$params = $_GET; //get参数

$params['xmlContent'] = file_get_contents('php://input'); //post的xml数据

# 记录入参

$params['date'] = date('Y-m-d H:i:s');

$paramsStr = json_encode($params);

$fp = fopen('./externalCallbackEvent_params.log', 'w');

fwrite($fp, $paramsStr);

try {

//若回调url验证通过,处理回调的xml消息

if (!empty($params['xmlContent']) && empty($params['echostr'])) {

$dealRes = $this->dealCallbackEvent($params);

if ('success' !== $dealRes['desc']) {

throw new \Exception($dealRes['desc']);

}

# 记录调用结果

$callbakcRes = json_encode(['date'=>date('Y-m-d H:i:s'),'result'=>$dealRes['data']]);

$fp2 = fopen('./externalCallbackEvent_result.log', 'w');

fwrite($fp2, $callbakcRes);

return $dealRes['data'];

} else { //验证url有效性

$verifyRes = $this->verifyUrl($params);

if ('success' !== $verifyRes['desc']) {

throw new \Exception($verifyRes['desc']);

}

return $verifyRes['data']; // 解密后的消息内容(务必原文输出),否则报错

}

} catch (\Exception $ex) {

return ['errCode'=>'0083','errMsg'=>$ex->getMessage()];

}

}

/**

* 验证(添加/删除外部联系人)回调事件消息加密

*/

public function dealCallbackEvent($data) {

try {

# 解密

$decryptMsg = $this->decryptMsg($data);

$decryptMsgArr = $this->XMLString2Array($decryptMsg);

switch ($decryptMsgArr['ChangeType']) {

case 'add_external_contact': // 添加外部联系人回调事件

$dealRes = $this->addExternalCallbackEvent($decryptMsgArr);

break;

case 'del_external_contact': // 删除外部联系人回调事件

$dealRes = $this->delExternalCallbackEvent($decryptMsgArr);

break;

default:

throw new \Exception($type . '回调事件类型不合法');

break;

}

return $dealRes;

}catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 消息加密

*/

public function encryptMsg($data) {

try {

// $sReqTimeStamp = "1409659813";

// $sReqNonce = "1372623149";

// $sRespData = "13488318601234567890123456128";

$sReqTimeStamp = $data['timestamp']; //时间戳

$sReqNonce = $data['nonce']; //随机字符串

$content = $data['content']; //被加密的消息内容

// 需要发送的明文

$sRespData = "".$sReqNonce."1234567890123456".Config::get('WEWORK_AGENT_ID')."";

$sEncryptMsg = ""; //xml格式的密文

$errCode = $this->_callbackObj->EncryptMsg($sRespData, $sReqTimeStamp, $sReqNonce, $sEncryptMsg);

if (0 !== $errCode) {

throw new \Exceptionexception('errCode: '.$errCode .', errMsg: '.$this->_callbackErrorMsgArr[$errCode]);

}

return $sEncryptMsg;

} catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 消息解密

*/

public function decryptMsg($data) {

try {

// $sReqMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";

// $sReqTimeStamp = "1409659813";

// $sReqNonce = "1372623149";

// $sReqData = "";

$sReqMsgSig = $data['msg_signature'];

$sReqTimeStamp = $data['timestamp'];

$sReqNonce = $data['nonce'];

$sReqData = $data['xmlContent']; // post请求的密文数据

$decryptMsg = ""; // 解析之后的明文

$errCode = $this->_callbackObj->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $decryptMsg);

if (0 !== $errCode) {

throw new \Exception('errCode: '.$errCode .', errMsg: '.$_callbackErrorMsgArr[$errCode]);

}

return $decryptMsg;

} catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 提取xml数据中的指定参数

* (转换原理:xml字符串->xml对象->json对象->数组)

* @param string xml格式数据

* @return array array格式数据

*/

public function XMLString2Array($xmlStr) {

try {

//xml字符转换为xml对象

$xmlObj = simplexml_load_string($xmlStr,'SimpleXMLElement', LIBXML_NOCDATA);

$jsonObj = json_encode($xmlObj);

return json_decode($jsonObj,true);

} catch (\Exception $ex) {

throw new Exception($ex->getMessage());

}

}

/**

* 添加外部联系人回调处理

* 原理:

* 1) 获取外部联系人userid(异步处理时,userid从队列中获取)

* 2) 获取外部联系人详情

* 3) 将外部联系人插入数据库

* @param array

*/

public function addExternalCallbackEvent($data) {

}

/**

* 删除外部联系人回调处理

* 原理:

* 1) 获取外部联系人userid(异步处理时,userid从队列中获取)

* 2) 删除本地数据库中外部联系人记录

*/

public function delExternalCallbackEvent($data) {

}

}

5. ThinkPHP5.*实例(先获取PHP-SDK)

use \think\Validate;//ThinkPHP5.*的验证类

class External {

protected $_weworkConfig = [

'corpId' => '',//企业ID

];

protected $_externalCallbackEvent = [//回调事件参数

'url'=> 'http://www.test.com/External/callbackEvent',

'token' => '',

'encodingAESKey' => ''

];

protected $_callbackObj; // 回调事件对象(外部联系人添加/删除)

protected $_callbackErrorMsgArr = [//企业微信(添加/删除外部联系人)回调事件错误码+错误信息

'0'=> 'success',

'-40001'=> '签名验证错误',

'-40002'=> 'xml解析失败',

'-40003'=> 'sha加密生成签名失败',

'-40004'=> 'encodingAesKey 非法',

'-40005'=> 'corpid 校验错误',

'-40006'=> 'aes 加密失败',

'-40007'=> 'aes 解密失败',

'-40008'=> '解密后得到的buffer非法',

'-40009'=> 'base64加密失败',

'-40010'=> 'base64解密失败',

'-40011'=> '生成xml失败',

];

public function __construct() {

# 回调事件对象

$this->_callbackObj = new \weworkapi\callback\WXBizMsgCrypt($this->_externalCallbackEvent['token'], $this->_externalCallbackEvent['encodingAESKey'], $this->_weworkConfig['corpId']); //企业应用回调,消息加密/解密类WXBizMsgCrypt

}

/**

* 测试企业微信的外部联系人事件回调

*/

public function callbackEvent() {

$params = Request::instance()->param(); //get参数

$params['xmlContent'] = file_get_contents('php://input'); //post的xml数据

# 记录入参

$params['date'] = date('Y-m-d H:i:s');

$paramsStr = json_encode($params);

$fp = fopen('./externalCallbackEvent_params.log', 'w');

fwrite($fp, $paramsStr);

try {

//若回调url验证通过,处理回调的xml消息

if (!empty($params['xmlContent']) && empty($params['echostr'])) {

$dealRes = $this->dealCallbackEvent($params);

'success' !== $dealRes['desc'] && exception($dealRes['desc']);

# 记录调用结果

$callbakcRes = json_encode(['date'=>date('Y-m-d H:i:s'),'result'=>$dealRes['data']]);

$fp2 = fopen('./externalCallbackEvent_result.log', 'w');

fwrite($fp2, $callbakcRes);

return $dealRes['data'];

} else { //验证url有效性

$verifyRes = $this->verifyUrl($params);

'success' !== $verifyRes['desc'] && exception($verifyRes['desc']);

return $verifyRes['data']; // 解密后的消息内容(务必原文输出),否则报错

}

} catch (\Exception $ex) {

$this->result([],'0083',$ex->getMessage(),'json');

}

}

/**

* 验证(添加/删除外部联系人)回调事件消息加密

*/

public function dealCallbackEvent($data) {

try {

$rules = [

'msg_signature' => 'require',

'timestamp' => 'require',

'nonce' => 'require',

'xmlContent' => 'require', //接企业微信服务器发送的回调的xml消息内容

];

$validate = new Validate($rules);

!$validate->check($params) && exception($validate->getError());

// $params['xmlContent'] = htmlspecialchars_decode($params['xmlContent']); //若配置中使用了htmlspecialchars函数,转义了等符号

# 解密

$decryptMsg = $this->decryptMsg($data);

$decryptMsgArr = $this->XMLString2Array($decryptMsg);

switch ($decryptMsgArr['ChangeType']) {

case 'add_external_contact': // 添加外部联系人回调事件

$dealRes = $this->addExternalCallbackEvent($decryptMsgArr);

break;

case 'del_external_contact': // 删除外部联系人回调事件

$dealRes = $this->delExternalCallbackEvent($decryptMsgArr);

break;

default:

throw new \Exception($type . '回调事件类型不合法');

break;

}

return $dealRes;

}catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 验证URL有效性

* @param array 参数

* @return int 错误码,0-正常,否则出错

*/

public function verifyUrl($data) {

try {

$rules = [

'msg_signature' => 'require',

'timestamp' => 'require',

'nonce' => 'require',

'echostr' => 'require',

];

$validate = new Validate($rules);

!$validate->check($params) && exception($validate->getError());

$sReplyEchoStr = "";

$verifyRes = $this->_callbackObj->VerifyURL($data['msg_signature'], $data['timestamp'], $data['nonce'], $data['echostr'],$sReplyEchoStr); // 此处不需要urldecode($data['echoStr']),初步猜测是TP5的Request类已经做过了urldecode处理

0 !== $verifyRes && exception('errCode: '.$verifyRes .', errMsg: '.$this->_callbackErrorMsgArr[$verifyRes]);

return $sReplyEchoStr;

} catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 消息加密

*/

public function encryptMsg($data) {

try {

// $sReqTimeStamp = "1409659813";

// $sReqNonce = "1372623149";

// $sRespData = "13488318601234567890123456128";

$sReqTimeStamp = $data['timestamp']; //时间戳

$sReqNonce = $data['nonce']; //随机字符串

$content = $data['content']; //被加密的消息内容

// 需要发送的明文

$sRespData = "".$sReqNonce."1234567890123456".Config::get('WEWORK_AGENT_ID')."";

$sEncryptMsg = ""; //xml格式的密文

$errCode = $this->_callbackObj->EncryptMsg($sRespData, $sReqTimeStamp, $sReqNonce, $sEncryptMsg);

0 !== $errCode && exception('errCode: '.$errCode .', errMsg: '.$this->_callbackErrorMsgArr[$errCode]);

return $sEncryptMsg;

} catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 消息解密

*/

public function decryptMsg($data) {

try {

// $sReqMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";

// $sReqTimeStamp = "1409659813";

// $sReqNonce = "1372623149";

// $sReqData = "";

$sReqMsgSig = $data['msg_signature'];

$sReqTimeStamp = $data['timestamp'];

$sReqNonce = $data['nonce'];

$sReqData = $data['xmlContent']; // post请求的密文数据

$decryptMsg = ""; // 解析之后的明文

$errCode = $this->_callbackObj->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $decryptMsg);

0 !== $errCode && exception('errCode: '.$errCode .', errMsg: '.$this->_callbackErrorMsgArr[$errCode]);

return $decryptMsg;

} catch (\Exception $ex) {

throw new \Exception($ex->getMessage());

}

}

/**

* 提取xml数据中的指定参数

* (转换原理:xml字符串->xml对象->json对象->数组)

* @param string xml格式数据

* @return array array格式数据

*/

public function XMLString2Array($xmlStr) {

try {

//xml字符转换为xml对象

$xmlObj = simplexml_load_string($xmlStr,'SimpleXMLElement', LIBXML_NOCDATA);

$jsonObj = json_encode($xmlObj);

return json_decode($jsonObj,true);

} catch (\Exception $ex) {

throw new Exception($ex->getMessage());

}

}

/**

* 添加外部联系人回调处理

* 原理:

* 1) 获取外部联系人userid(异步处理时,userid从队列中获取)

* 2) 获取外部联系人详情

* 3) 将外部联系人插入数据库

* @param array

*/

public function addExternalCallbackEvent($data) {

}

/**

* 删除外部联系人回调处理

* 原理:

* 1) 获取外部联系人userid(异步处理时,userid从队列中获取)

* 2) 删除本地数据库中外部联系人记录

*/

public function delExternalCallbackEvent($data) {

}

}

php 企业微信指令回调借款_企业微信外部联系人回调事件相关推荐

  1. 企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格

    企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格 点趣互动是企业微信系统的第三方应用提供厂商,用于管理员工企业微信的内一款系统软件.点趣互动企业微信scrm软件主 ...

  2. 企业微信加密消息体_企业微信机器人怎么发消息?企业微信机器人可以定时发消息吗?...

    企业微信外部群自带群机器人功能,可以协助员工高效管理社群,帮助我们运营客户,那么企业微信机器人怎么用呢? 使用企业微信机器人,需要管理员先进入企业微信管理后台,在[客户联系]-[效率工具]-[自动回复 ...

  3. 企业微信的外部联系人回调处理技巧

    一.关于设置接收事件服务器的信息 在企业微信管理后台的"客户联系-客户"页面,点开"API"小按钮,再点击"接收事件服务器"配置,进入配置页 ...

  4. PC企业微信hook接口,通过查询添加外部联系人教程

    通过查询添加为联系人(外部联系人) 操作码 102008 请求说明 参数名 必选 类型 说明 type 是 int 类型 addType 是 string 类型14微信用户 1企业用户 user_id ...

  5. 企业信息化投入中咨询服务_企业信息化咨询中的问题与对策研究

    龙源期刊网 http://www.qikan.com.cn 企业信息化咨询中的问题与对策研究 作者:宋 倩 张 燕 周 镭 来源:<商场现代化> 2008 年第 22 期 [ 摘要 ] 我 ...

  6. 企业信息化投入中咨询服务_企业信息化咨询问题研究

    龙源期刊网 http://www.qikan.com.cn 企业信息化咨询问题研究 作者:姜妍 来源:<中国管理信息化> 2018 年第 17 期 [ 摘 要 ] 随着互联网络技术的发展和 ...

  7. 企业信息化投入中咨询服务_企业信息化咨询业务分析报告.doc

    企业信息化咨询业务分析 目录 引言 企业信息化与管理咨询,是当前学术界.企业界谈论的一个焦点话题,也是被IT公司和管理咨询公司认为当前最富有商机的"交叉.边缘"领域之一.几年前,I ...

  8. Exchange企业实战技巧(15)启用向外部联系人发送邮件时的提醒

    Exchange在系统默认的设置下,对于外部联系人的邮件传递是不出现任何提示信息.为避免将企业内部的重要邮件误发给外部的收件人.可以通过EMS控制台修改系统设置,来开启提醒功能,实现当用户使用OWA或 ...

  9. 斐波纳契回调线_斐波那契回调线(黄金分割线)神级操作-经典

    斐波那契回调线(黄金分割线)神级操作-经典 斐波那契回调线,又称黄金分割线.在交易市场上,大多数的技术指标都具有滞后性,导致交易者在使用时不太好掌握.但是,斐波那契回调线具有提前性,能很好的帮助交易者 ...

最新文章

  1. MVP架构设计 初探
  2. 白话Elasticsearch67-不随意调节jvm和thread pool的原因jvm和服务器内存分配的最佳实践
  3. Android 程序适应多种多分辨率
  4. java线程知识梳理_Java多线程——多线程相关知识的逻辑关系梳理
  5. leetcode103. 二叉树的锯齿形层次遍历(bfs)
  6. python列表生成式内必须定义匿名函数_Python基础-----基础概念总结
  7. 用python numpy实现幻方
  8. 开发大型高负载类网站应用的几个要点
  9. H5_0020:判断安卓苹果平台
  10. POJ2513Colored Sticks
  11. xposed框架_把安卓手机开发到极致的框架xposed
  12. FMS3.5的安装使用
  13. 有效id和密码_ID和密码恢复
  14. androi的AT指令
  15. android xposed miui9,vxposed在小米-安卓9上闪退
  16. 使用evo工具评估ORB_SLAM2在TUM数据集上的运行轨迹
  17. 线性系统大作业——2.二阶倒立摆建模与控制系统设计(上)
  18. 企业级架构之LNMP
  19. 论文阅读:高炉炼铁工序入炉焦比预测的研究
  20. arm解锁 j-flash_J-Link固件烧录以及使用J-Flash向arm硬件板下载固件程序(示例代码)...

热门文章

  1. 网络工程师_记录的一些真题_2005上半年上午
  2. C语言,统计0~9出现次数。_只愿与一人十指紧扣_新浪博客
  3. 技术12期:如何设计rowkey使hbase更快更好用【大数据-全解析】
  4. OpenCV中BLOB特征提取与几何形状分类
  5. 深度学习最近发现详细分析报告
  6. 浙大版《C语言程序设计(第3版)》题目集 练习2-17 生成3的乘方表 (15 分)
  7. Android Studio第三十四期 - git企业级应用命令
  8. 修改tomcat6.0.25日志默认路径
  9. reactor与proactor模式
  10. atitit.mp4 视频文件多媒体格式结构详解