微信JSSDK之添加微信卡券
微信卡券的使用,是在之前的微信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 + "×tamp=" + 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之添加微信卡券相关推荐
- 微信jssdk开发 java_Java微信公众平台开发(十三)--微信JSSDK中Config配置
前端开发工程师和关注前端开发的开发者们在2015年中肯定被腾讯的JSSDk引爆过,搞APP的.搞前端的甚至是是搞后端的都跑过来凑热闹,一时之间也把微信JSSDK捧得特别牛逼,但是在我们的技术眼里它的实 ...
- php微信jssdk下载图片,微信JSSDK上传多张图片回调方法以及服务器端处理下载媒体...
有时候我们会在微信有这样的需求.在一个上传控件里面上传多个图片.而在微信里面使用file上传有些手机会崩溃而且.服务器端压缩图片效果也没有微信压缩效果好.这个就要使用微信的jssdk了. 如下就是微信 ...
- 微信jssdk上传图片java_微信JSSDK上传图片_javascript技巧
前不久微信公开了一些接口,其中有一个uploadImage接口用于上传图片,一般和chooseImage接口配合使用.先调用chooseImage接口让用户选择一张或者多张图片,用户选择完毕后微信会返 ...
- vue 使用微信jssdk, 调用微信相册上传图片
vue 使用微信jssdk 1.引入weixin-js-sdk npm install weixin-js-sdk 使用文档 https://www.npmjs.com/package/weixin- ...
- 微信jssdk批量添加卡券接口(踩坑经验)
1)首先是官方接口文档: 1.批量添加卡券接口:https://mp.weixin.qq.com/wiki?action=doc&id=mp1421141115&t=0.0861973 ...
- vue获取微信登陆权限_Vue获取微信JSSDK授权,以及微信分享
场景 Vue获取JSSDK授权以便在微信浏览器,或者嵌在小程序里面做些小事情,如朋友/朋友圈分享,微信支付,图片上传下载等 步骤一 配置安全域名: 在微信公众平台对应的公众号设置里面配上相应的JS接口 ...
- php微信jssdk获取位置,微信公众号获取用户地理位置
微信公众开放平台 接口文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140841 获取用户地理位置(需要用户点击同 ...
- 微信公众平台开发 微信JSSDK开发
根据微信开发文档步骤如下: 1.先登录微信公众平台进入"公众号设置"的"功能设置"里填写"JS接口安全域名". JS接口安全域名设置 mi. ...
- 微信JS-SDK 分享到朋友圈 分享给朋友 分享到QQ 拍照或从手机相册中选图 识别音频并返回识别结果 使用微信内置地图查看位置
微信JS-SDK 分享到朋友圈 分享给朋友 分享到QQ 拍照或从手机相册中选图 识别音频并返回识别结果 使用微信内置地图查看位置 一.JS部分 wx.ready(function () {// 1 判 ...
最新文章
- windows版influxDB安装与配置
- 内置锁的能力不足以满足需求
- 洛谷 - P4012 深海机器人问题(最大费用最大流)
- try-expect在集合处理中的应用
- win7显示桌面计算机图标,win7显示桌面图标不见了如何恢复【图文】
- 【unity】编辑模式预览Animator动作,2种实现。 其中一种 playback模式是Cinema Director用的会使Unity Crash
- 2021牛客暑期多校训练营#10:F-Train Wreck
- 大话设计模式之爱你一万年:第八章 结构型模式:外观(门面)模式:冬天有你不再寒冷:1.外观模式概念
- 040 罗尔定理与零点定理、介值定理综合应用;柯西中值定理; 型二( f(n) (ξ) =0 )
- mac brew 启动服务时报错“Bootstrap failed: 5: Input/output error”
- English语法_定语从句 - 小细节
- Mac下复制粘贴的快捷键是什么?随记
- Ollydbg之字符串、WindowsAPI搜索
- mysql null处理_MySQL中处理Null时要注意两大陷阱
- 你从哪里来你是谁你到哪里去_你到底在哪里?
- python背景颜色代码大全_python3中布局背景颜色代码分析
- web项目发布到iis中readystate一直处于 interactive_Framer Web 发布后,终于像个正经的设计软件了。...
- 宗镜录略讲——南怀瑾老师——系列6
- 淘宝API开发系列---阿里.聚石塔.开放平台的使用5
- VM15.5虚拟机安装openwrt系统作为旁路由
热门文章
- 阿贝云免费虚拟主机使用体验
- hdmi网线延长器_HDMI延长器怎么用?HDMI单网线延长器HE101安装使用图文教程-ekL官网...
- 什么是接地电阻?如何测量防雷接地电阻
- JDBC常见错误(1)——网络适配器无法连接到
- 如何用python爬虫爬取qq空间说说
- usaco 虫洞 洛谷
- 浅谈static_cast、dynamic_cast、const_cast、reinterpret_cast用法
- ETC卡PBOC操作
- http的无连接和无状态
- 近红外 CMOS 相机