1,首先需要有微信开发者账号
- 直接使用微信测试号:http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index。个人微信订阅号非认证状态不支持分享接口,所以只能使用测试号。
2,测试号配置说明
- 接口配置信息,先将本地ip内网穿透成外网可访问的域名,在通过你服务器的接口校验token(url指向你服务器端的接口)。 参考https://blog.csdn.net/Curtisjia/article/details/106041565?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.searchformbaiduhighlight&spm=1001.2101.3001.4242 内网穿透建议使用https://natapp.cn/免费的版本就够用了
-
JS接口安全域名。配置你服务器的域名,不加http(s)://这些,例如www.baidu.com详见开发文档
-
测试号二维码,自己微信关注就好了,微信号列就是对应的openId
-
体验接口权限表。有的接口需要手动设置或者开启,关注一下。
-
注意,如果后期过渡到服务号真实开发实现功能,还需要配置JS安全域名里的服务器秘钥
测试号不需要设置。
3,找到微信jssdk开发文档
4,引入微信的JS文件
-
可以直接引用
-
vue中也可以npm install weixin-js-sdk --save 然后在页面中引用
5,后端业务逻辑
- 部分后端处理代码
//前端请求的url
JSONObject reqJson = JSONObject.fromObject(reqStr);
String accessToken = WeixinUtil.getAccessToken();
String ticket = WeixinUtil.getJSAPITicket(accessToken);
Map<String, String> result = JSSign.sign(ticket,
JSONObject.fromObject(reqJson.getString("config")).getString("url"));
respJson.put("appId",Config.getWxConfig().getAppId());
respJson.put("nonceStr",result.get("nonceStr"));
respJson.put("signature",result.get("signature"));
respJson.put("timestamp",result.get("timestamp"));
respJson.put(ConstantUtil.RESP_CODE, ConstantUtil.RespCode.SUCC);
private static ConcurrentHashMap<String, String> TICKET_MAP = new ConcurrentHashMap<>();
private static final String JSAPI_TICKET = "jsapi_ticket";
private static final String EXPIRES_IN = "expires_in";
private static ConcurrentHashMap<String, String> ACCESS_TOKEN_MAP = new ConcurrentHashMap<>();
private static final String ACCESS_TOKEN = "access_token";
/**
* 获取微信access_token
* @return
* @throws Exception
*/
public static String getAccessToken() throws Exception{
String accessToken = null;
String response = null;
if (ACCESS_TOKEN_MAP.get(ACCESS_TOKEN) != null && Integer.parseInt(ACCESS_TOKEN_MAP.get(EXPIRES_IN)) > Utility.getCurrentTimeStamp()) {
accessToken = ACCESS_TOKEN_MAP.get(ACCESS_TOKEN);
log.info("getWxToken() end, access-token from redis, expires_in, accessToken={}"+accessToken);
return accessToken;
}
try {
//assessTokenUrl:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET
response = sendGET(Config.getWxConfig().getAccessTokeUrl().replace("APPID", appid).replace("SECRET", secret),null);
} catch (Exception e) {
log.error("getWxToken() exception. ", e);
}
if (response != null) {
JSONObject jsonObject = JSONObject.fromObject(response);
accessToken = jsonObject.getString("access_token");
Integer expiresIn = Integer.parseInt(jsonObject.getString("expires_in"));
ACCESS_TOKEN_MAP.put(ACCESS_TOKEN, accessToken);
ACCESS_TOKEN_MAP.put(EXPIRES_IN, String.valueOf(expiresIn + Utility.getCurrentTimeStamp() - 600));
}
return accessToken;
}
/**
* 获取jsapiticket
* @return
* @throws Exception
*/
public static String getJSAPITicket(String token) throws Exception{
String JsApiTicket = null;
String response = null;
if (TICKET_MAP.get(JSAPI_TICKET) != null && Integer.parseInt(TICKET_MAP.get(EXPIRES_IN)) > Utility.getCurrentTimeStamp()) {
JsApiTicket = TICKET_MAP.get(JSAPI_TICKET);
log.info("getJSAPITicket() end, js_api_ticket from redis, expires_in, accessToken={}"+JsApiTicket);
return JsApiTicket;
}
try {
//jsapiticket:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
response = sendGET(Config.getWxConfig().getJsapiTicketUrl().replace("ACCESS_TOKEN", token),null);
} catch (Exception e) {
log.error("getWxToken() exception. ", e);
}
if (response != null) {
JSONObject jsonObject = JSONObject.fromObject(response);
JsApiTicket = jsonObject.getString("ticket");
Integer expiresIn = Integer.parseInt(jsonObject.getString("expires_in"));
TICKET_MAP.put(JSAPI_TICKET, JsApiTicket);
TICKET_MAP.put(EXPIRES_IN, String.valueOf(expiresIn + Utility.getCurrentTimeStamp() - 600));
}
return JsApiTicket;
}
6,前端业务逻辑
<template>
<div>
<a @click="onShareClick">点击分享</a>
</div>
</template>
<script>
export default {
name: "TestShare",
created() {
let loading = this.$loading({
lock: true,
text: '初始化中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
let config = {
url: location.href.split('#')[0]
};
//下单
this.$post('api/Wx/getWxConfig.do', {
config: config
}).then((response) => {
if (response.respCode == '00') {
// // console.log("1111");
config.nonceStr = response.nonceStr;
config.signature = response.signature;
config.timestamp = response.timestamp;
config.appId = response.appId;
config.debug = true;
config.jsApiList = [ // 所有要调用的 API 都要加到这个列表中
'checkJsApi',
'updateAppMessageShareData',
'updateTimelineShareData',
],
wx.config(config);
wx.ready(() => {
this.wx = wx;
wx.checkJsApi({
jsApiList: ['updateAppMessageShareData'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function(res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
alert("success");
}
});
});
wx.error(function (res) {
alert(res.errMsg);
console.info('error 验证失败', res);
});
} else {
this.$message.error('微信SDK初始化失败:' + response.respDesc);
}
loading.close();
}).catch((e) => {
this.$message.error('微信SDK初始化失败!'+e);
loading.close();
});
},
share() {
wx.updateAppMessageShareData({
title: "分享", // 分享标题
desc: "分享", // 分享描述
link: "", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "", // 分享图标
success: function() {
alert("预分享成功");
// 设置成功
},
});
},
}
</script>
7,流程总结
前端上送当前地址url,请求token和ticket获取签名(需要服务端缓存)。拿到签名。在前端wx.config中初始化jssdk。初始化成功后就可以正常调用jssdk的方法啦。
-
8,注意事项
目前分享接口仅支持预分享(分享后还需点击右上角三个点手动分享),不支持点击直接跳出好友列表(以前支持的已废弃)。优化处理可以使用https://blog.csdn.net/chuchenqian9735/article/details/101019938的方法。
-
大坑!!!前端上送的当前页面url是 location.href.split('#')[0]。微信文档有的地方说需要加
但是实际签名中不需要加encodeURL():
所以上送就直接送当前地址就好了,不需要转义。
- ios设备真机调试时候会报签名失败,是因为ios设备在初始化config时只认第一次进入时的url(比如初始化的页面是http://xx/pay。但是发请求时候的地址是http://xx,所以导致上送签名的url和调用初始化的url不一致。可通过把初始化操作放在项目第一次进入的页面解决。android无此问题)