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


首先需要確認一點,一旦接入第三方服務器,微信就認為你已經具備了開發能力,像自動回復、關鍵詞回復、自定義菜單這些功能,微信公眾平台就不再提供了(需要開發者調用相關接口),停用服務器之后,這些功能也就恢復了,二者是互斥的。

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

一、搭建本地調試環境,需要將本地服務穿透出去,便於外網訪問,可以用花生殼或者ngrok等,能穿透內網就可以,這里就不多說了。

二、服務器配置及校驗

  現在我們已經有了一個可供外網訪問的本地服務,接下來說一下微信公眾平台的相關配置:

  登錄微信公眾平台,找到基本配置

  

  可以看到服務器配置一項(我這里是已經啟用過的),點擊右側的修改配置

  

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

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

  填寫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是按鈕類型,根據需要選擇就好:

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

 

根據需要,組織好你的json,填入body輸入框就行了,點擊檢查問題,如果檢查通過,菜單就創建成功了,檢查失敗的話,再具體看一下報錯信息。首次設置會立即生效,修改的話需要5分鍾才刷新,可以選擇先取消關注公眾號,然后再關注,就能立即看到效果了。

這里講的都是通過微信接口調試頁面做的,流程都是一樣的,當然也可以寫在你的程序里,按步驟調用相關接口就行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM