微信公众号生成个性化推广二维码

微信公众号生成个性化推广二维码

目标

针对不同的推广场景,生成特定的公众号二维码,以便系统统计用户来源,提供个性化服务;

环境要求

  1. 后台语言:Java
  2. IDE:IDEA
  3. JDK:1.8
  4. 微信服务号

实现思路

通过微信公众号提供的生成带参数的二维码来实现用户来源的识别。

目前有两类带参数的二维码:

  1. 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
  2. 永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。

当用户扫描带场景的二维码时,微信系统将向响应系统(什么是微信系统和响应系统?点击传送门)推送以下两种事件:

  1. 已关注用户将自动进入会话,微信系统将向响应系统推送带有场景值的扫描事件;
  2. 未关注用户则会先进入关注页面,如果用户选择关注公众号,那么微信系统将先推送关注事件给响应系统,然后再推送带有场景值的扫描事件;

获取二维码流程

概述

获取二维码一共分两步:

  1. 获取ticket:每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),也就是场景值,该值即为响应系统接收扫描事件的场景值,该值可以为整型数字,可以为字符串;
  2. 通过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下载

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值