文章目录
- 微信公众号生成个性化推广二维码
- 目标
- 环境要求
- 实现思路
- 获取二维码流程
- 概述
- 获取ticket
- 获取二维码下载地址
- 代码实现
- 核心代码
- 运行结果
微信公众号生成个性化推广二维码
目标
针对不同的推广场景,生成特定的公众号二维码,以便系统统计用户来源,提供个性化服务;
环境要求
- 后台语言:Java
- IDE:IDEA
- JDK:1.8
- 微信服务号
实现思路
通过微信公众号提供的生成带参数的二维码来实现用户来源的识别。
目前有两类带参数的二维码:
- 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
- 永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。
当用户扫描带场景的二维码时,微信系统将向响应系统(什么是微信系统和响应系统?点击传送门)推送以下两种事件:
- 已关注用户将自动进入会话,微信系统将向响应系统推送带有场景值的扫描事件;
- 未关注用户则会先进入关注页面,如果用户选择关注公众号,那么微信系统将先推送关注事件给响应系统,然后再推送带有场景值的扫描事件;
获取二维码流程
概述
获取二维码一共分两步:
- 获取ticket:每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),也就是场景值,该值即为响应系统接收扫描事件的场景值,该值可以为整型数字,可以为字符串;
- 通过ticket换取二维码:通过ticket换取二维码下载地址,该地址任何人都可访问;
获取ticket
因为有两种二维码,所以也有两种ticket
获取临时二维码ticket:
请求方式: POST
地址: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
数据格式:json
数据示例:场景值为整型数字:{"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}场景值为字符串:{"expire_seconds": 604800, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
获取永久二维码ticket:
请求方式: POST
地址: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
数据格式:json
数据示例:场景值为整型数字:{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}}场景值为字符串:{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}
返回结果说明:
{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm
3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}
首先贴上官方对参数的说明:
参数 | 说明 |
---|---|
expire_seconds | 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。 |
action_name | 二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值 |
action_info | 二维码详细信息 |
scene_id | 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000) |
scene_str | 场景值ID(字符串形式的ID),字符串类型,长度限制为1到64 |
个人觉得不好理解的是action_name。
首先我们可以发现获取临时和永久ticket的地址是一样的!这说明我们需要传递一些信息告诉微信系统我们需要的是什么;然后我们可以发现的就是json的属性不一样,于是就产生了这样的疑问:是不是通过expire_seconds这个字段判断的呢?
接下里看参数说明,action_name的介绍为:“QR_SCENE为临时的整型参数值”。我没明白的是QR_SCENE到底是一个整型参数值,还是一个标记值。因为看到了参数值,所以就理解为“可变”的了,加上我们也的确有判断是否是永久的方法(expire_seconds存在与否),于是似乎理所当然。。。就出错了。
事实上,它是一个标记值,也就是微信系统应该是通过action_name来判断我们的目的是什么。不过问题也不大,程序跑起来就能明白这个问题到底是什么了,因为随意指定该属性的值会抛错:“invalid action name”;
获取二维码下载地址
获得ticket的同时,我们也获得了一个URL,这个URL即为二维码解析后的地址,此时我们就可以自己为该地址生成二维码图片了,比如美化一下什么的。不过我们也可以从微信系统那里获取下载地址。
GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
提醒:TICKET记得进行UrlEncode
访问该地址即可获得对应场景、对应生命周期的二维码图片下载地址;
代码实现
核心代码
public class Main {public static void main(String[] args){//获取临时推广、场景值为字符串的二维码String temporaryQRCodeUrl= WeChatQRCodeManager.getTemporaryStrQRCodeDownloadUrl("teacher_wang");System.out.println(temporaryQRCodeUrl);//获取永久推广、场景值为字符串的二维码String permanentQRCodeUrl= WeChatQRCodeManager.getPermanentStrQRCodeDownloadUrl("teacher_liu");System.out.println(permanentQRCodeUrl);//获取临时推广、场景值为整型的二维码temporaryQRCodeUrl= WeChatQRCodeManager.getTemporaryIntQRCodeDownloadUrl(120);System.out.println(temporaryQRCodeUrl);//获取永久推广、场景值为整型的二维码permanentQRCodeUrl= WeChatQRCodeManager.getPermanentIntQRCodeDownloadUrl(119);System.out.println(permanentQRCodeUrl);}
}
public class WeChatQRCodeManager {/*** 获取临时特定场景推广二维码下载地址* @param sceneStr 临时特定场景的字符串标记* @return 二维码的下载地址* @see QRCodeInfo*/public static String getTemporaryStrQRCodeDownloadUrl(String sceneStr){QRCodeInfo info=new QRCodeInfo(false);info.setSceneStrId(sceneStr);return getQRCodeDownloadUrl(info);}/*** 获取永久特定场景推广二维码下载地址* @param sceneStr 永久特定场景的字符串标记* @return 二维码的下载地址* @see QRCodeInfo*/public static String getPermanentStrQRCodeDownloadUrl(String sceneStr){QRCodeInfo info=new QRCodeInfo(true);info.setSceneStrId(sceneStr);return getQRCodeDownloadUrl(info);}/*** 获取临时特定场景推广二维码下载地址* @param sceneInt 临时特定场景的整型标记* @return 二维码的下载地址* @see QRCodeInfo*/public static String getTemporaryIntQRCodeDownloadUrl(int sceneInt){QRCodeInfo info=new QRCodeInfo(false);info.setSceneIntId(sceneInt);return getQRCodeDownloadUrl(info);}/*** 获取永久特定场景推广二维码下载地址* @param sceneInt 永久特定场景的整型标记* @return 二维码的下载地址* @see QRCodeInfo*/public static String getPermanentIntQRCodeDownloadUrl(int sceneInt){QRCodeInfo info=new QRCodeInfo(true);info.setSceneIntId(sceneInt);return getQRCodeDownloadUrl(info);}/*** 获取二维码下载地址* @param info 二维码信息载体* @return 二维码下载地址* @see QRCodeInfo*/private static String getQRCodeDownloadUrl(QRCodeInfo info) {String responseString= HttpUtils.doPostJson(WeChatUrlManager.getQrCodeTicketUrl(),info.toJsonString());String ticket= JsonUtils.getTargetStringInfo("ticket",responseString);return WeChatUrlManager.getQrCodeUrl(ticket);}
}
/*** @author Shao Lixuan 2019/3/7 16:43*/
public class QRCodeInfo {private int expireSeconds;private String sceneStrId;private boolean isTemporary;private int sceneIntId;private boolean sceneIsStr;public QRCodeInfo(boolean temporary){isTemporary=temporary;sceneIsStr=true;}public boolean isTemporary() {return isTemporary;}public void setTemporary(boolean temporary) {isTemporary = temporary;}public int getExpireSeconds() {return expireSeconds;}public void setExpireSeconds(int expireSeconds) {this.expireSeconds = expireSeconds;}public String getSceneStrId() {return sceneStrId;}public void setSceneStrId(String sceneStrId) {this.sceneStrId = sceneStrId;sceneIsStr=true;}public int getSceneIntId() {return sceneIntId;}public void setSceneIntId(int sceneIntId) {this.sceneIntId = sceneIntId;sceneIsStr=false;}public String toJsonString(){String template;if(isTemporary){if(sceneIsStr) {template = "{\"expire_seconds\": %d, \"action_name\":\"QR_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}";return String.format(template, expireSeconds, sceneStrId);}else{template = "{\"expire_seconds\": %d, \"action_name\":\"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": %d}}}";return String.format(template, expireSeconds, sceneIntId);}}else {if(sceneIsStr) {template = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"%s\"}}}";return String.format(template, sceneStrId);}else{template = "{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": %d}}}";return String.format(template, sceneStrId);}}}
}
运行结果
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQGh8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyQllPSkFXRnVmRUMxMDAwMHcwN0QAAgRA5IBcAwQAAAAA
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQGG8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyMEtZaEJYRnVmRUMxS3NBMTFzY18AAgRg5IFcAwQ8AAAA
https://mp.weixin.qq.com/cgi-bin/sho wqrcode?ticket=gQHH8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyaGFiTEFiRnVmRUMxMDAwMGcwM1EAAgTj04FcAwQAAAAA
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQGK8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyRVZNMUJTRnVmRUMxS3NBMXhzMW4AAgRg5IFcAwQ8AAAA
项目源码CSDN下载