2018-12-17 23:17:04 m0_37739193 阅读数 268
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27803 人正在学习 去看看 秦子恒
开发背景:

需要用户通过二维码关注公司的公众号以后获得openID和用户ID(userid)关联,然后根据需求给用户发送预警消息
注意:在微信公众号后台,设置了服务器配置URL并启用后,会导致微信后台设置的回复规则,以及底部菜单都全部失效!直接清空了!因为这时候微信已经把公众号消息和事件推送给开发者配置的url中,让开发者进行处理了。

开发准备:

1.可以先阅读下官方文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140454
2.需要在微信后台配置服务器配置,微信会在你修改这个配置的时候给你填写的URL(一定要外网能访问的到)发送数据,需要提前把项目上传服务器
3.这个东西配置好之后可以点击开启了,开启后微信会把所有用户发送的信息,给转发给填写的url上。开发者必须在五秒内回复微信服务器。否则微信会进行三次重试,重试均失败后,会给用户提示“该公众号暂时无法提供服务,请稍后再试”。如果你的填写url的程序因某种原因挂掉的话根本就无法请求到的话会给用户提示“该公众号提供的服务出现故障,请稍后再试”
注意:
1.填写URL下方有这么一句话“必须以http://或https://开头,分别支持80端口和443端口。”。如果填ip的话必须得是这两个端口,要不然就会提示“请输入合法的URL”(如:“ http://36.105.244.13/openwx/eventpush ”等同于 “ http://36.105.244.13:80/openwx/eventpush ”、“ http://36.105.244.13:443/openwx/eventpush ”(不知道这个为啥不合法了就)),如果你的程序是其他端口如8081的话,可以用域名(该域名代理到程序ip的8081端口)的方式填写(如:“ http://xiaoqiang.tunnel.qydev.com/openwx/eventpush ”)
2.如果无法访问到你的url会报“请求URL超时”,如果填写的token和你程序里的不一致的话会报“token验证失败”,也不知道啥情况下会报“系统发生错误,请稍后重试”。(垃圾微信有时候好像报的不准)
3.填写的url接口还必须同时支持get(修改配置填写好url后点“提交”按钮后微信会向你的这个url发送get请求)和post(事件推送如取消/关注公众号事件、扫描带参数的二维码)请求

说明:

在这里插入图片描述
1.在我们首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),通过对签名(即signature)的效验,来判断此条消息的真实性。此后,每次接收用户消息的时候,微信也都会带上这三个参数(signature、timestamp、nonce)访问我们设置的URL,和第一次相同我们依然需要通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。
signature:微信加密签名,signature结合了我们自己填写的token参数和请求中的timestamp参数、nonce参数。通过检验signature对请求进行校验(代码在下面提供)。若确认此次GET请求来自微信服务器,则原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
timestamp:时间戳
nonce:随机数
echostr:随机字符串
2.令牌Token
Token:可由我们自行定义,主要作用是参与生成签名,与微信请求的签名进行比较
3.消息加解密密钥EncodingAESKey
EncodingAESKey:可由我们自行定义或随机生成,主要作用是参与接收和推送给公众平台消息的加解密
4.消息加解密方式
此处我选择的是明文模式,大家可以根据自己的具体需求,选择相应的模式
 

代码实例一:
package com.imooc.controller;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.imooc.conf.DefaultExpireKey;
import com.imooc.conf.ExpireKey;
import com.imooc.conf.SignatureUtil;
import com.imooc.conf.XMLMessage;
import com.imooc.conf.XMLTextMessage;

/**
 * 消息接收控制层
 * @author YaoShiHang
 * @Date 15:15 2017-10-16
 */
@Controller
//或者@RestController
public class WxqrcodeController {

    private final String TOKEN="weixin4";                      //开发者设置的token
    private Logger loger = Logger.getLogger(getClass());
    //重复通知过滤
    private static ExpireKey expireKey = new DefaultExpireKey();


    //微信推送事件 url
    @RequestMapping("/openwx/getticket")
    public void getTicket(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ServletOutputStream outputStream = response.getOutputStream(); 
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");

        //首次请求申请验证,返回echostr
        if(echostr!=null){
            outputStreamWrite(outputStream,echostr);
            return;
        }
        System.out.println("signature--->"+signature);

        // 验证请求签名,要不然不知道是不是你本人操作后向你程序发送的请求,无法保证安全性
        if(!signature.equals(SignatureUtil.generateEventMessageSignature(TOKEN,timestamp,nonce))){
            System.out.println("The request signature is invalid");
            return;
        }

        boolean isreturn= false;
        loger.info("1.收到微信服务器消息");
        Map<String, String> wxdata=parseXml(request);
        if(wxdata.get("MsgType")!=null){
            if("event".equals(wxdata.get("MsgType"))){
                loger.info("2.1解析消息内容为:事件推送");
                if( "subscribe".equals(wxdata.get("Event"))){
                    loger.info("2.2用户第一次关注 返回true哦");
                    isreturn=true;
                }
            }
        }

        if(isreturn == true){
            //转换XML
        	System.out.println("wxdata--->"+wxdata);
            String key = wxdata.get("FromUserName")+ "__"
                    + wxdata.get("ToUserName")+ "__"
                    + wxdata.get("MsgId") + "__"
                    + wxdata.get("CreateTime");
            loger.info("3.0 进入回复 转换对象:"+key);

            if(expireKey.exists(key)){
                //重复通知不作处理
                loger.info("3.1  重复通知了");
                return;
            }else{
                loger.info("3.1  第一次通知");
                expireKey.add(key);
            }

            loger.info("3.2  回复你好");
            //创建回复
            XMLMessage xmlTextMessage = new XMLTextMessage(
                    wxdata.get("FromUserName"),
                    wxdata.get("ToUserName"),
                    "你好");
            //回复
            xmlTextMessage.outputStreamWrite(outputStream);
            return;
        }
        loger.info("3.2  回复空");
        outputStreamWrite(outputStream,"");
    }
    
    /**
     * 数据流输出
     * @param outputStream
     * @param text
     * @return
     */
    private boolean outputStreamWrite(OutputStream outputStream, String text){
        try {
            outputStream.write(text.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * dom4j 解析 xml 转换为 map
     * @param request
     * @return
     * @throws Exception
     */
    public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<String, String>();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        for (Element e : elementList)
            map.put(e.getName(), e.getText());

        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }
}

其他的依赖文件可去这个开源项目里找:https://github.com/liyiorg/weixin-popular
在这里插入图片描述

代码实例二:

本来我想直接用上面的方式来做这个事件推送功能,虽然我们公司是spring boot项目,但是整合了Jersey,导致无法用一个接口同时处理get和post请求
失败代码:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Path("/openwx")	//这个必须有不能注释否则访问不到这个url链接,也是奇了怪了,好像和spring boot中的@RequestMapping用法不一样
@Component
public class BangdanItemResource {
	private static Logger logger = LoggerFactory.getLogger(BangdanItemResource.class);
	private final String TOKEN = "weixin4";

	@GET
	// @POST  //用@GET注解就无法使用@POST注解,就这一点导致该方案无法通过,其他代码逻辑都是对的
	@Path("/getticket")
	public Response getTicket(@Context HttpServletRequest request,
			@Context HttpServletResponse response) throws Exception {
		ServletOutputStream outputStream = response.getOutputStream();
		String signature = request.getParameter("signature");
		System.out.println("signature--->" + signature);
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		String echostr = request.getParameter("echostr");

		// 首次请求申请验证,返回echostr
		if (echostr != null) {
			outputStreamWrite(outputStream, echostr);
			System.out.println("1-------------------->");
			return Response.status(Response.Status.OK).build();
		}

		// 验证请求签名
		if (!signature.equals(generateEventMessageSignature(TOKEN, timestamp, nonce))) {
			System.out.println("The request signature is invalid");
			return Response.status(Response.Status.OK).build();
		}

		boolean isreturn = false;
		logger.info("收到微信服务器消息");
		Map<String, String> wxdata = parseXml(request);
		if (wxdata.get("MsgType") != null) {
			if ("event".equals(wxdata.get("MsgType"))) {
				logger.info("解析消息内容为:事件推送");
				if ("subscribe".equals(wxdata.get("Event"))) {
					logger.info("用户第一次关注");
					isreturn = true;
				}
			}
		}

		if (isreturn == true) {
			// 转换XML
			if (wxdata.get("Ticket") != null) { // 如果是通过扫描带参数二维码关注则可获得用户的openID
				String openid = wxdata.get("FromUserName");
				System.out.println("openid-->" + openid);
			}
		}
		return Response.status(Response.Status.OK).build();
	}

	// 数据流输出
	private boolean outputStreamWrite(OutputStream outputStream, String text) {
		try {
			outputStream.write(text.getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	// 生成事件消息接收签名
	public static String generateEventMessageSignature(String token, String timestamp, String nonce) {
		String[] array = new String[] { token, timestamp, nonce };
		Arrays.sort(array);
		String s = arrayToDelimitedString(array, "");
		return DigestUtils.shaHex(s);
	}

	public static String arrayToDelimitedString(Object[] arr, String delim) {
		if (arr == null || arr.length == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			if (i > 0) {
				sb.append(delim);
			}
			sb.append(arr[i]);
		}
		return sb.toString();
	}

	// dom4j 解析 xml 转换为 map
	public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
		// 将解析结果存储在HashMap中
		Map<String, String> map = new HashMap<String, String>();
		// 从request中取得输入流
		InputStream inputStream = request.getInputStream();
		// 读取输入流
		SAXReader reader = new SAXReader();
		Document document = reader.read(inputStream);
		// 得到xml根元素
		Element root = document.getRootElement();
		// 得到根元素的所有子节点
		List<Element> elementList = root.elements();

		// 遍历所有子节点
		for (Element e : elementList)
			map.put(e.getName(), e.getText());

		// 释放资源
		inputStream.close();
		inputStream = null;
		return map;
	}
}

转换思路:

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
@WebServlet(urlPatterns = "/openwx/getticket")
public class HuiController extends HttpServlet {
    private static final long serialVersionUID = -2776902810130266533L;
    private static Logger log = LoggerFactory.getLogger(HuiController.class);
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String signature = req.getParameter("signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        String echostr = req.getParameter("echostr");
        // 此处需要检验signature对网址接入合法性进行校验。我这里为了方便没弄,想弄的话可参考上面两例的代码
        log.info(signature + " : " + timestamp + " : " + nonce + " : " + echostr);
        PrintWriter out = resp.getWriter();
        out.write(echostr);
    }
     
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
    	System.out.println("1--->");
    	// 此处需要检验signature对网址接入合法性进行校验。
        Scanner scanner = new Scanner(req.getInputStream());
        resp.setContentType("application/xml");
        resp.setCharacterEncoding("UTF-8");
        // 1、获取用户发送的信息
        StringBuffer sb = new StringBuffer(100);
        while (scanner.hasNextLine()) {
            sb.append(scanner.nextLine());
        }
    }
}

注意:在spring boot中使用Servlet可能会报404访问不到页面的问题,那是可能因为你没在主方法上使用@ServletComponentScan注解造成的,详情请看我的另一篇文章:https://blog.csdn.net/m0_37739193/article/details/85097477

参考:
https://blog.csdn.net/chenmmo/article/details/78299238
https://www.oschina.net/code/snippet_778955_17411
https://blog.csdn.net/Goodbye_Youth/article/details/80590831 (文章里面抛出异常的写法值得借鉴)

2017-07-29 17:37:45 Francis123580 阅读数 1363
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27803 人正在学习 去看看 秦子恒

背景

因为要实现向用户推送一些消息,但是每个用户收到的消息按需求又是不同的

这里,比如在考试前向学生推送考试信息,那么每个用户收到信息的时间是不同的,收到的内容是不同的

所以,消息需要定制发送

在微信提供的接口中,我们设想了几种方案来实现,最终选择了按照openid列表群发接口来实现

功能

根据不同用户在不同的时间向用户发送定制的消息

代码

获取用户openid

if (!IsPostBack && (Request.QueryString["echostr"]==null))
{
    string appid = "YOUR APPID";
    string appsecret = "YOUR APPSECRET";
    string code = null;

    //调用10次微信服务,查询用户openid-暂时不用
    //for (int i = 0; i < 10; i++)
    //{
    code = Request.QueryString["code"];
    if (code == null)
    {
        Response.Redirect("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=http%3a%2f%2--你的域名--%2fET%2f&response_type=code&scope=snsapi_base&state=1#wechat_redirect");
    }
    else
    {
        string url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code";
        string jsonReturn = "";

        HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(url);
        httprequest.Method = "GET";
        HttpWebResponse response = (HttpWebResponse)httprequest.GetResponse();
        using (Stream steam = response.GetResponseStream())
        {
            StreamReader reader = new StreamReader(steam, Encoding.GetEncoding("gb2312"));
            jsonReturn = reader.ReadToEnd();
            steam.Close();
        }

        JObject jo = JObject.Parse(jsonReturn);
        OPENID = jo["openid"].ToString();


    }
}

 

获取access_token

public static string getAccessToken()
{
    string appid =  System.Configuration.ConfigurationManager.AppSettings["appid"];
    string appsecret =  System.Configuration.ConfigurationManager.AppSettings["appsecret"];
    string access_token = null;

    string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + appsecret;
    string jsonReturn = "";

    HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(url);
    httprequest.Method = "GET";
    HttpWebResponse response = (HttpWebResponse)httprequest.GetResponse();
    using (Stream steam = response.GetResponseStream())
    {
        StreamReader reader = new StreamReader(steam, Encoding.GetEncoding("gb2312"));
        jsonReturn = reader.ReadToEnd();
        steam.Close();
    }

    JObject jo = JObject.Parse(jsonReturn);
    access_token = jo["access_token"].ToString();
    return access_token;

} 

 

用post方式推送

private static string Post(string url, byte[] bytesToPost)
{
    if (String.IsNullOrEmpty(url))
        return "url参数为空值";
    if (bytesToPost == null)
        return "post数据为空值";
    string ResponseString = "";
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    System.Net.ServicePointManager.DefaultConnectionLimit = 50;
    request.KeepAlive = false;
    request.Method = "POST";
    request.ContentType = "text/xml";//提交xml   
    request.ContentLength = bytesToPost.Length;
    Stream writer = request.GetRequestStream();
    writer.Write(bytesToPost, 0, bytesToPost.Length);
    HttpWebResponse HttpWebRespon = (HttpWebResponse)request.GetResponse();
    StreamReader myStreamReader = new StreamReader(HttpWebRespon.GetResponseStream(), Encoding.UTF8);
    ResponseString = myStreamReader.ReadToEnd();
    myStreamReader.Close();

    writer.Flush();
    if (writer != null)
    {
        writer.Close();
    }
    if (request != null)
    {
        request.Abort();
    }
    return ResponseString;
} 

 

向openid发送定制消息

string access_token = getAccessToken();
string url = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=" + access_token;

string postText = "{\"touser\":[\"" + list[i].WeChatOpenId + "\",\"\"],\"msgtype\": \"text\",\"text\": { \"content\": \"【 考试提醒 】\\n\\n\\n" + list[i].StudentName + "同学,你好!\\n明天有考试,请做好准备\\n\\n考试科目:" + list[i].ExamName + "\\n考试日期:" + list[i].Date + "\\n考试时间:" + list[i].StartTime + " - " + list[i].EndTime + "\\n考试地点:" + list[i].ExamPlace + "-" + list[i].SeatNo + "号\\n\\n" + "请提前准备好文具和证件\\n详细情况可点击“查考场”进行查询\\n\\n" + "祝您考试顺利~~\\n\\n\\n" + "-------------------------------\\n" + "xx公司 · 技术支持\\n" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\"}}";



//因为汉字编码问题,出现40008错误,可采用以下方式对文本编码
//string contentText = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes("今天用微信和女神表白,什么排比、比喻各种修辞,可谓字字经典,句句动情,引经据典,声情并茂。最后,在我强烈的感情攻势下,女神终于回了一句:你谁啊?"));

Encoding encoding = Encoding.GetEncoding("utf-8");
byte[] bytesToPost = encoding.GetBytes(postText);
string res = Post(url, bytesToPost);

Console.WriteLine(res);

流程说明

1.首先需要获取用户openid,在用户访问页面的时候就可以获得,然后存到数据库中
2.获取access_token,用post方式调用微信接口向用户发送消息

注意:
具体什么时间发送,发送什么什么内容,根据自己的业务逻辑来编写
这里的定时发送采用的是Quartz框架,并把推送程序做成了一个服务

小结

在实现消息推送的过程中,遇到很多问题

比如:如何获取openid,获取access_token,在哪里获取,如何和微信接口交互,如何实现定时发送,程序应该是一个BS、CS、控制台、还是服务

在讨论和实践的过程中,每次叠加实现一个技术点,一步步实现了功能

2016-10-05 21:09:22 SFNES 阅读数 2942
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27803 人正在学习 去看看 秦子恒

​在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细说明请见本页末尾的微信推送消息与事件说明。

在用户触发事件后,微信会向开发者页面发送一组xml数据

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

这个就是关注/取消关注事件后 触发的数据;

参数说明:
参数 	描述
ToUserName 	开发者微信号
FromUserName 	发送方帐号(一个OpenID)
CreateTime 	消息创建时间 (整型)
MsgType 	消息类型,event
Event 	事件类型,subscribe(订阅)、unsubscribe(取消订阅) 

我们要做的是

①判断数据类型

<pre name="code" class="html">if ($msgType=='event'){
                $contentStr = "为关注或取消关注类型";
if($event==''){
//继续判断事件类型
 $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType,                                $contentStr);
                echo $resultStr;
     }
} 
最后根据事件类型作出反应.
其余事件类型与此相同,可以自己尝试测试;


2017-04-05 20:02:14 sinat_35861727 阅读数 1098
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27803 人正在学习 去看看 秦子恒

在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许。

//回复事件
private function _doEvent($postObj){
	switch($postObj->Event){
		case 'subscribe':              //关注
			$str = "终于等到你!\n这是启军的测试公众号!\n\n你可以回复'图文','图片','语音','单图文','多图文','音乐'或者直接发送你的地理位置~\n\n你还可以在这里闲聊哦^_^试试吧!";
			$this->_replayText($postObj,$str); 
			break;
		case 'LOCATION':
			{
				$latitude = $postObj->Latitude;   //纬度
				$longitude = $postObj->Longitude;  //经度
				$contentStr = "您的当前所在的\n纬度:{$latitude}\n纬度:{$longitude}\n位置是:{$result->formatted_address}";
				$this->_replayText($postObj,$contentStr);
			}
			break;
		case 'CLICK':              
			switch($postObj->EventKey){
				case 'TODAY_MUSIC':
					$this->_doMusic($postObj); 
					break;
				case 'TALK':
					$this->_doNews($postObj); 
					break;
				default:
					break;
			}
			break;
		default:
			break;
	}
}

当关注和上报地理位置时会触发相应的事件。


2019-05-09 10:57:08 weixin_38361347 阅读数 339
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27803 人正在学习 去看看 秦子恒

微信扫描二维码后 【未关注】 和 【关注】 之后的扫码事件推送

可能会遇到这么个需求,通过微信生成的二维码,在用户扫码后(这个期间,若用户第一次扫码,没关注公众号,此时会跳转到关注界面,点击关注之后,才会推送消息,然后就可以用到下面关注取消推送事件机制了。)

微信生成二维码 传送门

以下是官方文档说明:

接收事件推送

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

目录

1 关注/取消关注事件

2 扫描带参数二维码事件

关注/取消关注事件

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

微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。

关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。

假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

推送XML数据包示例:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[FromUser]]></FromUserName>
  <CreateTime>123456789</CreateTime>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[subscribe]]></Event>
</xml>

参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)

扫描带参数二维码事件

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

如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
  1. 用户未关注时,进行关注后的事件推送

推送XML数据包示例:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[FromUser]]></FromUserName>
  <CreateTime>123456789</CreateTime>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[subscribe]]></Event>
  <EventKey><![CDATA[qrscene_123123]]></EventKey>
  <Ticket><![CDATA[TICKET]]></Ticket>
</xml>

参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe
EventKey 事件KEY值,qrscene_为前缀,后面为二维码的参数值
Ticket 二维码的ticket,可用来换取二维码图片

  1. 用户已关注时的事件推送

推送XML数据包示例:

<![CDATA[toUser]]> <![CDATA[FromUser]]> 123456789 <![CDATA[event]]> <![CDATA[SCAN]]> <![CDATA[SCENE_VALUE]]> <![CDATA[TICKET]]>

参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,SCAN
EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
Ticket 二维码的ticket,可用来换取二维码图片

注意:未关注和关注推送的信息类型不一致

类型 MsgType 消息类型 Event 事件类型 EventKey事件KEY值 Ticket 二维码的ticket
未关注 event subscribe qrscene_(以此开头后面是key值) TICKET
关注 event SCAN SCENE_VALUE(值即为key值) TICKET

获取二维码中的值,根据MsgType (消息类型)一致 可以根据 Event (事件类型)来区分,然后根据key值判断获取!

不废话了直接上代码:
 		
 		// xmlData 是微信返回给你服务器的xml数据包,转为map后的数据格式。


		String event = xmlData.get("Event");
        String wxId = xmlData.get("FromUserName");

			//url事件
        if (event.equals("VIEW")) {
            return baseMsg = RequestParamTool.url(xmlData);
            //点击事件
        }else if (event.equals("CLICK")) {
            return  baseMsg = RequestParamTool.handleClick(xmlData);
			//Event 事件类型 判断关注和未关注
        } else if (event.equals("SCAN") || event.equals("subscribe")) {
            String eventKey = xmlData.get("EventKey");
            //
            if (eventKey.contains("qrscene_")) {
                eventKey = eventKey.replace("qrscene_", "");
            }
            
            log.info("eventKey   === {} " , eventKey); //eventKey
没有更多推荐了,返回首页