公众号授权给第三方平台的技术实现流程比较复杂。具体步骤讲解如下。
步骤1:微信服务器向第三方平台消息接收地址推送component_verify_ticket。
出于安全考虑,在第三方平台创建审核通过后,微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据。
微信服务器发送给服务自身的事件推送(如取消授权通知、Ticket推送等)中,XML消息体中没有ToUserName字段,而是AppID字段,即公众号服务的AppID。这种系统事件推送通知(现在包括推送component_verify_ticket协议和推送取消授权通知),服务开发者收到后也需进行解密,接收到后只需直接返回字符串“success”。
1 require_once('config.php'); 2 require_once('crypt/wxBizMsgCrypt.php'); 3 4 $signature = $_GET['signature']; 5 $timestamp = $_GET['timestamp']; 6 $nonce = $_GET['nonce']; 7 $encrypt_type = $_GET['encrypt_type']; 8 $msg_signature = $_GET['msg_signature']; 9 $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];10 11 // 解密12 $pc = new WXBizMsgCrypt(Token, EncodingAESKey, AppID); 13 $decryptMsg = ""; // 解密后的明文14 $errCode = $pc->DecryptMsg($msg_signature, $timestamp, $nonce, $postStr, $decryptMsg);15 $postStr = $decryptMsg;16 17 $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);18 $INFO_TYPE = trim($postObj->InfoType);19 20 // 消息类型分离21 switch ($INFO_TYPE)22 {23 case "component_verify_ticket":24 $component_verify_ticket = $postObj->ComponentVerifyTicket;25 // 更新component_verify_ticket到系统中26 file_put_contents('component_verify_ticket.json', '{"component_verify_ticket": "'.$postObj->ComponentVerifyTicket.'", "component_expires_time": '.time.'}');27 $result = "success";28 break;29 default:30 $result = "unknown msg type: ".$INFO_TYPE;31 break;32 }33 echo $result;
上述代码解读如下。
第4~9行:接收URL中GET参数的值,以及POST传过来的数据。
第11~15行:将发送过来的加密消息进行解密。
第17~18行:将解密后的XML字符串转换成对象。
第21~32行:检查消息类型,如果类型是component_verify_ticket,则获取对象中的ComponentVerifyTicket值,并存入本地文件中,同时设置返回内容为“success”。
第33行:回显返回内容给服务器。
在微信开放平台后台中,授权事件接收地址接收到的链接如下。
http:// open.fangbei.org/wxopen/auth.php?signature=745e6b4b2fee9e59810e120888d22e927965e1fd×tamp=1465800084&nonce=469026211&encrypt_type=aes&msg_signature=b722cd5cab3110f4c78d3e49973fba0f402f986e
POST传过来的数据如下。
<xml> <AppId><![CDATA[wx2ae3853dda623211]]></AppId> <Encrypt><![CDATA[JUdOMf32n9KarWBx6o4bTHmY5lzuksWJfvomNVKKeyDxWM/1Nz0jysradXmD/ FReeylakkLivi// gsk0YOTY7smreY+sdmNsLDF3TIezdwqh3a2M8Qk3jcxAbWogBpp4b7apbVoiEa HSnpuFSVL84KabVqoS+Y33Z3sD8YYHDWlx1Edr/D3VWzocbmcUuDvoDcDJbVf6NN8ENPO4vRqaIj KWli0JyJYQGHU5y9YfjOr72nTYk5p7/NvXCHVxr6mfBQ0z/eytz1RxUT9rHh9QwfbthzWccmUbyk y2ILm1cSQJsH3l19nzViUZIVF5I53da/qXXK9q4YbvtIuFEMBPB5VjgRRqW4kudT2UNuy8qwnL9n IW1cE+eOoh4AUYpyuMelrNMaTAhRLDicsfphOoENiigVGoeChlm0kgzE6NMWyGIS1XQ1rjElbJ1h xuM8d4qlHL6EA6ppPPJreysPLP9Q==]]></Encrypt></xml>
解密后的内容如下。
<xml> <AppId><![CDATA[wx2ae3853dda623211]]></AppId> <CreateTime>1465800084</CreateTime> <InfoType><![CDATA[component_verify_ticket]]></InfoType> <ComponentVerifyTicket><![CDATA[[email protected]@@rPTvFYrZWLRgV-3iKj5VNBSWadDJGJh0YhzJ- oteCpLz0lX83e9npHXFy3WsM31H-UyzYhdUMcW20QyNyuyYmQ]]></ComponentVerifyTicket></xml>
上述数据的参数说明如表22-18所示。
表22-18 ComponentVerifyTicket协议参数说明
可以看到,这次接收到的component_verify_ticket的值为“[email protected]@@rPTvFYrZWLRgV-3iKj5VNBSWadDJGJh0YhzJ-oteCpLz0lX83e9npHXFy3WsM31H-UyzYhdUMcW20QyNyuyYmQ”。
步骤2:第三方平台获取component_access_token。
compoment_access_token是第三方平台的接口调用凭据,也称为令牌。每个令牌是存在有效期(2小时)的,且令牌的调用不是无限制的。开发人员需要对第三方平台做好令牌管理,在令牌快过期时(如1小时50分)进行刷新。
compoment_access_token需要通过component_appid和component_appsecret(即微信开放平台管理中心的第三方平台详情页中的AppID和AppSecret),以及component_verify_ticket(每10分钟推送一次的安全ticket)来获取自己的接口调用凭据(component_access_token)。
获取第三方平台component_access_token的接口如下。
https:// api.weixin.qq.com/cgi-bin/component/api_component_token
获取第三方平台component_access_token时,POST数据示例如下。
{ "component_appid":"appid_value", "component_appsecret":"appsecret_value", "component_verify_ticket":"ticket_value"}
上述数据的参数说明如表22-19所示。
表22-19 获取第三方平台component_access_token接口的参数说明
正确创建时,返回的数据示例如下。
{ "component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD- yq8wZETSoE5NQgecigDrSHkPtIYA", "expires_in":7200}
上述数据的参数说明如表22-20所示。
表22-20 获取第三方平台component_access_token接口返回参数说明
获取第三方平台component_access_token的代码如下。
1 class class_wxthird 2 { 3 // 构造函数 2、获取第三方平台component_access_token 4 public function __construct 5 { 6 $this->component_appid = AppID; 7 $this->component_appsecret = AppSecret; 8 9 // 文件缓存 component_verify_ticket10 $res = file_get_contents('component_verify_ticket.json');11 $result = json_decode($res, true);12 $this->component_verify_ticket = $result["component_verify_ticket"];13 14 // 文件缓存 component_access_token15 $res = file_get_contents('component_access_token.json');16 $result = json_decode($res, true);17 $this->component_access_token = $result["component_access_token"];18 $this->component_expires_time = $result["component_expires_time"];19 if ((time > ($this->component_expires_time + 3600)) || (empty($this- >component_access_token))){20 $component = array('component_appid' => $this->component_appid,'component_ appsecret' => $this->component_appsecret,'component_verify_ticket' => $this->component_verify_ticket);21 $data = urldecode(json_encode($component));22 $url = "https:// api.weixin.qq.com/cgi-bin/component/api_component_token";23 $res = $this->http_request($url, $data);24 $result = json_decode($res, true);25 $this->component_access_token = $result["component_access_token"];26 $this->component_expires_time = time;27 file_put_contents('component_access_token.json', '{"component_access_token": "'.$this->component_access_token.'", "component_expires_time": '. $this->component_expires_time.'}');28 }29 }30 }
步骤3:获取预授权码pre_auth_code和授权码auth_code。
预授权码用于公众号授权时的第三方平台安全验证。
获取预授权码pre_auth_code的接口如下。
https:// api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=xxx
获取预授权码pre_auth_code时,POST数据示例如下。
{ "component_appid":"appid_value"}
上述数据的参数说明如表22-21所示。
表22-21 获取预授权码pre_auth_code接口的参数说明
正确创建时,返回的数据示例如下。
{ "pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw", "expires_in":600}
上述数据的参数说明如表22-22所示。
表22-22 获取预授权码pre_auth_code接口返回参数说明
上述接口已获得预授权码,接下来通过预授权码获得授权码。
第三方平台可以在自己的网站中放置“微信公众号授权”的入口,引导公众号运营者进入授权页。授权页网址如下。
https:// mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx
该网址中第三方平台需要提供第三方平台AppID、预授权码和回调URI。
用户进入第三方平台授权页后,需要确认并同意将自己的公众号登录授权给第三方平台,完成授权流程。公众号授权页面如图22-9所示。
图22-9 公众号授权
授权流程完成后,授权页会自动跳转进入回调URI,并在URL参数中返回授权码(authorization_code)和过期时间。其链接如下。
http:// open.fangbei.org/wxopen/[email protected]@@781GTHj_A0GOQ2v7vidjMLhGlKNEhFlJHt9G6cqYxIXGS0ECe433gLGGusd-Q5OURs9Mgg7ukaWbY59htR-shw&expires_in=3600
URL中的auth_code即为授权码参数。
步骤4:获取接口调用凭据和授权信息。
开发者需要使用授权码换取授权公众号的授权信息,并换取authorizer_access_token和authorizer_refresh_token授权码的获取,需要用户在第三方平台授权页中完成授权流程后,在回调URI中通过URL参数提供给第三方平台。
调用凭据和授权信息的接口如下。
https:// api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=xxxx
调用凭据和授权信息时,POST数据示例如下。
{ "component_appid":"appid_value", "authorization_code":"auth_code_value"}
上述数据的参数说明如表22-23所示。
表22-23 调用凭据和授权信息接口的参数说明
正确创建时,返回的数据示例如下。
{ "authorization_info": { "authorizer_appid": "wx1b7559b818e3c23e", "authorizer_access_token": "W8dOXLQikO51MtMGIeMchqCnAMhS_ZyZpnIK_3YtReGJm37EF6 rjNKRD3GoRpMcT3KcVBtE68xTxGb7z3b8ba4i7zNkhfEQL9hCJD6pdQIJhcv6j8cFlHZnvQWrvA34 hUKMcAMDYOQ", "expires_in": 7200, "authorizer_refresh_token": "[email protected]@@kIi8GNH-Pjrha0bdgGBSYvcwedz0e6xhO 157YkXKrk8", "func_info": [ {"funcscope_category": {"id": 1 }}, {"funcscope_category": {"id": 15 }}, {"funcscope_category": {"id": 7 }}, {"funcscope_category": {"id": 2 }}, {"funcscope_category": {"id": 3 }}, {"funcscope_category": {"id": 6 }}, {"funcscope_category": {"id": 8 }}, {"funcscope_category": {"id": 13 }}, {"funcscope_category": {"id": 9 }}, {"funcscope_category": {"id": 12 }} ] }}
上述数据的参数说明如表22-24所示。
表22-24 调用凭据和授权信息接口返回参数说明
步骤5:获取授权方公众号的基本信息。
授权方公众号的基本信息包括头像、昵称、账号类型、认证类型、微信号、原始ID和二维码图片的URL。
获取授权公众号基本信息的接口如下。
https:// api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=xxxx
获取授权公众号的基本信息时,POST数据示例如下。
{ "component_appid":"appid_value", "authorizer_appid":"auth_appid_value"}
上述数据的参数说明如表22-25所示。
表22-25 获取授权公众号基本信息接口的参数说明
正确创建时,返回的数据示例如下。
{ "authorizer_info":{ "nick_name":"方倍工作室", "head_img":"http:// wx.qlogo.cn/mmopen/JThERPIYjcWWaHpwW7YQlkZfl1UL9dIu0to4kFY 2V3Inyzc4cQRa87b0xJWUg5axn30r1kNlu4ueK5Bf8tapT3vVfNjvFcoib/0", "service_type_info":{"id":2}, "verify_type_info":{"id":0}, "user_name":"gh_fcc4da210ff0", "alias":"fbxxjs", "qrcode_url":"http:// mmbiz.qpic.cn/mmbiz/BIvw3ibibwAYMdZIyVZHeia0mt12LT5x nXUdhvP9AeA2uQAlka5Y2ibbBFPwicSib2TxQTSd2NjVtANkBTTp2sGibTOcw/0", "business_info":{ "open_pay":1, "open_shake":1, "open_scan":0, "open_card":1, "open_store":1 }, "idc":1 }, "authorization_info":{ "authorizer_appid":"wx1b7559b818e3c23e", "func_info":[ {"funcscope_category":{"id":1 }}, {"funcscope_category":{"id":15}}, {"funcscope_category":{"id":7 }}, {"funcscope_category":{"id":2 }}, {"funcscope_category":{"id":3 }}, {"funcscope_category":{"id":6 }}, {"funcscope_category":{"id":8 }}, {"funcscope_category":{"id":13}}, {"funcscope_category":{"id":9 }}, {"funcscope_category":{"id":12}} ] }}
上述数据的参数说明如表22-26所示。
表22-26 获取授权公众号基本信息接口返回参数说明
公众号授权第三方平台的代码如下。
$weixin = new class_wxthird;if (!isset($_GET["auth_code"])){ $result = $weixin->get_pre_auth_code; $pre_auth_code = $result["pre_auth_code"]; $redirect_uri = 'http:// '.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; $jumpurl = $weixin->component_login_page($pre_auth_code, $redirect_uri); Header("Location: $jumpurl");}else{ $authorization = $weixin->query_authorization($_GET["auth_code"]); $authorizer_appid = $authorization["authorization_info"]["authorizer_appid"]; $authorizer_info = $weixin->get_authorizer_info($authorizer_appid); var_dump($authorizer_info);}
步骤6:代公众号发起业务。
得到接口调用凭据后,第三方平台可以按照公众号开发者文档(mp.weixin.qq.com/wiki)的说明,调用公众号相关API(能调用哪些API,取决于用户将哪些权限集授权给了第三方平台,也取决于公众号自身拥有哪些接口权限),使用JS SDK等能力。
下面是代公众号调用客服接口的SDK函数的代码。
1 // 代发客服接口消息 2 public function send_custom_message($openid, $type, $data, $authorizer_access_token) 3 { 4 $msg = array('touser' =>$openid); 5 $msg['msgtype'] = $type; 6 switch($type) 7 { 8 case 'text': 9 $msg[$type] = array('content'=>urlencode($data));10 break;11 case 'news':12 $data2 = array;13 foreach ($data as &$item) {14 $item2 = array;15 foreach ($item as $k => $v) {16 $item2[strtolower($k)] = urlencode($v);17 }18 $data2 = $item2;19 }20 $msg[$type] = array('articles'=>$data2);21 break;22 case 'music':23 case 'image':24 case 'voice':25 case 'video':26 $msg[$type] = $data;27 break;28 default:29 $msg['text'] = array('content'=>urlencode("不支持的消息类型 ".$type));30 break;31 }32 $url = "https:// api.weixin.qq.com/cgi-bin/message/custom/send?access_token=". $authorizer_access_token;33 return $this->http_request($url, urldecode(json_encode($msg)));34 }
可以看到,其接口及参数定义和公众号本身的发送客服消息接口是一致的,唯一不同的是把公众号的access_token换成了第三方平台的authorizer_access_token。
该接口的调用示例代码如下。
1 require_once('wxthird.class.php');2 $weixin = new class_wxthird;3 $openid = "ozy4qt1eDxSxzCr0aNT0mXCWfrDE";4 $authorizer_access_token = "W8dOXLQikO51MtMGIeMchqCnAMhS_ZyZpnIK_3YtReGJm37EF6 rjNKRD3GoRpMcT3KcVBtE68xTxGb7z3b8ba4i7zNkhfEQL9hCJD6pdQIJhcv6j8cFlHZnvQWrvA 34hUKMcAMDYOQ";5 $result = $weixin->send_custom_message($openid, "text", "这是第三方平台通过客服接口 发送的文本消息", $authorizer_access_token);