最麻烦的事情是这玩意要在正式上调试,相当于你不能在内网自己调试,每发布一次就调试一次编辑
看到无数次这个invalid signature都崩溃了
最后我看到了config ok,觉得简直是奇迹。
这个链接是jssdk的使用说明,简单来说就是这个要前后端配合的。相信后端!
后端工作:
1.要是公众号的appid而不是小程序的,申请h5的域名,自己配好白名单。权限问题害得是后端
2.看文档后端自己去拿AccessToken,通过AccessToken拿jsapi_ticket ,拿到之后最谜语人头疼的的就是这步----加密算法
但其实就是把jsapi_ticket 变成 三个东西signature,timestamp,nonceStr。
其中因为jsapi_ticket是不能频繁获取,2小时内有效,所以后端需要缓存到数据库,2小时内jsapi_ticket不变,signature,timestamp,nonceStr是给前端的,这三个随时变,怎么转?其实已经给了你函数了,自己去下载吧
www.weixinsxy.com/jssdk/sampl…
上面有java,nodejs,python等,直接用就行
3.url这个特别关键,url不能硬编码hardCode,开给前端,让前端传给你去拿signature,timestamp,nonceStr,所以这个接口就是这样写的,前端给url和token,后端return回signature,timestamp,nonceStr
最后要用这个校验微信 JS 接口签名校验工具
概述 | 微信开放文档微信开发者平台文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
编辑
前端工作:
1.拿到接口,baseUrl必须是域名的,正在的接口叫www.xxxxx.com 。
(这也是白名单的接口),然后axios.post拿params是url,url是当前的url,url是最关键的,url后如果有?参数的话,可以把参数?后面的传入sessionToken里面,然后url传入?前面的那个短的就行,拿url也可以传入的时候写同一个就可以了。目的就是拿到正确的signature,timestamp,nonceStr,这步和后端联调校验确认好
2.下面就是引入sdk了,两种方法,第一种是编辑cnpm i weixin-js-sdk,然后import wx from "weixin-js-sdk";
但是由于我的框架是uniapp+vite+vue3+ts,uniapp它自带wx这个接口,所以会有变量冲突,污染的问题,所以我用了第二种方法在index.html里面
<script
type="text/javascript"
src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"
></script>
/**wxx是jssdk*/
window\["wxx"] = wx;
console.log("引入的wxsdk", window\["wxx"]);** 然后这样就行,写在body的前面,打印看看有什么接口
编辑
我们看到确实有scanQRCode这个接口,和小程序原生的scanCode是不同滴
3.引入jssdk后,并且拿到那三个后signature,timestamp,nonceStr,配置,注意是公众号的appID,而不是小程序的,debug暂时先开着true,后面上线再关掉,主要是看弹出来的signature是否有效
window["wxx"].config({
debug: false, // 是否开启调试模式,可以在开发阶段设置为true,便于调试
// appId: "wx5c1cdd31fc7a0898", // 小程序的AppID
appId: "wxa9d93851b1f8a5a0", // 公众号的AppID
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: ["scanQRCode"], // 需要使用的微信功能、列表
});
4. 如果以上都没问题,那下面就简单了,直接调用就可以,注意这个needResult一般都是1,如果不是1的话success的回调函数就不能执行了,扫到什么就弹出什么
/**wx的jssdk相机扫码 */
function QRCode() {
console.log("微信扫一扫")
window["wxx"].scanQRCode({
// onlyFromCamera: false,
needResult: 1,
scanType: ["qrCode", "barCode"],
success: (result) => {
console.log('result', result.resultStr);
let QRCode = result.resultStr.trim() + ""
// =======================================
BS.ED.emit("聚焦铝模板", QRCode);
selectedItem.value = QRCode
inputValue.value = QRCode
closeSearchContent()
},
fail: (res) => {
console.log('fail', res);
// let QRCode = "DGSSL-T4-yhy-A-Q40-59"
// BS.ED.emit("聚焦铝模板", QRCode);
wx.showToast({
title: "打开失败" + res,
icon: "none",
image: "",
duration: 1500,
mask: true,
});
},
});
}
5.这里的路由最好是用hash模式,因为ios端和android端的微信小程序webview是不同的,ios端据说是会存整个的url,所以你把#之前传给服务器就行,之前一直在ios系统扫不了码
在manifest.json文件中
"h5" : {
"router" : {
"mode" : "hash",
// "mode" : "history",
"base" : "/vr3d/"
// "base" : "/"
},
"sdkConfigs" : {
"maps" : {
"google" : {
"key" : ""
}
}
}
}
这个弄了差不多一个星期有多,非常头疼,需要前后端和发布人员极度配合,之前我也走了很多弯路,比如github看源代码,h5跳回小程序又跳回h5,通过url传参等等,也看到很多人在文档下面骂这个功能为什么这么难,都不行,希望能把微信小程序能把和h5配合的文档弄得简单点吧...........希望这篇文章能用简练的语言给大家一点启发!!
补更:关于ipad的扫码问题,微信的jssdk的扫一扫好像没有做ipad端的,相机能打开,但是扫了之后就不返回了 我只能自己再在网上找插件补一下ipad扫码了
/**判断是否是ipad,ipad只能用html5QRCode插件了 */
function isPad() {
let isiPad = navigator.userAgent.includes("iPad");
return isiPad;
}
// 初始化二维码扫描
const WXCMD_scan = () => {
console.log("点击微信扫一扫wxx", window["wxx"])
console.log("点击微信扫一扫wx", wx)
if (isPad()) {
// alert('这是ipad')
useCamera()
} else {
QRCode()
}
}
vue文件的demo在这,自取
<template>
<!-- h5自身扫码试验 -->
<div class="readerrrrrr" id="readerrrrrr" ref="readerRef">
</div>
<div v-if="isOpenScan" class="exitScan">
<button class="buttonn" @click="exitCamera()">退出扫码</button>
</div>
<div class="content2" v-if="showUI">
<button @click="useCamera()">使用相机扫一扫方式</button>
<div class="result">
<h3 ref="resultsRef">ddddd</h3>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import BottomNav from '../index/bottomNav.vue';
import ButtomSupport from '@/pages/index/bottomSupport.vue'
import { Html5Qrcode } from 'html5-qrcode'
let readerRef = ref(null)
const showUI = ref(true)
let resultsRef = ref(null)
let html5QrCode
let config
onMounted(() => {
//1.Html5QrcodeScanner是js提供的ui; 2.Html5Qrcode是自定义面板
html5QrCode = new Html5Qrcode("readerrrrrr");
config = { fps: 10, qrbox: { width: 300, height: 300 } }; //扫一扫相关设置
//1000/10=100毫秒扫描一次
})
const isOpenScan = ref(false)
function exitCamera() {
readerRef.value.style.display = "none";
isOpenScan.value = false
stop()
}
function stop() {
html5QrCode.stop().then((ignore) => {
// QR Code scanning is stopped.
console.log("QR Code scanning stopped.");
})
.catch((err) => {
// Stop failed, handle it.
console.log("Unable to stop scanning.");
});
}
//相机授权
function useCamera() {
hasScaned = false
readerRef.value.style.display = "block";
resultsRef.value.innerText = "";
// showUI.value=false
Html5Qrcode.getCameras()
.then((devices) => {
isOpenScan.value = true
if (devices && devices.length) {
let cameraId = "";
if (devices.length == 1) {
cameraId = devices[0].id; //前置摄像头
} else {
cameraId = devices[1].id; //后置摄像头
}
if (cameraId) {
startWithCameraId(cameraId);
}
} else {
startWithoutCameraId();
}
})
.catch((err) => {
console.log("没有获取摄像头设备...");
alert("没有获取摄像头设备...")
});
}
//带相机ID扫描
function startWithCameraId(cameraId) {
html5QrCode
.start(
{ deviceId: { exact: cameraId } },
config,
onScanSuccess,
onScanFailure
)
.catch((err) => {
console.log("通过摄像头扫码异常....", err);
});
}
//不带相机ID扫描,允许传递约束来代替相机设备 ID
function startWithoutCameraId() {
//environment 表示后置摄像头 换成user则表示前置摄像头
html5QrCode.start(
{ facingMode: "environment" } || {
facingMode: { exact: "environment" },
},
config,
onScanSuccess,
onScanFailure
);
}
let hasScaned = false
//扫码解析成功后按照自己的需求做后续的操作
function onScanSuccess(decodedText, decodedResult) {
resultsRef.value.innerText = "扫码成功结果:\n" + decodedText;
if (hasScaned) return
if (decodedText) {
alert("扫码结果是 " + decodedText);
hasScaned = true; // 将标志变量设为 true,表示已经弹出过对话框
stop()
readerRef.value.style.display = "none";
}
}
//扫码解析失败后按照自己的需求做后续的操作
function onScanFailure(error) {
resultsRef.value.innerText = "扫码失败:\n" + error;
}
</script>
<style scoped>
.content2 {
position: fixed;
display: flex;
width: 100vw;
height: 100vh;
background: linear-gradient(rgb(238, 238, 238), rgb(38, 79, 55));
}
.readerrrrrr {
position: absolute;
width: 100%;
height: 100%;
/* background: #414141; */
pointer-events: none;
z-index: 999;
display: flex;
justify-content: center;
/* align-items: center; */
}
/* .readerrrrrr{
position: absolute;
margin-top: 100px;
width:50%;
height: 50%;
background: #414141;
pointer-events: none;
z-index: 999;
} */
button {
display: block;
width: 100%;
margin: 6px;
outline: none;
height: 40px;
line-height: 40px;
color: #fff;
background-color: #26a2ff;
text-align: center;
border-radius: 4px;
border: none;
cursor: pointer;
}
.result {
margin-top: 40px;
position: fixed;
width: 100%;
height: 30%;
background: #e4e4e4;
z-index: 9999;
}
.buttonn {
position: fixed;
display: block;
width: 50%;
height: 40px;
bottom: 20px;
color: #fff;
background-color: #26a2ff;
text-align: center;
border-radius: 4px;
cursor: pointer;
pointer-events: visible;
}
.exitScan {
position: fixed;
pointer-events: none;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
/* background: #000; */
}
</style>
还有这个要求https的才能有权限,那我这里有两本证书
192.168.110.176-key.pem是
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC56EdZU9X7tDVO
1O1yJNqwK/lsEWUCV97GiFAJmL+cxahZ3CjI3tZJP11h+AtmBiWETRoPRsOzkTSn
b6YNZvGyMpR59PlCPwEcTc0Cz4DP0TqfLahht+2DtOuDt8TAsnzfjn9fcEJ7XNZf
eh5luWXJFrULb8F4z71c9wAH4oD4Ga1FproZsoDS1GapJvrt/NkZgmVUwE1VMqXj
SFgDLL65zuXEJ3Jb0ETgtA21eVb66KsZeXGeXWfBf4VAcPIeelFIyRIs3S6Z22D/
9efmkaQozyi5HU4EcBDjiTcF/H+eeq+YBlYalFdhN9tNw4aj+B0ZU1P+NqdecWoh
5+Ei4lfXAgMBAAECggEBALWGUOSwL6judX7b+l4SDlcnGQykj3SSSDyirUQxmieB
+LiFeG1q6OHstUoL4VVlewyMHH3+IukbR8aWBhXgBvBw4vmScjB1RKhWb2/nHWfO
bOcwtdF0vr5z0eNzdPKTVcsUYNrXypKaO9hObY/2T6TiPzVDki5c6hH0PFt2l2eo
QKvuglu15a4LHefWLo4ne89Nje9Q+nYvVaGMxnF+8lynPNVUWRkhad32TeI/Dlvp
LHw6hFjvpb3kphtyZUQjMxN82nqn2YL8g/7fqNlN9iCSVvtahTiHJM9CSZBMcc3q
j2jB1oJpCarF4sZwtm0yCBaBcUyKf/W7eBYWYDRgJoECgYEA3WMCtcrL4etG1528
756Of7ubboT2F7gYaFyCIK3jYneM8YSMZudb3czgve1otipWFvs8ztLEDXh8A4bR
3HiKP1VLGmZpQ4j2TAT9I8bVfg8lWXKJkZKit1ZSBOEhM2CROCi/Bi6WgwmCcKL7
f7ThefN1Hr4QsS6nL5KUD+lEzWkCgYEA1vk08BKi5QqgTBzGq5GnuXKadB6vIH6Q
QX3NtIH5frZ1L8H0BawZf9GsJ8Y4kRbCW+XA+Fw1iZjSdepMKMreV9I1S3/oG6sx
q+3+2RUtq1a5nxY+uy8nAPBeHATUHtnJN5r9tLeqsuIYgsfkG2zELIHHAYIX5Nbu
4c56OpptEz8CgYEAgBy8w8DAbVM+oqW4YR3rLoW9c55j3uP809+8ufaGoEO4f0cK
DL6Tze2ynJYXQ1uKiDAJR1J2e6kectgA98mVjwLnvDZJcfh/NwyoBJ0ajKFtJq7+
ZwxpNxkvy30QPnACeXIy4Pvyw4+sOUxHp+ZfmLfHLewlMrNhskjrf62o0AkCgYAT
yGUtvplM+JhdyLwjp8jGkRxTmUtGcz81N62JcfiSx5mrJm8dYoQKNjJgiqZD+9Mw
/8itUlb+7ZhYj5IootqpPEf5RbEHcs6kYsd0FLXaMaXtVO+67BDrzjLq4yreF+6j
dljom4pS4emdh2WffHflHKBCrEUkov7iusyWvgVRowKBgQCtbDogoaox+KtG0BWU
v/mub/E9o6L0wAWie9/VSPvsOYR5ykBSJcuOAvoAGbw23NTE7Zr45sD5t8knzbU5
wkS69rKVbdIt7W6xrvIH/WcRyjD1eOkKBCNz8lK4HD1KN1NQzNwW3zk3xQrL+pXm
TVnawjE7aPOQr1eVTp9YYPCwCA==
-----END PRIVATE KEY-----
192.168.110.176.pem是
-----BEGIN CERTIFICATE-----
MIIEXDCCAsSgAwIBAgIQf2yv1NlZ3DZc/QtEmSVNYDANBgkqhkiG9w0BAQsFADCB
lzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTYwNAYDVQQLDC1ERVNL
VE9QLTVSMVE3QzlcQWRtaW5pc3RyYXRvckBERVNLVE9QLTVSMVE3QzkxPTA7BgNV
BAMMNG1rY2VydCBERVNLVE9QLTVSMVE3QzlcQWRtaW5pc3RyYXRvckBERVNLVE9Q
LTVSMVE3QzkwHhcNMjMwODA5MDk0NzI4WhcNMjUxMTA5MDk0NzI4WjBhMScwJQYD
VQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxNjA0BgNVBAsMLURF
U0tUT1AtNVIxUTdDOVxBZG1pbmlzdHJhdG9yQERFU0tUT1AtNVIxUTdDOTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALnoR1lT1fu0NU7U7XIk2rAr+WwR
ZQJX3saIUAmYv5zFqFncKMje1kk/XWH4C2YGJYRNGg9Gw7ORNKdvpg1m8bIylHn0
+UI/ARxNzQLPgM/ROp8tqGG37YO064O3xMCyfN+Of19wQntc1l96HmW5ZckWtQtv
wXjPvVz3AAfigPgZrUWmuhmygNLUZqkm+u382RmCZVTATVUypeNIWAMsvrnO5cQn
clvQROC0DbV5Vvroqxl5cZ5dZ8F/hUBw8h56UUjJEizdLpnbYP/15+aRpCjPKLkd
TgRwEOOJNwX8f556r5gGVhqUV2E3203DhqP4HRlTU/42p15xaiHn4SLiV9cCAwEA
AaNZMFcwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1Ud
IwQYMBaAFE4/+pGaLmHU/O/gcfWZoXCUQnxiMA8GA1UdEQQIMAaHBMCobrAwDQYJ
KoZIhvcNAQELBQADggGBALCY4RL5iLvKQ4yMZ4ywUDElWhvifxrFdQYkxDFZLze8
OxT8KzMaJoK+OiU6pHcqlHa/azDgOgRNh60zQh0m3XOG5xDoq2TH0ooZp4ejY16f
UN53hGUIEnhRcmDmR6wvpXwp2xUBIgD0j24RJ85ryVEnsxYsGzGvl7FAR/g1mJLA
96DP8/b5dQckDUYhucJD3JHL1O6UQ5q/hOuuPMliqgGb8yo5Vc43G4t4/DrIugrn
Cke2pYZV5iwHjYadJqYZjn2vrVwE9KKtWjPmKa9x98/Hf/I8WB6hd5fcM2AT09hH
sF7z4uDzEYmZcx/6OAijXrFOX0ZnuupWhj3Lh29b37Cuk3QrSUV/v5MMfXS2rFSB
UNVYgTXfstZ+ZwRaUQzFYP4p8KNAFQ/r8Fw9q+i0yHyqeeMEb3kSpuSHEsGNJGZm
i1s6vklU4G5VDfi0iVVN31HzZi0fsyZJhZdB38gshVrsWnzoFKTf7YgSRg61hetu
v5WYoPUclENS8vc3mWazzw==
-----END CERTIFICATE-----
本地IP不同,重命名一下就可以了
在vite.config.ts设置,我是用uniapp写的,运行就npm run dev:h5
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
import path from "path";
export default defineConfig({
// base: "./", // 新增
plugins: [uni()],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
server: {
https: {
key: "./public/192.168.110.176-key.pem",
cert: "./public/192.168.110.176.pem",
},
port: 6600,
},
});