该文章主要是为了实现在没有扫码器的情况下,怎么识别分辨是哪个用户用扫一扫扫描的二维码。设计由来:主要是公司项目有个功能是一个关于将类似于优惠券的兑换码生成二维码后,用户需要向商家展示二维码,由商家扫描二维码进行核销。这样就涉及的如何判断是商家扫描的而不是普通用户扫描的?
用户点击优惠券->后台会返回相应的二维码(带有相应产品的信息)->用户/商户扫描二维码->进行微信网页授权->进入后台进行微信openid验证,判断是否为商户用户(提前将商户的微信openid保存下来,供本次核对商户信息)->为商户(进行核销)/普通用户(只显示兑换码信息等普通信息)
1.nodejs
1.qr-image(其实还有个用户量很多的node-qrcode,但是实现本功能比较繁琐,所以选择了小众的qr-image):主要是为了动态显示二维码,无需后台保存二维码图片。
2.微信网页授权:用于识别是哪个用户扫描的二维码。
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。(如果想让微信回调自己的路由时同时带有自己的参数,可以在回调路由里添加自己的参数,再进行url编码,这样即可带有自己的参数回调回来,具体实现可看下方实现)
首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
请求方法
获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
请求方法
http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
该调用,主要是为了获取用户的详细信息用于后台的核对,分辨是自己后台中的商户还是普通用户。
//bms.js
router.get("/QRcode",BMSServer.qrcode);
//bmsserver.js
//获取用户传过来的获取兑换码请求
qrcode:function(req,res){
let pid=req.body.pid;//用户告知是哪个商品(即优惠券ID)
res.render(renderPath+'qrcode',{pid:pid});
},
//qrcode.ejs
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='/stylesheets/style.css'/>
</head>
<body>
<h1 style="position:absolute;left:43%;top:5%">优惠券二维码</h1>
<img src="/Data/create_qrcode?url=https://xxxx.xxxxx.xxxx/xxx/qrcodeInfo&pid=<%=pid%>" style="width: 100%;height:100%;margin-top:10%"/>
</body>
</html>
//1.由img标签的src请求create_qrcode请求返回二维码
//data.js
router.get("/create_qrcode",DataServer.create_qrcode);
//dataserver.js
create_qrcode:function(req,res){
var url = req.query.url;//img标签src请求中带有的参数
var pid=req.query.pid;
let text=`${url}?pid=${pid}`;
try {
//text="哈哈";
var img = qr.image(text,{size :10});//将参数内容写入的二维码中,即商品信息参数和qrcodeInfo请求
res.writeHead(200, {'Content-Type': 'image/png'});
img.pipe(res);
} catch (e) {
res.writeHead(414, {'Content-Type': 'text/html'});
res.end('414 Request-URI Too Large
');
}
},
//2.用户扫描二维码
//bms.js
//用户扫描二维码后会自动请求该路由
router.get("/qrcodeInfo",BMSServer.qrcodeInfo);
//bmsserver.js
qrcodeInfo:function(req,res){
let pid=req.query['pid'];
res.render(renderPath+'qrcodeInfo',{pid:pid,title:"优惠券"});
},
//qrcodeInfo.ejs
//自动微信网页授权
<html>
<head>
<title><%=title%></title>
<link rel='stylesheet' href='/stylesheets/style.css'/>
<script src="/js/jquery.js"></script>
<script src="/js/jquery.mCustomScrollbar.concat.min.js"></script>
<link href="http://fonts.googleapis.com/css?family=Oxygen|Marck+Script" rel="stylesheet" type="text/css">
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="/css/font-awesome.css" rel="stylesheet">
<link href="/css/admin.css" rel="stylesheet">
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.js"></script>
<script src="/js/excanvas.min.js"></script>
<script src="/js/jquery.flot.min.js"></script>
<script src="/js/jquery.flot.resize.js"></script>
</head>
<script>
(function($){
$(window).load(function(){//加载该网页自动进行网页授权,不用手动点击
let pid=$("#pid").val();
let newUrl=escape(`https://xxxx.xxxxx.xxx/xxx/webLogin?pid=${pid}`);//进行url编码
location.href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd53d8ea8fc7e67cb&redirect_uri="+newUrl+"&response_type=code&scope=snsapi_base&state=123#wechat_redirect";//微信网页授权的第一步
});
})(jQuery);
</script>
<body>
<input type="text" value="<%=pid%>" id="pid" style="display: none">
</body>
</html>
//bms.js
router.get("/webLogin",BMSServer.weblogin);//微信网页授权
//bmsserver.js
weblogin:function(req,res){
let body=isAvailableData(req.query).data;
let code=body.code;//微信回调返回的code用于获取access_token
let state=body.state;
let pid=body.pid;//自己的参数用于查询商品的信息即兑换码或有效期等
request('https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code='+code+'&grant_type=authorization_code ', async function(error,respone,body){
if(!error&&respone.statusCode==200){
var wxData = JSON.parse(body);
console.log(wxData)
let access_token=wxData.access_token;//微信返回的access_token
let openid=wxData.openid;//微信返回的openid,此请求唯一一个可分辨用户的标识,如果想获取详细信息,就需要进行第三步:
request("https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid+"&lang=zh_CN ",async function(error,respone,body){
if(!error&&respone.statusCode==200){
let userInfo = JSON.parse(body);//微信返回的用户详细信息
console.log(userInfo)
let qrcodeUser=new QrCodeUser();//封装好的商户信息model
let qrcodeuser=await qrcodeUser.findQrCodeUserByOpenid(userInfo.openid);//查询该openid用户是否在商户表中
let title="";
if(qrcodeuser==null){
title="您不是商铺用户";
res.render(renderPath+'webLoginPage',{title:title,userInfo:null,msg:cdkey});
}else{
//可以随意做你需要的操作
title="您为商铺用户";
res.render(renderPath+'webLoginPage',{title:title,userInfo:"hahah",msg:"核销完成"});
}
}
});
}
});
}
//webLoginPage.ejs
//weui:为微信提供的一套网页版ui。具体参考:https://www.w3cschool.cn/weixinkaifawendang/k72f1qe4.html
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover">
<title>核销</title>
<link rel="stylesheet" href="/weui/dist/style/weui.css"/>
<link rel="stylesheet" href="/weui/dist/example/example.css"/>
</head>
<body ontouchstart>
<%if(userInfo!=null){%>
<%=title%>:核销兑换码结果为<%=msg%>
<%}else{%>
<%=title%>:只能查看兑换码<%=msg%>
<%}%>
</body>
</html>
扫描二维码时请使用微信中的扫一扫(微信扫一扫相当于一个扫码器),如果使用了其他扫一扫工具,会提示请使用微信客户端打开等信息。
欢迎提出更好的解决办法。