讀古今文學網 > 微信公眾平台開發:從零基礎到ThinkPHP5高性能框架實踐 > 22.5.2 公眾號授權第三方平台 >

22.5.2 公眾號授權第三方平台

公眾號授權給第三方平台的技術實現流程比較複雜。具體步驟講解如下。

步驟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&timestamp=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[ticket@@@rPTvFYrZWLRgV-3iKj5VNBSWadDJGJh0YhzJ-
    oteCpLz0lX83e9npHXFy3WsM31H-UyzYhdUMcW20QyNyuyYmQ]]></ComponentVerifyTicket>
</xml>
  

上述數據的參數說明如表22-18所示。

表22-18 ComponentVerifyTicket協議參數說明

可以看到,這次接收到的component_verify_ticket的值為「ticket@@@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_ticket
10         $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_token
15         $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/login.php?auth_code=queryauthcode@@@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": "refreshtoken@@@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);