300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 【最全】微信支付宝小程序蓝牙API开锁全流程

【最全】微信支付宝小程序蓝牙API开锁全流程

时间:2022-10-14 15:38:26

相关推荐

【最全】微信支付宝小程序蓝牙API开锁全流程

【最全】微信支付宝小程序蓝牙API开锁全流程

注意:微信小程序蓝牙API与支付宝小程序蓝牙API有略微不同之处,注意闭坑,本文所讲的是自己开发低功耗蓝牙开锁的功能和全流程。所用技术为Taro,具体用法可以去查Taro官网(Taro:https://taro-/taro/docs/README)。 有写的不好的或者意见和建议,请在评论区留言 。

蓝牙低功耗 (Bluetooth Low Energy, BLE)

蓝牙低功耗是从蓝牙 4.0 起支持的协议,与经典蓝牙相比,功耗极低、传输速度更快,但传输数据量较小。常用在对续航要求较高且只需小数据量传输的各种智能电子产品中,比如智能穿戴设备、智能家电、传感器等,应用场景广泛。

支付宝蓝牙与微信小程序蓝牙区别:

支付宝部分api名字与微信小程序蓝牙api名字有不一样的,具体见下文微信小程序搜索蓝牙设备api,结果返回advertisData和serivceData都是ArrayBuffer类型的,需要自己将ArrayBuffer转为16进制,最后在进行解析数据等操作,而支付宝小程序搜索蓝牙api返回的直接是16进制,不需要再次转换,但是需要把16进制转换为能识别的16进制字符串数组才可以进行接下来的数据解析等操作

支付宝低功耗蓝牙API概览:

支付宝传统蓝牙API概览:

微信低功耗蓝牙API概览:

【重点:如何使用API以及细节】:

1.【第一步】:需要调用Taro.openBluetoothAdapter(),判断蓝牙是否开启(注意事项⚠️:Taro.openBluetoothAdapter支付宝和微信小程序返回的结果不一样,注意区分判断)

if (Taro.getEnv() === "ALIPAY") {res = await Taro.openBluetoothAdapter();} else {const { errMsg } = await Taro.openBluetoothAdapter();res = errMsg;}//微信小程序蓝牙未打开if (Taro.getEnv() === "WEAPP" && res !== "openBluetoothAdapter:ok") {// TODO 没有打开蓝牙,需要提示用户打开 }//支付宝小程序 error: 12, errorMessage: "蓝牙未打开"if (Taro.getEnv() === "ALIPAY" && res.error === 12) {// TODO 没有打开蓝牙,需要提示用户打开 }

2.**【第二步】**开启蓝牙后,需要调用Taro.getLocation()开启地理位置,方便搜索到蓝牙,因android手机不兼容,所以必须要调用定位的方法,以更准确的查到附近的蓝牙设备

//获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用Taro.getLocation({}).then(res => {//TODO 通过后端接口获取你需要的设备信息,一般需要uuid,localKey 等等}).catch(err => {console.log("err", err);});

3.**【第三步】**通过后端接口获取你需要的设备信息,拿到对应的uuid,localKey,其中,uuid是为了与搜索到的众多的蓝牙设备相匹配

//TODO 调用接口拿到自己设备的信息const { uuid, localKey } = await getBleLocksInfoData(deviceIds);const loginKey = localKey.substr(0, 6);Taro.setStorageSync("loginKey", loginKey);//TODO 根据接口返回的uuid进行查找附近的设备并进行匹配searchBle(uuid);

4.**【第四步】**开始搜寻附近的蓝牙外围设备。此操作比较耗费系统资源,请在搜索并连接到设备后调用wx.stopBluetoothDevicesDiscovery方法停止搜索。另外,services:要搜索的蓝牙设备主 service 的 uuid 列表。某些蓝牙设备会广播自己的主 service 的 uuid。如果设置此参数,则只搜索广播包有对应 uuid 的主服务的蓝牙设备。建议主要通过该参数过滤掉周边不需要处理的其他蓝牙设备。

参数介绍:

//因为我们的services是["A201"],所以这里直接参数写为["A201"]来查询const data = await Taro.startBluetoothDevicesDiscovery({services: ["A201"]});//此处,微信小程序和支付宝小程序api返回的data结果不一样,请注意区分if (data.isDiscovering || data) {//TODO:搜索蓝牙设备,并匹配UUIDonDiscoveryBLE(uuid)//另注意:可以多次调用搜索蓝牙,但一定要及时关闭定时器//多次调用搜索蓝牙this.delayTimer = setInterval(() => {this.onDiscoveryBLE(uuid);}, 1000);//关闭定时器this.setTimer = setTimeout(() => {// 关闭蓝牙Taro.stopBluetoothDevicesDiscovery();// 清理定时clearInterval(this.delayTimer);}, 15000);}

5.**【第五步】**调用Taro.getBluetoothDevices获取附近蓝牙设备,并进行筛选匹配过滤(要注意微信和支付宝api返回的结果是不一样的,微信需要转为十六进制,支付宝需要转换为十六进制字符串数组)

// 搜索蓝牙 -- 微信 支付宝搜索蓝牙设备并连接onDiscoveryBLE = async uuid => {//获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。let isALiPay = Taro.getEnv() === "ALIPAY";let { devices } = await Taro.getBluetoothDevices();let macAddress = "";try {// 过滤一些设备devices = devices.filter(item => item.name && item.name !== "未知设备");//这里打印出设备列表,方便查看设备信息console.log("devices", devices);// 匹配 uuid 找到对应的设备if (devices.length) {for (const item of devices) {let {advertisServiceUUIDs,serviceData,advertisData,deviceId} = item;if (!advertisServiceUUIDs || !serviceData) return;if (isALiPay) { //支付宝//TODO hexStringToArray十六进制字符串转十六进制字符串数组(方法见下文)serviceData = utils.hexStringToArray(serviceData[advertisServiceUUIDs]);advertisData = utils.hexStringToArray(advertisData);} else { //微信 //TODO ab2hex ArrayBuffer转十六进制(方法见下文)serviceData = utils.ab2hex(serviceData[advertisServiceUUIDs]);advertisData = utils.ab2hex(advertisData);}//TODO 这里需要根据advertisData, serviceData这两个参数解密出uuid,然后与后端接口返回的uuid匹配,//为ture就表示是自己的设备const uuids = utils.getUuid(advertisData, serviceData);if (uuids === uuid) {console.log("数据有了:", item);macAddress = deviceId;Taro.setStorageSync("macAddress", macAddress);break;}}if (macAddress) {//TODO 找到自己的设备后,开始建立建立连接(【第六步】)connectBle();}}} catch (error) {//TODO 处理失败的逻辑 可给予弹窗提示}};

支付宝返回结果:

微信返回结果:

6.**【第六步】**在connectBle()这个方法内处理建立连接逻辑,拿到macAddress后,调用Taro.createBLEConnection()进行连接

//TODO 要注意支付宝和微信的区别const deviceId = macAddress;if (Taro.getEnv() === "ALIPAY") {BLEConnection = my.connectBLEDevice}else{BLEConnection = Taro.createBLEConnection}BLEConnection({ deviceId }).then(connect => {getBLEDeviceServices(connect,deviceId,resolve)}).catch(err => {console.log("connect error", err);//TODO 可以处理连接失败的操作,具体可以重连等//思路:先关闭连接,然后再重新初始化蓝牙Taro.closeBLEConnection({ deviceId }).then(res => {console.log("res", res);initBleConnect();}).catch(error => {console.log("error", error);initBleConnect();});});

const initBleConnect = () => {Taro.closeBluetoothAdapter().then(ddd => {console.log("ddd", ddd);// 一秒钟之后再去开启蓝牙setTimeout(() => {//初始化蓝牙Taro.openBluetoothAdapter().then(res => {console.log("蓝牙重启", res);const { errMsg } = res;if (errMsg === "openBluetoothAdapter:ok" || res.error !== 12) {console.log("蓝牙重启 -----===========");connectBluttoth(macAddress).then(connect => resolve(connect)).catch(err => reject(err));} else {reject({ code: 1 });}}).catch(err => {reject({ code: 1 });console.log("重启失败err", err);});}, 500);}).catch(rr => {console.log("rr", rr);});};

7.**【第七步】**在getBLEDeviceServices()处理获取蓝牙低功耗设备所有服务 (service),另外,调用notifyBLECharacteristicValueChange必须先启用notifyBLECharacteristicValueChange 才能监听到设备characteristicValueChange事件,所以在页面加载完成后我们需要加载此方法

const getBLEDeviceServices =(connect,deviceId,resolve)=>{//因为支付宝返回的{},所以在此做了兼容if (Number(connect.errCode) === 0 || JSON.stringify(connect) === '{}') {//处理获取蓝牙低功耗设备所有服务 (service)Taro.getBLEDeviceServices({ deviceId }).then(({ services }) => {console.log(services);if (!services.length) {console.log("未找到服务列表");return;}if (services.length) {const service = services[0];// 获取蓝牙低功耗设备某个服务中所有特征 (characteristic)。Taro.getBLEDeviceCharacteristics({deviceId,serviceId: service.uuid || service.serviceId}).then(({ characteristics }) => {// 获取设备数据if (!characteristics.length) {console.log("未找到设备特征值");return;}characteristics.forEach(item => {if (item.properties.notify) {//启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。注意:必须设备的特征支持 notify 或者 indicate 才可以成功调用。//另外,必须先启用 wx.notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件Taro.notifyBLECharacteristicValueChange({deviceId,serviceId: service.uuid || service.serviceId,characteristicId: item.uuid || item.characteristicId,state: true});} else if (item.properties.write) {clearTimeout(connectTimer);resolve({characteristicIdUuid: item.uuid || item.characteristicId,serviceUuid: service.uuid || service.serviceId});} else {console.log("该特征值属性: 其他");}});});}});}}

import aesjs from "aes-js"; //需要引入aes-jsasync componentDidMount() {global["events"].on("onPairResult", this.onPairResult, this);global["events"].on("openLockClick", this.openLockClick, this);global["events"].emit("initLockState", 1);//监听低功耗蓝牙设备的特征值变化事件。//必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。Taro.onBLECharacteristicValueChange(res => {// 在开锁中才接受数据if (this.isOpenLock) {const { value } = res;let key = ''if (Taro.getEnv() === "ALIPAY") {key = utils.HexString2Bytes(value)} else {const str = utils.ab2hex(value).join("");key = utils.HexString2Bytes(str);}console.log('parseDataReceived -- key:' + key);//开锁的逻辑,需要解析数据 -- 根据自己的业务和需求进行解析parseDataReceived.call(this, key);}});//监听低功耗蓝牙连接状态的改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等if (Taro.getEnv() === "ALIPAY") {my.onBLEConnectionStateChanged(res => {// TODO 可以处理失败的弹窗提示等});} else {Taro.onBLEConnectionStateChange(res => {// 该方法回调中可以用于处理连接意外断开等异常情况// TODO 可以处理失败的弹窗提示等});}}

8.【工具类】工具类

/*** 字符串转 16 进制* @param str 字符串*/function HexString2Bytes(str) {var pos = 0;var len = str.length;if (len % 2 != 0) {return null;}len /= 2;var arrBytes = new Array();for (var i = 0; i < len; i++) {var s = str.substr(pos, 2);var v = intToByte(parseInt(s, 16));arrBytes.push(v);pos += 2;}return arrBytes;}//十六进制字符串转换为数组function hexStringToArray(str) {var pos = 0;var len = str.length;if (len % 2 != 0) {return null;}len /= 2;var arrBytes = new Array();for (var i = 0; i < len; i++) {var s = str.substr(pos, 2);arrBytes.push(s);pos += 2;}return arrBytes;}/*** 把 ArrayBuffer 转换成 16 进制内容* @param buffer 二进制内容*/const ab2hex = buffer => {const arr = [] as any;Array.prototype.map.call(new Uint8Array(buffer), function(bit) {let item = ("00" + bit.toString(16)).slice(-2);arr.push(item);});return arr;};//通过advertisData, serviceData这两个参数获取uuid,与自己设备的uuid相匹配const getUuid = (advertisData, serviceData) => {serviceData = serviceData.slice(1, serviceData.length);const pid = serviceData.join("");const strByte = aesjs.utils.hex.toBytes(pid);const md5Val = md5.array(strByte);const uuidDataArr = advertisData.slice(8, advertisData.length);const uuidDataStr = uuidDataArr.join("");const encryptUuid = aesjs.utils.hex.toBytes(uuidDataStr);const aesCbc = new aesjs.ModeOfOperation.cbc(md5Val, md5Val);const decryptedBytes = aesCbc.decrypt(encryptUuid);const uuid = aesjs.utils.utf8.fromBytes(decryptedBytes);return uuid;};

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。