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

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

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

业务流程

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

本文主要围绕1234开展。

Scheme唤起小程序

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

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

 
js
体验AI代码助手
代码解读
复制代码
//请求地址
//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
体验AI代码助手
代码解读
复制代码
  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都会垂询用户,是否要重新唤起小程序

经试验,无法读取卡片内容,下面是翻车现场

在回调函数中,根据返回的res.techs字段匹配到卡片支持的NFC标准 见下图 image.png

支持的技术标准是"NFC-A""MIFARE Ultralight""NDEF"

点击查看文章→NfcA/NfcB/IsoDep/Ndef/Mifare 都是些什么玩意?

微信官方文档说明约等于没说,也没有示例代码。

adapter中的方法 参数

image.png

 
js
体验AI代码助手
代码解读
复制代码
  getIsoDep: ƒ ()
  getMifareClassic: ƒ ()
  getMifareUltralight: ƒ ()
  getNdef: ƒ ()
  getNfcA: ƒ ()
  getNfcB: ƒ ()
  getNfcF: ƒ ()
  getNfcV: ƒ ()
  offDiscovered: ƒ ()
  onDiscovered: ƒ ()
  startDiscovery: ƒ ()
  stopDiscovery: ƒ ()
  tech: {ndef: "NDEF", nfcA: "NFC-A", nfcB: "NFC-B", isoDep: "ISO-DEP", nfcF: "NFC-F", …}
  _nfcA: gme {techType: "NFC-A"}

尝试1 getMifareClassic,失败。

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

 
js
体验AI代码助手
代码解读
复制代码
// 连接不成功,先放着
 // 读取数据
  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'
            })
          }
        })
      }
    });
  },

方式一报错,这个报错是对的,因为咱们确实不支持M1。

8f1c725a2d271a0bb40ffb9803c1bc6.png

尝试2 getNdef

失败方式二

 
js
体验AI代码助手
代码解读
复制代码
  // 读取数据
  readNFC(res) {
    const { nfcAdapter:adapter } = this.data
    console.log('res.techs', res.techs)
    
    // 获取 NDEF 类型的 NFC 适配器
    const ndefAdapter = adapter.getNdef()
    
    // 连接到 NFC 标签
    ndefAdapter.connect({
      success() {
        console.log('已连接到 NFC 标签');

        // 在这里可以进行与 NFC 标签的通信操作
        // 例如读取 NDEF 记录、写入 NDEF 记录等

        // 断开与 NFC 标签的连接
        ndefAdapter.close({
          success() {
            console.log('已断开与 NFC 标签的连接');
          },
          fail(err) {
            console.error('断开与 NFC 标签的连接失败:', err);
          }
        });
      },
      fail(err) {
        console.error('连接到 NFC 标签失败:', err);
      }
    });
   
  },

方式二报错

image.png

尝试3 getNfcA

NFC-A 类型的 NFC 适配器

 
js
体验AI代码助手
代码解读
复制代码
// 读取数据
  readNFC(res) {
    const { nfcAdapter:adapter } = this.data
    console.log('res.techs', res.techs)
    debugger
    // 获取 NFC-A 类型的 NFC 适配器
    const nfcAAdapter = adapter.getNfcA()
    
    // 连接到 NFC 标签
    nfcAAdapter.connect({
      success() {
        console.log('已连接到 NFC 标签');

        // 在这里可以进行与 NFC 标签的通信操作
        // 例如发送读取命令,获取数据等

        // 断开与 NFC 标签的连接
        nfcAAdapter.close({
          success() {
            console.log('已断开与 NFC 标签的连接');
          },
          fail(err) {
            console.error('断开与 NFC 标签的连接失败:', err);
          }
        });
      },
      fail(err) {
        console.error('连接到 NFC 标签失败:', err);
      }
    });
   
  },

连接到 NFC 标签失败: {errMsg: "connect:fail:system internal error: NFC service died", errCode: 13017, errno: 1504210} image.png

尝试4 gtIsoDep

 
js
体验AI代码助手
代码解读
复制代码
const isoDepAdapter = adapter.getIsoDep();
    
     // 连接到 NFC 标签
     isoDepAdapter.connect({
       success() {
         console.log('已连接到 NFC 标签');
 
         // 在这里可以进行与 NFC 标签的通信操作
         // 例如发送 APDU 指令,读取或写入数据等
 
         // 断开与 NFC 标签的连接
         isoDepAdapter.close({
           success() {
             console.log('已断开与 NFC 标签的连接');
           },
           fail(err) {
             console.error('断开与 NFC 标签的连接失败:', err);
           }
         });
       },
       fail(err) {
         console.error('连接到 NFC 标签失败:', err);
       }
     });

image.png

尝试5 getMifareUltralight

 
js
体验AI代码助手
代码解读
复制代码
const mifareUltralightAdapter = adapter.getMifareUltralight()
    
    // 连接到 NFC 标签
    mifareUltralightAdapter.connect({
      success() {
        console.log('已连接到 NFC 标签');

        // 在这里可以进行与 NFC 标签的通信操作
        // 例如读取 NDEF 记录、写入 NDEF 记录等

        // 断开与 NFC 标签的连接
        mifareUltralightAdapter.close({
          success() {
            console.log('已断开与 NFC 标签的连接');
          },
          fail(err) {
            console.error('断开与 NFC 标签的连接失败:', err);
          }
        });
      },
      fail(err) {
        console.error('连接到 NFC 标签失败:', err);
      }
    });

image.png

参考文档:

参考博客一

参考博客二

待参考博客三


作者:山间板栗
链接:https://juejin.cn/post/7332383052562677797
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted on 2025-03-28 14:14  大西瓜3721  阅读(5)  评论(0)    收藏  举报

导航