原本是使用uniapp开发微信小程序,扫码功能非常好实现。
但是由于业务原因,需要将小程序转换成H5的方式,发现 uni.scanCode
方法不好用了。
网上查询多个解决方案,但是由于我们的二维码过于复杂。
最终决定使用微信的扫一扫功能,通过js-sdk方式进行调用扫码功能。
扫码主要流程为:
appId
和 appSecret
,不懂如何获取的请自行百度。此处可以自行通过其他方式实现,仅供参考。
weixin:
app-id: xxxxx
app-secret: xxxxx
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "weixin")
@Data
public class WeixinConfig {
private String appId;
private String appSecret;
}
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(WeixinConfig.class)
@AutoConfigureAfter(WeixinConfig.class)
public class WeixinConfiguration {
}
public String getAccessToken() {
// 从redis获取token
String token = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN);
// 若redis为空,则需要从微信处获取token
if (StringUtils.isEmpty(token)) {
String requestUrl = ACCESS_TOKEN_URL.replace("APPID", weixinConfig.getAppId()).replace("SECRET", weixinConfig.getAppSecret());
String response = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONUtil.parseObj(response);
// 判断微信返回结果是否异常
if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
// 持久化到redis中
redisUtil.set(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN, jsonObject.getStr(ACCESS_TOKEN), 7000);
return jsonObject.getStr(ACCESS_TOKEN);
} else {
logger.error(jsonObject.toStringPretty());
throw new ServiceException("获取公众号token信息异常");
}
} else {
return token;
}
}
public String jsapiTicket() {
// 从redis获取ticket
String ticket = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.JS_TICKET);
// 若为空,则从微信服务器获取
if (StringUtils.isEmpty(ticket)) {
String accessToken = getAccessToken();
String requestUrl = ACCESS_TICKET_URL.replace("ACCESS_TOKEN", accessToken);
String response = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONUtil.parseObj(response);
// 判断微信返回结果是否异常
if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
// 持久化到redis中
redisUtil.set(RedisConstant.WEIXIN + RedisConstant.JS_TICKET, jsonObject.getStr(TICKET), 7000);
return jsonObject.getStr(TICKET);
} else {
logger.error(jsonObject.toStringPretty());
throw new ServiceException("获取公众号ticket信息异常");
}
}
return ticket;
}
public static String SHA1(String decript) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hcframe.base.common.ServiceException;
import com.hcframe.redis.RedisUtil;
import com.repchain.nfr.common.config.RedisConstant;
import com.repchain.nfr.common.config.WeixinConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Component
public class WeixinUtil {
final WeixinConfig weixinConfig;
final RedisUtil redisUtil;
private final Logger logger = LoggerFactory.getLogger(WeixinUtil.class);
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
public final static String ACCESS_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
public final static String ERRCODE = "errcode";
public final static String ACCESS_TOKEN = "access_token";
public final static String TICKET = "ticket";
public WeixinUtil(WeixinConfig weixinConfig,
RedisUtil redisUtil) {
this.weixinConfig = weixinConfig;
this.redisUtil = redisUtil;
}
/**
* @return java.lang.String
* @author lhc
* @description // 获取微信accessToken
* @date 2022/8/11 10:29
* @params []
**/
public String getAccessToken() {
// 从redis获取token
String token = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN);
// 若redis为空,则需要从微信处获取token
if (StringUtils.isEmpty(token)) {
String requestUrl = ACCESS_TOKEN_URL.replace("APPID", weixinConfig.getAppId()).replace("SECRET", weixinConfig.getAppSecret());
String response = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONUtil.parseObj(response);
// 判断微信返回结果是否异常
if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
// 持久化到redis中
redisUtil.set(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN, jsonObject.getStr(ACCESS_TOKEN), 7000);
return jsonObject.getStr(ACCESS_TOKEN);
} else {
logger.error(jsonObject.toStringPretty());
throw new ServiceException("获取公众号token信息异常");
}
} else {
return token;
}
}
/**
* @return cn.hutool.json.JSONObject
* @author lhc
* @description // 获取微信jssdk授权
* @date 2022/8/11 10:30
* @params []
**/
public String jsapiTicket() {
// 从redis获取ticket
String ticket = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.JS_TICKET);
// 若为空,则从微信服务器获取
if (StringUtils.isEmpty(ticket)) {
String accessToken = getAccessToken();
String requestUrl = ACCESS_TICKET_URL.replace("ACCESS_TOKEN", accessToken);
String response = HttpUtil.get(requestUrl);
JSONObject jsonObject = JSONUtil.parseObj(response);
// 判断微信返回结果是否异常
if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
// 持久化到redis中
redisUtil.set(RedisConstant.WEIXIN + RedisConstant.JS_TICKET, jsonObject.getStr(TICKET), 7000);
return jsonObject.getStr(TICKET);
} else {
logger.error(jsonObject.toStringPretty());
throw new ServiceException("获取公众号ticket信息异常");
}
}
return ticket;
}
public static String SHA1(String decript) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
@RestController
@RequestMapping("/weixintest")
public class WeixinController {
final WeixinService weixinService;
public WeixinController(WeixinService weixinService) {
this.weixinService = weixinService;
}
@GetMapping("jssdktest")
public ResultVO<Object> getJsSdkInfo(String url) {
return weixinService.getJsSdkInfo(url);
}
}
import com.hcframe.base.common.ResultVO;
import com.repchain.nfr.common.config.WeixinConfig;
import com.repchain.nfr.common.utils.WeixinUtil;
import com.repchain.nfr.module.weixin.service.WeixinService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
public class WeixinServiceImpl implements WeixinService {
final WeixinUtil weixinUtil;
final WeixinConfig weixinConfig;
public WeixinServiceImpl(WeixinUtil weixinUtil,WeixinConfig weixinConfig) {
this.weixinUtil = weixinUtil;
this.weixinConfig = weixinConfig;
}
@Override
public ResultVO<Object> getJsSdkInfo(String url) {
if (StringUtils.isEmpty(url)) {
url = weixinConfig.getJsUrl();
}
String ticket = weixinUtil.jsapiTicket();
// 随机字符串
String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
// 时间戳
String timestamp = String.valueOf(System.currentTimeMillis());
String str = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
// SHA1签名字符串
String signature = WeixinUtil.SHA1(str);
// 返回前端结果
Map<String,String> map=new HashMap<>(5);
map.put("timestamp",timestamp);
map.put("appId",this.weixinConfig.getAppId());
map.put("noncestr",noncestr);
map.put("signature",signature);
return ResultVO.getSuccess(map);
}
}
yarn add weixin-js-sdk
此处可以自行实现,主要目的就是从服务端拉去数据。
import { $get } from "@/common/http";
export const getJsSdkInfo = (url: string) =>
$get(`/weixintest/jssdktest`, { url }, true);
getConfig() {
// 此处为域名
const url = "https://xxxx.xxx.com/";
// 通过服务端获取所需信息
getJsSdkInfo(url)
.then(res => {
this.configInfo = res.data;
this.wxConfig(
res.data.appId,
res.data.timestamp,
res.data.noncestr,
res.data.signature
);
})
.catch(() => {
uni.hideLoading();
uni.showToast({
title: "请稍后再试",
icon: "error"
});
});
}
// 微信的js-sdk初始化
wxConfig(appId: any, timestamp: any, nonceStr: any, signature: any) {
wx.config({
debug: false, // 开启调试模式,
appId: appId, // 必填,企业号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature: signature, // 必填,签名
jsApiList: ["scanQRCode", "checkJsApi"] // 必填,需要使用的JS接口列表
});
wx.ready(() => {
uni.hideLoading();
});
wx.error(function (res: any) {
console.log(res);
uni.hideLoading();
uni.showToast({
title: "请使用微信打开,或稍后再试",
icon: "error"
});
});
}
scanClick() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this;
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
success: function (res: any) {
console.log(res.resultStr); //返回结果
},
fail: function () {
uni.showToast({
title: "识别二维码失败!",
duration: 2000,
icon: "none"
});
}
});
}
<template>
<view>
<view class="auth-icon-bind">
<view>
<u-icon name="scan" size="200" color="#50416f"></u-icon>
</view>
</view>
<view class="bind-title">
<uni-title
type="h1"
title="请扫描二维码绑定藏品"
align="center"
color="#50416f"
></uni-title>
</view>
<view class="auth-button">
<button class="btn-logout" @click="scanClick">点击扫描二维码</button>
</view>
</view>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import Setting from "@/settings";
import Settings from "@/settings";
import wx from "weixin-js-sdk";
import { getJsSdkInfo } from "@/api/wxApi";
import { isInWeixin } from "@/utils/agent";
import { router } from "@/router/router";
@Component({
name: "BlockList",
components: { hcPass }
})
export default class extends Vue {
theme = Setting.theme;
showBindView = false;
tranJson = "";
result = "";
error = "";
onLoad() {
uni.showLoading({ title: "加载中" });
}
getConfig() {
const url = "https://scicollection.las.ac.cn/";
getJsSdkInfo(url)
.then(res => {
this.configInfo = res.data;
this.wxConfig(
res.data.appId,
res.data.timestamp,
res.data.noncestr,
res.data.signature
);
})
.catch(() => {
uni.hideLoading();
uni.showToast({
title: "请稍后再试",
icon: "error"
});
});
}
wxConfig(appId: any, timestamp: any, nonceStr: any, signature: any) {
wx.config({
debug: false, // 开启调试模式,
appId: appId, // 必填,企业号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature: signature, // 必填,签名
jsApiList: ["scanQRCode", "checkJsApi"] // 必填,需要使用的JS接口列表
});
wx.ready(() => {
uni.hideLoading();
});
wx.error(function (res: any) {
console.log(res);
uni.hideLoading();
uni.showToast({
title: "请使用微信打开,或稍后再试",
icon: "error"
});
});
}
onShow() {
// 判断是否为微信环境
if (isInWeixin()) {
this.getConfig();
} else {
uni.hideLoading();
uni.showModal({
title: "提示",
content: "请在微信中打开网站,使用扫一扫功能",
confirmColor: Settings.theme,
success: function () {
router.pushTab("/xxxx");
}
});
}
}
scanClick() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this;
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
success: function (res: any) {
console.log(res.resultStr); //返回结果
},
fail: function () {
uni.showToast({
title: "识别二维码失败!",
duration: 2000,
icon: "none"
});
}
});
}
}
</script>