微信JSSDK|Vue微信公众号H5调用jssdk jsapi,例如(微信扫一扫)

官方文档地址
https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
1、先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
【微信JSSDK|Vue微信公众号H5调用jssdk jsapi,例如(微信扫一扫)】2、H5或Web项目下:
调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js

3、Vue项目下:
3-1、安装
cnpm install weixin-jsapi --save

4、后台代码:
//以下为缓存使用的key值 /** 获取到的凭证 */ private static final String WX_ACCESS_TOKEN_KEY = "wxAccessTokenKey"; /** 凭证有效时间,单位:秒 */ private static final String WX_EXPIRES_IN_KEY = "wxExpiresInKey"; /** 获取凭证时的时间 */ private static final String WX_TOKEN_TIME_KEY = "wxTokenTimeKey"; /** 获取到的ticket */ private static final String WX_TICKET_KEY = "wxTicketKey"; /** ticket有效时间,单位:秒 */ private static final String WX_TICKET_EXPIRES_IN_KEY = "wxTicketExpiresInKey"; /** 获取ticket时的时间 */ private static final String WX_TICKET_TIME_KEY = "wxTicketTimeKey"; /** * 获取对应的access_token * @author lan * @since 2020-5-6 * @url https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx10557a0b4df259ef&secret=fed31df017dc2b9db5b4530f9405ca60 * @return {"access_token":"ACCESS_TOKEN","expires_in":7200} * @return {"errcode":40164,"errmsg":"invalid ip 192.168.0.1 ipv6 *:ffff:192.168.0.1, not in whitelist hint: [gtGupa07444814]"} */ private Map, Object> getAccessToken() { String appId = "申请的APPID"; //平台配置的AppId String appSecret = "申请的AppSecret"; //平台配置的AppSecret Map, Object> result = new HashMap, Object>(); try { // 从缓存中读取 if (null != redisService.getCodeVal(WX_ACCESS_TOKEN_KEY) && null != redisService.getCodeVal(WX_EXPIRES_IN_KEY) && !"".equals(redisService.getCodeVal(WX_ACCESS_TOKEN_KEY)) && !"".equals(redisService.getCodeVal(WX_EXPIRES_IN_KEY))) { long expirsIn = Long.valueOf(redisService.getCodeVal(WX_EXPIRES_IN_KEY).toString()); long tokenTime = Long.valueOf(redisService.getCodeVal(WX_TOKEN_TIME_KEY).toString()); long currentTime = new Date().getTime() / 1000; if ((currentTime - tokenTime) < expirsIn) { String accessToken = redisService.getCodeVal(WX_ACCESS_TOKEN_KEY).toString(); if (!StringUtils.isBlank(accessToken)) { System.out.println("access_token为缓存取值:" + accessToken); result.put("success", true); result.put("accessToken", accessToken); return result; } else { System.out.println("access_token为缓存取值为空,立即接口取值"); } } }if (!StringUtils.isBlank(appId) && !StringUtils.isBlank(appSecret) && !StringUtils.isBlank(AccessTokenUrl)) { System.out.println("access_token为接口取值"); long currentTime = new Date().getTime() / 1000; redisService.saveCode(WX_TOKEN_TIME_KEY, currentTime + "", 7200L); StringBuffer sb = new StringBuffer("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"); sb.append("&appid=").append(appId).append("&secret=").append(appSecret); String res = httpGet(sb.toString()); if (!StringUtils.isBlank(res)) { JSONObject obj = JSONObject.parseObject(res); if (obj != null) { if (obj.get("access_token") != null) { result.put("success", true); result.put("accessToken", obj.get("access_token").toString()); // 加入缓存 redisService.saveCode(WX_ACCESS_TOKEN_KEY,obj.get("access_token").toString(), 7200L); redisService.saveCode(WX_EXPIRES_IN_KEY, obj.get("expires_in").toString(), 7200L); } else { result.put("success", false); result.put("accessToken", obj.get("errmsg")); } } else { result.put("success", false); result.put("accessToken", "操作失败"); } } else { result.put("success", false); result.put("accessToken", "操作失败"); } } else { result.put("success", false); result.put("accessToken", "操作失败"); } return result; } catch (Exception e) { System.out.println("操作失败" + e); result.put("success", false); result.put("message", e.getMessage()); return result; } }/** * 获取对应的jsapi_ticket * @author lan * @since 2020-5-6 * @url https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi * @return { "errcode":0, "errmsg":"ok","ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", *"expires_in":7200 } * @return {"errcode":40164,"errmsg":"invalid ip 192.168.0.1 ipv6 *::ffff:192.168.0.1, not in whitelist hint: [gtGupa07444814]"} */ private String getJsapiTicket(String accessToken) { System.out.println("执行getJsapiTicket"); try { // 从缓存中读取 if (null != redisService.getCodeVal(WX_TICKET_KEY) && null != redisService.getCodeVal(WX_TICKET_EXPIRES_IN_KEY) && !"".equals(redisService.getCodeVal(WX_TICKET_KEY)) && !"".equals(redisService.getCodeVal(WX_TICKET_EXPIRES_IN_KEY))) { long expirsIn = Long.valueOf(redisService.getCodeVal(WX_TICKET_EXPIRES_IN_KEY).toString()); long tokenTime = Long.valueOf(redisService.getCodeVal(WX_TICKET_TIME_KEY).toString()); long currentTime = new Date().getTime() / 1000; if ((currentTime - tokenTime) < expirsIn) { String ticket = redisService.getCodeVal(WX_TICKET_KEY).toString(); if (!StringUtils.isBlank(ticket)) { System.out.println("ticket为缓存取值:" + ticket); return ticket; } else { System.out.println("ticket为缓存取值为空,立即接口取值"); } } }if (!StringUtils.isBlank(accessToken)) { System.out.println("ticket为接口取值"); long currentTime = new Date().getTime() / 1000; redisService.saveCode(WX_TICKET_TIME_KEY, currentTime + "", 7200L); StringBuffer sb = new StringBuffer("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"); sb.append("&access_token=").append(accessToken); String res = httpGet(sb.toString()); if (!StringUtils.isBlank(res)) { JSONObject obj = JSONObject.parseObject(res); if (obj != null) { if (obj.get("ticket") != null && "0".equals(obj.get("errcode").toString())) { // 加入缓存 redisService.saveCode(WX_TICKET_KEY, obj.get("ticket").toString(), 7200L); redisService.saveCode(WX_TICKET_EXPIRES_IN_KEY, obj.get("expires_in").toString(), 7200L); return obj.get("ticket").toString(); } else { return null; } } else { return null; } } else { return null; } } else { return null; } } catch (Exception e) { System.out.println("操作失败" + e); return null; } }//提供一个供项目调用的接口 public ApiResult getJssdkSign(@RequestBody String jsonStr) { Map, Object> map = new LinkedHashMap<>(); try { JSONObject jsonObject = JSON.parseObject(jsonStr); Map, Object> accessTokenMap = storeOrderService.getAccessToken(); if (!Boolean.valueOf(accessTokenMap.get("success").toString())) { return ApiResult.fail(ApiCode.FAIL); } else { String appId = systemConfigService.getData(SystemConfigConstants.WECHAT_APPID); //平台配置的AppId String jsapiTicket = storeOrderService.getJsapiTicket(accessTokenMap.get("accessToken").toString()); if (!StringUtil.isBlank(jsapiTicket)) { Map, String> ret = CreateWxSignUtil.getWxSign(jsapiTicket, jsonObject.getString("url").toString()); map.put("appId", "wxff65c98dac9c8a2b"); map.put("jsApiList",new String[] { "scanQRCode" }); map.put("nonceStr", ret.get("nonceStr")); map.put("signature", ret.get("signature")); map.put("timestamp", ret.get("timestamp")); map.put("url", ret.get("url")); } } } catch (Exception e) { System.out.println("操作失败" + e); } return ApiResult.ok(map); }
工具类
import java.util.UUID; import java.util.Map; import java.util.HashMap; import java.util.Formatter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.io.UnsupportedEncodingException; public class CreateWxSignUtil { public static Map, String> getWxSign(String jsapi_ticket, String url) { Map, String> ret = new HashMap, String>(); String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String string1; String signature = ""; //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }ret.put("url", url); ret.put("jsapiTicket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; }private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; }private static String create_nonce_str() { return UUID.randomUUID().toString(); }private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } }

5、前端
5-1、使用
import wx from "weixin-jsapi";

5-2、配置
//自定义一个POST请求 export function getJssdkSign(data) { return request.post("http:127.0.0.1:8080/projectName/getJssdkSign", data); }//微信扫一扫调用sdk核心配置 let url = location.href.split("#")[0]; //这里【url参数一定是去参的动态的当前页网址】 let params = {}; params.url = url; getJssdkSign(params).then(res => { wx.config({ // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 debug: false, // 必填,公众号的唯一标识 appId: res.data.appId, // 必填,生成签名的时间戳 timestamp: res.data.timestamp, // 必填,生成签名的随机串 nonceStr: res.data.nonceStr, // 必填,签名 signature: res.data.signature, // 必填,需要使用的JS接口列表,所有JS接口列表 jsApiList: ["scanQRCode","checkJsApi", ......] }); wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 }); wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 }); }); //调起微信扫一扫 openQRCode: function() { let that = this; wx.scanQRCode({ needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, //scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有 success: function(res) { let result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果 if (result != null && result != "" && result != undefined) { //扫码结果 //业务逻辑处理...... } } }); }

6、注意事项:
6-1、网上有说使用 “cnpm install weixin-js-sdk --save”,但我使用时出现-wx:undefined,后改用3-1即可,据了解微信提供了Vue环境下直接引用,未深究。
6-2、当出现 “config:invalid signature” 时,检查签名是否正确,可用官方提供的验签工具验证:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
6-3、签名时必须使用jsapi_ticket生成,生成签名时所需的URL必须为不包含#号的动态的本网页地址。
6-4、测试时需要使用服务器部署项目,使用nginx指向本地ip进行调试。

    推荐阅读