•  基于H5开发项目,嵌入到企业微信中,并接入js-sdk 并运用企业微信接口。 开发环境:  腾讯企业微信最新版本。后端环境不做要求,能跑通即可(这里我选用的是eclipse的maven项目,之前也出了一篇搭建后台项目的...

    目标:

             基于H5开发项目,嵌入到企业微信中,并接入js-sdk 并运用企业微信接口。

    开发环境:

            腾讯企业微信最新版本。后端环境不做要求,能跑通即可(这里我选用的是eclipse的maven项目,之前也出了一篇搭建后台项目的博客,可以了解一下)

    需要工具:

            企业微信注册。花生壳开通内网穿透(花费6元)

    一、创建项目  能运行即可

            随便一个能运行的项目即可。

    二、花生壳配置

            参考另一篇文章:https://blog.csdn.net/zh5220909/article/details/81188185

    三、申请企业微信

            (1)安卓手机上下载  企业微信APP

            (2)直接使用微信登录即可,需要新建一个公司,直接根据提示申请一个测试的即可

           (3)完成新建公司,则可以去登录管理端了。管理端网址:https://work.weixin.qq.com/wework_admin/loginpage_wx?from=myhome

            (4)使用企业微信扫一扫登录即可

            (5)创建一个应用

           

    设置网页授权JS-SDK

    设置成功后

    四、前端集成js-sdk

    直接上页面代码,页面上的注释注意看看

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta name="viewport"
    	content="width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta charset="utf-8" />
    <meta content="telephone=no" name="format-detection" />
    <meta name="viewport"
    	content="width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <title>企业微信</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
    <!-- 企业微信的JS-SDK -->
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    
    <style type="text/css">
    .daiban {
    	width: 60%;
    	height: 40px;
    	text-align: center;
    	line-height: 2.5;
    	border: 0px solid #ddd;
    	background-color: yellow;
    	margin: 20px 20% 0px 20%;
    	border-radius: 5px;
    }
    
    .genzong {
    	width: 60%;
    	height: 40px;
    	text-align: center;
    	line-height: 2.5;
    	border: 0px solid #ddd;
    	background-color: pink;
    	margin: 20px 20% 0px 20%;
    	border-radius: 5px;
    }
    </style>
    </head>
    <body>
    	<div id="a1" class="daiban">待办</div>
    	<div id="a2" class="genzong">交换跟踪</div>
    	<script type="text/javascript">
    	
    		//初始化方法
    		function init() {
    			getToken();
    			bindClick();
    		}
    
    		//从后台获取wx.config中所需要的参数
    		function getToken() {
    			$.ajax({
    				url : "/zboxService/zwwx/getSignature",
    				type : "get",
    				dataType : "json",
    				success : function(data) {
    					console.log(data)
    					var json = data;
    					wx.config({
    						beta : true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
    						debug : false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    						appId : 'ww01c11527d43106b5', // 必填,企业微信的corpID
    						timestamp : json.timestamp, // 必填,生成签名的时间戳
    						nonceStr : json.noncestr, // 必填,生成签名的随机串
    						signature : json.signature,// 必填,签名,见附录1
    						jsApiList : [ 'checkJsApi', 'chooseImage',
    								'openEnterpriseChat' ]
    					// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    					});
    					wx.ready(function() {
    						wx.checkJsApi({
    							jsApiList : [ 'chooseImage' ], // 需要检测的JS接口列表,所有JS接口列表见附录2,
    							success : function(ress) {
    								alert(2)
    								// 以键值对的形式返回,可用的api值true,不可用为false
    								// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
    							}
    						});
    					});
    				}
    			})
    
    		}
    		
    		//绑定两个按钮的点击事件
    		function bindClick() {
    			
    			//这个接口是打开userIds对话框,运行则微信端直接跳到聊天框
    			$("#a1").click(function() {
    				wx.openEnterpriseChat({
    					// 注意:userIds和externalUserIds至少选填一个,且userIds+openIds总数不能超过2000。
    					userIds : 'DingSaiSai02;ZhuHao', //参与会话的企业成员列表,格式为userid1;userid2;...,用分号隔开。
    					externalUserIds : '', // 参与会话的外部联系人列表,格式为userId1;userId2;…,用分号隔开。
    					groupName : '讨论组', // 必填,会话名称。单聊时该参数传入空字符串""即可。
    					success : function(res) {
    						// 回调
    					},
    					fail : function(res) {
    						if (res.errMsg.indexOf('function not exist') > -1) {
    							alert('版本过低请升级')
    						}
    					}
    				});
    			});
    			
    			//这是选择本地图片的接口
    			$("#a2").click(function() {
    				wx.chooseImage({
    					count : 1, // 默认9
    					sizeType : [ 'original', 'compressed' ], // 可以指定是原图还是压缩图,默认二者都有
    					sourceType : [ 'album', 'camera' ], // 可以指定来源是相册还是相机,默认二者都有
    					defaultCameraMode : "batch", //表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。(注:用户进入拍照界面仍然可自由切换两种模式)
    					success : function(res) {
    						var localIds = res.localIds; // 返回选定照片的本地ID列表,
    						// andriod中localId可以作为img标签的src属性显示图片;
    						// 而在IOS中需通过上面的接口getLocalImgData获取图片base64数据,从而用于img标签的显示
    					}
    				});
    			});
    
    		}
    		init();
    	</script>
    </body>
    </html>
    

    后台代码,本人直接封装成了工具类

    constens文件,里面写了几个对接企业微信的远程接口URL及参数格式

    package com.hao.qywx.web;
    
    public class ZwwxPostTencentConstants {
    
    	/**
    	 * 获取企业微信token地址及对应参数
    	 */
    	public static final String QYWX_GET_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
    	public static final String QYWX_GET_TOKEN_URL_PARAM_CORPID = "corpid";
    	public static final String QYWX_GET_TOKEN_URL_PARAM_CORPSECRET = "corpsecret";
    
    	/**
    	 * 获取企业微信token返回成功对应errcode值
    	 */
    	public static final String QYWX_GET_TOKEN_RETURN_SUCCESS_CODE = "0";
    	public static final String QYWX_GET_TOKEN_RETURN_ERRCODE = "errcode";
    	public static final String QYWX_GET_TOKEN_RETURN_ERRMSG = "errmsg";
    	public static final String QYWX_GET_TOKEN_RETURN_TOKEN = "access_token";
    
    	/**
    	 * 获取企业微信引入JS-SDK的ticket地址及对应参数
    	 */
    	public static final String QYWX_GET_JSAPITICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
    	public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TOKEN = "access_token";
    
    	public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TICKET = "jsapi_ticket";
    	public static final String QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR = "noncestr";
    	public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP = "timestamp";
    	public static final String QYWX_GET_JSAPITICKET_URL_PARAM_URL = "url";
    
    	public static final String QYWX_GET_JSAPITICKET_RETURN_SIGNATURE = "signature";
    
    	/**
    	 * 获取用户ticket参数
    	 */
    	public static final String QYWX_GET_JSAPITICKET_RETURN_SUCCESS_CODE = "0";
    	public static final String QYWX_GET_JSAPITICKET_RETURN_ERRCODE = "errcode";
    	public static final String QYWX_GET_JSAPITICKET_RETURN_ERRMSG = "errmsg";
    	public static final String QYWX_GET_JSAPITICKET_RETURN_TICKET = "ticket";
    
    	/**
    	 * 通过code获取用户信息url及其对应参数
    	 */
    	public static final String QYWX_GET_USERINFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
    	public static final String QYWX_GET_USERINFO_URL_PARAM_TOKEN = "access_token";
    	public static final String QYWX_GET_USERINFO_URL_PARAM_CODE = "code";
    
    	/**
    	 * 通过code获取用户信息返回参数
    	 */
    	public static final String QYWX_GET_USERINFO_RETURN_SUCCESS_CODE = "0";
    	public static final String QYWX_GET_USERINFO_RETURN_ERRCODE = "errcode";
    	public static final String QYWX_GET_USERINFO_RETURN_ERRMSG = "errmsg";
    	public static final String QYWX_GET_USERINFO_RETURN_USERID = "UserId";
    
    	/**
    	 * 企业微信的CorpID,在企业微信管理端查看
    	 */
    	public static final String QYWX_CORPID = "";
    
    	/**
    	 * 授权方的网页应用ID,在具体的网页应用中查看
    	 */
    	public static final String QYWX_AGENTID = "";
    
    	/**
    	 * 应用的凭证密钥,在具体的网页应用中查看
    	 */
    	public static final String QYWX_CORPSECRET = "";
    
    	/**
    	 * 参数连接符
    	 */
    	public static final String QYWX_AND = "&";
    	public static final String QYWX_EQUAL = "=";
    	public static final String QYWX_QUERY = "?";
    }
    

    工具类,

    package com.hao.qywx.web;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    import com.alibaba.fastjson.JSONObject;
    
    public class ZwwxPostTencentUtil {
    
    	/**
    	 * @name 中文名称
    	 * @description 根据页面URL和页面ticket生成接入JS-SDK接入码
    	 * @time 创建时间:2018年7月23日19:58:05
    	 * @param url:接入js-sdk的页面地址
    	 * 			ticket:通过token生成的接入js-sdk的ticket
    	 * @return 请求返回接入js-sdk所需json对象
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	public static JSONObject getSignature(String url, String ticket) {
    		JSONObject rul = new JSONObject();
    
    		String noncestr = getRandomString(16);
    		String timestamp = (int)(System.currentTimeMillis()/1000)+"";
    		String sign = "";
    		sign += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TICKET
    				+ ZwwxPostTencentConstants.QYWX_EQUAL
    				+ ticket
    				+ ZwwxPostTencentConstants.QYWX_AND
    				+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR
    				+ ZwwxPostTencentConstants.QYWX_EQUAL
    				+ noncestr
    				+ ZwwxPostTencentConstants.QYWX_AND
    				+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP
    				+ ZwwxPostTencentConstants.QYWX_EQUAL + timestamp
    				+ ZwwxPostTencentConstants.QYWX_AND
    				+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_URL
    				+ ZwwxPostTencentConstants.QYWX_EQUAL + url;
    		String signature = "";
    		try {
    			// 指定sha1算法
    			MessageDigest digest = MessageDigest.getInstance("SHA-1");
    			digest.update(sign.getBytes());
    			// 获取字节数组
    			byte messageDigest[] = digest.digest();
    			// Create Hex String
    			StringBuffer hexString = new StringBuffer();
    			// 字节数组转换为 十六进制 数
    			for (int i = 0; i < messageDigest.length; i++) {
    				String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
    				if (shaHex.length() < 2) {
    					hexString.append(0);
    				}
    				hexString.append(shaHex);
    			}
    			signature = hexString.toString();
    
    		} catch (NoSuchAlgorithmException e) {
    			e.printStackTrace();
    		}
    		rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_SIGNATURE,
    				signature);
    		rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR,
    				noncestr);
    		rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP,
    				timestamp);
    		return rul;
    	}
    
    	/**
    	 * @name 中文名称
    	 * @description 根据扫码登录返回的code值和应用token值获取扫码登录人用户id(注:id为企业微信扫码人id)
    	 * @time 创建时间:2018年7月23日14:07:28
    	 * @param code
    	 *            :前台扫码登录后微信js插件会自动跳转链接,且附带该code。 accessToken: 应用调用接口凭证,
    	 *            该类下getTencentToken()方法可直接获取。
    	 * @return 请求返回userid
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	public static String getTencentUserInfo(String code, String accessToken) {
    		String url = "";
    		url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL
    				+ ZwwxPostTencentConstants.QYWX_QUERY;
    		url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL_PARAM_TOKEN
    				+ ZwwxPostTencentConstants.QYWX_EQUAL + accessToken;
    		url += ZwwxPostTencentConstants.QYWX_AND;
    		url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL_PARAM_CODE
    				+ ZwwxPostTencentConstants.QYWX_EQUAL + code;
    		JSONObject userInfoJson = sendPostRequest(url);
    		String errcode = userInfoJson
    				.getString(ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_ERRCODE);
    		if (ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_SUCCESS_CODE
    				.equals(errcode)) {
    			return userInfoJson
    					.getString(ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_USERID);
    		} else {
    		}
    		return null;
    	}
    
    	/**
    	 * @name 中文名称
    	 * @description 获取企业微信Token(应用调用接口凭证) 该值具有以下特性: 1、通用性:该企业微信应用下通用, 2、
    	 *              有效性:该值有效时长为7200秒, 3、微信端不可长期无限制请求,推荐使用缓存保存改值
    	 * @time 创建时间:2018年7月23日14:07:28
    	 * @param 请求URL地址
    	 *            ,参数需自行拼接
    	 * @return 请求返回token值
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	public static String getTencentToken() {
    		String url = "";
    		url += ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL
    				+ ZwwxPostTencentConstants.QYWX_QUERY;
    		url += ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL_PARAM_CORPID
    				+ ZwwxPostTencentConstants.QYWX_EQUAL
    				+ ZwwxPostTencentConstants.QYWX_CORPID;
    		url += ZwwxPostTencentConstants.QYWX_AND
    				+ ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL_PARAM_CORPSECRET
    				+ ZwwxPostTencentConstants.QYWX_EQUAL;
    		url += ZwwxPostTencentConstants.QYWX_CORPSECRET;
    		JSONObject tokenJson = sendPostRequest(url);
    		String errcode = tokenJson
    				.getString(ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_ERRCODE);
    		if (ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_SUCCESS_CODE
    				.equals(errcode)) {
    			return tokenJson
    					.getString(ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_TOKEN);
    		} else {
    		}
    		return null;
    	}
    
    	/**
    	 * @name 中文名称
    	 * @description 根据token获取接入js-sdk的ticket
    	 * @time 创建时间:2018年7月23日19:33:47
    	 * @param accessToken
    	 *            : 应用调用接口凭证, 该类下getTencentToken()方法可直接获取。
    	 * @return 请求返回ticket值。
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	public static String getTencentJSSDKTicket(String token) {
    		String url = "";
    		url += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL
    				+ ZwwxPostTencentConstants.QYWX_QUERY;
    		url += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TOKEN
    				+ ZwwxPostTencentConstants.QYWX_EQUAL + token;
    		JSONObject ticketJson = sendPostRequest(url);
    		String errcode = ticketJson
    				.getString(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_ERRCODE);
    		if (ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_SUCCESS_CODE
    				.equals(errcode)) {
    			return ticketJson
    					.getString(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_TICKET);
    		} else {
    		}
    		return null;
    	}
    
    	/**
    	 * @name 中文名称
    	 * @description 相关说明
    	 * @time 创建时间:2018年7月23日11:48:33
    	 * @param 请求URL地址
    	 *            ,参数需自行拼接
    	 * @return 请求返回json对象
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	private static JSONObject sendPostRequest(String url) {
    		StringBuffer stringBuffer = new StringBuffer("");
    		try {
    			URL postUrl = new URL(url);
    			HttpURLConnection connection = (HttpURLConnection) postUrl
    					.openConnection();
    			connection.setDoOutput(true);
    			connection.setDoInput(true);
    			connection.setRequestMethod("POST");
    			connection.setUseCaches(false);
    			connection.setInstanceFollowRedirects(true);
    			connection.setRequestProperty("Content-Type", "application/json");
    			BufferedReader reader = new BufferedReader(new InputStreamReader(
    					connection.getInputStream()));
    			String line;
    			while ((line = reader.readLine()) != null) {
    				stringBuffer.append(line);
    			}
    			reader.close();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return JSONObject.parseObject(stringBuffer.toString());
    	}
    
    	/**
    	 * @name 中文名称
    	 * @description 获取指定位数的随机字符串(包含小写字母、大写字母、数字,0<length)
    	 * @time 创建时间:2018年7月23日14:17:21
    	 * @param 获取字符串长度
    	 * @return 对应长度的随机字符串
    	 * @author 朱浩
    	 * @history 修订历史(历次修订内容、修订人、修订时间等)
    	 */
    	private static String getRandomString(int length) {
    		// 随机字符串的随机字符库
    		String KeyString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    		StringBuffer sb = new StringBuffer();
    		int len = KeyString.length();
    		for (int i = 0; i < length; i++) {
    			sb.append(KeyString.charAt((int) Math.round(Math.random()
    					* (len - 1))));
    		}
    		return sb.toString();
    	}
    }
    

    实现接口

            JSONObject rul = new JSONObject();
    		String token = ZwwxPostTencentUtil.getTencentToken();
    		String ticket = ZwwxPostTencentUtil.getTencentJSSDKTicket(token);
    		rul = ZwwxPostTencentUtil.getSignature(url, ticket);

    五、踩坑记录

    (1)必须有可以外网访问的项目,推荐花生壳内网穿透直接搞定。

    (2)在企业微信后台配置js-sdk可信域名的时候需要根据提示下载一个文件放在项目根目录下

    (3)网页端是无法调试JS-SDK接口的,包括微信开发工具其实也不是很好用,最好是用手机测试

    (4)我在用手机测试的时候一直没有反应,但是在微信开发工具中测试又是有反应的(这里的反应指的是alert),最终在大神的指导下,写在APP从新安装(好吧,这个确实是个坑,当你在微信开发工具中测试有反应,但手机上没反应的话可以试试)

    (5)注意一下我获取Signature这段代码,其中的timestamp 单位是秒  秒 秒

    (6)还有各种坑,可能想不起来了,欢迎各位留言,有可能我就遇到过!

    六、各种文献链接

    企业微信后台登录链接:https://work.weixin.qq.com/

    企业微信开发文档API:https://work.weixin.qq.com/api/doc

    花生壳官网:https://hsk.oray.com/

    花生壳管理端:https://login.oray.com/login/?tplname=qrcode&s_url=https%3A%2F%2Fhsk.oray.com%2Fconsole%2Fmanage%2F

    项目文件 github:https://github.com/zhuhao18/Zbox-qywx

    展开全文
  • 微信公众号开发技术要点 微信公众号开发技术要点 微信公众号及其接口功能介绍 基本概念 公众号开发者模式 代码验证及图示 Open ID与Union ID 基本概念 使用说明 Access_token 基本介绍 注意事项 获取流程 ...

    微信公众号开发技术要点

    前言

    本文将介绍微信公众号开发中涉及的一些技术概念,以便读者可以快速掌握公众号开发过程中的基本操作和流程。文档不涉及具体开发技术和流程的介绍,该文档作用相当于官方文档中关键点的详细注解。

    在公众号开发流程中,涉及到三种系统:

    1. 微信方面的系统,负责监听用户的操作并将相关消息和事件推送到响应系统,下称微信系统;
    2. 接收微信系统所推送的事件和消息的系统,下称响应系统;
    3. 业务逻辑系统,实现业务逻辑处理,下称业务系统。

    响应系统和业务系统需要自己开发;其中业务系统中的页面即为下文中提到的第三方页面;

    微信公众号及其接口功能介绍

    基本概念

    公众号是为微信用户提供资讯和服务的平台;公众平台开发接口则是通过后台业务系统向用户提供服务的基础;

    不同的公众号类型有不同数量的接口权限,服务号要多于订阅号;

    公众号开发者模式

    开启公众号开发模式后,微信系统将以事件推送的形式告知响应系统用户在公众号里的相关行为,包括:关注/取关事件、二维码扫描事件、自定义菜单事件、跳转链接事件时的事件、点击菜单拉取消息时的事件,从而开发者可以获取到该消息并做出响应;

    1. 开启公众号开发者模式
    2. 填写服务器配置
    3. 验证服务器地址的有效性

    这些步骤都是在微信官方公众号后台里开发->基本配置里完成的

    代码验证及图示

    配置图示:

    这里写图片描述

    服务器代码:

    public class WeChatServer extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req,resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            PrintWriter writer=resp.getWriter();
            System.out.println(req.getParameter("echostr"));
            writer.println(req.getParameter("echostr"));
        }
    }
    //微信系统对响应系统地址的有效性验证是通过返回值(echostr)完成的,这里没有添加对微信系统身份的检查,直接输出
    

    完整代码见WeChatWebSystem

    Open ID与Union ID

    基本概念

    为识别用户,微信为每一个用户提供了针对特定公众号平台的Open ID;同一个用户在不同公众号里的有不同的Open ID;同一个公众号里不同用户有不同的Open ID;获取用户的Open ID是无需用户同意的,但是获取用户的基本信息(所在地址、昵称等)是需要用户同意的;

    为了实现在多个公众平台、移动应用之间做到用户统一管理,提供了Union ID机制。同一用户在同一开放平台账号下的所有公众号和应用里Union ID是相同的;

    使用说明

    1. Open ID的获取和使用

      获取

      1. Open ID会在微信系统向响应系统推送普通消息以及事件推送时附带于消息体内;
      2. 从公众号进入第三方页面时,业务系统需要获得该用户的Open ID;

      使用:

      1. 发送被动消息时(包括对事件的响应和消息的响应),使用Open ID来标记发送对象;
      2. 发送主动消息时(模板消息),使用Open ID来标记发送对象;
    2. Union ID的获取和使用

      从公众号进入第三方页面时,业务系统可以选择获取Union ID来实现业务逻辑,其具体流程为:

      准备阶段:

      1. 获得微信网页授权:微信公众号后台->开发->接口权限;
      2. 设置授权回调域名:一旦设置成功,该域名下的所有页面均可以进行OAuth2.0鉴权;
      3. 以snsapi_userInfo为scope发起授权以获得该用户的基本信息;

      获取阶段:

      1. 引导用户进入授权页面,用户同意后,获得code;

        https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        
        其中APPID/REDIRECT_URI/SCOPE/STATE为变量,参数顺序不可改变;跳转回调的redirect_uri,应当使用https链接来确保授权code的安全性。以上链接需要自微信客户端里打开,如果用户同意授权,则页面跳转到:redirect_uri/?code=CODE&state=STATE
        
        参数	是否必须	说明
        appid	是	公众号的唯一标识
        redirect_uri	是	授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
        response_type	是	返回类型,请填写code
        scope	是	应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
        state	否	重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
        #wechat_redirect	是	无论直接打开还是做页面302重定向时候,必须带此参数
        
      2. 使用code换取网页授权access_token(用于获取用户基本信息的token,不是微信基础服务里的access_token,在这一步里,获取access_token的同时也会获得用户的open ID);

        获取code后,请求以下链接获取access_token:  https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        
        其中APPID/SECRET/CODE为变量
        
        正确返回的JSON数据如下:
        { "access_token":"ACCESS_TOKEN",
        "expires_in":7200,
        "refresh_token":"REFRESH_TOKEN",
        "openid":"OPENID",
        "scope":"SCOPE" }
        
      3. 如有必要,开发者刷新该access_token,避免过期;

        获取第二步的refresh_token后,请求以下链接获取access_token:
        https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
        
        access_token拥有较短的有效期,refresh_token则有30天的有效期;
        
        返回结果同上
        { "access_token":"ACCESS_TOKEN",
        "expires_in":7200,
        "refresh_token":"REFRESH_TOKEN",
        "openid":"OPENID",
        "scope":"SCOPE" }
        
      4. 通过access_token和openID获得用户基本信息;

         https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
         
         access_token为网页授权时使用的access_token,不是调用微信接口时使用的access_token;
         返回数据:
         {    "openid":" OPENID",
        " nickname": NICKNAME,
        "sex":"1",
        "province":"PROVINCE"
        "city":"CITY",
        "country":"COUNTRY",
        "headimgurl":    "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
        "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
        "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
        }
        
        

      从响应系统内获得Union ID是通过其他接口实现的:

       请求方式: GET
       请求地址:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
       参数说明:access_token即为接口访问令牌,openid即为消息的发送者
    
    1. 注意事项

      使用snaapi_base为scope的网页授权对用户是静默的,用户感觉直接进入了公众号网页;

      对于已关注公众号的用户,如果用户从公众号会话或者自定义菜单进入网页授权页,即使scope为snaapi_userInfo,也是静默的;

    Access_token

    基本介绍

    access_token是公众号内全局唯一接口调用凭据;其有效期为2小时,需要定时刷新,重新获取将导致上次access_token失效;

    注意事项

    1. 建议公众号使用统一的中控服务器来获取和刷新access_token;
    2. access_token的有效期通过expire_in标志;在刷新过程中中控服务器可以继续对外输出旧的access_token,刷新完毕5分钟内新旧access_token都有效;
    3. 中控服务器应当提供被动刷新access_token的机制;
    4. 公众号和小程序均可以使用AppID和AppSecret调用本接口来获取access_token;

    获取流程

    https请求方式: GET
    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    
    正确返回结果
    {"access_token":"ACCESS_TOKEN","expires_in":7200}
    出粗返回结果
    {"errcode":40013,"errmsg":"invalid appid"}
    

    代码验证及图示

    下图为访问失败时的截图,因为本地开发中使用的IP地址不在公众号后台的IP访问白名单所致,正式部署后将不存在该问题:

    这里写图片描述

    下图为服务器端浏览器接口调试页面

    这里写图片描述

    自定义菜单

    开启服务模式后,将无法在微信公众号后台内实现对菜单的自定义,但是微信公众号后台内可以自定义的菜单点击类型十分有限(2/10),可以通过使用微信提供的菜单管理接口来对菜单进行管理。

    基本介绍

    1. 微信公众号内允许3个一级菜单,每个一级菜单允许5个二级子菜单;一级菜单最多4个汉字,二级菜单最多7个汉字;
    2. 菜单项共有10种类型:
      1. click:当用户点击该类子菜单时,微信系统将向响应系统推送类型为event的消息并附带该菜单项的key值,开发者可以据此对该事件做出响应;
      2. view:当用户点击该类子菜单时,微信客户端将打开开发者在该菜单项中设置的网页URL,在该URL内开发者可以配合网页授权接口获得用户的基本信息以开展业务服务;
      3. scancode_push:当用户点击此类型子菜单时,微信客户端将调起扫一扫工具,并将结果展示给用户,如果识别结果为URL,将进入该URL,同时响应系统将接收到该消息;
      4. scancode_waitmsg:当用户点击该类子菜单时,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
      5. pic_sysphoto:当用户点击该类子菜单时,微信客户端将调起系统相机,完成拍照操作后,将拍摄的图片发送给响应系统,同时收起系统相机,等待开发者下发消息;
      6. pic_photo_or_album:基本功能同上,但是会给用户两种选择:拍照或者系统相册上传;
      7. pic_weixin:基本功能同上,但是会使用微信相册;
      8. location_select:弹出地理位置选择器,完成操作后,将选择地理信息发送给响应系统,收起地理位置选择器后,等待开发者下发消息;
      9. media_id:当用户点击该类子菜单项后,微信系统将对应永久素材id的素材下发给用户;
      10. view_limited:当用户点击该类型子菜单时,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL;

    创建菜单

    接口说明:

     接口地址:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
     访问方式:POST
     参数说明:
     ACCESS_TOKEN即为获取的access_token;
     POST的数据为JSON字符串,其中button定义了菜单,为一个JSON数组;数组中每一个元素都是一个一级菜单,其中sub_button属性为该一级菜单的二级菜单,同样也是JSON数组;每一个菜单项包含type(上面提到的10种)、name、key等信息。
     {
         "button":[
         {    
              "type":"click",
              "name":"今日歌曲",
              "key":"V1001_TODAY_MUSIC"
          },
          {
               "name":"菜单",
               "sub_button":[
               {    
                   "type":"view",
                   "name":"搜索",
                   "url":"http://www.soso.com/"
                },
                {//跳转小程序
                     "type":"miniprogram",
                     "name":"wxa",
                     "url":"http://mp.weixin.qq.com",
                     "appid":"wx286b93c14bbf93aa",
                     "pagepath":"pages/lunar/index"
                 },
                {
                   "type":"click",
                   "name":"赞一下我们",
                   "key":"V1001_GOOD"
                }]
           }]
     }
     正确返回消息:
     {"errcode":0,"errmsg":"ok"}
     出错时返回消息:
     {"errcode":40018,"errmsg":"invalid button name size"}
     
    

    这里需要注意的是,POST的内容类型(content-type)需要设置为application/json;

    查询菜单

    创建自定义菜单后,可使用该接口查询自定义菜单的结构。如果使用了个性化菜单,那么该接口将返回默认菜单和全部个性化菜单的信息;

    请求方式:GET
    https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
    
    返回数据(无个性化菜单时):
    {
        "menu": {
            "button": [
                {
                    "type": "click", 
                    "name": "今日歌曲", 
                    "key": "V1001_TODAY_MUSIC", 
                    "sub_button": [ ]
                }, 
                {
                    "type": "click", 
                    "name": "歌手简介", 
                    "key": "V1001_TODAY_SINGER", 
                    "sub_button": [ ]
                }, 
                {
                    "name": "菜单", 
                    "sub_button": [
                        {
                            "type": "view", 
                            "name": "搜索", 
                            "url": "http://www.soso.com/", 
                            "sub_button": [ ]
                        }, 
                        {
                            "type": "view", 
                            "name": "视频", 
                            "url": "http://v.qq.com/", 
                            "sub_button": [ ]
                        }, 
                        {
                            "type": "click", 
                            "name": "赞一下我们", 
                            "key": "V1001_GOOD", 
                            "sub_button": [ ]
                        }
                    ]
                }
            ]
        }
    }
    
    返回结果(有个性化菜单时):
    {
        "menu": {
            "button": [
                {
                    "type": "click", 
                    "name": "今日歌曲", 
                    "key": "V1001_TODAY_MUSIC", 
                    "sub_button": [ ]
                }
            ], 
            "menuid": 208396938
        }, 
        "conditionalmenu": [
            {
                "button": [
                    {
                        "type": "click", 
                        "name": "今日歌曲", 
                        "key": "V1001_TODAY_MUSIC", 
                        "sub_button": [ ]
                    }, 
                    {
                        "name": "菜单", 
                        "sub_button": [
                            {
                                "type": "view", 
                                "name": "搜索", 
                                "url": "http://www.soso.com/", 
                                "sub_button": [ ]
                            }, 
                            {
                                "type": "view", 
                                "name": "视频", 
                                "url": "http://v.qq.com/", 
                                "sub_button": [ ]
                            }, 
                            {
                                "type": "click", 
                                "name": "赞一下我们", 
                                "key": "V1001_GOOD", 
                                "sub_button": [ ]
                            }
                        ]
                    }
                ], 
                "matchrule": {
                    "group_id": 2, 
                    "sex": 1, 
                    "country": "中国", 
                    "province": "广东", 
                    "city": "广州", 
                    "client_platform_type": 2
                }, 
                "menuid": 208396993
            }
        ]
    }
    

    删除菜单

    接口说明:

    http请求方式:GET
    https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
    正确返回消息:
    {"errcode":0,"errmsg":"ok"}
    错误返回消息同上
    

    这里需要注意的是,该方式将删除整个菜单。

    个性化菜单

    为了帮助公众号实现灵活的业务运营,微信公众平台新增了个性化菜单接口,开发者可以通过该接口,让公众号的不同用户群体看到不一样的自定义菜单。该接口开放给已认证订阅号和已认证服务号。

    开发者可以使用如下方式标志用户:

    1. 用户标签(开发者的业务需求可以借助用户标签来完成)
    2. 性别
    3. 手机操作系统地区(用户在微信客户端设置的地区)
    4. 语言(用户在微信客户端设置的语言)

    使用个性化菜单需要有以下几点注意:

    1. 个性化菜单要求用户的微信客户端版本在iPhone6.2.2,Android 6.2.4以上,暂时不支持其他版本微信;
    2. 菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果;
    3. 普通公众号的个性化菜单的新增接口每日限制次数为2000次,删除接口也是2000次,测试个性化菜单匹配结果接口为20000次;
    4. 出于安全考虑,一个公众号的所有个性化菜单,最多只能设置为跳转到3个域名下的链接;
    5. 创建个性化菜单之前必须先创建默认菜单(默认菜单是指使用普通自定义菜单创建接口创建的菜单)。如果删除默认菜单,个性化菜单也会全部删除;
    6. 个性化菜单接口支持用户标签,请开发者注意,当用户身上的标签超过1个时,以最后打上的标签为匹配;

    创建个性化菜单:

    请求方式:POST(请使用https协议)
    https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=ACCESS_TOKEN
    POST数据为JSON对象;
    {
         "button":[
         {    
            "type":"click",
            "name":"今日歌曲",
             "key":"V1001_TODAY_MUSIC" },
        {     "name":"菜单",
            "sub_button":[
            {            
                "type":"view",
                "name":"搜索",
                "url":"http://www.soso.com/"},
                {
                             "type":"miniprogram",
                             "name":"wxa",
                             "url":"http://mp.weixin.qq.com",
                             "appid":"wx286b93c14bbf93aa",
                             "pagepath":"pages/lunar/index"
                },
                 {
            "type":"click",
            "name":"赞一下我们",
            "key":"V1001_GOOD"
               }]
     }],
    "matchrule":{
      "tag_id":"2",
      "sex":"1",
      "country":"中国",
      "province":"广东",
      "city":"广州",
      "client_platform_type":"2",
      "language":"zh_CN"
      }
    }
    正确返回消息:
    {"menuid":"208379533"}——menuid即为该菜单的标记;可用于以后删除使用;
    

    删除个性化菜单:

    请求方式:POST(请使用https协议)
    https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=ACCESS_TOKEN
    参数说明:
    POST数据为JSON字符串
    {"menuid":"208379533"}
    正确返回:
    {"errcode":0,"errmsg":"ok"}
    错误返回:
    通用
    

    自定义菜单事件推送

    注意,第3个到第8个的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。

    click类型的消息推送:
    <xml>
    <ToUserName><![CDATA[toUser]]></ToUserName>
    <FromUserName><![CDATA[FromUser]]></FromUserName>
    <CreateTime>123456789</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[CLICK]]></Event>
    <EventKey><![CDATA[EVENTKEY]]></EventKey>
    </xml>
    
    view类型的消息推送:
    <xml>
    <ToUserName><![CDATA[toUser]]></ToUserName>
    <FromUserName><![CDATA[FromUser]]></FromUserName>
    <CreateTime>123456789</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[VIEW]]></Event>
    <EventKey><![CDATA[www.qq.com]]></EventKey>
    <MenuId>MENUID</MenuId>//指菜单ID,如果是个性化菜单,则可以通过这个字段,知道是哪个规则的菜单被点击了。
    </xml>
    
    scancode_push类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408090502</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[scancode_push]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType>
    <ScanResult><![CDATA[1]]></ScanResult>
    </ScanCodeInfo>
    </xml>
    
    scancode_waitmsg类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408090606</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[scancode_waitmsg]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType>
    <ScanResult><![CDATA[2]]></ScanResult>
    </ScanCodeInfo>
    </xml>
    
    pic_sysphoto类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408090651</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[pic_sysphoto]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <SendPicsInfo><Count>1</Count>
    <PicList><item><PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>
    </item>
    </PicList>
    </SendPicsInfo>
    </xml>
    
    pic_photo_or_album类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408090816</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[pic_photo_or_album]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <SendPicsInfo><Count>1</Count>
    <PicList><item><PicMd5Sum><![CDATA[5a75aaca956d97be686719218f275c6b]]></PicMd5Sum>
    </item>
    </PicList>
    </SendPicsInfo>
    </xml>
    
    pic_weixin类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408090816</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[pic_weixin]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <SendPicsInfo><Count>1</Count>
    <PicList><item><PicMd5Sum><![CDATA[5a75aaca956d97be686719218f275c6b]]></PicMd5Sum>
    </item>
    </PicList>
    </SendPicsInfo>
    </xml>
    
    location_select类型的消息推送:
    <xml><ToUserName><![CDATA[gh_e136c6e50636]]></ToUserName>
    <FromUserName><![CDATA[oMgHVjngRipVsoxg6TuX3vz6glDg]]></FromUserName>
    <CreateTime>1408091189</CreateTime>
    <MsgType><![CDATA[event]]></MsgType>
    <Event><![CDATA[location_select]]></Event>
    <EventKey><![CDATA[6]]></EventKey>
    <SendLocationInfo><Location_X><![CDATA[23]]></Location_X>
    <Location_Y><![CDATA[113]]></Location_Y>
    <Scale><![CDATA[15]]></Scale>
    <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>
    <Poiname><![CDATA[]]></Poiname>
    </SendLocationInfo>
    </xml>
    
    
    

    用户管理

    标签管理

    开发者可以使用用户标签管理的相关接口,实现对公众号标签的管理;标签可以用于对用户的分类管理;

    每个公众号可以创建100个标签;创建方式如下:

    请求方式:POST(使用https协议);
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/create?access_token=ACCESS_TOKEN
    POST数据格式:
    {   "tag":{ "name" : "广东"//标签名长度不能超过30字节}}
    返回结果:
    {   "tag":{ "id":134,id "name":"广东"   } }//包含tag-id
    

    获取公众号已创建的标签:

    请求方式:GET(使用https协议) 
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/get?access_token=ACCESS_TOKEN
    返回说明:
    {   "tags":[{       "id":1,       "name":"每天一罐可乐星人",       "count":0 //此标签下粉丝数 },{   "id":2,   "name":"星标组",   "count":0 },{   "id":127,   "name":"广东",   "count":5 }   ] }
    

    编辑标签:

    请求方式:POST(请使用https协议)
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/update?access_token=ACCESS_TOKEN
    POST数据:
    {   "tag" : {     "id" : 134,     "name" : "广东人"   } }
    返回说明:
    正确返回:{   "errcode":0,   "errmsg":"ok" }
    错误返回:详见错误返回码
    

    删除标签:

    请注意,当某个标签下的粉丝超过10w时,后台不可直接删除标签。此时,开发者可以对该标签下的openid列表,先进行取消标签的操作,直到粉丝数不超过10w后,才可直接删除该标签。

    请求方式:POST(使用https协议) 
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/delete?access_token=ACCESS_TOKEN
    POST数据:
    {   "tag":{        "id" : 134   } }
    返回说明:
    正确返回:{   "errcode":0,   "errmsg":"ok" }
    错误返回:详见错误返回码
    

    获取标签下的粉丝列表:

    请求方式:POST(请使用https协议) 
    请求地址:https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token=ACCESS_TOKEN
    POST数据格式:JSON
    {   "tagid" : 134,   "next_openid":""//第一个拉取的OPENID,不填默认从头开始拉取 }
    返回说明:
    { 
    	"count":2,//这次获取的粉丝数量   
    	"data":{//粉丝列表
    		"openid":[  
    			"ocYxcuAEy30bX0NXmGn4ypqx3tI0",    
    			"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"
    		]  
    	},  
    	"next_openid":"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"//拉取列表最后一个用户的openid 
    }
    

    用户标签管理

    标签功能支持为公众号用户进行打标签、取消标签等操作;该功能可以实现个性化菜单定制

    批量为用户打标签

    请求方式:POST(请使用https协议)
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging?access_token=ACCESS_TOKEN
    POST数据:
    {   
    	"openid_list" : [//粉丝列表    
    		"ocYxcuAEy30bX0NXmGn4ypqx3tI0",    
    		"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"],   
    	"tagid" : 134 
    }
    返回说明:
    {   
    	"errcode":0,   
    	"errmsg":"ok"
    }
    

    批量为用户取消标签

    请求方式:POST(请使用https协议) 
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/members/batchuntagging?access_token=ACCESS_TOKEN
    POST数据格式:JSON
    {
    	"openid_list" : [//粉丝列表     
    		"ocYxcuAEy30bX0NXmGn4ypqx3tI0",     
    		"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"],   
    	"tagid" : 134
    }
    返回说明:{"errcode":0, "errmsg":"ok"}
    

    获取用户的标签列表

    请求方式:POST(请使用https协议)
    请求地址:https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=ACCESS_TOKEN
    POST数据格式:JSON
    {   "openid" : "ocYxcuBt0mRugKZ7tGAHPnUaOW7Y" }
    返回说明:{   "tagid_list":[//被置上的标签列表 134, 2   ] }
    

    设置用户备注名

    请求方式: POST(请使用https协议)
    请求地址:https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token=ACCESS_TOKEN
    POST数据格式:JSON
    POST数据例子:
    {
        "openid":"oDF3iY9ffA-hqb2vVvbr7qxf6A0Q",
        "remark":"pangzi"//标签名
    }
    返回说明:{"errcode":0,"errmsg":"ok"}
    

    获取用户基本信息

    注意,这里获的用户基本信息是在关注者和公众号产生消息交互后,公众号可获得该用户的Open ID,之后在响应系统中,通过Open ID获的Union ID以及基本信息

    请求方式: GET
    请求地址:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
    返回说明:正常情况下,微信会返回下述JSON数据包给公众号
    {
        "subscribe": 1, 
        "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
        "nickname": "Band", 
        "sex": 1, 
        "language": "zh_CN", 
        "city": "广州", 
        "province": "广东", 
        "country": "中国", 
        "headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
        "subscribe_time": 1382694957,
        "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
        "remark": "",
        "groupid": 0,
        "tagid_list":[128,2],
        "subscribe_scene": "ADD_SCENE_QR_CODE",
        "qr_scene": 98765,
        "qr_scene_str": ""
    }
    

    批量获得用户基本信息

    请求方式: POST
    请求地址:https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
    POST:JSON
    {
        "user_list": [
            {
                "openid": "otvxTs4dckWG7imySrJd6jSi0CWE", 
                "lang": "zh_CN"
            }, 
            {
                "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg", 
                "lang": "zh_CN"
            }
        ]
    }
    返回说明:
    {
       "user_info_list": [
           {
               "subscribe": 1, 
               "openid": "otvxTs4dckWG7imySrJd6jSi0CWE", 
               "nickname": "iWithery", 
               "sex": 1, 
               "language": "zh_CN", 
               "city": "揭阳", 
               "province": "广东", 
               "country": "中国", 
    
               "headimgurl": "http://thirdwx.qlogo.cn/mmopen/xbIQx1GRqdvyqkMMhEaGOX802l1CyqMJNgUzKP8MeAeHFicRDSnZH7FY4XB7p8XHXIf6uJA2SCunTPicGKezDC4saKISzRj3nz/0",
    
              "subscribe_time": 1434093047, 
               "unionid": "oR5GjjgEhCMJFyzaVZdrxZ2zRRF4", 
               "remark": "", 
    
               "groupid": 0,
               "tagid_list":[128,2],
               "subscribe_scene": "ADD_SCENE_QR_CODE",
               "qr_scene": 98765,
               "qr_scene_str": ""
    
          }, 
           {
               "subscribe": 0, 
               "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg"
           }
       ]
    }
    

    获取用户列表

    公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。

    请求方式: GET(请使用https协议)
    请求地址:https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
    返回说明:
    {
    	"total":2,
    	"count":2,
    	"data":{
    		"openid":["OPENID1","OPENID2"]
    	},
    	"next_openid":"NEXT_OPENID"
    }
    

    当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求。

    具体而言,就是在调用接口时,将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值。

    消息管理

    接收普通消息

    当普通用户向公众号发送消息时,微信系统将POST消息的XML数据包到开发者填写的URL上;

    注意事项:

    1. 微信服务器在5s内收不到响应并不会断掉连接,并且重新发起请求,总共重试3次;如果服务器无法保证在5s内做出响应,应当回复空串。
    2. 如果开发者需要在5s内对用户发送的消息做出回应,即使用发送消息->被回复消息接口向用户被动回复消息,那么需要选择对消息的加密方式,详情见 消息加密;

    各种消息体结构(XML格式组织,可使用输入流的方式读取):

    注意,首次出现的XML标签将给出注释,第二次出现则不再注释,该规则同样适用于后面的接收事件消息

    1. 文本消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>	//开发者微信号
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>		//用户的openid
      	<CreateTime>1348831860</CreateTime>
      	<MsgType>< ![CDATA[text] ]></MsgType>	//文本为text
      	<Content>< ![CDATA[this is a test] ]></Content>
      	<MsgId>1234567890123456</MsgId>		//64位整型
      </xml>
      
    2. 图片消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1348831860</CreateTime>
      	<MsgType>< ![CDATA[image] ]></MsgType>	//图片为image
      	<PicUrl>< ![CDATA[this is a url] ]></PicUrl>
      	<MediaId>< ![CDATA[media_id] ]></MediaId>	//可使用图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
      	<MsgId>1234567890123456</MsgId>
      </xml>
      
    3. 语音消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1357290913</CreateTime>
      	<MsgType>< ![CDATA[voice] ]></MsgType>	//语音为voice
      	<MediaId>< ![CDATA[media_id] ]></MediaId>
      	<Format>< ![CDATA[Format] ]></Format>	//语音格式,如amr,speex等
      	<MsgId>1234567890123456</MsgId>
      	<Recognition>< ![CDATA[腾讯微信团队] ]></Recognition>
      </xml>
      

      这里需要注意的是,如果开启了语音识别,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增加一个Recongnition字段标志识别结果;

    4. 视频消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1357290913</CreateTime>
      	<MsgType>< ![CDATA[video] ]></MsgType>	//视频为video
      	<MediaId>< ![CDATA[media_id] ]></MediaId>	//可使用图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
      	<ThumbMediaId>< ![CDATA[thumb_media_id] ]></ThumbMediaId>	//视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
      	<MsgId>1234567890123456</MsgId></xml>
      
    5. 小视频消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1357290913</CreateTime>
      	<MsgType>< ![CDATA[shortvideo] ]></MsgType>		//小视频为shortvideo
      	<MediaId>< ![CDATA[media_id] ]></MediaId>
      	<ThumbMediaId>< ![CDATA[thumb_media_id] ]></ThumbMediaId>
      	<MsgId>1234567890123456</MsgId>
      </xml>
      
    6. 地理位置消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1351776360</CreateTime>
      	<MsgType>< ![CDATA[location] ]></MsgType>	//地理位置为	location
      	<Location_X>23.134521</Location_X>	// 地理纬度
      	<Location_Y>113.358803</Location_Y>	// 地理经度
      	<Scale>20</Scale>	//地图缩放大小
      	<Label>< ![CDATA[位置信息] ]></Label>	//地理位置信息
      	<MsgId>1234567890123456</MsgId>
      </xml>
      
      
    7. 链接消息

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>1351776360</CreateTime>
      	<MsgType>< ![CDATA[link] ]></MsgType>	//链接消息为 link
      	<Title>< ![CDATA[公众平台官网链接] ]></Title>	//标题
      	<Description>< ![CDATA[公众平台官网链接] ]></Description>	//描述
      	<Url>< ![CDATA[url] ]></Url>	//消息链接
      	<MsgId>1234567890123456</MsgId>
      </xml>
      
      

    接收事件消息

    在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信系统通过事件推送的形式通知响应系统,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细内容如下:

    1. 关注/取消关注事件
    2. 扫描带参数二维码事件
    3. 上报地理位置事件
    4. 自定义菜单事件
    5. 点击菜单拉取消息的事件
    6. 点击菜单跳转链接时的事件

    各种事件介绍:

    1. 关注/取消关注事件

      用户在关注与取消关注公众号时,微信系统会把这个事件推送到响应系统,方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。

      推送XML数据包示例:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>	//开发者微信号
      	<FromUserName>< ![CDATA[FromUser] ]></FromUserName>		//用户的OennID
      	<CreateTime>123456789</CreateTime>	//创建时间,int整型
      	<MsgType>< ![CDATA[event] ]></MsgType>
      	<Event>	//消息类型为event< ![CDATA[subscribe] ]></Event>	//事件类型,subscribe(订阅)、unsubscribe(取消订阅)
      </xml>
      
      
    2. 扫描带参数二维码事件:

      用户扫描带场景值二维码时,可能推送以下两种事件:

      1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信系统会将带场景值关注事件推送给响应系统。
      2. 如果用户已经关注公众号,则微信系统会将带场景值的扫描事件推送响应系统。

      推送XML数据包示例:

      用户未关注时,进行关注后的事件推送
      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[FromUser] ]></FromUserName>
      	<CreateTime>123456789</CreateTime>
      	<MsgType>< ![CDATA[event] ]></MsgType>		//消息类型为event
      	<Event>< ![CDATA[subscribe] ]></Event>	//事件类型,subscribe
      	<EventKey>< ![CDATA[qrscene_123123] ]></EventKey>	//事件KEY值,qrscene_为前缀,后面为二维码的参数值
      	<Ticket>< ![CDATA[TICKET] ]></Ticket>	//二维码的ticket,可用来换取二维码图片
      </xml>
      
      用户已关注时的事件推送
      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName> 
      	<FromUserName>< ![CDATA[FromUser] ]></FromUserName> 
      	<CreateTime>123456789</CreateTime> 
      	<MsgType>< ![CDATA[event] ]></MsgType>
          <Event>< ![CDATA[SCAN] ]></Event>
          <EventKey>< ![CDATA[SCENE_VALUE] ]></EventKey>	//创建二维码是的scene_id
          <Ticket>< ![CDATA[TICKET] ]></Ticket>	//二维码的ticket,可用来换取图片
      </xml>
      
      
    3. 上报地理位置事件

      用户同意上报地理位置后,每次进入公众号会话时,都会进入上报地理位置,或在进入会话后每5秒报一次地理位置。上报地理位置时,微信系统将上报地理位置事件推送到响应系统;

      推送XML数据包示例:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>123456789</CreateTime>
      	<MsgType>< ![CDATA[event] ]></MsgType>
      	<Event>< ![CDATA[LOCATION] ]></Event>
      	<Latitude>23.137466</Latitude>	//维度
      	<Longitude>113.352425</Longitude>	//经度
      	<Precision>119.385040</Precision>	//地理位置精度
      </xml>
      
      
    4. 自定义菜单事件

      用户点击自定义菜单后,微信系统会把点击事件推送给响应系统,点击菜单弹出子菜单,不会产生上报。

      推送XML数据包示例:

      点击菜单拉取消息时的事件推送
      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[FromUser] ]></FromUserName>
      	<CreateTime>123456789</CreateTime>
      	<MsgType>< ![CDATA[event] ]></MsgType>
      	<Event>< ![CDATA[CLICK] ]></Event>	//事件类型为CLICK
      	<EventKey>< ![CDATA[EVENTKEY] ]></EventKey>	//事件KEY值,与自定义菜单接口中KEY值对应
      </xml>
      
      点击菜单跳转链接时的事件推送
      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[FromUser] ]></FromUserName>
      	<CreateTime>123456789</CreateTime>
      	<MsgType>< ![CDATA[event] ]></MsgType>	
      	<Event>< ![CDATA[VIEW] ]></Event>	//事件类型为 VIEW
      	<EventKey>< ![CDATA[www.qq.com] ]></EventKey>	//事件的KEY值,设置的跳转URL
      </xml>
      
      

    被动回复用户消息

    当用户发送消息给公众号时,或某些特定的用户操作引发的事件推送时,微信系统会将一个POST请求发送到响应系统,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应,这种方式称为被动回复用户消息。

    现支持回复文本、图片、图文、语音、视频、音乐。

    发送被动响应消息其实并不是一种接口,而是对微信系统发过来消息的一次回复,只是这次回复将发送到用户。

    被动回复消息的数据格式:

    注意:

    1. 回复图片(不支持gif动图)等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。

    2. 一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:

      1. 开发者在5秒内未回复任何内容;
      2. 开发者回复了异常数据,比如JSON数据等;
    3. 回复文本消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>	//用户Open id
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>	//开发者 微信号;这两者都可以从响应系统收到的消息里获得
          <CreateTime>12345678</CreateTime>
          <MsgType>< ![CDATA[text] ]></MsgType>	//消息类型为:text
          <Content>< ![CDATA[你好] ]></Content>
      </xml>
      
      
    4. 回复图片消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>12345678</CreateTime>
      	<MsgType>< ![CDATA[image] ]></MsgType>	//消息类型为:image
      	<Image>
      		<MediaId>< ![CDATA[media_id] ]></MediaId>//通过素材管理中的接口上传多媒体文件,得到的id。
      	</Image>
      </xml>
      
      
    5. 回复语音消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>12345678</CreateTime>
      	<MsgType>< ![CDATA[voice] ]></MsgType>	//消息类型为:voice
      	<Voice>
      		<MediaId>< ![CDATA[media_id] ]></MediaId>
      	</Voice>
      </xml>
      
      
    6. 回复视频消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>12345678</CreateTime>
      	<MsgType>< ![CDATA[video] ]></MsgType>	//消息类型为:video
      	<Video>
      		<MediaId>< ![CDATA[media_id] ]></MediaId>
      		<Title>< ![CDATA[title] ]></Title>	//视频的标题
      		<Description>< ![CDATA[description] ]></Description>	//视频的描述
      	</Video>
      </xml>
      
      
    7. 回复音乐消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>12345678</CreateTime>
      	<MsgType>< ![CDATA[music] ]></MsgType>
      	<Music>
      		<Title>< ![CDATA[TITLE] ]></Title>
      		<Description>< ![CDATA[DESCRIPTION] ]></Description>
      		<MusicUrl>< ![CDATA[MUSIC_Url] ]></MusicUrl>
      		<HQMusicUrl>< ![CDATA[HQ_MUSIC_Url] ]></HQMusicUrl>
      		<ThumbMediaId>< ![CDATA[media_id] ]></ThumbMediaId>
      	</Music>
      </xml>
      
      
    8. 回复图文消息

      XML格式数据:

      <xml>
      	<ToUserName>< ![CDATA[toUser] ]></ToUserName>
      	<FromUserName>< ![CDATA[fromUser] ]></FromUserName>
      	<CreateTime>12345678</CreateTime>
      	<MsgType>< ![CDATA[news] ]></MsgType>
      	<ArticleCount>2</ArticleCount>
      	<Articles>
      		<item>
      			<Title>< ![CDATA[title1] ]></Title>
      			<Description>< ![CDATA[description1] ]></Description>
      			<PicUrl>< ![CDATA[picurl] ]></PicUrl>
      			<Url>< ![CDATA[url] ]></Url>
      		</item>
      		<item>
      			<Title>< ![CDATA[title] ]></Title>
      			<Description>< ![CDATA[description] ]></Description>
      			<PicUrl>< ![CDATA[picurl] ]></PicUrl>
      			<Url>< ![CDATA[url] ]></Url>
      		</item>
      	</Articles>
      </xml>
      
      

    主动发送消息

    主动发送消息是通过模板消息实现的,模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。

    为了发送模板消息,需要首先设置公众号所属的行业,之后公众号则只能使用该行业内的模板来发送模板消息;设置行业可以通过微信公众平台后台来完成,每月可修改一次;也可以通过接口调用的方式来修改所属行业;

    定义模板消息时需要注意的地方:

    1. 模板消息调用时主要需要模板ID和模板中各参数的赋值内容;
    2. 模板中参数内容必须以".DATA"结尾,否则视为保留字;
    3. 模板保留符号"{{ }}";
    4. 详情见:获得已添加到微信公众号里的所有模板列表;

    设置所属行业:

    请求方式: POST
    请求地址:https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN
    POST数据格式:
    {"industry_id1":"1","industry_id2":"4"}
     
    
    

    获得模板ID:

    该过程可在公众号后台完成,同时提供接口:

    请求方式: POST
    请求地址:https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token=ACCESS_TOKEN
    POST数据格式:
    {"template_id_short":"TM00015"}
    正确返回消息说明:JSON
    {"errcode":0,"errmsg":"ok","template_id":"Doclyl5uP7Aciu-qZ7mJNPtWkbkYnWBWVja26EGbNyk"}
    错误返回消息说明:
    错误码通用。
    
    

    获取已添加到微信公众号里的所有模板列表:

    请求方式:GET
    请求地址:https//api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN
    返回结果说明:
    {    
     "template_list": [{
          "template_id": "iPk5sOIt5X_flOVKn5GrTFpncEYTojx6ddbt8WYoV5s",
          "title": "领取奖金提醒",
          "primary_industry": "IT科技",
          "deputy_industry": "互联网|电子商务",
          "content": "{ {result.DATA} }\n\n领奖金额:{ {withdrawMoney.DATA} }\n领奖  时间:{ {withdrawTime.DATA} }\n银行信息:{ {cardInfo.DATA} }\n到账时间:  { {arrivedTime.DATA} }\n{ {remark.DATA} }",
          "example": "您已提交领奖申请\n\n领奖金额:xxxx元\n领奖时间:2013-10-10 12:22:22\n银行信息:xx银行(尾号xxxx)\n到账时间:预计xxxxxxx\n\n预计将于xxxx到达您的银行卡"
       }]
    }
    
    

    删除模板:

    该过程可在公众号后台完成,同时提供接口:

    请求方式:POST
    请求地址:https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token=ACCESS_TOKEN
    POST数据格式:
    {"template_id" : "Dyvp3-Ff0cnail_CDSzk1fIc6-9lOkxsQE7exTJbwUE"}
    正确返回消息说明:JSON
    错误返回消息说明:
    {"errcode" : 0,"errmsg" : "ok"}
    错误码通用。
    
    

    发送模板消息:

    请求方式: POST
    请求地址:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
    POST数据格式:
    {
    	"touser":"OPENID",
    	"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
    	"url":"http://weixin.qq.com/download",  
         "miniprogram":{
         	"appid":"xiaochengxuappid12345",
         	"pagepath":"index?foo=bar"
         },
         "data":{
         	"first": {
         		"value":"恭喜你购买成功!",
         		"color":"#173177"
         	},
         	"keyword1":{
         		"value":"巧克力",
         		"color":"#173177"
         	},
         	"keyword2": {
            	"value":"39.8元",
            	"color":"#173177"
            },
            "keyword3": {
            	"value":"2014年9月22日",
            	"color":"#173177"
            },
            "remark":{
            	"value":"欢迎再次购买!",
            	"color":"#173177"
            }
        }
    }
    正确返回消息格式:
    {"errcode":0,"errmsg":"ok","msgid":200228332}
    
    

    事件推送:

    在模版消息发送任务完成后,微信系统会将是否送达成功作为通知,发送到响应系统。

    送达成功时,推送的XML如下:
    <xml>
    	<ToUserName>< ![CDATA[gh_7f083739789a] ]></ToUserName>
    	<FromUserName>< ![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8] ]></FromUserName>
    	<CreateTime>1395658920</CreateTime>
    	<MsgType>< ![CDATA[event] ]></MsgType>
    	<Event>< ![CDATA[TEMPLATESENDJOBFINISH] ]></Event>
    	<MsgID>200163836</MsgID>
    	<Status>< ![CDATA[success] ]></Status>
    </xml>
    
    没有送达时,推送的XML如下:
    <xml>
    	<ToUserName>< ![CDATA[gh_7f083739789a] ]></ToUserName>
    	<FromUserName>< ![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8] ]></FromUserName>
    	<CreateTime>1395658984</CreateTime>
    	<MsgType>< ![CDATA[event] ]></MsgType>
    	<Event>< ![CDATA[TEMPLATESENDJOBFINISH] ]></Event>
    	<MsgID>200163840</MsgID>
    	<Status>< ![CDATA[failed:user block] ]></Status>	//failed: system failed——其他原因;failed:user block——用户拒绝接收该公众号的消息
    </xml>
    
    

    消息加密

    公众号消息加解密是为了进一步加强公众号安全保障,提供的新机制。公众账号主动调用API的情况不受影响。只有被动回复用户的消息时,才需要进行消息加解密。消息加解密方式:

    1. 明文模式:维持现有模式,没有适配加解密新特性,消息体明文收发,默认设置为明文模式;
    2. 兼容模式:公众平台发送消息内容将同时包括明文和密文,消息包长度增加到原来的3倍左右;公众号回复明文或密文均可,不影响现有消息收发;开发者可在此模式下进行调试;
    3. 安全模式(推荐):公众平台发送消息体的内容只含有密文,公众账号回复的消息体也为密文,建议开发者在调试成功后使用此模式收发消息

    启用加解密功能(即选择兼容模式或安全模式)后,微信系统在向响应系统推送消息时,URL将新增加两个参数(加密类型和消息体签名),并以此来体现新功能。加密算法采用AES。具体方案见该模块代码实现。

    获取微信系统服务器地址

    出于安全等考虑,如需要获知微信服务器的IP地址列表,可以通过该接口获得微信服务器IP地址列表或者IP网段信息。

    请求方式: GET
    请求地址:https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN
    返回消息:JSON格式
    {"ip_list":["127.0.0.1","127.0.0.2","101.226.103.0/25"]}
    
    

    客服消息及管理

    当用户和公众号产生特定动作的交互时(具体动作列表请见下方说明),微信系统将会把消息数据推送给响应系统,响应系统可以在一段时间内(目前修改为48小时)调用客服接口,通过POST一个JSON数据包来发送消息给普通用户。此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务。

    目前允许的动作列表如下(公众平台会根据运营情况更新该列表,不同动作触发后,允许的客服接口下发消息条数不同,下发条数达到上限后,会遇到错误返回码,具体请见返回码说明页):

    1. 用户发送信息
    2. 点击自定义菜单(仅有点击推事件、扫码推事件、扫码推事件且弹出“消息接收中”提示框这3种菜单类型将会触发客服接口)
    3. 关注公众号
    4. 扫描二维码
    5. 支付成功
    6. 用户维权

    客服账号管理:

    请注意,必须先在公众平台官网为公众号设置微信号后才能使用该能力。

    1. 添加客服账号

      请求方式: POST
      请求地址:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN
      POST数据示例如下:
      {
           "kf_account" : "test1@test",
           "nickname" : "客服1",
           "password" : "pswmd5",
      }
      返回说明(正确时的JSON返回结果):
      {"errcode" : 0,"errmsg" : "ok"}
      返回错误码是通用的
      
      
    2. 删除客服账号

      请求方式: GET
      请求地址:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN
      POST数据示例如下:
      {
           "kf_account" : "test1@test",
           "nickname" : "客服1",
           "password" : "pswmd5",
      }
      返回说明(正确时的JSON返回结果):
      {"errcode" : 0,"errmsg" : "ok"}
      返回错误码是通用的
      
      
    3. 修改客服账号

      请求方式: POST
      请求地址:https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN
      POST数据示例如下:
      {
           "kf_account" : "test1@test",
           "nickname" : "客服1",
           "password" : "pswmd5",
      }
      返回说明(正确时的JSON返回结果):
      {"errcode" : 0,"errmsg" : "ok"}
      返回错误码是通用的
      
      

    设置客服账号头像

    开发者可调用该接口来上传图片作为客服人员的头像,头像图片文件必须是jpg格式,推荐使用640*640大小的图片以达到最佳效果。该接口调用请求如下:

    请求方式: POST/FORM
    请求地址:http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT
    这里需要注意的是,官方文档中提到的是使用curl工具POST一个多媒体文件;这里我想只要通过代码将文件提交到上述地址就OK,因为curl是一个命令行工具,网上有在php中使用curl的,但是对Java的支持很惨淡,这一部分详见代码验证及图示。
    
    

    获取所有客服账号

    请求方式: GET
    请求地址:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN
    返回数据:
    {
        "kf_list": [
            {
                "kf_account": "test1@test", 
                "kf_nick": "ntest1", 
                "kf_id": "1001"	//这里需要注意的是,kf_id是第一次在这里出现,创建的时候并没有返回该字段;
                "kf_headimgurl": " http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjfUS8Ym0GSaLic0FD3vN0V8PILcibEGb2fPfEOmw/0"
            }, 
            {
                "kf_account": "test2@test", 
                "kf_nick": "ntest2", 
                "kf_id": "1002"
                "kf_headimgurl": " http://mmbiz.qpic.cn/mmbiz/4whpV1VZl2iccsvYbHvnphkyGtnvjfUS8Ym0GSaLic0FD3vN0V8PILcibEGb2fPfEOmw /0"
            }
        ]
    }
    
    

    客服接口-发送消息

    请求方式: POST
    请求地址:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
    各消息类型所需的JSON数据包如下:
    文本消息:
    {
        "touser":"OPENID",
        "msgtype":"text",
        "text":
        {
             "content":"Hello World"
        }
    }
    发送文本消息时,文本内容中可以携带跳转小程序的文字链:<a href="http://www.qq.com" data-miniprogram-appid="appid" data-miniprogram-path="pages/index/index">点击跳小程序</a>
    参数说明:
    1.data-miniprogram-appid 项,填写小程序appid,则表示该链接跳小程序;
    2.data-miniprogram-path项,填写小程序路径,路径与app.json中保持一致,可带参数;
    3.对于不支持data-miniprogram-appid 项的客户端版本,如果有herf项,则仍然保持跳href中的网页链接;
    
    注意,data-miniprogram-appid对应的小程序必须与公众号有绑定关系。
    
    发送图片消息:
    {
        "touser":"OPENID",
        "msgtype":"image",
        "image":
        {
          "media_id":"MEDIA_ID"
        }
    }
    
    发送语音消息:
    {
        "touser":"OPENID",
        "msgtype":"voice",
        "voice":
        {
          "media_id":"MEDIA_ID"
        }
    }
    
    发送视频消息:
    {
        "touser":"OPENID",
        "msgtype":"video",
        "video":
        {
          "media_id":"MEDIA_ID",
          "thumb_media_id":"MEDIA_ID",
          "title":"TITLE",
          "description":"DESCRIPTION"
        }
    }
    
    发送音乐消息:
    {
        "touser":"OPENID",
        "msgtype":"music",
        "music":
        {
          "title":"MUSIC_TITLE",
          "description":"MUSIC_DESCRIPTION",
          "musicurl":"MUSIC_URL",
          "hqmusicurl":"HQ_MUSIC_URL",
          "thumb_media_id":"THUMB_MEDIA_ID" 
        }
    }
    
    发送图文消息(点击跳转到外链) 图文消息条数限制在8条以内,注意,如果图文数超过8,则将会无响应。
    {
        "touser":"OPENID",
        "msgtype":"news",
        "news":{
            "articles": [
             {
                 "title":"Happy Day",
                 "description":"Is Really A Happy Day",
                 "url":"URL",
                 "picurl":"PIC_URL"
             },
             {
                 "title":"Happy Day",
                 "description":"Is Really A Happy Day",
                 "url":"URL",
                 "picurl":"PIC_URL"
             }
             ]
        }
    }
    
    发送图文消息:
    {
        "touser":"OPENID",
        "msgtype":"mpnews",
        "mpnews":
        {
             "media_id":"MEDIA_ID"
        }
    }
    
    发送卡券:
    {
      "touser":"OPENID", 
      "msgtype":"wxcard",
      "wxcard":{              
           "card_id":"123dsdajkasd231jhksad"        
       },
    }
    发送小程序卡片:
    {
        "touser":"OPENID",
        "msgtype":"miniprogrampage",
        "miniprogrampage":
        {
            "title":"title",
            "appid":"appid",
            "pagepath":"pagepath",
            "thumb_media_id":"thumb_media_id"
        }
    }
    
    

    微信网页开发

    网页授权获取用户基本信息

    用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

    这里需要注意的是同用户管理中的获取用户基本信息的区别:用户管理是在响应系统的完成的;而用户授权是在业务系统中完成的;两者工作的环境也是不同的,所以请求接口是不同的:第三方授权是在公众号内通过微信客户端进入网页时发生,而用户管理是公众号内部的管理行为;

    使用流程:

    1. 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。
    2. 授权回调域名配置规范为全域名。配置后,该域名下的所有网页都可以进行OAuth2.0鉴权。

    两种请求模式:

    1. 以snsapi_base为scope发起的网页授权,用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务系统的入口);

    2. 以snsapi_userinfo为scope发起的网页授权,用来获取用户的基本信息的。这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

      注意:用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。该接口以及包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

    在OAuth2.0用户授权机制下,存在一个反应用户是否同意授权的标记,该标记在OAuth2.0的环境下称为:access_token。这和响应系统用于请求微信接口时使用的access_token是不一样的,但是它们的性质是相同的;

    特殊环境下的静默授权:

    1. 上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;
    2. 对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。

    具体而言,网页授权流程分为四步:

    1. 引导用户进入授权页面同意授权,获取code

       请求地址:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
       参数说明:APPID/REDIRECT_URI/SCOPE/StATE等为变量,其余为常量;参数顺序不可变;
      
      

      如果用户同意授权,页面将跳转至 REDIRECT_URI/?code=CODE&state=STATE。 code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

    2. 通过code换取网页授权access_token(与基础支持中的access_token不同)

      请求地址:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
      参数说明:APPID/SECRET/CODE等为变量,其余为常量;参数顺序可变;
      返回数据:JSON
      { 
      	"access_token":"ACCESS_TOKEN",//	网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
      	"expires_in":7200,
      	"refresh_token":"REFRESH_TOKEN",//用户刷新access_token
      	"openid":"OPENID",
      	"scope":"SCOPE"
      }
      
      

      注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。即当前请求是可以通过客户端进行的。

      另外,此时已经获得open id,即如果不需要使用union id 的话,网页授权到此结束;

    3. 如果需要,开发者可以刷新网页授权access_token,避免过期

      请求地址:https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
      参数说明:APPID/REFRESH_TOKEN为变量,REFRESH_TOKEN即为第二步获得数据
      
      
    4. 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

      请求方式:GET
      请求地址:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
      参数说明:ACCESS_TOKEN即为第二步获得网页授权数据;OPENID一样;
      返回数据说明:JSON
      {
      	"openid":" OPENID",
      	" nickname": NICKNAME,
      	"sex":"1",
      	"province":"PROVINCE"
      	"city":"CITY",
      	"country":"COUNTRY",
      	"headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
      	"privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
      	"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
      }
      
      

      整体来看,App Secret作为秘密数据,其获取也应该通过业务系统的验证以检查正在请求App Secret的网页是不是友军;

    微信JS-SDK的功能

    JS-SDK为第三方网页在微信客户端内使用本地资源提供JS封装的接口;这里不涉及具体API的使用,仅介绍JS-SDK的引入、调用接口权限的验证配置。

    引入JS文件:

    同普通JS文件。

    权限验证:

    所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用,同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复。

    wx.config({
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '', // 必填,公众号的唯一标识
        timestamp: , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,签名算法见后专门模块
        jsApiList: [] // 必填,需要使用的JS接口列表
    });
    
    wx.ready(function(){
        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
    });
    
    wx.error(function(res){
        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
    });
    
    

    所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:

    1. success:执行成功时的回调函数;
    2. fail:调用接口失败时执行的回调函数;
    3. complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
    4. cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
    5. trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。

    以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:

    1. 调用成功时:“xxx:ok” ,其中xxx为调用的接口名
    2. 用户取消时:“xxx:cancel”,其中xxx为调用的接口名
    3. 调用失败时:其值为具体错误信息

    签名算法

    生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。 开发者必须在自己的服务全局缓存jsapi_ticket 。

    生成签名的流程:

    1. 获得access_token:具体见上文目录;

    2. 用第一步拿到的access_token获得jsapi_ticket

      请求方法:GET
      请求地址:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
      返回数据说明:JSON
      {
      	"errcode":0,
      	"errmsg":"ok",
      	"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
      	"expires_in":7200
      }
      
      
    3. 签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。 sha1加密后的结果即为signature;

    展开全文
  • 那一抹淡淡的忧伤—–微信开发基础 2. 用纯js是不可能用纯js了,这辈子都不用纯js了 —– 微信JSSDK开发以及问题解答 3. 要你命3000 —— 微信支付开发系列问题解决导语 微信JS-SDK是微信公众平台面向网页...

    目录 List

    1. 那一抹淡淡的忧伤—–微信开发基础
    2. 用纯js是不可能用纯js了,这辈子都不用纯js了 —– 微信JSSDK开发以及问题解答
    3. 要你命3000 —— 微信支付开发系列问题解决

    导语

    微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
    通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
    本文重点讲最新的JSSDK版本1.2,关于1.2版本的说明可以参考这里
    尤其注意从1.2版本开始微信针对IOS系统WKWebview内核做出了重大改变,所以广大开发者当以最新JSSDK1.2版本为开发源码,否则可能造成IOS部分功能无效


    开发前准备

    步骤一:绑定域名

    先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
    备注:登录后可在“开发者中心”查看对应的接口权限。


    步骤二:引入JS文件
    在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js


    步骤三:通过config接口注入权限验证配置

    wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: ”, // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: ”, // 必填,生成签名的随机串
    signature: ”,// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 });

    直接用代码演示

    //加载微信JSSDK config
    wx.config({
            debug: false,
            appId: 'APPID',
            timestamp: time,
            nonceStr: 'NONCESTR',
            signature: 'SIGNATURE',
            jsApiList: [
                'checkJsApi',
                'onMenuShareTimeline',
                'onMenuShareAppMessage',
                'onMenuShareQQ',
                'onMenuShareWeibo',
                'onMenuShareQZone',
                'hideMenuItems',
                'showMenuItems',
                'hideAllNonBaseMenuItem',
                'showAllNonBaseMenuItem',
                'translateVoice',
                'startRecord',
                'stopRecord',
                'onVoiceRecordEnd',
                'playVoice',
                'onVoicePlayEnd',
                'pauseVoice',
                'stopVoice',
                'uploadVoice',
                'downloadVoice',
                'chooseImage',
                'previewImage',
                'uploadImage',
                'downloadImage',
                'getNetworkType',
                'openLocation',
                'getLocation',
                'hideOptionMenu',
                'showOptionMenu',
                'closeWindow',
                'scanQRCode',
                'chooseWXPay',
                'openProductSpecificView',
                'addCard',
                'chooseCard',
                'openCard',
            ]
        });
        //JSSDK错误报告
        wx.error(function(res){
            console.log(location.href.split('#')[0]);
        });

    config里面所涉及到的参数我们通过JSSDK官方demo来提供数据,我对官方的demo进行了再次封装,直接在这里贴出来:

    class Jssdk{
        private $appId;
        private $appSecret;
    
        public function __construct($appId, $appSecret) {
            $this->appId = $appId;
            $this->appSecret = $appSecret;
        }
    
        public function getSignPackage() {
            $jsapiTicket = $this->getJsApiTicket();
    
            // 注意 URL 一定要动态获取,不能 hardcode.
            $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
            $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
    
            $timestamp = time();
            $nonceStr = $this->createNonceStr();
    
            // 这里参数的顺序要按照 key 值 ASCII 码升序排序
            $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
    
            $signature = sha1($string);
    
            $signPackage = array(
                "appId"     => $this->appId,
                "nonceStr"  => $nonceStr,
                "timestamp" => $timestamp,
                "url"       => $url,
                "signature" => $signature,
                "rawString" => $string
            );
            return $signPackage;
        }
    
        private function createNonceStr($length = 16) {
            $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            $str = "";
            for ($i = 0; $i < $length; $i++) {
                $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
            }
            return $str;
        }
    
        private function getJsApiTicket() {
            // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
            $data = json_decode($this->get_php_file("jsapi_ticket.php"));
            if ($data->expire_time < time()) {
                $accessToken = $this->getAccessToken();
                // 如果是企业号用以下 URL 获取 ticket
                // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken";
                $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
                $res = json_decode($this->httpGet($url));
                $ticket = $res->ticket;
                if ($ticket) {
                    $data->expire_time = time() + 7000;
                    $data->jsapi_ticket = $ticket;
                    $this->set_php_file("jsapi_ticket.php", json_encode($data));
                }
            } else {
                $ticket = $data->jsapi_ticket;
            }
    
            return $ticket;
        }
    
        private function getAccessToken() {
            // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
            $data = json_decode($this->get_php_file("access_token.php"));
            if ($data->expire_time < time()) {
                // 如果是企业号用以下URL获取access_token
                // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
                $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
                $res = json_decode($this->httpGet($url));
                $access_token = $res->access_token;
                if ($access_token) {
                    $data->expire_time = time() + 7000;
                    $data->access_token = $access_token;
                    $this->set_php_file("access_token.php", json_encode($data));
                }
            } else {
                $access_token = $data->access_token;
            }
            return $access_token;
        }
    
        private function httpGet($url) {
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_TIMEOUT, 500);
            // 为保证第三方服务器与微信服务器之间数据传输的安全性,所有微信接口采用https方式调用,必须使用下面2行代码打开ssl安全校验。
            // 如果在部署过程中代码在此处验证失败,请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);
            curl_setopt($curl, CURLOPT_URL, $url);
    
            $res = curl_exec($curl);
            curl_close($curl);
    
            return $res;
        }
    
        private function get_php_file($filename) {
            return trim(substr(file_get_contents($filename), 15));
        }
        private function set_php_file($filename, $content) {
            $fp = fopen($filename, "w");
            fwrite($fp, "<?php exit();?>" . $content);
            fclose($fp);
        }
    }

    务必先拿到数据后注册给JSconfig,否则微信JSSDK是无效的

    等你一切准备得当的时候请打开网页调试里的console来查看效果

    正确调用的情况:
    这里写图片描述

    到这里所有的准备工作就完成了


    图片选择

    调用JSSDK的图片接口

    wx.chooseImage({
                count: 1,
                success: function (res) {
                    //选择图片成功后
                    images.localId = res.localIds;//安卓可以直接使用localIds 预览图片
                    if(res.localIds.indexOf("wxlocalresource") != -1){
                        res.localIds = res.localIds.replace("wxlocalresource", "wxLocalResource");
                    }
                  )};  
                   //以下的适配IOS
                   //先使用阅览接口(适配IOSWKWebview版本)
                        wx.getLocalImgData({
                            localId: images.localId[i], // 图片的localID
                            success: function (res) {
                                var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
                                $("#chooseImage").parent().before("<li class='upload_action'><img src='"+localData+"'/></li>");
                            }
    
                        });

    图片上传

    直接采用递归方法来上传多张图片

    function upload() {
        wx.uploadImage({
                            localId: images.localId[i].toString(),
                            isShowProgressTips: 1, // 默认为1,显示进度提示
                            success: function (res) {
                                i++;
                                var media_id = res.serverId;
    
                                images.serverId.push(res.serverId);//上传到了服务器的ID
    
                                if (i < length) {
                                    upload();
                                }
                            },
                            fail: function (res) {
                                alert(JSON.stringify(res));
                            }
                        });
    
    }

    将上传到服务器的图片下载到本地项目

    public function getmedia(){
            $media_id = get.sevrid;//前端返回的上传后的媒体id
            $weixin = new Weixinpay(); //实例化微信类
            $access_token = $weixin->getToken(); //获取token
    
            $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=".$access_token."&media_id=".$media_id;
            //获取微信“获取临时素材”接口返回来的内容(即刚上传的图片)
            $a = file_get_contents($url);
            //echo $a;
            $filename = date('YmdHis').rand(1000,9999).'.jpg';
            //以读写方式打开一个文件,若没有,则自动创建
            $resource = fopen($_SERVER["DOCUMENT_ROOT"].'/Uploads/User_img/'.$filename, 'w+');
            //将图片内容写入上述新建的文件
            fwrite($resource, $a);
            //关闭资源
            fclose($resource);
            echo '/Car/Common/Uploads/User_img/'.$filename;
        }

    通过以上方法我们能达到 用户用微信自带的上传图片系统上传到我们自己服务器上


    完整代码

    var images = {
            localId: [],
            serverId: []
        };
        choose.onclick = function(){
            wx.chooseImage({
                count: 1,
                success: function (res) {
                    //选择图片成功后
                    images.localId = res.localIds;
                    if(res.localIds.indexOf("wxlocalresource") != -1){
                        res.localIds = res.localIds.replace("wxlocalresource", "wxLocalResource");
                    }
                    var i = 0, length = images.localId.length;
    
                    function upload() {
                        //先使用阅览接口(适配IOSWKWebview版本)
                        wx.getLocalImgData({
                            localId: images.localId[i], // 图片的localID
                            success: function (res) {
                                var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
                                $("#chooseImage").parent().before("<li class='upload_action'><img src='"+localData+"'/></li>");
                            }
    
                        });
                        wx.uploadImage({
                            localId: images.localId[i].toString(),
                            isShowProgressTips: 1, // 默认为1,显示进度提示
                            success: function (res) {
                                i++;
                                var media_id = res.serverId;
    
                                images.serverId.push(res.serverId);
                                $.ajax({
                                    url:'getmedia.php',
                                    data:{'mid':res.serverId.toString()},
                                    type:'get',
                                    success:function(res){
                                        $("input[name='qu_img']").val(res);
                                        $("#chooseImage").hide();
                                    }
    
                                });
                                if (i < length) {
                                    upload();
                                }
                            },
                            fail: function (res) {
                                alert(JSON.stringify(res));
                            }
                        });
    
                    }
                    upload();
                }
            });
    
        }

    演示

    这里写图片描述

    讨论和询问可以加QQ

    1577452412

    展开全文
  • 微信小程序开发文档:https://developers.weixin.qq.com/miniprogram/dev/component/camera.html 首先,需要用户授权摄像头权限,这一步是必须的 具体步骤: 获取用户当前授权状态,看是否已经授权,如果已经授权...

    微信小程序开发文档:https://developers.weixin.qq.com/miniprogram/dev/component/camera.html
    首先,需要用户授权摄像头权限,这一步是必须的
    具体步骤:

    1. 获取用户当前授权状态,看是否已经授权,如果已经授权直接显示摄像头
    2. 如果用户还没有授权,则调起授权弹框,用户允许授权则显示摄像头
    3. 如果用户不允许,则提示用户去设置页面打开摄像头权限

    用户授权之后,就可以进行拍摄了,微信的camera组件无法显示为圆形,我这里是用一张图片遮盖了
    上代码:
    wxml:

    <view class='camera'>
      <image src="/images/border.png" mode="widthFix"></image>
      <camera wx:if="{{isAuth}}" device-position="back" flash="off" binderror="error"></camera>
    </view>
    <button class="takePhoto" type="primary" bindtap="takePhoto">拍照</button>
    

    wxss:

    .camera {
      width: 430rpx;
      height: 430rpx;
      border-radius: 50%;
      margin: 20px auto 0;
      position: relative;
    }
    
    .camera image {
      position: absolute;
      width: 100%;
      height: 100%;
      z-index: 10;
    }
    
    .camera camera {
      width: 428rpx;
      height: 428rpx;
    }
    
    button.takePhoto:not([size='mini']) {
      position: fixed;
      bottom: 0;
      left: 0;
      width: 100vw;
      height: 90rpx;
      border-radius: 0;
    }
    
    

    js:

    Page({
      data: {
        isAuth: false,
        src: ''
      },
      onLoad() {
        const _this = this
        wx.getSetting({
          success: res => {
            if (res.authSetting['scope.camera']) {
              // 用户已经授权
              _this.setData({
                isAuth: true
              })
            } else {
              // 用户还没有授权,向用户发起授权请求
              wx.authorize({
                scope: 'scope.camera',
                success() { // 用户同意授权
                  _this.setData({
                    isAuth: true
                  })
                },
                fail() { // 用户不同意授权
                  _this.openSetting().then(res => {
                    _this.setData({
                      isAuth: true
                    })
                  })
                }
              })
            }
          },
          fail: res => {
            console.log('获取用户授权信息失败')
          }
        })
      },
    
      // 打开授权设置界面
      openSetting() {
        const _this = this
        let promise = new Promise((resolve, reject) => {
          wx.showModal({
            title: '授权',
            content: '请先授权获取摄像头权限',
            success(res) {
              if (res.confirm) {
                wx.openSetting({
                  success(res) {
                    if (res.authSetting['scope.camera']) { // 用户打开了授权开关
                      resolve(true)
                    } else { // 用户没有打开授权开关, 继续打开设置页面
                      _this.openSetting().then(res => {
                        resolve(true)
                      })
                    }
                  },
                  fail(res) {
                    console.log(res)
                  }
                })
              } else if (res.cancel) {
                _this.openSetting().then(res => {
                  resolve(true)
                })
              }
            }
          })
        })
        return promise;
      },
    
      takePhoto() {
        const ctx = wx.createCameraContext()
        ctx.takePhoto({
          quality: 'high',
          success: (res) => {
            this.setData({
              src: res.tempImagePath
            })
            wx.previewImage({
              current: res.tempImagePath, // 当前显示图片的http链接
              urls: [res.tempImagePath] // 需要预览的图片http链接列表
            })
          }
        })
      }
    })
    
    展开全文
  • 因为小小创客是属于移动端微信开发的一部分,如果不能很好的兼容苹果,android,一切都是徒劳的。为了解决这一问题,我们团队从原来的使用上传转向调用微信现成接口实现上传。 微信接口上传的思路为:移动端调用微信...

    最开始实现图片上传用的是<input type=”file”>插件来实现的。用这种方法在pc以及苹果的平台上可以很好的兼容,但是在android上各种机型的兼容性各有差异,总体上看兼容性很差。因为小小创客是属于移动端微信开发的一部分,如果不能很好的兼容苹果,android,一切都是徒劳的。为了解决这一问题,我们团队从原来的使用<input type=”file”>上传转向调用微信现成接口实现上传。

    微信接口上传的思路为:移动端调用微信接口拍照或者从相册选择图片——>调用微信上传接口上传图片到微信服务器(图片只能在微信服务器保存3天)——>利用图片下载接口将上传到微信服务器上的图片下载到自己服务器,这样就完成了图片的上传。

    微信为开发者提供了一套非常完备的接口:

           

    上图是微信开发js-sdk文档中的部分接口,其中就有专门的图像接口,而且功能十分完备有拍照、从相册选择图片,预览图片,上传图片,下载图片的接口。

    实现微信接口上传的第一步便是要获得接口权限。

    1.  接口权限获得方法

    步骤一:绑定域名

    先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。如果你使用了支付类接口,请确保支付目录在该安全域名下,否则将无法完成支付。

    备注:登录后可在“开发者中心”查看对应的接口权限。

    步骤二:引入JS文件

     

    在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js

    请注意,如果你的页面启用了https,务必引入https://res.wx.qq.com/open/js/jweixin-1.0.0.js ,否则将无法在iOS9.0以上系统中成功使用JSSDK

    如需使用摇一摇周边功能,请引入 jweixin-1.1.0.js

    备注:支持使用AMD/CMD 标准模块加载方法加载

    步骤三:通过config接口注入权限验证配置

    所有需要使用JS-SDK的页面必须先注入配置信息

    wx.config({

        debug: true, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

        appId: '', //必填,公众号的唯一标识

        timestamp: , //必填,生成签名的时间戳

        nonceStr: '',//必填,生成签名的随机串

        signature:'',//必填,签名,见附录1

        jsApiList: []//必填,需要使用的JS接口列表,所有JS接口列表见附录2

    });

          其中最重要的一步就是签名生成。签名生成步骤:使用AppID和AppSecret调用本接口来获取access_token,然后用拿到的access_token采用http GET方式请求获得jsapi_ticket

    成功返回如下JSON:

    {
    "errcode":0,
    "errmsg":"ok",
    "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
    "expires_in":7200
    }

         获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。

         签名生成算法:

           (签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。)

            步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1

            步骤2.string1进行sha1签名,得到signature

    2.调用接口实现上传功能

      当第一步中config接口注入权限验证配置完成后便能使用微信提供的接口来实现上传功能了。

      wx.ready(function(){
     
        // 在这里面调用微信接口
       debug: false,
          appId: '',
          timestamp:’’,
          nonceStr: '',
          signature: '',
          jsApiList: [
                 //这里是所要调用的接口名称,因为要实现上传,所以调用图像的四个接口
                'chooseImage',
                'previewImage',
                'uploadImage',
                'downloadImage', 
           ]
        //在这里写具体调用接口的函数
    });


    完成了这步,我们的图片已经上传到微信的服务器上了,接下来我们要做的是将图片从微信服务器上下载到自己的服务器上。

    3.下载上传到微信服务器上的图片到自己服务器

        每张图片上传到微信服务器上会自动返回用户一个media_id,用户可以根据所返回media_id来下载图片到自己的服务器上 方法为:

       

    $url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=".$token."&media_id=".$media_id."";
    function http_get_data($url) {  
    	  
    	$ch = curl_init ();  
    	curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, 'GET' );  
    	curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, false );  
    	curl_setopt ( $ch, CURLOPT_URL, $url );  
    	ob_start ();  
    	curl_exec ( $ch );  
    	$return_content = ob_get_contents ();  
    	ob_end_clean ();  
    	  
    	$return_code = curl_getinfo ( $ch, CURLINFO_HTTP_CODE );  
    	return $return_content;  
    } 
    $return_content = http_get_data($url);  
    $return_content中就是图片,然后通过file_put_contents($file_url,$return_content)将图片放到你想要的地方

    这样就完成了图片的上传

     四个接口的使用方法:

           拍照或从手机相册中选图接口

                wx.chooseImage({
        count: 1, // 默认9
        sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
        sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
        success: function (res) {
            var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
        }
    });


    预览图片接口

    wx.previewImage({
        current: '', // 当前显示图片的http链接
        urls: [] // 需要预览的图片http链接列表
    });


    上传图片接口

    wx.uploadImage({
        localId: '', // 需要上传的图片的本地ID,由chooseImage接口获得
        isShowProgressTips: 1, // 默认为1,显示进度提示
        success: function (res) {
            var serverId = res.serverId; // 返回图片的服务器端ID
        }
    });


     

    下载图片接口

    wx.downloadImage({
        serverId: '', // 需要下载的图片的服务器端ID,由uploadImage接口获得
        isShowProgressTips: 1, // 默认为1,显示进度提示
        success: function (res) {
            var localId = res.localId; // 返回图片下载后的本地ID
        }
    });



    展开全文
  • 由于项目需要,前端开发使用的是angular4.0技术,微信开发工具v1.02。前端开发公众号最坑的就是微信JSSDK的使用,前面坑路漫漫,我还得一步一步趟过去。JSSDK使用报错1. config:invalid signature一直报这个错误建议...
  • 在某些安卓手机上无法使用上传图片功能,你怎么点都问题。 微信并没有对type=file做任何特殊处理。但是在Android 4.4.1/4.4.2系统中,这的确没法使用。其它版本中如果客户端如果需要对H5的 标签做支持,即在H5支持...
  • 微信公众平台开发

    2014-01-18 11:18:26
    微信公众平台开发(80) 上传下载多媒体文件 摘要: 微信公众账号在回复图片、语音、视频的时候,将使用media_id来调用相关文件,很多朋友咨询这个如何开发实现。本文将介绍在微信公众平台开发过程中,如何上传...
  • 微信公众平台开发模式 微信 公众平台 图片识别 人脸识别  作者:方倍工作室[QQ:1354386063] 原文:http://www.cnblogs.com/txw1958/archive/2013/02/05/weixin-if20-face-recognition.html    一、前言 ...
  • 微信公众账号开发

    2014-02-27 15:00:52
    微信开发 微信公众平台开发(82) 天气预报 摘要: 在这篇教程中,我们将介绍如何在微信公众平台上开发天气预报功能。我们将使用中国天气网的气象数据接口来获取天气信息。这篇教程将介绍以下内容:获取...
  • 首先请阅读微信JS-SDK说明文档,了解微信JS的相关说明。 根据官方的使用步骤,关键的有以下几步绑定域名(很关键)引入JS文件(很简单)通过config接口注入权限验证配置(很重要)通过ready接口处理成功验证(还...
  • 最近开发人脸识别登录系统,需要在微信中调用相机实现拍照上传功能,最开始使用H5&lt;input&gt;控件实现,但界面不太美观,H5的getMedia只兼容几个主流的浏览器,所以就研究了微信相机组件的开发,过程经历...
  • 微信小程序中要保存图片到本地相册,需要获取相册权限。总之整个功能实现下来需要如下几个小程序的API:wx.getSetting,wx.authorize,wx.openSetting,wx.downloadFile,wx.saveImageToPhotosAlbum 但是在...
  • 有个需求要在微信企业号里面做开发,有个功能是选择图片,使用input标签肯定是不管用了,Android手机上不能多选,所以使用了微信的JS-SDK提供的相关API,这个地方真的是有坑,记录一下。按照文档直接引入js文件即可...
  • 微信小程序开发基础知识总结 微信小程序在无论在功能、文档及相关支持方面,都是优于前面几种微信账号类型,它提供了很多原生程序才有的接口,使得我们的小程序在很多方面突破H5页面应用的限制,更加接近原生...
  • 微信公众平台快速开发框架 For Core 2.0 beta –JCSoft.WX.Core 5.2.0 beta发布 摘要: 自从NetCore发布以后,一直想把JCWX更新到Core版本,从6月开始就着手更新了,大概花了一个月时间,更新到了Net Core 1.1版本...
  • 简介为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程。用产品的话说就是:云信 IM 小程序 SDK 的能力演示提供开发者小程序开发参考换句话说就是在微信里面通过我们云信的...
  • 一、相关SDK ...小程序基础库提供丰富的微信原生API,可以方便的调起微信提供的能力,如获取用户信息,本地存储,支付功能等。 JS-SDK 小程序内嵌的h5页面的开发,通过调用微信提供的 JS-SDK(htt...
  • ...功能描述 ... 实现思路 消息类型为文本样式 ...代码开发 为便于扩展,将消息处理的方法写到服务层,新建收发信息的dto 一、便于扩展为不同类型的消息,修改收发消息的封装,改用dto模式 基础消息实体类 @Da...
1 2 3 4 5 ... 20
收藏数 722
精华内容 288
关键字:

微信开发获取手机拍照点击没反应