NFC读卡能力 支持安卓/iOS/鸿蒙 UTS插件

313 阅读6分钟

NFC读卡能力 支持安卓/iOS/鸿蒙 UTS插件

介绍

  1. 支持 Android、iOS、HarmonyOS 的 NFC 读卡能力封装,可用于读取卡片基础信息、NDEF 记录、透传 APDU 以及部分 Mifare 操作。
  2. 适用于 uni-app 与 uni-app x App 平台,不支持 Web 和各类小程序平台。
  3. 读卡流程统一为:先 initNFC 初始化,再 readCard 建立卡片会话,之后调用 transceive、readNDEFRecords、readBlock 等方法,结束后调用 closeNFC 关闭会话。

猛戳这里去插件市场看看

平台支持

平台支持情况
Android支持
iOS支持
HarmonyOS支持
Web不支持
小程序不支持

API说明

方法名称参数返回说明
initNFCInitNFCParams回调 ResponseEntity初始化 NFC 读卡配置,检查权限和设备能力
readCardReadCardParams回调 ResponseEntity,data 为 NfcTag开始轮询卡片并返回卡片信息
transceiveTransceiveParams回调 ResponseEntity,data 为十六进制字符串与卡片进行 APDU 或原始指令透传
readNDEFRecordsReadNDEFParams回调 ResponseEntity,data 为 NdefInfo[]JSON数据读取当前卡片上的 NDEF 记录
writeNDEFRecordsWriteNDEFParams回调 ResponseEntity写入 NDEF 记录
makeNdefReadOnlyMakeNDEFReadOnlyParams回调 ResponseEntity将当前 NDEF 标签设置为只读,操作不可逆
authenticateSectorAuthenticateSectorParams回调 ResponseEntity,data 为 boolean对 Mifare Classic 扇区进行 KeyA 或 KeyB 认证
readSectorReadSectorParams回调 ResponseEntity,data 为十六进制字符串或 false读取 Mifare Classic 整个扇区
readBlockReadBlockParams回调 ResponseEntity,data 为十六进制字符串或 false读取 Mifare Classic 块或 Mifare Ultralight 页
writeBlockWriteBlockParams回调 ResponseEntity,data 为 boolean写入 Mifare Classic 块或 Mifare Ultralight 页
closeNFCCloseNFCParams回调 ResponseEntity关闭当前 NFC 会话

平台差异说明

不同平台底层 NFC 能力不同,部分 API 在三端的支持范围并不完全一致,接入前建议先阅读本节。

APIAndroidiOSHarmonyOS说明
initNFC支持支持支持三端都支持初始化,但底层参数映射不完全一致
readCard支持支持支持三端都支持读卡与返回基础卡信息
transceive支持支持支持三端都支持,但不同卡型支持范围取决于系统底层能力
readNDEFRecords支持支持支持当前三端实际返回的 data 为 JSON 字符串格式的记录列表
writeNDEFRecords支持支持支持三端都支持 NDEF 写入,但实际可写能力取决于卡片类型与系统支持
makeNdefReadOnly支持支持支持三端都支持,但是否允许设只读取决于标签本身
authenticateSector支持不支持,固定返回 true支持iOS 无 Mifare Classic 扇区认证能力;为兼容前置调用链,默认返回成功
readSector支持不支持支持iOS 当前未实现整扇区读取
readBlock支持部分支持部分支持iOS 主要支持 MiFare/ISO15693;鸿蒙当前 ISO15693 需走 transceive 兜底
writeBlock支持部分支持部分支持iOS 主要支持 MiFare/ISO15693;鸿蒙当前 ISO15693 需走 transceive 兜底
closeNFC支持支持支持三端都支持关闭当前会话

iOS 特别说明

  1. authenticateSector 在 iOS 中不支持真实的 Mifare Classic 扇区认证。
  2. 由于很多业务会把 authenticateSector 作为后续读写流程的前置 API,为避免直接中断调用链,iOS 侧当前默认返回 true。
  3. readSector 在 iOS 中不支持,当前会直接返回失败。
  4. readBlock / writeBlock 在 iOS 中并不是完整对齐 Android 的 Mifare Classic 语义,主要依赖 CoreNFC 对 MiFare 与 ISO15693 的能力支持。

HarmonyOS 特别说明

  1. readBlock / writeBlock 当前对 ISO15693 没有直接暴露独立块读写封装,建议通过 transceive 作为兜底方案。
  2. HarmonyOS 当前实现中,卡片技术类型筛选依赖 readIso14443A、readIso14443B、readIso18092、readIso15693 的组合;如果全部关闭,不会自动回退为全卡型扫描。

Android 特别说明

  1. androidSound、androidCheckNDEF、androidReaderModeFlags、extraReaderPresenceCheckDelay 仅 Android 平台生效。
  2. readBlock / writeBlock 在 Android 侧对 Mifare Classic、Mifare Ultralight、ISO15693 的支持相对更完整。

数据结构说明

ResponseEntity

type ResponseEntity = {
  success: boolean
  data?: any | null
  message?: string | null
}

NfcTag

readCard 成功后返回的卡片信息对象,常用字段如下:

  • id:卡片 ID,十六进制字符串
  • type:卡片类型,如 iso7816、mifare_classic、mifare_ultralight、iso15693、iso18092
  • standard:卡片协议标准
  • ndefAvailable:是否支持 NDEF
  • ndefType:NDEF 标签类型
  • ndefWritable:NDEF 是否可写
  • ndefCanMakeReadOnly:是否可设置为只读
  • mifareInfo:Mifare 相关信息,包含容量、块大小、块数量、扇区数量等

NdefInfo

type NdefInfo = {
  identifier: string
  payload: string
  type: string
  typeNameFormat: string
}

字段均为十六进制字符串,业务层可按需自行转为文本。

快速开始

1. 引入插件

import * as reader from "@/uni_modules/cz-nfc-reader"

2. 初始化 NFC

reader.initNFC({
  readIso14443A: true,
  readIso14443B: true,
  readIso15693: true,
  readIso18092: true,
  completeListener(res) {
    if (res.success) {
      console.log("NFC 初始化成功")
    } else {
      console.log(`NFC 初始化失败: ${res.message}`)
    }
  }
})

3. 读卡并获取卡片信息

reader.readCard({
  completeListener(res) {
    if (!res.success || res.data == null) {
      console.log(`读卡失败: ${res.message}`)
      return
    }
    const tag = res.data as reader.NfcTag
    console.log(`卡号: ${tag.id}`)
    console.log(`标准: ${tag.standard}`)
    console.log(`类型: ${tag.type}`)
    console.log(`支持NDEF: ${tag.ndefAvailable}`)
  }
})

4. 读取 NDEF

reader.readCard({
  completeListener(card) {
    if (!card.success || card.data == null) {
      return
    }
    const tag = card.data as reader.NfcTag
    if (tag.ndefAvailable != true) {
      console.log("当前卡片不支持 NDEF")
      reader.closeNFC({})
      return
    }
    reader.readNDEFRecords({
      cached: true,
      completeListener(res) {
        console.log(`NDEF 结果: ${res.data}`)
        reader.closeNFC({})
      }
    })
  }
})

5. APDU透传示例

reader.readCard({
  completeListener(card) {
    if (!card.success || card.data == null) {
      return
    }
    reader.transceive({
      capdu: "00A4040009A00000000386980701",
      completeListener(res) {
        console.log(`透传结果: ${res.success} ${res.data}`)
        reader.closeNFC({})
      }
    })
  }
})

常用方法说明

initNFC

用于初始化 NFC 能力和读卡配置。

常用参数:

参数类型默认值说明
timeoutnumber30000读卡超时时间,单位毫秒,iOS 无效
readIso14443Abooleantrue是否启用 ISO14443A
readIso14443Bbooleantrue是否启用 ISO14443B
readIso18092booleanfalse是否启用 ISO18092/FeliCa
readIso15693booleantrue是否启用 ISO15693
androidSoundbooleantrueAndroid 发现标签时是否播放系统音效
androidCheckNDEFbooleantrueAndroid 是否自动检查 NDEF
androidReaderModeFlags0x80 | 0x100无Android ReaderMode 附加标志
extraReaderPresenceCheckDelaynumber无Android Presence Check 额外延迟
iosAlertMessagestring无iOS 会话开始提示语
iosMultipleTagMessagestring无iOS 多标签提示语

readCard

启动一次读卡轮询,成功后返回 NfcTag。后续如果要继续调用 transceive、readNDEFRecords、writeNDEFRecords、readBlock 等方法,应保持当前会话不要提前关闭。

transceive

向当前卡片发送十六进制指令字符串并接收十六进制字符串响应。调用前必须先成功执行 readCard。

readNDEFRecords / writeNDEFRecords

用于读取或写入 NDEF 记录,参数和返回值中的 identifier、payload、type 均为十六进制字符串。

makeNdefReadOnly

将标签设置为只读,该操作不可恢复,调用前请确认卡片支持且业务允许。

authenticateSector / readSector / readBlock / writeBlock

主要用于 Mifare Classic 与部分 Mifare Ultralight 场景。

  • authenticateSector:对指定扇区认证
  • readSector:读取整个扇区
  • readBlock:读取一个块或页
  • writeBlock:写入一个块或页

注意事项:

  1. keyA、keyB 必须是 6 字节十六进制字符串,例如 FFFFFFFFFFFF。
  2. Mifare Classic 单块写入数据长度必须为 16 字节。
  3. Mifare Ultralight 单页写入数据长度必须为 4 字节。

closeNFC

结束当前 NFC 会话。建议每次读卡、透传、读写 NDEF 或块操作完成后都调用一次,避免会话残留。

完整示例

import * as reader from "@/uni_modules/cz-nfc-reader"

export default {
  methods: {
    startRead() {
      reader.initNFC({
        readIso14443A: true,
        readIso14443B: true,
        readIso15693: true,
        readIso18092: true,
        completeListener: (initRes) => {
          if (!initRes.success) {
            uni.showModal({
              content: initRes.message ?? "初始化失败",
              showCancel: false
            })
            return
          }

          reader.readCard({
            completeListener: (cardRes) => {
              if (!cardRes.success || cardRes.data == null) {
                uni.showModal({
                  content: cardRes.message ?? "读卡失败",
                  showCancel: false
                })
                return
              }

              const tag = cardRes.data as reader.NfcTag
              console.log(`卡片信息: ${JSON.stringify(tag)}`)

              if (tag.ndefAvailable == true) {
                reader.readNDEFRecords({
                  cached: true,
                  completeListener: (ndefRes) => {
                    console.log(`NDEF: ${JSON.stringify(ndefRes.data)}`)
                    reader.closeNFC({})
                  }
                })
              } else {
                reader.closeNFC({})
              }
            }
          })
        }
      })
    }
  }
}

权限说明

Android

插件已包含 NFC 权限声明:

<uses-permission android:name="android.permission.NFC"/>

iOS

插件已包含以下配置:

/// Info.plist 参考配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NFCReaderUsageDescription</key>
	<string>需要使用NFC读取卡片信息</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>nfc</string>
	</array>
	<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
	<array>
		<string>88B4</string>
	</array>
	<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
	<array>
		<string>A00000000386980701</string>
	</array>
</dict>
</plist>


/// UTS.entitlements 参考配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.nfc.readersession.formats</key>
	<array>
		<string>TAG</string>
		<string>NDEF</string>
	</array>
</dict>
</plist>

同时已预置 FeliCa 与 ISO7816 相关会话配置。

iOS 侧还需要注意以下事项:

  1. 插件要求 iOS 13+。
  2. 需要在项目的 UTS.entitlements 中添加 Near Field Communication Tag Reader Session Formats 能力。
  3. 需要在 Info.plist 中添加 NFCReaderUsageDescription。
  4. 根据业务需要,在 Info.plist 中添加:
    • com.apple.developer.nfc.readersession.felica.systemcodes
    • com.apple.developer.nfc.readersession.iso7816.select-identifiers
  5. 特别注意:在 iOS 14.5 及更早版本上,如果调用轮询前启用了 readIso18092 或 readIso15693,那么必须先正确配置上面的 FeliCa / ISO7816 相关项。否则受 CoreNFC 已知问题影响,设备 NFC 可能在重启前都不可用。
  6. 如果是原生工程联调,打开 Runner.xcworkspace,进入 Xcode 的项目设置页面,在 Signing & Capabilities 中为目标应用添加 Near Field Communication Tag Reading 能力。

HarmonyOS

插件已声明:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.NFC_TAG"
    }
  ]
}

注意事项

  1. 仅支持手机真机调试,不支持模拟器。
  2. 使用 transceive、readNDEFRecords、writeNDEFRecords、readBlock、writeBlock 前,必须先成功调用 readCard。
  3. 调用完成后建议执行 closeNFC 关闭会话,避免影响下次读卡。
  4. makeNdefReadOnly 为不可逆操作,请谨慎使用。
  5. 不同平台、不同卡型支持能力不同,业务接入前建议先通过 readCard 返回的 type、standard、ndefAvailable 等字段进行判断。