微信卡券的使用,是在之前的微信jsapi基础上,再加上一次卡券的单独验签,

这里对上篇微信JSSDK的使用稍作修改:

1. 微信的accessToken的获取有时间限制,之前是将token的读取放在一个单独的服务上, 单控

2.基于开个别的服务比较繁琐,现在使用redis缓存,来控制访问频率,至于并发,由锁来控制

代码如下:

package com.mazing.commons.wx;

import java.util.HashMap;import java.util.Map;

import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;import com.mazing.commons.cache.redis.Redis;import com.mazing.commons.cache.redis.RedisLock;import com.mazing.commons.server.context.MainframeBeanFactory;import com.mazing.commons.utils.HttpClientUtils;import com.mazing.commons.utils.JsonUtils;import com.mazing.commons.utils.LogUtil;import com.mazing.commons.utils.cfg.EncryptionPropertyPlaceholderConfigurer;

/** * access token 改为缓存读取方式,失效再读取 *  * @author sky * */public class KFwxAccessTokenReader {

   private static final Logger logger = LoggerFactory.getLogger(KFwxAccessTokenReader.class);

 /**    * 获取间隔    */   private static final int INTERVAL = 2 * 60 ;

   // access token 在 7200秒过期, private static int TOKEN_TIMEOUT = 7200 - INTERVAL;

    private static String appid = null;

    private static String secret = null;

   /**    * access token    */   public String wxAccessToken = "";

    /**    * jsapi_ticket    */   public String wxJsapiTicket = "";

    /**    * 卡券api_ticket    */   public String wxCardApiTicket = "";

  private static Redis redis;

 static {

        readConfig();     //clearCache();   }

   public static Map<String, String> readAccessToken() {

     Map<String, String> map = new HashMap<>();

     String atKey = getJsAccessTokenCacheKey();       String accessToken = redis().get(atKey);     String jsapiTicket = "";       String cardApiTicket = "";     if (StringUtils.isBlank(accessToken)) {

         accessToken = readToken();           jsapiTicket = readTicket(accessToken);           cardApiTicket = readCardTicket(accessToken);

           logger.info("kf#wx#accessToken | cache为空,通过接口重新获取数据 | accessToken: {}, jsapiTicket: {},cardApiTicket: {}", LogUtil.hidePassport(accessToken),                    LogUtil.hidePassport(jsapiTicket),  LogUtil.hidePassport(cardApiTicket));        } else {

            jsapiTicket = redis().get(getJsapiTicketCacheKey());         cardApiTicket = redis().get(getCardApiTicketCacheKey());

           logger.info("kf#wx#accessToken | cache不为空, 获取缓存数据 | accessToken: {}, jsapiTicket: {},cardApiTicket: {}", LogUtil.hidePassport(accessToken),                   LogUtil.hidePassport(jsapiTicket),  LogUtil.hidePassport(cardApiTicket));        }

       map.put(WeixinAccessToken.KF_ACCESS_TOKEN, accessToken);      map.put(WeixinAccessToken.KF_JSAPI_TICKET, jsapiTicket);      map.put(WeixinAccessToken.KF_CARD_API_TICKET, cardApiTicket);

       return map;

 }

   private static String getJsAccessTokenCacheKey() {        return "KF_wxAccessTokenKey";   }

   /**    * jsapi_ticket cache key  *     * @return    */   private static String getJsapiTicketCacheKey() {      return "KF_wxJSapiTicketKey";   }

   /**    * 卡券 api_ticket cache key     *     * @return    */   private static String getCardApiTicketCacheKey() {        return "KF_wxCardApiTicketKey"; }

   public static void clearCache() {

       redis().del(getJsAccessTokenCacheKey());      redis().del(getJsapiTicketCacheKey());        redis().del(getCardApiTicketCacheKey());

        logger.info("清除微信卡券相关缓存....");  }

   private static Redis redis() {        if (null == redis)          redis = (Redis) MainframeBeanFactory.getBean("redis");     return redis; }

   /**    * 读取配置,只读一次    *     * @throws Exception  */   public static void readConfig() {

       appid = (String) EncryptionPropertyPlaceholderConfigurer.getConfig().get("kf_appid");      secret = (String) EncryptionPropertyPlaceholderConfigurer.getConfig().get("kf_appsecret"); }

   /**    * 读取 access token     */   private static String readToken() {

     RedisLock lock = new RedisLock(redis, "KFWxAccessTokenKey");

     String wxAccessToken = "";

       if (lock.lock()) {            Map<String, String> params = new HashMap<>(4);           params.put("grant_type", "client_credential");            params.put("appid", appid);         params.put("secret", secret);           String json = HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/token", 10000, params);         if (StringUtils.isBlank(json)) {              logger.error("读取微信 access token 错误");           }         Map<String, Object> map = JsonUtils.parseObject(json.trim(), new TypeReference<Map<String, Object>>() {            });           if (map.containsKey("access_token")) {              wxAccessToken = (String) map.get("access_token");              logger.info("读取微信 access token 成功");                if (map.containsKey("expires_in")) {                    int expiresIn = ((Number) map.get("expires_in")).intValue();                   logger.info("过期时间 (s):" + expiresIn);                   TOKEN_TIMEOUT = expiresIn  - INTERVAL;                   // 缓存,并设置过期时间                  redis().set(getJsAccessTokenCacheKey(), wxAccessToken, TOKEN_TIMEOUT);                }

           } else {              logger.error("读取微信 access token 错误:" + json);           }

           lock.unlock();        }

       return wxAccessToken; }

   /**    * 读取 jsapi_ticket     */   private static String readTicket(String wxAccessToken) {

        String wxJsapiTicket = "";

       Map<String, String> params = new HashMap<>(4);       params.put("access_token", wxAccessToken);      params.put("type", "jsapi");      String json = HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket", 10000, params);      Map<String, Object> map = JsonUtils.parseObject(json.trim(), new TypeReference<Map<String, Object>>() {        });       int errcode = -1;        if (map.get("errcode") != null) {          errcode = ((Number) map.get("errcode")).intValue();            logger.info("读取微信 jsapi_ticket 结果,errcode: {}, errmsg: {}", errcode, map.get("errmsg"));           if (errcode == 0) {             wxJsapiTicket = (String) map.get("ticket");

              // set to cache               redis().set(getJsapiTicketCacheKey(), wxJsapiTicket);

               logger.info("读取微信 jsapi_ticket 成功!");           }     } else {          logger.error("读取微信 jsapi_ticket 错误:" + json);       }

       return wxJsapiTicket; }

   /**    * 获取卡券 api_ticket     */   private static String readCardTicket(String wxAccessToken) {

        String wxCardApiTicket = "";

     Map<String, String> params = new HashMap<>(4);       params.put("access_token", wxAccessToken);      params.put("type", "wx_card");        String json = HttpClientUtils.doGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket", 10000, params);      Map<String, Object> map = JsonUtils.parseObject(json.trim(), new TypeReference<Map<String, Object>>() {        });       int errcode = -1;        if (map.get("errcode") != null) {          errcode = ((Number) map.get("errcode")).intValue();            logger.info("读取微信  卡券 api_ticket 结果,errcode: {}, errmsg: {}", errcode, map.get("errmsg"));         if (errcode == 0) {

               wxCardApiTicket = (String) map.get("ticket");              // set to cache               redis().set(getCardApiTicketCacheKey(), wxCardApiTicket);

               logger.info("读取微信 卡券 api_ticket 成功! ticket: {}", LogUtil.hidePassport(wxCardApiTicket));            }     } else {          logger.error("读取微信 卡券 api_ticket 错误:" + json);      }

       return wxCardApiTicket;   }

}

util类

package com.mazing.mobile.web.kaifun;

import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;

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

import org.apache.commons.lang3.RandomStringUtils;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;

import com.mazing.CommonConstants;import com.mazing.commons.utils.JsonUtils;import com.mazing.commons.utils.LogUtil;import com.mazing.commons.utils.cfg.EncryptionPropertyPlaceholderConfigurer;import com.mazing.commons.utils.encrypt.EncryptUtil;import com.mazing.commons.wx.KFwxAccessTokenReader;import com.mazing.commons.wx.WeixinAccessToken;import com.mazing.core.web.ResultCode;import com.mazing.core.web.exception.ServiceException;

/** * 对接公众号的微信平台 工具类(验签参数获取) *  * @author sky 2016-05-57 * */public class KaiFunWXUtil {

  private static final Logger logger = LoggerFactory.getLogger(KaiFunWXUtil.class);

  /**    * 微信分享的attr   */   public static final String WX_CONFIG_ATTR = "wxConfig";

  /**    * 开饭 微信appid  */   private static String KF_WX_APPID = null;

  // 原始值    public static String KF_WX_CARDID = null;

  /**    * 检查当前的请求是否是 https 的,如果不是则重定向成为https<br>     * 如果执行了重定向,返回true  */   public static boolean redirectFullUrl2Https(HttpServletRequest request, HttpServletResponse response) throws IOException {        String params = request.getQueryString();        String url = request.getRequestURL().toString();     String scheme = request.getHeader("x-forwarded-proto");

      boolean need2https = (url.startsWith("http://") // 如果是http的请求      && (StringUtils.isBlank(scheme) || !("https".equals(scheme))));// nginx上报的不是https协议

       // 重定向请求为https,并返回true     if (need2https) {         url = url.replaceFirst("http://", "https://");

         if (params != null)              url += "?" + params;

           response.sendRedirect(url);           return true;      }

       return false; }

   /**    * 返回当前页面完整的 URL,包括“?”后面的参数     *     * @param request     * @return    */   public static String getFullUrl(HttpServletRequest request) {     String params = request.getQueryString();        String url = request.getRequestURL().toString();

       // nginx 的 upstream配置使用的是 http,导致外部是https的请求,来到代码却是http     // nginx 会带上scheme参数(443端口才设置)      String scheme = request.getHeader("x-forwarded-proto");        if (StringUtils.isNotBlank(scheme) && "https".equals(scheme))           url = url.replaceFirst("http://", "https://");

     if (params != null) {            url += "?" + params;     }     return url;   }

   public static void main(String[] args) {      String url = "http://xxx.com?callback=http://123.com";        System.out.println(url.replaceFirst("http", "https"));    }

   /**    * 微信公众号的appid     *     * @return    */   private static String getWxMpAppId() {        if (KF_WX_APPID == null) {          KF_WX_APPID = (String) EncryptionPropertyPlaceholderConfigurer.getConfig().get("kf_appid");        }     return KF_WX_APPID;   }

   private static String getWxMpCardId() {       if (KF_WX_CARDID == null) {         KF_WX_CARDID = (String) EncryptionPropertyPlaceholderConfigurer.getConfig().get("kf_cardId");      }     return KF_WX_CARDID;  }

   /**    * 微信分享的 config    *     * @param url 当前页面完整的地址,包括参数   * @return    */   public static String getWxMpConfig(String url) {      String noncestr = RandomStringUtils.random(16, CommonConstants.LETTER_NUMBER_CHARS);     long timestamp = System.currentTimeMillis() / 1000; // 秒

       String appId = getWxMpAppId();

     Map<String, String> amap = KFwxAccessTokenReader.readAccessToken();        String jsapiTicket = amap.get(WeixinAccessToken.KF_JSAPI_TICKET);

      logger.info("mobile#share#getWxMpConfig | 设置微信分享配置 |url: {}, KF_JSAPI_TICKET: {}", url, LogUtil.hidePassport(jsapiTicket));     String sourceStr = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;

     String signature = EncryptUtil.getSHA1(sourceStr).toLowerCase();     String[] jsApiList = new String[] { //       "checkJsApi", //                "onMenuShareTimeline", //               "onMenuShareAppMessage",//              "onMenuShareQQ",//              "onMenuShareWeibo", //              "onMenuShareQZone",//               "hideAllNonBaseMenuItem",//             "addCard",//                "showAllNonBaseMenuItem" };     Map<String, Object> map = new HashMap<>(8);      map.put("nonceStr", noncestr);      map.put("timestamp", timestamp);        map.put("appId", appId);        map.put("signature", signature);        map.put("debug", false);        map.put("jsApiList", jsApiList);        return JsonUtils.toJSONString(map);   }

   /**    * 获取卡券的签名     *     * @return    */   public static void setCardSignature(HttpServletRequest request) {

       long timestamp = System.currentTimeMillis() / 1000; // 秒

       String cardId = getWxMpCardId();     String timeStr = timestamp + "";

        Map<String, String> map = KFwxAccessTokenReader.readAccessToken();     String apiTicket = map.get(WeixinAccessToken.KF_CARD_API_TICKET);

      if (StringUtils.isBlank(apiTicket)) {         throw new ServiceException(ResultCode.FAILURE, "系统繁忙, 请稍后再试");       }

       logger.info("kaifun#wx#signature | 验签参数 | cardId: {}, timestamp: {}, apiTicket: {}", cardId, timeStr, LogUtil.hidePassport(apiTicket));

       List<String> list = new ArrayList<>();

     list.add(timeStr);        list.add(cardId);     list.add(apiTicket);

        Collections.sort(list);

     String sourceStr = "";

       for (Object obj : list) {         sourceStr += obj;       }

       String signature = EncryptUtil.getSHA1(sourceStr).toLowerCase();

       //logger.info("kaifun#wx#signature | 验签参数排序后 | sourceStr: {}, signature:  {}", sourceStr, signature);

     Map<String, Object> ext = new HashMap<>();

     ext.put("timestamp", timeStr);      ext.put("signature", signature);

      request.setAttribute("ext", JsonUtils.toJSONString(ext));

     request.setAttribute("_cardid", KF_WX_CARDID);

    }

   /**    * 设置微信分享JSSDK 需要的config配置信息<br>     *     * 凡是需要设置分享操作菜单(分享至朋友圈、QQ、微博, 或者屏蔽分享功能,只保留设置字体、刷新 等 菜单) 相关的页面, 都需要调用该方法进行config设置     *     * @param model   * @param request     * @author sky 2016-04-19     */   public static void setWxJsConfig(HttpServletRequest request) {        // 微信相关的内容        String url = getFullUrl(request);

      request.setAttribute(WX_CONFIG_ATTR, getWxMpConfig(url));

       setCardSignature(request);

  }

}

controller下发

package com.mazing.mobile.web.kaifun;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;

@Controller@RequestMapping("/open/wx/card")public class KFCardController {

    @RequestMapping("obtain/index")    public String searchIndex(Model model, HttpServletRequest request) {

      // 微信相关的内容        KaiFunWXUtil.setWxJsConfig(request);

        return "/open/kaifun/obtain_index"; }}

jsp页面



<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><script type="text/javascript"  src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script><script type="text/javascript">

  // 微信分享的配置    wx.config(<c:out value="${wxConfig}" escapeXml="false"/>);

  wx.error(function(res) {      //alert(JSON.stringify(res)); });

 /**    http://www.xiaomeiti.com/note/3561    **/  function WeixinShare(shareData) {     this.shareData = shareData;      if (wx && wx.checkJsApi) {            this.shareType = "api";            this.initByAPI();     } else {          this.shareType = "bridge";         this.initByBridge();      } }

   WeixinShare.prototype.initByAPI = function() {       var me = this;

       wx.ready(function() {

            document.querySelector('#addCard').onclick = function () {                 wx.addCard({                   cardList: [                     {                       cardId: '${_cardid}',                       cardExt: '<c:out value="${ext}" escapeXml="false"/>'                     }                   ],                   success: function (res) {                     alert('已添加卡券:' + JSON.stringify(res.cardList));                   }                 });             };

             /* */   

         var shareData = {                title : me.getParam("title"),               desc : me.getParam("desc"),             link : me.getParam("link"),             imgUrl : me.getParam("imgUrl"),             trigger : function(res) {                 /*                    this.title = me.getParam("title");                 this.desc = me.getParam("desc");                   this.link = me.getParam("link");                   this.imgUrl = me.getParam("imgUrl");                    */               }         };

          wx.onMenuShareAppMessage(shareData);

            var shareData2 = {               title : me.getParam("title"),               desc : me.getParam("desc"),             link : me.getParam("link"),             imgUrl : me.getParam("imgUrl"),             trigger : function(res) {             }         };            var timelineTitle = me.getParam("timelineTitle");          if (timelineTitle) {              shareData2.title = timelineTitle;            }         wx.onMenuShareTimeline(shareData2);//分享朋友圈            wx.onMenuShareQQ(shareData);//分享至QQ           wx.onMenuShareWeibo(shareData);//分享到微博            wx.onMenuShareQZone(shareData);//分享到QZone

         //这里调用show all,是因为其他页面可能会调用了hide all(貌似是全局生效) 后跳转至 需要show all的页面           //wx.showAllNonBaseMenuItem({         //  success : function() {            //      //MazingEnv.log('已显示所有非基本菜单项');         //  }         //});     });   };

  WeixinShare.prototype.initByBridge = function() {        var me = this;       document.addEventListener('WeixinJSBridgeReady',                function onBridgeReady() {                    window.alert('Bridge triggered.');                  WeixinJSBridge.on('menu:share:appmessage', function(argv) {                     me.shareFriend()                  });                   WeixinJSBridge.on('menu:share:timeline', function(argv) {                       me.shareTimeline()                    });               }, false);    };

  WeixinShare.prototype.getParam = function(name) {        var val = this.shareData[name];      if (typeof val == "function") {           return val();     }

       return val;   };

  WeixinShare.prototype.shareFriend = function() {     WeixinJSBridge.invoke('sendAppMessage', {           appid : this.getParam("appid"),         img_url : this.getParam("imgUrl"),          img_width : 120,          img_height : 120,         link : this.getParam("link"),           title : this.getParam("title"),         desc : this.getParam("desc")        }, function(res) {            _report('send_msg', res.err_msg);       });   };

  WeixinShare.prototype.shareTimeline = function() {       WeixinJSBridge.invoke('shareTimeline', {            appid : this.getParam("appid"),         img_url : this.getParam("imgUrl"),          img_width : 120,          img_height : 120,         link : this.getParam("link"),           title : this.getParam("title"),         desc : this.getParam("desc")        }, function(res) {            _report('timeline', res.err_msg);       });   };

  var wxApi = new WeixinShare(shareData);</script>

微信JSSDK之添加微信卡券相关推荐

  1. 微信jssdk开发 java_Java微信公众平台开发(十三)--微信JSSDK中Config配置

    前端开发工程师和关注前端开发的开发者们在2015年中肯定被腾讯的JSSDk引爆过,搞APP的.搞前端的甚至是是搞后端的都跑过来凑热闹,一时之间也把微信JSSDK捧得特别牛逼,但是在我们的技术眼里它的实 ...

  2. php微信jssdk下载图片,微信JSSDK上传多张图片回调方法以及服务器端处理下载媒体...

    有时候我们会在微信有这样的需求.在一个上传控件里面上传多个图片.而在微信里面使用file上传有些手机会崩溃而且.服务器端压缩图片效果也没有微信压缩效果好.这个就要使用微信的jssdk了. 如下就是微信 ...

  3. 微信jssdk上传图片java_微信JSSDK上传图片_javascript技巧

    前不久微信公开了一些接口,其中有一个uploadImage接口用于上传图片,一般和chooseImage接口配合使用.先调用chooseImage接口让用户选择一张或者多张图片,用户选择完毕后微信会返 ...

  4. vue 使用微信jssdk, 调用微信相册上传图片

    vue 使用微信jssdk 1.引入weixin-js-sdk npm install weixin-js-sdk 使用文档 https://www.npmjs.com/package/weixin- ...

  5. 微信jssdk批量添加卡券接口(踩坑经验)

    1)首先是官方接口文档: 1.批量添加卡券接口:https://mp.weixin.qq.com/wiki?action=doc&id=mp1421141115&t=0.0861973 ...

  6. vue获取微信登陆权限_Vue获取微信JSSDK授权,以及微信分享

    场景 Vue获取JSSDK授权以便在微信浏览器,或者嵌在小程序里面做些小事情,如朋友/朋友圈分享,微信支付,图片上传下载等 步骤一 配置安全域名: 在微信公众平台对应的公众号设置里面配上相应的JS接口 ...

  7. php微信jssdk获取位置,微信公众号获取用户地理位置

    微信公众开放平台 接口文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140841 获取用户地理位置(需要用户点击同 ...

  8. 微信公众平台开发 微信JSSDK开发

    根据微信开发文档步骤如下: 1.先登录微信公众平台进入"公众号设置"的"功能设置"里填写"JS接口安全域名". JS接口安全域名设置 mi. ...

  9. 微信JS-SDK 分享到朋友圈 分享给朋友 分享到QQ 拍照或从手机相册中选图 识别音频并返回识别结果 使用微信内置地图查看位置

    微信JS-SDK 分享到朋友圈 分享给朋友 分享到QQ 拍照或从手机相册中选图 识别音频并返回识别结果 使用微信内置地图查看位置 一.JS部分 wx.ready(function () {// 1 判 ...

最新文章

  1. windows版influxDB安装与配置
  2. 内置锁的能力不足以满足需求
  3. 洛谷 - P4012 深海机器人问题(最大费用最大流)
  4. try-expect在集合处理中的应用
  5. win7显示桌面计算机图标,win7显示桌面图标不见了如何恢复【图文】
  6. 【unity】编辑模式预览Animator动作,2种实现。 其中一种 playback模式是Cinema Director用的会使Unity Crash
  7. 2021牛客暑期多校训练营#10:F-Train Wreck
  8. 大话设计模式之爱你一万年:第八章 结构型模式:外观(门面)模式:冬天有你不再寒冷:1.外观模式概念
  9. 040 罗尔定理与零点定理、介值定理综合应用;柯西中值定理; 型二( f(n) (ξ) =0 )
  10. mac brew 启动服务时报错“Bootstrap failed: 5: Input/output error”
  11. English语法_定语从句 - 小细节
  12. Mac下复制粘贴的快捷键是什么?随记
  13. Ollydbg之字符串、WindowsAPI搜索
  14. mysql null处理_MySQL中处理Null时要注意两大陷阱
  15. 你从哪里来你是谁你到哪里去_你到底在哪里?
  16. python背景颜色代码大全_python3中布局背景颜色代码分析
  17. web项目发布到iis中readystate一直处于 interactive_Framer Web 发布后,终于像个正经的设计软件了。...
  18. 宗镜录略讲——南怀瑾老师——系列6
  19. 淘宝API开发系列---阿里.聚石塔.开放平台的使用5
  20. VM15.5虚拟机安装openwrt系统作为旁路由

热门文章

  1. 阿贝云免费虚拟主机使用体验
  2. hdmi网线延长器_HDMI延长器怎么用?HDMI单网线延长器HE101安装使用图文教程-ekL官网...
  3. 什么是接地电阻?如何测量防雷接地电阻
  4. JDBC常见错误(1)——网络适配器无法连接到
  5. 如何用python爬虫爬取qq空间说说
  6. usaco 虫洞 洛谷
  7. 浅谈static_cast、dynamic_cast、const_cast、reinterpret_cast用法
  8. ETC卡PBOC操作
  9. http的无连接和无状态
  10. 近红外 CMOS 相机