微信小程式開發(十七)模板訊息

湖工電氣發表於2018-01-05

最近兩個月在重構公司的一個商城專案,小程式端,PC端,後臺部分也大部分是我做的。比較忙,所以部落格寫的比較少。這兩天在除錯微信支付之後個使用者傳送一個模板訊息的功能。一直出現errcode: 41028, errmsg: “invalid form id hint:的錯誤。在網上看了一個基本沒有一個正確的答案,基本都是照搬微信的文件,並沒有說出除錯的細節。關於文件,大家可以直接看微信的文件-小程式模板訊息。下面寫下我的整個開發過程,首先請詳細閱讀微信相關的文件。

選擇模板

1.登入小程式的微信公眾平臺-模板庫
這裡寫圖片描述
選擇你需要的模板。
2.設定那你需要的欄位
這裡寫圖片描述
3.獲取模板訊息ID
在完成上面兩步之後,在我的模板裡面就可以看見了。複製模板ID即可。
這裡寫圖片描述

獲取ACCESS_TOKEN

注意點

關於ACCESS_TOKEN這塊大家注意兩點即可:
1.access_token 是全域性唯一介面呼叫憑據,開發者呼叫各介面時都需使用 access_token,請妥善儲存。access_token 的儲存至少要保留512個字元空間。
2.access_token 的有效期目前為2個小時,需定時重新整理,重複獲取將導致上次獲取的 access_token 失效。
微信給了一種access_token的解決辦法:
這裡寫圖片描述
我沒有使用中繼伺服器。我是儲存在本地資料庫,給一個介面去獲取這個access_token。具體的業務邏輯:
1.呼叫後端寫的獲取access_token介面,
2.資料庫沒有access_token記錄,就向騰訊獲取一個儲存資料庫並返回,
3.資料庫有記錄且最新一天記錄的儲存時間距離現在時間小於1小時55分,就直接把這個access_token返回給前臺,
4.資料庫有記錄,但最新一條的儲存時間距離現在時間大於1小時55分,就重複步驟2.
為什麼是1小時55分呢?上面的截圖第2點
目前 access_token 的有效期通過返回的 expires_in 來傳達,目前是7200秒之內的值。中控伺服器需要根據這個有效時間提前去重新整理新 access_token。在重新整理過程中,中控伺服器對外輸出的依然是老 access_token,此時公眾平臺後臺會保證在重新整理短時間內,新老 access_token 都可用,這保證了第三方業務的平滑過渡這我預留5分鐘,以避免這個問題。

程式碼實現

router.get('/jkyx/access_token', function(req, res, next) {
    pool.getConnection(function(err, connection) {
        if(err){
            res.end(JSON.stringify({
                msg: '資料庫連線失敗',
                status:'101',
            }));
        }
        var sql = 'select * from access_token order by id desc limit 1';
        connection.query(sql,function (err, results) {
            if (err){
                res.end(JSON.stringify({
                    msg: '資料庫查詢失敗',
                    status:'102',
                    err: err
                }));
            }else{
                var resultsLen = results.length;
                // 首次 收據庫 沒資料
                if (resultsLen == 0) {
                    var urlStr = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + Wx_config.AppID + '&secret='  + Wx_config.Secret;
                    request(urlStr, function (error, response, body) {
                        if (!error && response.statusCode == 200) {
                            var body = JSON.parse(body);
                            var access_token = body.access_token;
                            var create_time = CommonJS.timestamp();
                            var insertSql = 'insert into access_token (access_token,create_time) values (?,?)';
                            connection.query(insertSql,[access_token,create_time],function (err, results) {
                                connection.release();
                                if (err){
                                    res.end(JSON.stringify({
                                        msg: '資料庫查詢失敗',
                                        status:'103',
                                        err: err
                                    }));
                                }else{
                                    res.end(JSON.stringify({
                                        msg: '操作成功',
                                        status:'100',
                                        access_token: access_token
                                    }));
                                }
                            })
                        }
                    })
                } else{ // 資料庫有資料
                    var item = results[0];
                    var access_token = item.access_token; // 上一次的access_token
                    var create_time = item.create_time; // 上一次的獲取時間
                    var create_time2 = CommonJS.timestamp(); // 當前時間戳
                    var spec_time = create_time2 - create_time;
                    if (spec_time > 6900) {  // 時間戳差值  1小時55分。  餘留5分鐘給系統。
                        // 再次獲取時間戳
                        var urlStr = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + Wx_config.AppID + '&secret='  + Wx_config.Secret;
                        request(urlStr, function (error, response, body) {
                            if (!error && response.statusCode == 200) {
                                var body = JSON.parse(body);
                                var access_tokenr = body.access_token;
                                var create_timer = CommonJS.timestamp();
                                var insertSql = 'insert into access_token (access_token,create_time) values (?,?)';
                                connection.query(insertSql,[access_tokenr,create_timer],function (err, results) {
                                    connection.release();
                                    if (err){
                                        res.end(JSON.stringify({
                                            msg: '資料庫查詢失敗',
                                            status:'103',
                                            err: err
                                        }));
                                    }else{
                                        res.end(JSON.stringify({
                                            msg: '操作成功',
                                            status:'100',
                                            access_token: access_tokenr
                                        }));
                                    }
                                })
                            }
                        })
                    } else{
                        connection.release();
                        // 取上一次的時間戳
                        res.end(JSON.stringify({
                            msg: '操作成功',
                            status:'100',
                            access_token: access_token
                        }));
                    }

                }
            }
        })
    })
});

傳送模板訊息

傳送模板訊息就好處理了。
這裡寫圖片描述
這個就是一個處理POST請求引數的問題了。
這裡需要注意的是:form_id 是 表單提交場景下,為 submit 事件帶上的 formId;支付場景下,為本次支付的 prepay_id
我的是支付成功的通知,這裡一定儲存整個支付流程都是在真機上面除錯,一定不能是手機掃碼除錯工具上面的二維碼支付,不然會一直報錯:errcode: 41028, errmsg: “invalid form id hint:
小程式端傳送模板訊息:

sendMessage: function (prepay_id, payTime){
    var self = this;
    var url = app.baseUrl + 'access_token';
    var access_token = ''; 
    // 微信支付的package 欄位的格式是:
    // prepay_id=wx201801xxxxx
    // 這裡擷取 只需要wx201801xxxxx
    var prepay_id2 = prepay_id.split('=')[1];
    wx.request({
      url: url,
      success: function(res){
        var data = res.data;
        if(data.status == 100){
          access_token = data.access_token;
          var accUrl = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=' + access_token;

          wx.request({
            url: accUrl,
            method: 'post',  
            data: {
              touser: app.getOpen_id(), 
              template_id: '模板訊息ID',
              page: 'pages/order/index',
              form_id: prepay_id2,
              data: {
                keyword1: {
                  value: payTime, 
                  color: "#173177"
                },
                keyword2: {
                  value: self.data.order_no,
                  color: "#173177"
                },
                keyword3: {
                  value: self.data.pay_amount + '元',
                  color: "#173177"
                }
              }
            },
            success: function (res) {
              var data = res.data;
              console.log(data);
            },
            fail: function () {
              console.log('請求失敗');
            }
          })
        }else{
          console.log('access_token請求失敗');
        }
      },
      fail: function(){
        console.log('請求失敗');
      }
    })
  },

至此傳送模板訊息是做完了。但是這裡有存在一個缺陷,如果是線上的小程式版本,需要及時修改模板訊息格式就不好了。後來我做的是後臺傳送模板訊息。這個修改就很好做了,這裡不再贅述。

注意

一定整個支付流程都是在自己的手機上面完成,一定不要去掃除錯工具生成的二維碼支付。

相關文章