微信公衆號接入第三方服務器,設置自動回覆、關鍵回覆、自定義菜單,配置及開發流程

首先須要確認一點,一旦接入第三方服務器,微信就認爲你已經具有了開發能力,像自動回覆、關鍵詞回覆、自定義菜單這些功能,微信公衆平臺就再也不提供了(須要開發者調用相關接口),停用服務器以後,這些功能也就恢復了,兩者是互斥的。javascript

說明一下,本文的例子是node+express搭建服務,消息加解密方式爲明文模式,請酌情參考。java

1、搭建本地調試環境,須要將本地服務穿透出去,便於外網訪問,能夠用花生殼或者ngrok等,能穿透內網就能夠,這裏就很少說了。node

2、服務器配置及校驗express

  如今咱們已經有了一個可供外網訪問的本地服務,接下來講一下微信公衆平臺的相關配置:json

  登陸微信公衆平臺,找到基本配置數組

  

  能夠看到服務器配置一項(我這裏是已經啓用過的),點擊右側的修改配置服務器

  

  先說一下大體流程,信息編輯完畢以後點擊提交,微信服務器會向你所填寫的URL發送一條get請求,你的服務器必需要能接到這條請求,而後拿微信服務器帶來的參數進行驗證,驗證完畢以後,返回驗證結果給微信,微信拿到想要的結果以後(至於具體返回什麼,後面會說),你的服務器就算是在微信服務器「備案」成功了,接下來點擊啓用就能夠了,啓用以後,微信服務器一旦收到消息,就會向你所填寫的URL發送一條post請求(確保你的服務器在5秒內作出響應,否則會發生一些錯誤,具體錯誤可查看微信文檔),請求攜帶的參數是xml格式的,注意配置一下,不要以json的形式去接收,解析xml,能拿到信息發送者、接收者、信息內容、事件類型等數據,而後就能夠根據事件類型、信息等作出相應的處理。微信

  填寫URL:支持https和http,格式爲http://xxxxxx.com+接口路徑,例如https://www.baidu.com/authorize,https://www.baidu.com是你的服務地址,authotize是你的接口路徑。app

  填寫Token:這兒的token是驗證服務器的令牌,是你本身定義的,符合格式要求就行,後面會用到(注意區別access_token,二者不是一回事)。微信公衆平臺

  填寫EncodingAESKey:能夠隨機生成,也能夠本身定義,符合格式要求就好,當設置消息加解密方式爲加密模式時會用來解密消息(本文采用明文模式)。

  選擇消息加解密方式:本文選擇明文模式。

  服務器驗證:

  

// 服務器驗證
// /authorize爲接口路徑,

router.get('/authorize', (req, res) => {
  //接收到微信服務器的請求後,取出參數signature,timestamp,echostr,nonce
    let signature = req.query.signature;
    let timestamp = req.query.timestamp;
    let echostr = req.query.echostr;
    let nonce = req.query.nonce;
    // 把token、timestamp、nonce進行字典排序,CONFIG.token換爲你本身的token就好
    let arr = [CONFIG.token, timestamp, nonce].sort();
    // sha1加密
    let str = arr.join('');
    let hashCode = crypto.createHash('sha1');
    let result = hashCode.update(str).digest('hex');
    // 與signature對比後返回結果
    if (result === signature) {
    // 驗證正確以後,把echostr原封不動返回給微信就好了
        res.send(echostr);
    } else {
     // 驗證錯誤的話也要返回信息,告訴微信不要再嘗試請求了,微信官方建議直接返回success字符串,固然返回空也是能夠的
        res.send('success');
    }
});

必定要處理好服務器驗證邏輯以後再點擊提交按鈕,不然是提交不成功的。

提交成功以後,就算是接入服務器了,可是點擊啓用按鈕,服務器才能起做用。

接下來是消息處理邏輯:

微信服務器在接收到用戶消息以後,就會向你的服務器發送請求,URL和驗證服務器的URL同樣,只不過請求方式爲post

// 消息處理
// 用xml2js模塊來處理xml
let parseString = require('xml2js').parseString;
router.post('/authorize', (req, res) => {
    try {
        let buffer = [];
        // 監聽data事件,用於接收數據,用req.body是拿不到數據的
        req.on('data', (data) => {
            buffer.push(data);
        });
        // 監聽end事件,用於處理接收完成的數據
        req.on('end', () => {
            parseString(Buffer.concat(buffer).toString('utf-8'), {
                explicitArray: false
            }, (err, result) => {
                // 處理錯誤
                if (err) {
                    console.log('解析微信服務器發來的消息出錯了:');
                    console.log(err);
                    res.send('success');
                    return false;
                }

                if (!result || !result.xml) {
                    // 未接收到有效消息,告訴微信服務器不要再嘗試鏈接
                    res.send('success');
                    return console.log('未接收到任何消息也未發生任何事件');
                }

                result = result.xml;
                // 接收方微信(注意接收方和發送方的轉換)
                let toUser = result.FromUserName;
                // 發送方微信
                let fromUser = result.ToUserName;
                let userMessage = result.Content;

                console.log('-----------------------開始處理消息-----------------------');

                if (result.Event == 'subscribe') {
                    // 若是是用戶關注
                    console.log('--------------------有用戶關注了---------------------------');
                    handleAutoReply(res, toUser, fromUser, 'subscribe');
                } else {
                    // 其餘消息
                    if (result.MsgType != 'text') {
                        res.send('success');
                        console.log('------------------不是文本類型的消息暫不處理----------------------');
                        return false;
                    }
                    
                    // 文本消息

                    // 這裏能夠處理一些特殊回覆,好比發送編碼查詢等

                    // 處理關鍵詞自動回覆
                    console.log('-----------------------如今處理關鍵詞回覆------------------------');
                    handleAutoReply(res, toUser, fromUser, userMessage);
                }
            });
        });
    } catch(err) {
        console.log(err);
        res.send('success');
    }
});
/**
 * [handleAutoReply description]
 * @param  {Object} res         [response對象]
 * @param  {String} toUser      [接收方]
 * @param  {String} fromUser    [發送方]
 * @param  {String} keyword     [關鍵詞]
 * @return {String} xmlContent  [消息模板]
 */
function handleAutoReply(res, toUser, fromUser, keyword) {
    // messageMap是含有關鍵詞回覆key-value的json,根據不一樣的關鍵詞,向用戶發送不一樣消息
    let messageMap = JSON.parse(JSON.stringify(messageJson));
    let content = messageMap[keyword];
    if (!content) {
        res.send('success');
        return false;
    }

    let xml = returnText(toUser, fromUser, content);
    res.send(xml); 
}
/**
 * [returnText description]
 * @param  {String} toUser      [接收方]
 * @param  {String} fromUser    [發送方]
 * @param  {String} content     [消息內容]
 * @return {String} xmlContent  [消息模板]
 */
function returnText(toUser, fromUser, content) {
    let xmlContent = `<xml><ToUserName><![CDATA[${toUser}]]></ToUserName>
    <FromUserName><![CDATA[${fromUser}]]></FromUserName>
    <CreateTime>${new Date().getTime()}</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[${content}]]></Content></xml>`;
    return xmlContent;
}

必定要注意錯誤處理,微信收不到正確響應時,會嘗試從新請求,因此一旦程序發生未知錯誤,要及時處理,而且通知微信不要再嘗試發送請求了(發送success字符串便可),不然微信會提示用戶接入的服務器異常。

至此,消息回覆的邏輯已經處理完了。可是接入本身的服務器以後,以前在微信公衆平臺設置的自定義菜單也沒了,須要咱們調用接口去配置;

 

配置自定義菜單:

打開微信接口調試頁面:https://mp.weixin.qq.com/debug

輸入你的appid和secret,因爲配置自定義菜單以後,菜單就會一直存在,不須要代碼去維持,因此我選擇了在這兒獲取access_token,固然你也能夠在你的程序中去獲取,而後再寫個配置菜單的頁面,那就更方便了。

而後選擇接口類型爲自定義菜單:

access_token填你剛纔獲取的就好,注意這個是有時效的,通常爲7200秒,過時的話再從新獲取就行了。

body是你配置菜單的json,簡單講一下:

 {
     "button":[
     {    
          "type":"click",
          "name":"今日歌曲",
          "key":"V1001_TODAY_MUSIC"
      },
      {
           "name":"菜單",
           "sub_button":[
           {    
               "type":"view",
               "name":"搜索",
               "url":"http://www.soso.com/"
            },
            {
                 "type":"miniprogram",
                 "name":"wxa",
                 "url":"http://mp.weixin.qq.com",
                 "appid":"wx286b93c14bbf93aa",
                 "pagepath":"pages/lunar/index"
             },
            {
               "type":"click",
               "name":"贊一下咱們",
               "key":"V1001_GOOD"
            }]
       }]
 }

button是一級菜單數組,每一個元素表明一個一級菜單,注意一級菜單最多三個,每一個菜單最多4個字,超出顯示...,每一個一級菜單下的二級菜單最多5個,每一個二級菜單最多7個字,超出顯示...。

type是按鈕類型,根據須要選擇就好:

一、click:點擊推事件用戶點擊click類型按鈕後,微信服務器會經過消息接口推送消息類型爲event的結構給開發者(參考消息接口指南),而且帶上按鈕中開發者填寫的key值,開發者能夠經過自定義的key值與用戶進行交互;
二、view:跳轉URL用戶點擊view類型按鈕後,微信客戶端將會打開開發者在按鈕中填寫的網頁URL,可與網頁受權獲取用戶基本信息接口結合,得到用戶基本信息。
三、scancode_push:掃碼推事件用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後顯示掃描結果(若是是URL,將進入URL),且會將掃碼的結果傳給開發者,開發者能夠下發消息。
四、scancode_waitmsg:掃碼推事件且彈出「消息接收中」提示框用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後,將掃碼的結果傳給開發者,同時收起掃一掃工具,而後彈出「消息接收中」提示框,隨後可能會收到開發者下發的消息。
五、pic_sysphoto:彈出系統拍照發圖用戶點擊按鈕後,微信客戶端將調起系統相機,完成拍照操做後,會將拍攝的相片發送給開發者,並推送事件給開發者,同時收起系統相機,隨後可能會收到開發者下發的消息。
六、pic_photo_or_album:彈出拍照或者相冊發圖用戶點擊按鈕後,微信客戶端將彈出選擇器供用戶選擇「拍照」或者「從手機相冊選擇」。用戶選擇後即走其餘兩種流程。
七、pic_weixin:彈出微信相冊發圖器用戶點擊按鈕後,微信客戶端將調起微信相冊,完成選擇操做後,將選擇的相片發送給開發者的服務器,並推送事件給開發者,同時收起相冊,隨後可能會收到開發者下發的消息。
八、location_select:彈出地理位置選擇器用戶點擊按鈕後,微信客戶端將調起地理位置選擇工具,完成選擇操做後,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨後可能會收到開發者下發的消息。
九、media_id:下發消息(除文本消息)用戶點擊media_id類型按鈕後,微信服務器會將開發者填寫的永久素材id對應的素材下發給用戶,永久素材類型能夠是圖片、音頻、視頻、圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。
十、view_limited:跳轉圖文消息URL用戶點擊view_limited類型按鈕後,微信客戶端將打開開發者在按鈕中填寫的永久素材id對應的圖文消息URL,永久素材類型只支持圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。

 

根據須要,組織好你的json,填入body輸入框就好了,點擊檢查問題,若是檢查經過,菜單就建立成功了,檢查失敗的話,再具體看一下報錯信息。首次設置會當即生效,修改的話須要5分鐘才刷新,能夠選擇先取消關注公衆號,而後再關注,就能當即看到效果了。

這裏講的都是經過微信接口調試頁面作的,流程都是同樣的,固然也能夠寫在你的程序裏,按步驟調用相關接口就好了。

相關文章
相關標籤/搜索