本文旨在记录如何扫码带参数的二维码事件 本文为作者原创 转载请注明出处 尊重一下笔者的劳动成果 十分感谢
什么是带参数的二维码事件?
当我们需要扫描二维码进入公众号并且期望可以做一些自定义的业务处理,比如说某人通过谁的邀请关注了公众号,需要对这个人和关注人做业务处理的时候就可以使用带参数的二维码事件
带参数的二维码扫描后在用户未关注和关注后都会给我们的开发服务器推送消息,很方便我们做相关场景的业务处理
用户扫描带场景值二维码时,可能推送以下两种事件:
- 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
- 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
扫描带参数二维码事件流程
1. 创建带参数的二维码(注意:调用微信接口创建二维码)
2. 换取二维码
3. 扫描二维码 接受事件推送
详细扫描二维码事件推送可查看官方文档 :扫描带参数二维码事件
正文
第一步 调用微信接口 创建生成带参数的二维码
二维码有两种 临时和永久 临时最长可保存30天 永久最多上限10万个 可根据开发者自己的业务处理 代码如下:
/**
* 生成临时/永久二维码
* @param tel 代理标识 此处传值手机号
* @param isLimitScene 是否是永久二维码 true 永久二维码 false 临时二维码
* @param time 临时二维码时长 默认604800 单位秒 2592000 =30天
* @return 更换二维码的ticket
*/
@RequestMapping("wex/create_qrcode")
@ResponseBody
public Object createQrcode(String tel,@RequestParam(defaultValue = "true")Boolean isLimitScene,@RequestParam(defaultValue = "604800") int time){
try {
String access_token = wx.get_access_token();
String url ="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";
url=url.replace("TOKEN", access_token);
JSONObject param = new JSONObject();
if(isLimitScene){//永久二维码
//二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
param.put("action_name","QR_LIMIT_STR_SCENE");
}else{
param.put("action_name","QR_STR_SCENE");
param.put("expire_seconds",time);//二维码生成后的30天(即2592000秒)后过期604800
}
param.put("action_info",new JSONObject().put("scene",new JSONObject().put("scene_str",tel)));//二维码的详细信息
log.info("url:"+url);
log.info("send data:"+param);
String jsonstr = HttpUtil.postJsonForString(url,param);
log.info("jsonstr:"+jsonstr);
JSONObject jsonobj = new JSONObject(jsonstr);
log.info("jsonobj:"+jsonobj);
// String url2 = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET";
// url2=url2.replace("TICKET", jsonobj.get("ticket").toString());
// String img = HttpUtil.getStringFromUrl(url2);
// return jsonobj.get("ticket").toString();
return jsonobj.toString();
}catch (Exception e){
e.printStackTrace();
log.warn("error:"+e.getMessage());
}
return "";
}
生成带参数的二维码官方文档:生成带参数的二维码
第二步 通过ticket换取二维码或者url生成二维码
第一步创建以后我们还不能直接使用 需要调用换取二维码的接口 此处有两种方法可以获取到二维码,项目中我使用第二种方法获取 因为涉及生成二维码要制作海报:
1. 根据ticket换取二维码
2. 将url利用生成二维码组件生成二维码
通过ticket换取二维码
获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用。
请求说明
HTTP GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
提醒:TICKET记得进行UrlEncode
返回说明
ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。
HTTP头(示例)如下:
Accept-Ranges:bytes
Cache-control:max-age=604800
Connection:keep-alive
Content-Length:28026
Content-Type:image/jpg
Date:Wed, 16 Oct 2013 06:37:10 GMT
Expires:Wed, 23 Oct 2013 14:37:10 +0800
Server:nginx/1.4.1
错误情况下(如ticket非法)返回HTTP错误码404。
第三步 开发者服务器接受用户扫描二维码后的消息推送
如果要接收微信服务器的推送肯定要先在我们的公众号后台开启并配置服务器 ,开启服务器需要校验URL真实性 配置URL以后每次微信服务器给开发者服务器推送消息都请求这个URL对用的方法 要做好功能处理哦 文章尾部会附上校验URL真实性的代码哦,下面是开发者接受微信服务器推送的代码处理:
@Autowired
HttpServletRequest request;
/**
* 处理服务器推送消息
*/
@RequestMapping(value = "wex", method = { RequestMethod.GET, RequestMethod.POST })
@ResponseBody
public void liaotian(HttpServletRequest request, HttpServletResponse response) {
try {
if(request.getMethod().equals("GET")){
log.info("++++++++++++++++++++++++校验URL真实性+++++++++++++++++++++++++++++++++");
access(request,response);
}else{
WexHandeler();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//微信功能处理
public String WexHandeler() throws Exception {
try {
JSONObject info = XML.toJSONObject(IOUtils.toString(request.getInputStream()));
// info:
// {
// "xml": {
// "FromUserName": "oLqaP1sWgZqNIBWSUsNQlcz4Gsyg",
// "EventKey": "",
// "Event": "unsubscribe",
// "CreateTime": 1548060689,
// "ToUserName": "gh_2bc393a7842d",
// "MsgType": "event"
// }
// }
// {
// "xml": {
// "Ticket": "gQEY8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAycUVXOHhNS0NlZm0xc0IwZU5zMUYAAgSlhUVcAwSAOgkA",
// "FromUserName": "oLqaP1sWgZqNIBWSUsNQlcz4Gsyg",
// "EventKey": "qrscene_0",
// "Event": "subscribe",
// "CreateTime": 1548060879,
// "ToUserName": "gh_2bc393a7842d",
// "MsgType": "event"
// }
// }
log.info("info:" + info);
if (info.get("xml") != null) {
JSONObject event = (JSONObject) info.get("xml");
if (event.get("MsgType") != null) {
String Event = (String) event.get("Event");//事件类型 关注事件还是取消关注等
if (!StringUtils.isEmpty(Event)) { //如果是事件
try {
if ("subscribe".equals(Event)) { //带参数二维码的关注事件
//关注方法
String toUserName = (String) event.get("ToUserName");//开发者微信号
String fromUserName = (String) event.get("FromUserName");//发送方帐号(一个OpenID)
String eventKey = (String) event.get("EventKey");//事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
String ticket = (String) event.get("Ticket");//二维码的ticket,可用来换取二维码图片
log.info(">>>>>>>>>>>>>>>>>>接受的数据为:");
log.info("toUserName:"+toUserName);
log.info("fromUserName:"+fromUserName);
log.info("scene_id:"+eventKey);
log.info("ticket:"+ticket);
bindSuperior(fromUserName,eventKey);
} else if ("SCAN".equals(Event)) {//扫描带参数二维码事件 用户已关注时的事件推送
String toUserName = (String) event.get("ToUserName");//开发者微信号
String fromUserName = (String) event.get("FromUserName");//发送方帐号(一个OpenID)
String eventKey = (String) event.get("EventKey");//事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
String ticket = (String) event.get("Ticket");//二维码的ticket,可用来换取二维码图片
log.info(">>>>>>>>>>>>>>>>>>接受的数据为:");
log.info("toUserName:"+toUserName);
log.info("fromUserName:"+fromUserName);
log.info("scene_id:"+eventKey);
log.info("ticket:"+ticket);
bindSuperior(fromUserName,eventKey);
} else if("unsubscribe".equals(Event)){
}
} catch (Exception e) {
return e.getMessage();
}
}
}
}
} catch (Exception e) {
return request.getParameter("echostr");
}
return request.getParameter("echostr");
}
附录一 校验URL真实性
/**
* 签名校验 验证URL真实性
* @param request request
* @param response response
*/
protected void access(HttpServletRequest request, HttpServletResponse response) {
try {
System.out.println("开始签名校验");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
ArrayList<String> array = new ArrayList<String>();
array.add(signature);
array.add(timestamp);
array.add(nonce);
//排序
String sortString = sort(token, timestamp, nonce);
//加密
String mytoken = Decript.SHA1(sortString);
//校验签名
if (mytoken != null && !mytoken.equals("") && mytoken.equals(signature)) {
System.out.println("签名校验通过。");
response.getWriter().println(echostr);
} else {
System.out.println("签名校验失败。");
}
}catch (Exception e){
e.printStackTrace();
log.warn("校验签名异常:"+e.getMessage());
}
}
/**
* 排序
*/
public static String sort(String token, String timestamp, String nonce) {
String[] strArray = { token, timestamp, nonce };
Arrays.sort(strArray);
StringBuilder sbuilder = new StringBuilder();
for (String str : strArray) {
sbuilder.append(str);
}
return sbuilder.toString();
}
附录二 根据openid和access_token获取用户信息
/**
* 根据openid和access_token 获取用户信息
* @param openid openid
* @return Object 微信用户信息
*/
@RequestMapping("wex/auth/userinfo")
@ResponseBody
public Object getUserInfoByOpenid(String openid){
Map<String,Object> map = new HashMap<String, Object>();
try {
String access_token = wx.get_access_token();
String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
url=url.replace("ACCESS_TOKEN", access_token);
url=url.replace("OPENID", openid);
String jsonstr = HttpUtil.getStringFromUrl(url);
// JSONObject jsonobj = new JSONObject(jsonstr);
map.put(RETINFO.RET_CODE,RETINFO.RET_CODE_SUCCESS);
map.put(RETINFO.RET_DATA,jsonstr);
log.info("return data map:" + map);
return map;
}catch (Exception e){
log.warn("获取授权用户信息异常:"+e.getMessage());
e.printStackTrace();
map.put(RETINFO.RET_CODE,RETINFO.RET_CODE_ERROR);
map.put(RETINFO.RET_MSG,e.getMessage());
return map;
}
}
附录三 获取jssdk网页授权签名
/**
* 网页授权签名生成
* @param curr_url 授权的当前地址
* @return 签名信息
* @throws Exception
*/
@RequestMapping("wex/auth/get_signature")
public @ResponseBody Map<String, Object> getSignatureForWexPublic(String curr_url) throws Exception {
Map retMap = new HashMap<String, Object>();
System.out.println("获取jssdk签名---->>>>>>>>>>>>>>>"+curr_url);
try {
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
String noncestr = UUID.randomUUID().toString();
String url = java.net.URLDecoder.decode(curr_url, "utf-8");
if(url.indexOf("#")>=0){
url = url.substring(0, url.indexOf("#"));
}
//获取全局access_token
String accessToken = wx.get_access_token();
//获取jsapi的 jsapi_ticket
String jsapi_ticket = wx.get_ticket();
String preSiger = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url="
+ url;
String signature = DigestUtils.shaHex(preSiger);
System.out.println("accessToken----->>>>"+accessToken+">>>>>>>>>>>>>>>>");
System.out.println("jsapi_ticket----->>>>"+jsapi_ticket+">>>>>>>>>>>>>>>>");
System.out.println("signature----->>>>"+signature+">>>>>>>>>>>>>>>>");
//配置
retMap.put(RETINFO.RET_CODE, RETINFO.RET_CODE_SUCCESS);
retMap.put(RETINFO.RET_MSG, "执行成功");
retMap.put("appid", appid);
retMap.put("noncestr", noncestr);
retMap.put("timestamp", timestamp);
retMap.put("signature", signature);
return retMap;
} catch (Exception e) {
retMap.put(RETINFO.RET_CODE, RETINFO.RET_CODE_ERROR);
retMap.put(RETINFO.RET_MSG, "未获取到签名"+e.getMessage());
return retMap;
}
}
附录四 根据code获取用户信息
/**
* 根据code获取授权用户信息
* @param code code
* @return 微信用户信息
*/
@RequestMapping("wex/auth")
@ResponseBody
public Object getOpenidByAuto(String code){
Map<String,Object> map = new HashMap<String, Object>();
String openid="";
try {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url=url.replace("APPID", appid);
url=url.replace("SECRET", appSecret);
url=url.replace("CODE", code);
String jsonstr = HttpUtil.getStringFromUrl(url);
JSONObject jsonobj = new JSONObject(jsonstr);
openid = jsonobj.getString("openid");
log.warn("返回数据:"+jsonobj);
map.put(RETINFO.RET_CODE,RETINFO.RET_CODE_SUCCESS);
map.put(RETINFO.RET_DATA,openid);
log.info("return data :" + map);
return map;
} catch (Exception e) {
log.warn("根据code获取授权access_token异常:"+e.getMessage());
e.printStackTrace();
map.put(RETINFO.RET_CODE,RETINFO.RET_CODE_ERROR);
map.put(RETINFO.RET_MSG,e.getMessage());
return map;
}
}