微信小程序NFC监听、读取;Scheme唤起小程序

NFC

一个需求,要NFC封签唤起小程序,期望用小程序来做一些管理工作。又因为NFC的可复制性过于便捷,还要做加密处理,防止被复制盗用。

业务流程

  1. Scheme唤起小程序
  2. 小程序获取NFC实例、开启监听(仅支持安卓)
  3. 监听到NFC实例,读取卡片信息
  4. 监听到NFC实例,读取卡片内容
  5. 处理业务逻辑

本文主要围绕1234开展。

Scheme唤起小程序

Scheme唤起小程序,是微信官方支持的一个场景。NFC封签只要写入了Scheme,就能唤起。(写入不难,微信里搜一下NFC,就有很多小程序可以用)

主要是要先生成Scheme,需要小程序申请生成Scheme接口。申请到了,用相应接口生成就好。简单贴一下需要的传参。

js 复制代码
//请求地址
//POST https://api.weixin.qq.com/wxa/generatenfcscheme?access_token=ACCESS_TOKEN
{
	"jump_wxa": {
		"path": "非必填, 请输入字符串, 例如'a'",
		"query": "非必填, 请输入字符串, 例如'a'",
		"env_version": "非必填, 请输入字符串, 例如'a'"
	},
	"model_id": "必填, 请输入字符串, 例如'a'",
	"sn": "非必填, 请输入字符串, 例如'a'"
}

小程序获取NFC实例、开启监听

js 复制代码
  onLoad(options) {
    this.initialNFC()
  }
  
  initialNFC() {
    // 获取NFC实例
    const nfc = wx.getNFCAdapter()
    this.setData({ nfcAdapter: nfc})
    const _this = this
  
    nfc.startDiscovery({
      success(res) {
        console.log('NFC读取功能已开启')
        nfc.onDiscovered(_this.discoverHandler)
      },
      fail(err) {
        if (!err.errCode) {
          return console.log('微信(IOS)不支持NFC识别,请使用微信(安卓)')
        }
      }
    })
  },
  
  // 监听方法,监听到NFC实例,读取卡片信息,res里就是卡片信息,不过需要转换一下
  discoverHandler(res) {
    const { nfcAdapter:adapter } = this.data
    const str = this.buf2hex(res.id)
    if (str) {
      // 这里拿到UID,可以做一点业务逻辑了
    }
  },

  // ArrayBuffer转16进制
  buf2hex(arrayBuffer) {
    return Array.prototype.map.call(new Uint8Array(arrayBuffer), x => ('00' + x.toString(16)).slice(-2)).join('');
  },

读取卡片内容

有卡片信息(物理载体)还不够,还需要卡片中的内容(载体里写入的数据)。

(之所以走到这一步,是因为Scheme唤起小程序,会在onLoad(options)方法中的 options里可以拿到写在Scheme上的参数,在小程序初次加载时被动获取的)

一旦监听开启,用户刷一下NFC卡,能拿到的是卡片信息。我们业务逻辑中,需要用到 写在Scheme上的参数 和 卡片id。

也不能告诉用户,麻烦你关掉小程序再打开,就能正确展示页面内容了。

在这里隆重地吐槽一下,安卓和微信,这两个残疾人,一个能用一半,要是能拼起来我就没那么多事情了。

安卓:可以读取NFC,但是Scheme一旦唤起页面,若手机保持在小程序页面,即使读取到Scheme,也不会再唤起小程序

IOS:不可以读取NFC,但是每次检测到Scheme都会垂询用户,是否要重新唤起小程序

需要读取的NFC卡片类型是"MIFARE Classic"。不是常规类型"NFC-A""MIFARE Ultralight""NDEF"

人生南北多歧路,微信官方文档说明约等于没说,也没有示例代码。

"MIFARE Classic",M1是加密卡,有密钥、分扇区,所以一定要问清楚写入卡数据的同学,让他提供扇区第三方密钥

js 复制代码
// 连接不成功,先放着
 // 读取数据
  readNFC(res) {

    const { nfcAdapter:adapter } = this.data

    // res.techs 只有三个"NFC-A"、"MIFARE Ultralight"、"NDEF"。
    // 没有"MIFARE Classic",所以关闭这个判断
    // if (res.techs.includes(adapter.tech.mifareClassic)) {
    // }
    console.log('发现' + adapter.tech.mifareClassic + '卡');

    let mifareClassic = adapter.getMifareClassic();
    debugger
    mifareClassic.connect({
      success: res => {
        console.log("设备已连接", res)
        console.log("开始拼接验密指令。。。");
        debugger
        

        // var arr = [0x60, 0x04, 0x11, 0x22, 0x33, 0x44, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
        
        let shanqu = [0x60, 0x00] //查询扇,后边需要拼接上秘钥,
        let newCardPsd = new Uint8Array(cardPsd) //转换命令格式
        let arr = [...shanqu,...newCardPsd] //把查询扇区的初始跟查询ID拼接到一起


        var arrayBuffer = new Uint8Array(arr).buffer
        console.log("解密指令为:", arrayBuffer);
        mifareClassic.transceive({
          data: arrayBuffer,
          success: function (res) {
            console.log('发送数据并解密成功, 接收数据如下:', res);
          },
          fail: function (err) {
            console.log('发送数据失败A', err);
            wx.showToast({
              title: 'nfc失败!',
              icon:'error'
            })
          }
        })
      },
      fail: function (err) {
        debugger
        console.log('设备连接失败', err);
      }
  })

  mifareClassic.isConnected({
      success: function (isConnected) {
        debugger
        console.log('成功连接', isConnected);
        var arr01 = [0x30, 0x04];
        var arrayBuffer01 = new Uint8Array(arr01).buffer
        console.log('arrayBuffer02',arrayBuffer01);
        var strList = {
          orgId: '',
          orgUserId: ''
        }
        mifareClassic.transceive({
          data: arrayBuffer01,
          success: function (res) {
            console.log('读取数据:', res);
            wx.showLoading({
              title: '识别中...',
              mask: true,
              success: (res) => {},
              fail: (res) => {},
              complete: (res) => {},
            })

            const arrayBuffer = res.data    // 获取通讯数据,类型为ArrayBuffer
            const data16 = that.buf2hex(arrayBuffer)    // ArrayBuffer转16进制
            const requestData = that.hexToStr(data16) // 16进制转字符串
            let arr = requestData.split('')
            let str = ''
            arr.forEach((item,index)=>{
              console.log(item,item.length);
              if(item>=0){
                str += item
              }
            })
            strList.orgId = str
            console.log('requestData',str,'arr',arr)
          },
          fail: function (err) {
            console.log('失败', err);
            wx.showToast({
              title: 'nfc失败!',
              icon:'error'
            })
          }
        })
        var arr02 = [0x30, 0x05];
        var arrayBuffer02 = new Uint8Array(arr02).buffer
        console.log('arrayBuffer02',arrayBuffer02);
        mifareClassic.transceive({
          data: arrayBuffer02,
          success: function (res) {
            debugger
            console.log('读取数据:', res);
            wx.showLoading({
              title: '识别中...',
              mask: true,
              success: (res) => {},
              fail: (res) => {},
              complete: (res) => {},
            })

            const arrayBuffer = res.data    // 获取通讯数据,类型为ArrayBuffer
            const data16 = that.buf2hex(arrayBuffer)    // ArrayBuffer转16进制
            const requestData = that.hexToStr(data16) // 16进制转字符串
            let arr = requestData.split('')
            let str = ''
            arr.forEach((item,index)=>{
              console.log(item,item.length);
              if(item>=0){
                str += item
              }
            })
            strList.orgUserId = str
            console.log('requestData2',strList)
            that.getNfcCardList(strList)
          },
          fail: function (err) {
            console.log('失败', err);
            wx.showToast({
              title: 'nfc失败!',
              icon:'error'
            })
          }
        })
      }
    });
  },

参考文档:

参考博客一

参考博客二

待参考博客三

相关推荐
胡西风_foxww2 分钟前
【ES6复习笔记】Spread 扩展运算符(8)
前端·笔记·es6·扩展·运算符·spread
小林爱27 分钟前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
跨境商城搭建开发37 分钟前
一个服务器可以搭建几个网站?搭建一个网站的流程介绍
运维·服务器·前端·vue.js·mysql·npm·php
hhzz38 分钟前
vue前端项目中实现电子签名功能(附完整源码)
前端·javascript·vue.js
秋雨凉人心41 分钟前
上传npm包加强
开发语言·前端·javascript·webpack·npm·node.js
NoneCoder1 小时前
CSS系列(37)-- Overscroll Behavior详解
前端·css
Nejosi_念旧1 小时前
使用Webpack构建NPM Library
前端·webpack·npm
前端切圖仔1 小时前
失业,仲裁,都赶上了(二)
前端·javascript·程序员
冰红茶-Tea2 小时前
typescript数据类型(二)
前端·typescript
slongzhang_2 小时前
elementPlus消息组件多按钮案例
前端·javascript·vue.js