2018-01-15 22:14:46 MYTLJP 阅读数 4947
  • 微信支付开发-微信公众号开发12-微信开发php

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

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

用了好几个小时的时间,整理了一下关于公众号的思维导图,由于CSDN不能上传相对应的文件,所以萍子一一的分解开的截图附上来,希望对大家有所帮助哦~
因为是电脑设备自动截图,又鉴于内容比较多,可能不是太清楚,需要的小伙伴们,可以联系我,我可以随时发给你。

学习微信公众号需要的配置

这里写图片描述


微信开发的主体、请求方式、数据类型和环境

主体

这里写图片描述

请求方式

这里写图片描述
这里写图片描述

数据类型和环境

这里写图片描述


开始开发

获取access_token值并存储

这里写图片描述
这里写图片描述


账号管理

生成带参数的二维码

这里写图片描述

长链接转短链接

这里写图片描述


消息管理

获取微信服务器的IP地址

接收普通消息

接收事件推送

被动回复用户消息

这里写图片描述


微信网页授权

微信网页授权步骤

第一步:跳转页面,用户同意授权,获取code

这里写图片描述

第二步:通过code换取网页授权access_token(与普通的access_token不一样,具体见下方)
第三步:如果需要,刷新access_token值

这里写图片描述

第四步:拉取用户信息(需要scope为snsapi_userinfo)
第五步:附,检验授权凭证access_token是否有效,一般用不到

这里写图片描述


微信JS-SDK

说明

这里写图片描述

JS-SDK使用步骤

这里写图片描述

上传图片素材

这里写图片描述


微信公众号

握手协议

这里写图片描述

自定义菜单

自定义菜单类型

这里写图片描述

自定义菜单的设置

这里写图片描述

自定义菜单参数说明

这里写图片描述

获取自定义菜单

这里写图片描述


自定义菜单事件推送/被动回复用户信息

自定义事件推送

这里写图片描述

被动回复用户信息

这里写图片描述

根据菜单类型的点击,被动回复

点击按钮事件

这里写图片描述

公众号首次被某个用户关注的推送

这里写图片描述

微信扫一扫,并展示扫的内容

这里写图片描述

推送图文消息

这里写图片描述

回复图片消息
没有点击按钮

这里写图片描述

2019-03-22 11:41:56 weixin_44482225 阅读数 123
  • 微信支付开发-微信公众号开发12-微信开发php

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

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

现象:

  • 微信自定义菜单接口进入平台,哪个页面是从公众号里点击进入平台的,当第二次切换页面时,这个页面就切换不进去了。

解决:

  • 1、在"微信web开发者工具"中测试没问题,确定页面正常
  • 2、测试从公众号那个页面进入平台出现那个页面切换不过去,初步确定为接口问题
  • 3、解决方案:页面还是原来的地址,在公众号里设置链接时加一个无关要的参数即可,如:进入首页的链接为…/index.html 那么在公众号里设置入口链接时为 …/index.html?time=new Date().getTime() 而?time=new Date().getTime()这些字符可以自定义
2018-01-29 21:53:59 BuFanQi_Info 阅读数 1432
  • 微信支付开发-微信公众号开发12-微信开发php

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

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

前段时间在公司根据客户需求做微信公众号的开发,客户需求之一就是,接入后台同时支持多个公众号运行。

  • 微信配置参数切换
  • AccessToken入库存储

System和Environment

此前读者需了解System系统类和Environment,详情请查阅笔者System系统类和Environment环境抽象博文

微信基本配置类

WxConfig微信基本参数配置类,需单例

package com.bigbigbu.wx.tools.api;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import com.bigbigbu.wx.tools.bean.WxAccessToken;
import com.bigbigbu.wx.tools.exception.WxErrorException;
import com.bigbigbu.wx.tools.util.StringUtils;

/**
* <p>Title: WxAccessToken</p>
* <p>Description:  微信全局配置对象-从配置文件读取,原作者antgan于2016/12/14码</p>
* <p>Company: BIGBIGBU</p>
* @author    FANQIBU 
* @date       2017年12月7日
*/
public class WxConfig {
    private static final String configFile = "/wx.properties";//默认加载resources下的wx配置文件
    private static WxConfig config = null;

    //配置文件读取项
    private volatile String appId;
    private volatile String appSecret;
    private volatile String token;
    private volatile String aesKey;
    private volatile String mchId;
    private volatile String apiKey;

    //内存更新
    private volatile String accessToken;
    private  volatile  long expiresTime;
    private volatile String jsapiTicket;
    private volatile long jsapiTicketExpiresTime;
    //多公众号模式下自定义系统回调
    private  IWxManySysService wxManySysService;

    /**   
     * @Title:  WxConfig   
     * @Description:    FANQIBU 动态加载配置文件   
     * @param:    
     * @throws   
     */  
    public WxConfig() {
        //写读配置文件代码
        Properties p = new Properties();
        InputStream inStream = this.getClass().getResourceAsStream(configFile);
        if(inStream == null){
            try {
                throw new WxErrorException("根目录找不到文件");
            } catch (WxErrorException e) {
                e.printStackTrace();
            }
        }
        try {
            p.load(inStream);
            if(StringUtils.isEmpty(System.getProperty("wx.appId"))){
                this.appId = p.getProperty("wx.appId");
            }else{
                this.appId = System.getProperty("wx.appId");
            }
            if(StringUtils.isNotBlank(this.appId)) this.appId = this.appId.trim();
            if(StringUtils.isEmpty(System.getProperty("wx.appSecret"))){
                this.appSecret = p.getProperty("wx.appSecret");
            }else{
                this.appSecret = System.getProperty("wx.appSecret");
            }
            if(StringUtils.isNotBlank(this.appSecret)) this.appSecret = this.appSecret.trim();
            if(StringUtils.isEmpty(System.getProperty("wx.token"))){
                this.token = p.getProperty("wx.token");
            }else{
                this.token = System.getProperty("wx.token");
            }
            if(StringUtils.isNotBlank(this.token)) this.token = this.token.trim();
            if(StringUtils.isEmpty(System.getProperty("wx.aesKey"))){
                this.aesKey = p.getProperty("wx.aesKey");
            }else{
                this.aesKey = System.getProperty("wx.aesKey");
            }
            if(StringUtils.isNotBlank(this.aesKey)) this.aesKey = this.aesKey.trim();
            if(StringUtils.isEmpty(System.getProperty("wx.mchId"))){
                 this.mchId = p.getProperty("wx.mchId");
            }else{
                this.mchId = System.getProperty("wx.mchId");
            }
            if(StringUtils.isNotBlank(this.mchId)) this.mchId = this.mchId.trim();
            if(StringUtils.isEmpty(System.getProperty("wx.apiKey"))){
                 this.apiKey = p.getProperty("wx.apiKey");
            }else{
                this.mchId = System.getProperty("wx.apiKey");
            }
            if(StringUtils.isNotBlank(this.apiKey)) this.apiKey = this.apiKey.trim();
            if(!StringUtils.isEmpty(System.getProperty("wx.accessToken"))){
                this.accessToken = System.getProperty("wx.accessToken");
            }
            if(!StringUtils.isEmpty(System.getProperty("wx.expiresTime"))){
                this.expiresTime = Long.parseLong(System.getProperty("wx.expiresTime"));
            }
            inStream.close();
        } catch (IOException e) {
            try {
                throw new WxErrorException("load wx.properties error,class根目录下找不到wx.properties文件");
            } catch (WxErrorException e1) {
                e1.printStackTrace();
            }
        }
        System.out.println("load wx.properties success");
        System.out.println(this.toString());
    }

    /**
     * 同步获取/加载单例
     * @return
     */
    public static synchronized WxConfig getInstance(){
        if(config == null){
            config = new WxConfig();
        }
        return config;
    }
    /**
     * 同步获取/加载单例
     * @return
     */
    public static synchronized void refreshInstance(){
        config = new WxConfig();
    }
    public String getAccessToken() {
        return accessToken;
    }

    public boolean isAccessTokenExpired() {
        return System.currentTimeMillis() > this.expiresTime;
    }
    public void expireAccessToken() {
        this.expiresTime = 0;
    }
    /**
    * <p>Title: updateAccessToken</p>
    * <p>Description: 更新接口调用凭证</p>
    * @param WxAccessToken accessToken
    * @param boolean       isCallback   是否调用系统回调
    */
    public synchronized void updateAccessToken(WxAccessToken accessToken,boolean isCallback) {
        updateAccessToken(accessToken.getAccess_token(), accessToken.getExpires_in(),isCallback);
    }
    /**
    * <p>Title: updateAccessToken</p>
    * <p>Description: FANQIBU修改更新接口调用凭证</p>
    * @param String accessToken
    * @param long       expiresInSeconds
    * @param boolean isCallback   是否调用系统回调
    */
    public synchronized void updateAccessToken(String accessToken, long expiresInSeconds,boolean isCallback) {
        this.accessToken = accessToken;
        this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
        //调用微信WxSysCallback
        if(this.getWxManySysService()!=null && isCallback){
            //微信刷新access_token接口调用凭证回调(PS:针对多公众号情况下access_token更新入库操作)
            this.getWxManySysService().wxAccessTokenCallback(this.accessToken, this.expiresTime);
        }
    }
    public String getJsapiTicket() {
        return jsapiTicket;
    }

    public void setJsapiTicket(String jsapiTicket) {
        this.jsapiTicket = jsapiTicket;
    }

    public long getJsapiTicketExpiresTime() {
        return jsapiTicketExpiresTime;
    }

    public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) {
        this.jsapiTicketExpiresTime = jsapiTicketExpiresTime;
    }

    public boolean isJsapiTicketExpired() {
        return System.currentTimeMillis() > this.jsapiTicketExpiresTime;
    }

    public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
        this.jsapiTicket = jsapiTicket;
        // 预留200秒的时间
        this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
    }

    public void expireJsapiTicket() {
        this.jsapiTicketExpiresTime = 0;
    }


    //getter


    public String getAppId() {
        return appId;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public String getToken() {
        return token;
    }

    public String getAesKey() {
        return aesKey;
    }

    public String getMchId() {
        return mchId;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }



    public IWxManySysService getWxManySysService() {
        return wxManySysService;
    }

    public void setWxManySysService(IWxManySysService wxManySysService) {
        this.wxManySysService = wxManySysService;
    }

    @Override
    public String toString() {
        return "WxConfig [appId=" + appId + ", appSecret=" + appSecret + ", token=" + token + ", aesKey=" + aesKey
                + ", mchId=" + mchId + ", apiKey=" + apiKey + ", accessToken=" + accessToken + ", expiresTime="
                + expiresTime + ", jsapiTicket=" + jsapiTicket + ", jsapiTicketExpiresTime=" + jsapiTicketExpiresTime
                + "]";
    }


}

微信配置参数切换

定义接口IWxManySysService 主要作用多公众号模式下动态加载配置信息和接口调用凭证access_token更新

package com.bigbigbu.wx.tools.api;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.bigbigbu.wx.tools.bean.WxAccessToken;
import com.bigbigbu.wx.tools.bean.many.WxBaseConfig;
import com.bigbigbu.wx.tools.util.StringUtils;

/**
* <p>Title: IWxManySysService</p>
* <p>Description: 多公众号模式下动态加载配置信息和接口调用凭证access_token更新</p>
* <p>Company: DINGGE</p>
* @author    FANQIBU
* @date       2017年12月7日
*/
public interface IWxManySysService {
    /**
    * <p>Title: wxAccessTokenCallback</p>
    * <p>Description: 微信刷新access_token接口调用凭证回调(PS:一般针对多公众号情况下access_token更新入库操作)</p>
    * @param accessToken
    * @param expiresInSeconds
    */
    public void wxAccessTokenCallback(String accessToken, long expiresInSeconds);
    /**
     * <p>Title: loadWxProperties</p>
     * <p>Description:多公众号动态加载微信配置参数 </p>
     * @param String wxsystoken 本地系统wx令牌(从数据库查找当前公众号配置参数)
     */
    public default boolean loadWxProperties(String newwxsystoken,String oldwxsystoken,String oldappId,String oldappSecret,
            String oldtoken,String oldaesKey,String oldmchid,String oldapiKey){
        if(StringUtils.isEmpty(newwxsystoken)){
            return false;
        }
        try {
            Map<String, Object> parmer=new HashMap<>();
            parmer.put("wxsystoken", newwxsystoken);
            //根据系统公众号token获取公众号配置信息
            List<WxBaseConfig> configs=this.listWxbaseConfig(parmer);
            if(configs != null && configs.size()>0){
                if(configs.get(0).appid!=null && !configs.get(0).appid.equals(oldappId))
                    System.setProperty("wx.appId",configs.get(0).appid);
                if(configs.get(0).appsecret!=null && !configs.get(0).appsecret.equals(oldappSecret))
                    System.setProperty("wx.appSecret",configs.get(0).appsecret);
                if(configs.get(0).token!=null && !configs.get(0).token.equals(oldtoken))
                    System.setProperty("wx.token",configs.get(0).token);
                if(configs.get(0).encodingaeskey!=null && !configs.get(0).encodingaeskey.equals(oldaesKey)){
                    System.setProperty("wx.aesKey",configs.get(0).encodingaeskey);
                }
                if(configs.get(0).mchid!=null && !configs.get(0).mchid.equals(oldmchid)){
                    System.setProperty("wx.mchId",configs.get(0).mchid);
                }
                if(configs.get(0).apiKey!=null && !configs.get(0).apiKey.equals(oldapiKey)){
                    System.setProperty("wx.apiKey",configs.get(0).apiKey);
                }
                System.setProperty("wx.gz.id",configs.get(0).gzid+"");
                System.setProperty("wx.gz.remark",configs.get(0).remark);
                if(!newwxsystoken.equals(oldwxsystoken)){
                    System.setProperty("wx.wxsystoken",configs.get(0).wxsystoken);
                }
                //查询数据库token数据
                WxAccessToken accessToken=this.getWeixinAccessToken(parmer);
                if(accessToken != null){
                    System.setProperty("wx.accessToken",accessToken.getAccess_token());
                    System.setProperty("wx.expiresTime",accessToken.getExpires_in()+"");
                }
                WxConfig.refreshInstance();
                WxConfig.getInstance().setWxManySysService(this);
            }else{
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    public List<WxBaseConfig>  listWxbaseConfig(Map<String, Object> parmer);

    public WxAccessToken getWeixinAccessToken(Map<String, Object> parmer);
}

接口实现

package com.lh.wx.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import com.lh.wx.dao.WeixinAccessTokenMapper;
import com.lh.wx.dao.WeixinConfigMapper;
import com.lh.wx.model.WeixinAccessTokenDto;
import com.lh.wx.servlet.InitServlet;
import com.bigbigbu.wx.tools.api.IWxManySysService;
import com.bigbigbu.wx.tools.bean.WxAccessToken;
import com.bigbigbu.wx.tools.bean.many.WxBaseConfig;

/**
* <p>Title: WxSysCallback</p>
* <p>Description: 自定义系统回调</p>
* <p>Company: DINGGE</p>
* @author    FANQIBU
* @date       2017年12月7日
*/
@Service
public class WxManySysService implements IWxManySysService{
    private @Inject WeixinConfigMapper weixinConfigMapper;
    private @Inject WeixinAccessTokenMapper weixinAccessTokenMapper;
    private  @Inject  Environment  environment;
    @Override
    public void wxAccessTokenCallback(String accessToken, long expiresInSeconds) {
        String wxsystoken=environment.getProperty("wx.wxsystoken");
        Map<String, Object> parmer=new HashMap<>();
        parmer.put("wxsystoken", wxsystoken);
        List<WeixinAccessTokenDto> accessTokenDtos=weixinAccessTokenMapper.listWeixinAccessToken(parmer);
        WxAccessToken wxAccessToken=new WxAccessToken();
        wxAccessToken.setAccess_token(accessToken);
        wxAccessToken.setExpires_in(expiresInSeconds);
        if(accessTokenDtos != null && accessTokenDtos.size()>0){
            WeixinAccessTokenDto resultDto=accessTokenDtos.get(0);
            resultDto.setAccessToken(wxAccessToken);
            weixinAccessTokenMapper.upWeixinAccessToken(resultDto);
        }else{
            WeixinAccessTokenDto resultDto=new WeixinAccessTokenDto();
            resultDto.setWxsystoken(wxsystoken);
            resultDto.setAccessToken(wxAccessToken);
            weixinAccessTokenMapper.addWeixinAccessToken(resultDto);
        }
    }
    @Override
    public List<WxBaseConfig> listWxbaseConfig(Map<String, Object> parmer) {
        WxBaseConfig wxBaseConfig=InitServlet.wxBaseConfig.get(parmer.get("wxsystoken"));
        if(wxBaseConfig != null){
            List<WxBaseConfig> wxBaseConfigs =new ArrayList<>();
            wxBaseConfigs.add(wxBaseConfig);
            return wxBaseConfigs;
        }
        return weixinConfigMapper.listWeixinConfig(parmer);
    }
    @Override
    public WxAccessToken getWeixinAccessToken(Map<String, Object> parmer) {
        List<WeixinAccessTokenDto> accessTokenDtos=weixinAccessTokenMapper.listWeixinAccessToken(parmer);
        if(accessTokenDtos != null && accessTokenDtos.size()>0){
            //int expires_in =new Long((System.currentTimeMillis() -  accessTokenDtos.get(0).getUpdatetime().getTime())/1000).intValue();
            //accessTokenDtos.get(0).getAccessToken().setExpires_in(expires_in);
            return  accessTokenDtos.get(0).getAccessToken();
        }
        return null;
    }
}

调用

package com.lh.wx.service;

import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

@Service
public class WechatService {
     private  @Inject  Environment  environment;
    private @Inject WxManySysService wxManySysService;
    public boolean loadWxSysConfigure(String wxsystoken,boolean isMust){
         if(!StringUtils.isEmpty(wxsystoken)){
                return wxManySysService.loadWxProperties(wxsystoken,environment.getProperty("wx.wxsystoken"),environment.getProperty("wx.appId")
                        ,environment.getProperty("wx.appSecret"),environment.getProperty("wx.token"),environment.getProperty("wx.aesKey")
                        ,environment.getProperty("wx.mchId"),environment.getProperty("wx.apiKey"));
         }
         if(isMust){
             return false;
         }
         return true;
    }
}
2017-03-31 15:01:11 sunhuwh 阅读数 12536
  • 微信支付开发-微信公众号开发12-微信开发php

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

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

配置位置
进入公众号:https://mp.weixin.qq.com
进入开发者工具:公众号页面左下角
进入公众平台测试账号:开发者工具页面中

下面有张图片,介绍配置:
这里写图片描述

下面来看看Token验证。注意:本次微信开发专栏,全部使用spring boot进行开发。
CODE:servlet

import java.io.IOException;
import java.io.PrintWriter;

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

import com.shw.vote.web.service.AuthService;

public class ServerPortal extends HttpServlet {
    private static final long serialVersionUID = 1L;
    //我们的Token
    private static final String token = "tokensh";

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ServerPortal() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        System.out.println("signature:" + signature);
        System.out.println("timestamp:" + timestamp);
        System.out.println("nonce:" + nonce);
        System.out.println("echostr:" + echostr);
        PrintWriter pw = response.getWriter();
        pw.append(echostr);
        pw.flush();
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

spring boot需要注入servlet:

@Bean  
public ServletRegistrationBean testServletRegistration() {  
    ServletRegistrationBean registration = new ServletRegistrationBean(new ServerPortal());  
    //我们的URL
    registration.addUrlMappings("/portal");  
    return registration;  
}

上面我们设定Token为tokensh:
private static final String token = “tokensh”;

由于Token验证接口URL必须是域名,我这边使用了ngrok来处理域名的问题:
参考http://www.qydev.com/
注意,Token验证的地址一定要用80端口。
假设我们的域名为https://sunhush.tunnel.qydev.com
URL处我们填:https://sunhush.tunnel.qydev.com/portal

Token , URL都设定了,提交,提示提交成功,则配置完毕。

参考资料:http://www.cnblogs.com/janken/p/5593737.html

2018-11-13 15:09:15 qq_38021767 阅读数 95
  • 微信支付开发-微信公众号开发12-微信开发php

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

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

温馨提示:为保证数据传输安全,提高业务安全性,公众平台将不再支持HTTP方式调用。避免影响正常使用中含有HTTP方式调用的服务,请开发者尽快调整,将现有通过HTTP方式调用的切换成HTTPS调用,平台将于2017年12月30日停止对HTTP方式调用的支持。

一、微信公众开发前期准备

 

  1.   申请公众号-微信公众号:

  2. 外网服务器资源(互联网可以访问到的),也就是说我们的后台应用要在互联网条件下访问到。

            先说下消息交互流程图:(http:80端口,https443端口)

                

         可以使用百度的BAE:免费申请而且不用维护,但是这样就不利于的本地调试。腾讯云:学生便宜

        其实我们只需要把我们的内网应用通过一个域名映射到公网即可,这里使用花生壳的域名工具,免费给你一个域名,通过添加映射的方式就可以了,具体操作如下图:

      

这样端口也是80的,应用名称不用去管,就把192.168.0.10:9088映射到了178b647i13.iask.in,可以自行测试一下。

 

  1.    配置微信平台的接口配置服务:

 

1)测试号的服务器配置:

       

  •         URL: 以上是测试号的配置,其中URL是开发者用来接收微信消息和事件的接口URL,http的端口号固定使用80,不可填写其他,https(443);

  •         Token:自主设置,这个token与公众平台wiki中常提的access_token不是一回事。这个token只用于验证开发者服务器

 

 

2)真实公众号配置服务器:

 

!说明:启用该配置后,原来设置的自动回复消息以及点击时间会被转发到这里了!只验证一次

 

在填写了该配置提交的时候:微信会立马就行该服务器安全性有效性的验证,具体验证流程如下图:

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

二、消息推送和消息回复

  1. 消息推送:粉丝给公众号发消息,当普通用户向公众帐号发消息时,微信服务器将POST该消息(xml)到填写的URL上;(消息类封装暂时没用)

微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

 

19:14:23,056 DEBUG Finalizer conn.DefaultClientConnection:169 - Connection 0.0.0.0:63825<->101.226.212.27:443 closed

19:14:23,104 DEBUG Finalizer conn.DefaultClientConnection:169 - Connection 0.0.0.0:64259<->58.246.220.31:443 closed

19:14:23,249  INFO http-bio-8081-exec-4 service.MessageService:42 - {MsgId=6482660711707982028, FromUserName=oyYFvw5agVtD6IQE9lA9Hu4HdkNY, CreateTime=1509362066, Content=公共, ToUserName=gh_eaf29fa02b4a, MsgType=text}

19:14:28,549  INFO http-bio-8081-exec-3 service.MessageService:42 - {MsgId=6482660711707982028, FromUserName=oyYFvw5agVtD6IQE9lA9Hu4HdkNY, CreateTime=1509362066, Content=公共, ToUserName=gh_eaf29fa02b4a, MsgType=text}

19:14:33,192  INFO http-bio-8081-exec-10 service.MessageService:42 - {MsgId=6482660711707982028, FromUserName=oyYFvw5agVtD6IQE9lA9Hu4HdkNY, CreateTime=1509362066, Content=公共, ToUserName=gh_eaf29fa02b4a, MsgType=text}

 

  • 文本消息示例:xml数据包 , 涉及技术xml解析:示例dom4j 源码示例。

 

 

 

<xml>

 <ToUserName><![CDATA[toUser]]></ToUserName>

 <FromUserName><![CDATA[fromUser]]></FromUserName>

 <CreateTime>1348831860</CreateTime>

 <MsgType><![CDATA[text]]></MsgType>

 <Content><![CDATA[this is a test]]></Content>

 <MsgId>1234567890123456</MsgId>

 </xml>

 

 

ToUserName

开发者微信号

FromUserName

发送方帐号(一个OpenID)

CreateTime

消息创建时间 (整型)

MsgType

text

Content

文本消息内容

MsgId

消息id,64位整型

 

 

OpenID :为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID

 

  •   图片消息

<xml>

<ToUserName><![CDATA[toUser]]></ToUserName>

<FromUserName><![CDATA[fromUser]]></FromUserName>

<CreateTime>1348831860</CreateTime>

<MsgType><![CDATA[image]]></MsgType>

<PicUrl><![CDATA[this is a url]]></PicUrl>

<MediaId><![CDATA[media_id]]></MediaId>

<MsgId>1234567890123456</MsgId>

</xml>

 

 

ToUserName

开发者微信号

FromUserName

发送方帐号(一个OpenID)

CreateTime

消息创建时间 (整型)

MsgType

image

PicUrl

图片链接(由系统生成)

MediaId

图片消息媒体id,可以调用多媒体文件下载接口拉取数据。

MsgId

消息id,64位整型

 

   

 

特别说明:

 

PicUrl: 这个参数是微信系统把用户发送的图片消息自动转化成url。 这个url可用浏览器打开查看到图片。

      MediaId: 是微信系统产生的id 用于标记该图片,详情可参考wiki 素材管理/获取临时素材

 

 示例:发送图片的参数:

{MsgId=6482681349030657007,

FromUserName=oyU-W0c1DXqbgmL17_X0tMuXpz_0,

CreateTime=1509366871,

MediaId=CK8tkH-kpdVop5pBndX47KpoYDc9zJJdz-UUs5sVCUkeOcBqbeWFMfyKueJsbwQN,

PicUrl=http://mmbiz.qpic.cn/mmbiz_jpg/AcmwDUnD36FYicFicSEsjGr5VA1Tay2ZkQlX7Gyq786hmWdlFUj1CkYFSv7dUwduwIGVvMoBHiam6qVuNiasce0nMA/0,

ToUserName=gh_354d2c1b1485, MsgType=image}

 

  我们可以通过MediaId获取图片素材,存在微信的服务器上的临时素材也可以直接使用该MediaId回复用户同样的图片。

 请求说明:

http请求方式: GET,https调用

https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID

这里用到了ACCESS_TOKEN

ACCESS_TOKENaccess_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token

 

特征:①最大值512个字符;②:有效期:2个小时

获取方式:公众号可以使用AppID和AppSecret调用本接口来获取access_token,

https请求方式: GET:只有在公众号设置了IP白名单的IP才能调用该接口

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

返回说明

正常情况下,微信会返回下述JSON数据包给公众号:

{"access_token":"ACCESS_TOKEN","expires_in":7200}

容易遇到的错误:access_token is  not latest hint(过期)建议公众号开发者使用中控服务器统一获取和刷新Access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务,在5分钟内老的token是不会失效的(Jedis很容易解决这个问题

 

成功获取access_token后就可以调用素材获取接口开获取用户发送的图片,语音,视频等消息。

   

   2.回复消息 :涉及java对象转换xml  示例:XStream

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

①、开发者在5秒内未回复任何内容

②、开发者回复了异常数据,比如JSON数据等

 

  •       文本消息:把接受消息的发送方和接受方互换

<xml>

<ToUserName><![CDATA[toUser]]></ToUserName>

<FromUserName><![CDATA[fromUser]]></FromUserName>

<CreateTime>12345678</CreateTime>

<MsgType><![CDATA[text]]></MsgType>

<Content><![CDATA[你好]]></Content>

</xml>

                

 

ToUserName

接收方帐号(收到的OpenID)

FromUserName

开发者微信号

CreateTime

消息创建时间 (整型)

MsgType

text

Content

回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)

 

  • 回复图片消息:需要先通过素材上传接口上传图片得到MediaId,媒体文件在微信后台保存时间为3天,即3天后media_id失效,大小限制参考官方文档(回复图文消息示例)

 

<xml>

<ToUserName><![CDATA[toUser]]></ToUserName>

<FromUserName><![CDATA[fromUser]]></FromUserName>

<CreateTime>12345678</CreateTime>

<MsgType><![CDATA[image]]></MsgType>

<Image>

<MediaId><![CDATA[media_id]]></MediaId>

</Image>

</xml>

 

 

 

ToUserName

接收方帐号(收到的OpenID)

FromUserName

开发者微信号

CreateTime

消息创建时间 (整型)

MsgType

image

MediaId

通过素材管理中的接口上传多媒体文件,得到的id。

 

 

三、自定菜单

 由于本账号无创建菜单的权限,故使用测试账号,测试账号的配置上很简单,在上面就有答案。

特别注意:

 

1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。

2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。

3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

4.编辑中的菜单不会马上被用户看到,点击发布后,会在24小时后在手机端同步显示。

 

自定义菜单接口可实现常用类型按钮:

 

1、click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;

2、view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。

 

接口调用请求说明

http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

 

{

"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"

}]

}]

}

 

 

 

button

一级菜单数组,个数应为1~3个

sub_button

二级菜单数组,个数应为1~5个

type

菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型

name

菜单标题,不超过16个字节,子菜单不超过60个字节

key

click等点击类型必须

菜单KEY值,用于消息接口推送,不超过128字节

url

view、miniprogram类型必须

网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。

media_id

media_id类型和view_limited类型必须

调用新增永久素材接口返回的合法media_id

appid

miniprogram类型必须

小程序的appid(仅认证公众号可配置)

pagepath

miniprogram类型必须

小程序的页面路径

 

 

如何封装:上图可知是JSON对象对应到java可以封装成javaPOJO

所有类型都有name属性:父类:(name)

①:定义click类型按钮(type,key

②:定义view类型按钮(type,url

③:定义包含二级菜单的一级菜单 :(BaseButton[] sub_button

④:定义菜单项:Menu(BaseButton[] button)

 

响应菜单事件:(推送XML数据包)

 用户点击自定义菜单后,微信会把点击事件推送给开发者,请注意,点击菜单弹出子菜单,不会产生上报:

 

点击菜单:

 

 

ToUserName

开发者微信号

FromUserName

发送方帐号(一个OpenID)

CreateTime

消息创建时间 (整型)

MsgType

消息类型,event

Event

事件类型,CLICK

EventKey

事件KEY值,与自定义菜单接口中KEY值对应

 

 

跳转菜单:直接跳转我们的设置的链接,当然后台也可以获取此事件,点击事件示例发送图文消息

演示订阅,回复图文消息

 

、利用微信JS-SDK网页开发

   微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包,就是一个js库,通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力!

 如何使用js_sdk:

    步骤 ①绑定js接口安全域名:公众号设置——>功能设置,:只有设置了该域名或路径,且我们在该域名或路径下发起到js-sdk接口的调用才能调用成功。我们可以按照如下图步骤设置,举一个例子:我们设置一个178b647i13.iask.in/static,有两点保证:1.必须是域名,且http支持80端口,https支持443端口(同样使用花生壳)该路径可直接访问到MP......txt,拿到浏览器来测试,2,我们调用js-sdk接口的路径至少与该路径一样,也就是我们的页面至少在static路径下。点击提交的时候微信会自动验证该路径,会提示你验证失败或者成功,但不会提示具体的原因!

     

         

.

    步骤:应用js文件(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js,改js提供了wx对象,如果发现wx is not defined,说明没有引用成功,检查应用方式是否正确,建议直接下载该文件放在本地引用避免各种奇葩愿意引用不到!

    步骤通过config接口注入权限验证配置,所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用)

       

wx.config({

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

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

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

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

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

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

});

这里面signature是最关键的,也是最容易出错的地方。

获取步骤:

   ①临时票据jsapi_ticket:jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期ye为7200秒,通过access_token来获取,该access_token就是上面我们所说的前参考前面内容 !

      用拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):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

}

 

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

     

签名算法

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

 

注意事项

1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

2.签名用的url必须是调用JS接口页面的完整URL(不包含#及其后面部分)。

3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

 

 

 

常见错误:

1.invalid url domain当前页面所在域名与使用的appid没有绑定,请确认正确填写绑定的域名。例如配置的js接口安全路径是178b647i13.iask.in/static ,(因为178b647i13.iask.in这个我们是放不了文件的)但是我们的页面地址是178b647i13.iask.in/#/input,传到后台参与验签的地址是178b647i13.iask.in,这时就会出现这个错误。

2.invalid signature签名错误。建议按如下顺序检查:

     1.确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验,得到的signature与我们自己生成的signature比较如果不一致说明算法有问题。

     2.确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。

     3.确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

    4.确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

    5 .确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。https://www.xin-e-jia.com/btbMobile/vx/?from=singlemessage&isappinstalled=0#/RegPre?json=8fAsMYNk9o1s6pEHZhV7zKku%2FFzDan88WSbpu%2FA6%20Hszy3FcEut84gUvi%20zEl%2FpAWi4l%2FflGaKCgGjZkgrHM0A%3D%3D

  步骤④:通过ready接口处理成功验证

 

wx.ready(function(){

    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

});

 

步骤⑤:通过error接口处理失败验证

 

 

wx.error(function(res){

    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

});

 

 

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

1.success:接口调用成功时执行的回调函数。

2.fail:接口调用失败时执行的回调函数。

3.complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。

4.cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。

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

调用成功时:"xxx:ok" ,其中xxx为调用的接口名

用户取消时:"xxx:cancel",其中xxx为调用的接口名

调用失败时:其值为具体错误信息

草图:

 

 

 

 

 

示例:选择照片

业务域名可以用来防止页面被重新排版,认证后才有!

 

 

   

 

 

 

没有更多推荐了,返回首页