最近两个月在重构公司的一个商城项目,小程序端,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('请求失败');
}
})
},
至此发送模板消息是做完了。但是这里有存在一个缺陷,如果是线上的小程序版本,需要及时修改模板消息格式就不好了。后来我做的是后台发送模板消息。这个修改就很好做了,这里不再赘述。
注意
一定整个支付流程都是在自己的手机上面完成,一定不要去扫调试工具生成的二维码支付。