2018-01-29 21:53:59 BuFanQi_Info 阅读数 1513
  • 微信开发者工具使用教程 如何上传、发布、审核等

    微信开发者工具使用教程 如何上传、发布、审核等 2020年3月31日录 - 微信开发者工具使用教程 01-微信开发者工具的下载和安装 02-微信小程序账号的注册、登录、appid的获取 03-微信小程序开发和公众号开发模式的切换 04-微信小程序如何导入项目 05-不在以下request合法域名列表如何设置解决 06-模拟器、编辑器、调试器的使用 07-本地如何调试、手机测试小程序 08-公众号账号关联和管理多个小程序 09-微信小程序项目管理、上传、审核、发布 10-如何能学会微信小程序开发

    123 人正在学习 去看看 黄菊华

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

  • 微信配置参数切换
  • 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;
    }
}
2019-03-22 11:41:56 weixin_44482225 阅读数 200
  • 微信开发者工具使用教程 如何上传、发布、审核等

    微信开发者工具使用教程 如何上传、发布、审核等 2020年3月31日录 - 微信开发者工具使用教程 01-微信开发者工具的下载和安装 02-微信小程序账号的注册、登录、appid的获取 03-微信小程序开发和公众号开发模式的切换 04-微信小程序如何导入项目 05-不在以下request合法域名列表如何设置解决 06-模拟器、编辑器、调试器的使用 07-本地如何调试、手机测试小程序 08-公众号账号关联和管理多个小程序 09-微信小程序项目管理、上传、审核、发布 10-如何能学会微信小程序开发

    123 人正在学习 去看看 黄菊华

现象:

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

解决:

  • 1、在"微信web开发者工具"中测试没问题,确定页面正常
  • 2、测试从公众号那个页面进入平台出现那个页面切换不过去,初步确定为接口问题
  • 3、解决方案:页面还是原来的地址,在公众号里设置链接时加一个无关要的参数即可,如:进入首页的链接为…/index.html 那么在公众号里设置入口链接时为 …/index.html?time=new Date().getTime() 而?time=new Date().getTime()这些字符可以自定义
2017-12-26 13:25:56 qq_34207892 阅读数 20807
  • 微信开发者工具使用教程 如何上传、发布、审核等

    微信开发者工具使用教程 如何上传、发布、审核等 2020年3月31日录 - 微信开发者工具使用教程 01-微信开发者工具的下载和安装 02-微信小程序账号的注册、登录、appid的获取 03-微信小程序开发和公众号开发模式的切换 04-微信小程序如何导入项目 05-不在以下request合法域名列表如何设置解决 06-模拟器、编辑器、调试器的使用 07-本地如何调试、手机测试小程序 08-公众号账号关联和管理多个小程序 09-微信小程序项目管理、上传、审核、发布 10-如何能学会微信小程序开发

    123 人正在学习 去看看 黄菊华

微信小程序web-view公众号与小程序支付间的切换使用

        最近小程序开放了新功能,支持内嵌网页。html写的网页,官网,网站,运行在浏览器上的,有域名的那种,可以内嵌到小程序里了!

那么这意味着什么呢?你还需要开发独立开发官网小程序吗?之前的微信公众号功能大部分也可以直接通过小程序webview实现了。
这几天刚好公司也有这方面的需求,怀着激动心情的我开始了踩坑之旅。

     开发之前我们需要把需要的环境配置好,这里就引用官方的介绍了————

1. 开发者登录小程序后台,选择设置-开发设置-业务域名,新增配置域名模块。目前小程序内嵌网页能力暂不开放给个人类型帐号和海外类型帐号。
2.配置的业务域名必须时https

那么配置好后,马上可以进入开发啦!
这里写图片描述

  页面中引入公众号的首页,小程序端的代码不是很多,主要的是后台进行结合。

1.在后端添加一个小程序的入口方法,

/**
 * 小程序第一次入口
 * @return
 */
public String wxIndex(){//返回首页
    sessions=request.getSession().getId();
    return "home";
}

这里我没有做任何的处理,这个项目首页是不用登陆的,直接返回首页;来到这一步时我们发现之前公众号后台保存在session中的信息获取不到,使用后发现小程序每次请求sessionId都会发生改变,这样后台无法使用session储存数据。

解决方案:

小程序第一次请求后台返回一个sessionId,之后小程序在参数或header中带入这个sessionId,后台使用这个session来处理。注意session销毁以及过期设置。


wx.login({
  success: function (res) {        wx.getSetting({
      success(setRes) {
        // 判断是否已授权  
        if (!setRes.authSetting['scope.userInfo']) {
          // 授权访问  
          wx.authorize({
            scope: 'scope.userInfo',
            success() {
              //获取用户信息  
              wx.getUserInfo({
                lang: "zh_CN",
                success: function (userRes) {
                  // var session_id= wx.getStorageSync('JSESSIONID');
                  //发起网络请求  
                  wx.request({
                    url: 'https://www.ulin5.com/vip/buser-bind-mobile_wxXiaoChengXuLogin.html',
                    data: {
                      code: res.code,
                      encryptedData: userRes.encryptedData,
                      iv: userRes.iv
                    },
                    header: {
                      "Content-Type": "application/x-www-form-urlencoded"
                    },
                    method: 'POST',
                    //服务端的回掉  
                    success: function (result) {
                      console.log(result)
                      // var session_id = wx.getStorageSync('JSESSIONID');//本地取存储的sessionID  
                      // if (session_id == null || session_id == "") {
                        wx.setStorageSync('JSESSIONID', result.data.sessionId) //如果本地没有就说明第一次请求 把返回的session id 存入本地  

                      // }
                      console.log(result.data.openId)
                      getApp().openid = result.data.openId
                      // var data = result.data.result;
                      // data.expireTime = nowDate + EXPIRETIME;
                      // wx.setStorageSync("userInfo", data);
                      // userInfo = data;
                    }
                  })
                }
              })
            }
          })
          })

 把小程序所需要的openid和unionid拿到,由于公众号的openid   和小程序的openid不同,所以这需要用到unionid。
换句话说,同一用户,对同一个微信开放平台帐号下的不同应用,UnionID是相同的。
此前的OpenID机制,每个微信号对应每个公众号只有唯一的OpenID,所以不同微信公众号之间是不能共享用户的,现在有了UnionID就可以了。
   前面说到小程序每次请求的sessionId不同,所以通过sessionId可以获取到我们存储的数据信息。
   注意Servlet2.1之后不支持SessionContext里面getSession(String id)方法。
   我这里通过HttpSessionListener监听器和全局静态map自己实现一个SessionContext。

public class MySessionContext {

 private static HashMap mymap = new HashMap();

    public static synchronized void AddSession(HttpSession session) {
        if (session != null) {
            mymap.put(session.getId(), session);
        }
    }

    public static synchronized void DelSession(HttpSession session) {
        if (session != null) {
            mymap.remove(session.getId());
        }
    }

    public static synchronized HttpSession getSession(String session_id) {
        if (session_id == null)
        return null;
        return (HttpSession) mymap.get(session_id);
    }
    }

        public class SessionListener implements HttpSessionListener {
/*public static Map userMap = new HashMap();  */
/*private   MySessionContext myc=MySessionContext.getInstance();  */

@Override
public void sessionCreated(HttpSessionEvent arg0) {
    /*myc.AddSession(arg0.getSession()); */
    MySessionContext.AddSession(arg0.getSession());
}

@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
    HttpSession session = arg0.getSession();  
    /*myc.DelSession(session);*/
     MySessionContext.DelSession(session);
}

}

web.xml添加一个监听器:

<listener>  
<listenerclass>com.common.util.SessionListener</istener-class>  
</listener>  

根据sessionId获取Session对象:

String sessionId = request.getParameter("sessionId");

HttpSession session = MySessionContext.getSession(sessionId);

写到这里已经算是完成一半了,只要在后台加一个处理小程序进来的方法,把所需要的用户信息保存到数据库中。
我们不需要改变以前写好处理公众号的任何代码(根据项目进行整合),

public String wxLogin(){//java后端
String Code = request.getParameter("code");// 登陆状态码
    String typeDate = request.getParameter("encryptedData");// 用户加密信息,用于解密获取unionid
    String iv = request.getParameter("iv");// 解密密钥
    String OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret"
            + "=SECRET&js_code=JSCODE&grant_type=authorization_code";// 获取openid的地址
    UserInfo userInfo = null;
    String url = OPENID_URL.replace("APPID", Pkcs7Encoder.APPID)
            .replace("SECRET", Pkcs7Encoder.APPSECRET)
            .replace("JSCODE", Code);
    JSONObject jsonObject = null;
    jsonObject = Pkcs7Encoder.doGetStr(url);// 调取Get提交方法
    String OpenId = jsonObject.getString("openid"); // 获取openid
    /*
     * ResourceBundle resource = ResourceBundle.getBundle("weixin");
     * //读取属性文件
     */String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; // 请求地址
    /* String wxUserInfo=Pkcs7Encoder.sendPost(requestUrl, requestUrlParam); */
    // 获取会话密钥(session_key)
    String session_key = jsonObject.getString("session_key");   
    ....     //通过逻辑代码保存我们需要的信息
}

  接下来到最重要的环节了,到了我们最期待的支付环节了。由于web-view目前还不支持支付接口。
  我们需要判断是否运行在小程序环境中进行支付的互换,从而实现支付功能。

<script type="text/javascript"src="https://res.wx.qq.com/open/js/jweixin-1.3.0.js"></script>

在网页内可通过window.__wxjs_environment变量判断是否在小程序环境。


// web-view下的页面内
wx.ready(function() {
console.log(window.__wxjs_environment === 'miniprogram') // true
})

例如:

  var isWxMini = window.__wxjs_environment ===       'miniprogram';
    if (isWxMini) {//判断是否在微信小程序环境中调用支付接口
    $("#wxloading").hide();
    var jumpUrl = encodeURIComponent(window.location)
    //把要用到的参数传到小程序中进行支付
    var path = '/pages/pay/pay?jhNum='+jhNum+'&cproType='+cproType+'&zcProId='+zcProId+'&url='+url+'&cproId='+cproId+'&esOrderc='+esOrderc;
    wx.miniProgram.navigateTo({
        url: path
    })

}else{//公众号支付
window.location.href="/vip/buy-group_saveJoinPt.html?cproId="+cproId+"&jhNum="+jhNum+"&cproType="+cproType+"&zcProId="+zcProId+"&esOrderc="+esOrderc;
}

小程序pay文件夹下新建pay.js和pay.wxml

 pay.js
          Page({
  onLoad: function (options) {
  console.log(options)
    // 获取网页传过来的值
 // TODO 用es6解构来获取值TODO
var jhNum = options.jhNum
var cproType = options.cproType
var zcProId = options.zcProId
var cproId = options.cproId
var esOrderc = options.esOrderc
// var jumpUrl = decodeURIComponent(options.jumpUrl)
var tourl=options.url
// console.log(tourl)
var openid = getApp().openid
console.log(openid)
var session_id = wx.getStorageSync('JSESSIONID')//本地取存储的sessionID
// console.log('----------sessionid' + session_id+'-----------------')
wx.request({
  url: tourl,
  data: {
    openid: openid,
    jhNum: jhNum,
    cproType: cproType,
    zcProId: zcProId,
    cproId: cproId,
    esOrderc: esOrderc
  },
  header: {
    "Content-Type": "application/x-www-form-urlencoded",
    'Cookie': 'JSESSIONID=' + session_id
  },
  method: 'POST',
  success: function (res){
    console.log(res)
    wx.requestPayment({
      'timeStamp': res.data.timeStamp,
      'nonceStr': res.data.nonceStr,
      'package': res.data.package,
      'signType': 'MD5',
      'paySign': res.data.sign,
      'success': function (res) {
        console.log('支付成功')
         wx.navigateBack() //返回会上个页面
      },
      'fail': function (res) {
        console.log('支付失败')
          wx.navigateBack() //返回会上个页面
      }
    })
  }
})
}
})

到这基本上已经完成,个人感觉这种实现方式在用户体验度上并不是很友好,但是基本的功能都可以实现。
相信官方也会对webview更进一步的优化。能在webview中直接调用支付的接口。
如果大家有更好的实现思路,千万别忘了告诉我哦。
由于作者还是个工作不到一年的小菜。在代码编写上还有很多的不足,欢迎大家指正。
这也是我的第一篇博客,嗯嗯嗯还是有点小激动。^_^
2018-11-13 15:09:15 qq_38021767 阅读数 109
  • 微信开发者工具使用教程 如何上传、发布、审核等

    微信开发者工具使用教程 如何上传、发布、审核等 2020年3月31日录 - 微信开发者工具使用教程 01-微信开发者工具的下载和安装 02-微信小程序账号的注册、登录、appid的获取 03-微信小程序开发和公众号开发模式的切换 04-微信小程序如何导入项目 05-不在以下request合法域名列表如何设置解决 06-模拟器、编辑器、调试器的使用 07-本地如何调试、手机测试小程序 08-公众号账号关联和管理多个小程序 09-微信小程序项目管理、上传、审核、发布 10-如何能学会微信小程序开发

    123 人正在学习 去看看 黄菊华

温馨提示:为保证数据传输安全,提高业务安全性,公众平台将不再支持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为调用的接口名

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

草图:

 

 

 

 

 

示例:选择照片

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

 

 

   

 

 

 

2018-09-30 17:03:26 YCM1101743158 阅读数 397
  • 微信开发者工具使用教程 如何上传、发布、审核等

    微信开发者工具使用教程 如何上传、发布、审核等 2020年3月31日录 - 微信开发者工具使用教程 01-微信开发者工具的下载和安装 02-微信小程序账号的注册、登录、appid的获取 03-微信小程序开发和公众号开发模式的切换 04-微信小程序如何导入项目 05-不在以下request合法域名列表如何设置解决 06-模拟器、编辑器、调试器的使用 07-本地如何调试、手机测试小程序 08-公众号账号关联和管理多个小程序 09-微信小程序项目管理、上传、审核、发布 10-如何能学会微信小程序开发

    123 人正在学习 去看看 黄菊华

微信公众号功能模块划分,底栏分为两部分:3*5功能栏,和用来切换3*5功能栏与发送信息的按钮;中间部分是信息交流部分,用户可以发送文字语音,也可以接收公众号发送的图文链接、模板消息、文字。

在自定义开发的过程中,系统默认的满不足不了我们的需求,很多时候需要我们进行扩展,下面汇总一下几个扩展接口

微信服务号通过认证后会开放9个高级接口分别是:

  1. 语音识别接口
  2. 客服接口
  3. OAuth2.0网页授权
  4. 生成带参数的二维码
  5. 获取用户地理信息
  6. 获取用户基本信息
  7. 获取关注者列表
  8. 用户分组
  9. 上传下载多媒体文件

产品设计的过程中能进一步帮我们扩展相关我们想要的功能

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