微信小程序开发(十七)模板消息

最近两个月在重构公司的一个商城项目,小程序端,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('请求失败');
      }
    })
  },

至此发送模板消息是做完了。但是这里有存在一个缺陷,如果是线上的小程序版本,需要及时修改模板消息格式就不好了。后来我做的是后台发送模板消息。这个修改就很好做了,这里不再赘述。

注意

一定整个支付流程都是在自己的手机上面完成,一定不要去扫调试工具生成的二维码支付。

本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院