更新记录

1.0.0(2026-05-18)

  • 支持安卓、iOS、鸿蒙NFC基础功能
  • 调试NDEF读写卡,完善功能

平台兼容性

uni-app(4.45)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 × × 1.0.0 1.0.0 5.0 1.0.0 13 1.0.0 1.0.0
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(4.45)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
× × 5.0 1.0.0 13 1.0.0 1.0.0 ×

其他

多语言 暗黑模式 宽屏模式

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

介绍

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

插件试用

APK下载

平台支持

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

数据结构说明

所有 API 均通过回调中的 ResponseEntity 返回结果,各业务数据类型定义如下。

ResponseEntity

通用响应体,所有方法回调均使用此类型。

type ResponseEntity = {
  success: boolean           // 操作是否成功
  data?: any | null          // 业务数据,具体类型取决于调用的方法
  message?: string | null    // 错误信息,success 为 false 时说明失败原因
}

NfcTag

readCard 成功后 data 字段返回的卡片信息对象。

type NfcTag = {
  type?: string | null                    // 卡片类型,如 "iso7816"、"mifare_classic"、"mifare_ultralight"、"iso15693"、"iso18092"
  id?: string | null                      // 卡片 ID(UID),十六进制字符串
  standard?: string | null                // 卡片协议标准描述
  atqa?: string | null                    // ATQA(ISO14443A 应答),十六进制字符串
  sak?: string | null                     // SAK(Select Acknowledge),十六进制字符串
  historicalBytes?: string | null         // 历史字节(ISO7816),十六进制字符串
  protocolInfo?: string | null            // 协议信息,十六进制字符串
  applicationData?: string | null         // 应用数据,十六进制字符串
  hiLayerResponse?: string | null         // 高层响应,十六进制字符串
  manufacturer?: string | null            // 制造商信息
  systemCode?: string | null              // 系统码(FeliCa),十六进制字符串
  dsfId?: string | null                   // DSF ID(ISO15693),十六进制字符串
  ndefAvailable?: boolean | null          // 是否支持 NDEF
  ndefType?: string | null                // NDEF 标签类型,如 "TYPE1"、"TYPE2"、"TYPE3"、"TYPE4"、"MIFARE_CLASSIC"
  ndefWritable?: boolean | null           // NDEF 是否可写
  ndefCanMakeReadOnly?: boolean | null    // 是否可将 NDEF 设置为只读
  ndefCapacity?: number | null            // NDEF 容量(字节)
  mifareInfo?: NfcMifareInfo | null       // Mifare 卡片详细参数,非 Mifare 卡为 null
}

NfcMifareInfo

NfcTag.mifareInfo 的子结构,描述 Mifare 卡片的物理参数。

type NfcMifareInfo = {
  type?: string | null        // Mifare 子类型,如 "Classic"、"Ultralight"、"Ultralight C"
  size?: number | null        // 卡片总容量(字节)
  blockSize?: number | null   // 单块/单页大小(字节)
  blockCount?: number | null  // 块/页总数
  sectorCount?: number | null // 扇区总数(Mifare Classic 为 16 或 40)
}

NdefInfo

readNDEFRecords 成功后 data 字段返回的 NDEF 记录列表元素。所有字段均为十六进制字符串,业务层可按需转为文本。

type NdefInfo = {
  identifier: string         // 记录标识符,十六进制字符串
  payload: string            // NDEF 消息有效负载,十六进制字符串
  type: string               // NDEF 记录类型,十六进制字符串(如 "54" 表示 Text、"55" 表示 URI)
  typeNameFormat: string     // TNF(类型名称格式),取值见下表
}

TNF(typeNameFormat)常见值:

含义
"0" 空(Empty)
"1" NFC Forum 已知类型(Well Known)
"2" MIME 媒体类型
"3" 绝对 URI
"4" NFC Forum 外部类型
"5" 未知类型

AndroidReaderModeFlags

initNFCandroidReaderModeFlags 参数类型,用于控制 Android Reader Mode 的附加行为,可按位组合(| 运算符)。

type AndroidReaderModeFlags = 0x80 | 0x100
常量 十六进制 说明
跳过 NDEF 自动发现 0x80 跳过系统自动 NDEF 检测,可将标签发现速度提升约 50ms
禁用系统音效/振动 0x100 发现标签时不播放系统提示音也不振动

组合示例:0x80 | 0x100 表示既跳过 NDEF 发现又不播放音效。

API 参考

API 一览

方法名称 参数类型 回调 data 类型 说明
initNFC InitNFCParams 初始化 NFC 读卡配置,检查权限和设备能力
readCard ReadCardParams NfcTag 开始轮询卡片并返回卡片信息
transceive TransceiveParams string(十六进制) 与卡片进行 APDU 或原始指令透传
readNDEFRecords ReadNDEFParams NdefInfo[](JSON) 读取当前卡片上的 NDEF 记录
writeNDEFRecords WriteNDEFParams 写入 NDEF 记录到卡片
makeNdefReadOnly MakeNDEFReadOnlyParams 将当前 NDEF 标签设置为只读,操作不可逆
authenticateSector AuthenticateSectorParams boolean 对 Mifare Classic 扇区进行 KeyA 或 KeyB 认证
readSector ReadSectorParams string(十六进制) 读取 Mifare Classic 整个扇区
readBlock ReadBlockParams string(十六进制) 读取 Mifare Classic 块或 Mifare Ultralight 页
writeBlock WriteBlockParams boolean 写入 Mifare Classic 块或 Mifare Ultralight 页
closeNFC CloseNFCParams 关闭当前 NFC 会话

initNFC

初始化 NFC 能力和读卡配置。所有 NFC 操作的第一步,调用成功后才能进行读卡。

参数:InitNFCParams

参数 类型 默认值 平台 说明
timeout number 30000 Android / HarmonyOS 读卡超时时间,单位毫秒。iOS 该参数无效
readIso14443A boolean true 全部 是否启用 ISO14443A 类型卡片(Mifare Classic / Ultralight 等)
readIso14443B boolean true 全部 是否启用 ISO14443B 类型卡片
readIso18092 boolean false 全部 是否启用 ISO18092 / FeliCa 类型卡片
readIso15693 boolean true 全部 是否启用 ISO15693 类型卡片
androidSound boolean true 仅 Android 发现标签时是否播放系统提示音
androidCheckNDEF boolean true 仅 Android 是否启用系统自动 NDEF 检查
androidReaderModeFlags AndroidReaderModeFlags 仅 Android Reader Mode 附加标志,见上方 AndroidReaderModeFlags 说明
extraReaderPresenceCheckDelay number 仅 Android Presence Check 额外延迟(毫秒),用于兼容部分响应慢的卡片
iosAlertMessage string 仅 iOS 会话开始时系统弹窗显示的提示文字,如 "请将手机靠近 NFC 标签"
iosMultipleTagMessage string 仅 iOS 检测到多张标签时系统弹窗显示的提示文字
completeListener (res: ResponseEntity) => void - 全部 操作完成回调

示例:

reader.initNFC({
  timeout: 30000,
  readIso14443A: true,
  readIso14443B: true,
  readIso15693: true,
  readIso18092: false,
  androidSound: true,
  iosAlertMessage: "请将手机靠近 NFC 标签",
  completeListener(res) {
    if (res.success) {
      console.log("NFC 初始化成功")
    } else {
      console.log(`NFC 初始化失败: ${res.message}`)
    }
  }
})

readCard

启动一次读卡轮询,靠近卡片后自动识别并返回卡片信息。后续如果要继续调用 transceivereadNDEFRecordswriteNDEFRecordsreadBlock 等方法,必须在 readCard 回调成功后保持会话,不要提前 closeNFC

参数:ReadCardParams

参数 类型 默认值 说明
completeListener (res: ResponseEntity) => void - 操作完成回调,res.dataNfcTag

回调 data NfcTag — 详见上方「数据结构说明」。

示例:

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.type}`)
    console.log(`标准: ${tag.standard}`)
    console.log(`支持NDEF: ${tag.ndefAvailable}`)
    console.log(`NDEF可写: ${tag.ndefWritable}`)
    console.log(`NDEF容量: ${tag.ndefCapacity}`)
    if (tag.mifareInfo != null) {
      console.log(`Mifare类型: ${tag.mifareInfo.type}`)
      console.log(`容量: ${tag.mifareInfo.size} 字节`)
      console.log(`扇区数: ${tag.mifareInfo.sectorCount}`)
      console.log(`块数: ${tag.mifareInfo.blockCount}`)
    }
  }
})

transceive

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

  • ISO7816(NfcA / IsoDep)卡片:发送标准 APDU 指令
  • ISO15693(NfcV)卡片:发送原始指令
  • FeliCa(NfcF)卡片:发送原始指令

参数:TransceiveParams

参数 类型 默认值 平台 说明
capdu string - 全部 APDU 或原始指令,十六进制字符串,如 "00A4040009A00000000386980701"
timeout number 3000 Android / HarmonyOS 指令超时时间,单位毫秒。iOS 该参数无效
completeListener (res: ResponseEntity) => void - 全部 操作完成回调,res.data 为十六进制响应字符串

示例:

// 选择 PPSE(支付环境)
reader.transceive({
  capdu: "00A404000E325041592E5359532E444446303100",
  timeout: 5000,
  completeListener(res) {
    if (res.success) {
      console.log(`响应: ${res.data}`)
    } else {
      console.log(`透传失败: ${res.message}`)
    }
  }
})

readNDEFRecords

读取当前卡片上的 NDEF 记录。

调用前必须先成功执行 readCard,建议先通过 tag.ndefAvailable 判断卡片是否支持 NDEF。

参数:ReadNDEFParams

参数 类型 默认值 平台 说明
cached boolean false 仅 Android 是否允许读取缓存中的 NDEF 消息(首次扫描时系统可能已读取)
completeListener (res: ResponseEntity) => void - 全部 操作完成回调,res.dataNdefInfo[] 的 JSON 字符串

回调 data NdefInfo[] — 详见上方「数据结构说明」。注意实际返回为 JSON 字符串格式,可用 JSON.parse 解析。

示例:

reader.readNDEFRecords({
  cached: false,
  completeListener(res) {
    if (!res.success || res.data == null) {
      console.log(`读取 NDEF 失败: ${res.message}`)
      return
    }
    const records: reader.NdefInfo[] = JSON.parse(res.data)
    records.forEach((record, index) => {
      console.log(`记录 ${index}:`)
      console.log(`  TNF: ${record.typeNameFormat}`)
      console.log(`  类型: ${record.type}`)
      console.log(`  标识符: ${record.identifier}`)
      console.log(`  负载: ${record.payload}`)
    })
  }
})

writeNDEFRecords

将 NDEF 记录写入卡片。调用前必须先成功执行 readCard,建议先通过 tag.ndefWritable 确认卡片可写。

参数:WriteNDEFParams

参数 类型 默认值 说明
data string - NDEF 记录列表的 JSON 字符串,格式为 NdefInfo[]
completeListener (res: ResponseEntity) => void - 操作完成回调

注意: data 参数中的 identifierpayloadtype 字段均需为十六进制字符串,不是纯文本。例如 Text 记录的 payload 需要先将文本转 hex。

示例:写入一条 Text 记录和一条 URI 记录

// 构建 NDEF 记录
const records: reader.NdefInfo[] = [
  {
    identifier: "",                                          // 无标识符
    payload: "02656E48656C6C6F",                             // Text: "en" + "Hello" 的 hex
    type: "54",                                              // "T" = Text 类型
    typeNameFormat: "1"                                      // Well Known
  },
  {
    identifier: "",
    payload: "0368747470733A2F2F7777772E6578616D706C652E636F6D", // URI: "https://www.example.com"
    type: "55",                                              // "U" = URI 类型
    typeNameFormat: "1"
  }
]

reader.writeNDEFRecords({
  data: JSON.stringify(records),
  completeListener(res) {
    if (res.success) {
      console.log("NDEF 写入成功")
    } else {
      console.log(`NDEF 写入失败: ${res.message}`)
    }
  }
})

makeNdefReadOnly

将当前 NDEF 标签设置为只读。该操作不可逆,调用前请确认卡片支持(tag.ndefCanMakeReadOnlytrue)且业务允许。

参数:MakeNDEFReadOnlyParams

参数 类型 默认值 说明
completeListener (res: ResponseEntity) => void - 操作完成回调

示例:

reader.makeNdefReadOnly({
  completeListener(res) {
    if (res.success) {
      console.log("已将标签设为只读")
    } else {
      console.log(`设只读失败: ${res.message}`)
    }
  }
})

authenticateSector

对 Mifare Classic 卡片的指定扇区进行密钥认证。使用 KeyA 或 KeyB 认证后,才能对该扇区的块进行读写操作。

iOS 平台: CoreNFC 不支持直接操作 Mifare Classic 扇区认证,iOS 侧固定回调 res.success = trueres.data = true,以避免影响业务调用链。

参数:AuthenticateSectorParams

参数 类型 默认值 说明
index number - 扇区索引,从 0 开始。Mifare Classic 1K 为 0–15,4K 为 0–39
keyA string 6 字节 KeyA,十六进制字符串(12 个字符),如 "FFFFFFFFFFFF"。与 keyB 二选一
keyB string 6 字节 KeyB,十六进制字符串(12 个字符),如 "FFFFFFFFFFFF"。与 keyA 二选一
completeListener (res: ResponseEntity) => void - 操作完成回调,res.databoolean 表示认证是否成功

示例:

// 用默认 KeyA 认证第 1 扇区
reader.authenticateSector({
  index: 1,
  keyA: "FFFFFFFFFFFF",
  completeListener(res) {
    if (res.success && res.data == true) {
      console.log("扇区 1 认证成功,可以读写")
    } else {
      console.log(`认证失败: ${res.message}`)
    }
  }
})

readSector

读取 Mifare Classic 卡片的整个扇区(包含所有块的数据)。

iOS 平台: 不支持此操作,回调返回失败。

参数:ReadSectorParams

参数 类型 默认值 说明
index number - 扇区索引,从 0 开始
keyA string 6 字节 KeyA,十六进制字符串。与 keyB 二选一
keyB string 6 字节 KeyB,十六进制字符串。与 keyA 二选一
completeListener (res: ResponseEntity) => void - 操作完成回调,res.data 为整个扇区数据的十六进制字符串

注意: 插件内部会自动先执行认证再读取,无需手动调用 authenticateSector。但如果扇区密钥不是默认值,需要通过 keyAkeyB 传入正确的密钥。

示例:

// 读取扇区 0(通常包含厂商数据)
reader.readSector({
  index: 0,
  keyA: "FFFFFFFFFFFF",
  completeListener(res) {
    if (res.success) {
      const sectorData = res.data as string
      console.log(`扇区 0 数据: ${sectorData}`)
      // 扇区 0 共 4 个块(Mifare Classic 1K),每块 32 个 hex 字符(16 字节)
      // 块 0: sectorData.substring(0, 32)
      // 块 1: sectorData.substring(32, 64)
      // 块 2: sectorData.substring(64, 96)
      // 块 3: sectorData.substring(96, 128) — 密钥区
    } else {
      console.log(`读取扇区失败: ${res.message}`)
    }
  }
})

readBlock

读取 Mifare Classic 的一个块(16 字节)或 Mifare Ultralight 的一个页(4 字节)。也可用于读取 ISO15693 卡片块数据。

iOS 平台: 主要支持 MiFareISO15693 类型,不支持 Mifare Classic 原生扇区认证。 HarmonyOS 平台: ISO15693 类型建议通过 transceive 兜底。

参数:ReadBlockParams

参数 类型 默认值 说明
block number - 块/页索引,从 0 开始
index number 扇区索引,从 0 开始。Mifare Classic 必填,Mifare Ultralight 可不传
keyA string 6 字节 KeyA,十六进制字符串。与 keyB 二选一
keyB string 6 字节 KeyB,十六进制字符串。与 keyA 二选一
iso15693Flags number ISO15693 请求标志位(仅 ISO15693 卡片有效)
iso15693ExtendedMode boolean 是否使用扩展模式,支持块编号 0–255(仅 ISO15693 卡片有效)
completeListener (res: ResponseEntity) => void - 操作完成回调,res.data 为十六进制字符串

注意: 对于 Mifare Classic,插件内部会自动先对 index 指定扇区执行认证再读取块,无需手动调用 authenticateSector。Mifare Ultralight 无需认证,不传 index / keyA / keyB 即可。

示例 1:读取 Mifare Classic 块

// 读取扇区 1 的块 0(即绝对块号 4)
reader.readBlock({
  block: 0,
  index: 1,
  keyA: "FFFFFFFFFFFF",
  completeListener(res) {
    if (res.success) {
      console.log(`块数据: ${res.data}`)  // 32 个 hex 字符 = 16 字节
    } else {
      console.log(`读块失败: ${res.message}`)
    }
  }
})

示例 2:读取 Mifare Ultralight 页

// 读取第 4 页
reader.readBlock({
  block: 4,
  completeListener(res) {
    if (res.success) {
      console.log(`页数据: ${res.data}`)  // 8 个 hex 字符 = 4 字节
    } else {
      console.log(`读页失败: ${res.message}`)
    }
  }
})

示例 3:读取 ISO15693 块

reader.readBlock({
  block: 0,
  iso15693Flags: 0x20,          // 高位地址模式
  iso15693ExtendedMode: false,
  completeListener(res) {
    if (res.success) {
      console.log(`ISO15693 块数据: ${res.data}`)
    } else {
      console.log(`读块失败: ${res.message}`)
    }
  }
})

writeBlock

写入 Mifare Classic 的一个块(16 字节)或 Mifare Ultralight 的一个页(4 字节)。也可用于写入 ISO15693 卡片块数据。

平台注意:readBlock

参数:WriteBlockParams

参数 类型 默认值 说明
block number - 块/页索引,从 0 开始
index number 扇区索引,从 0 开始。Mifare Classic 必填,Mifare Ultralight 可不传
keyA string 6 字节 KeyA,十六进制字符串。与 keyB 二选一
keyB string 6 字节 KeyB,十六进制字符串。与 keyA 二选一
iso15693Flags number ISO15693 请求标志位(仅 ISO15693 卡片有效)
iso15693ExtendedMode boolean 是否使用扩展模式(仅 ISO15693 卡片有效)
data string - 要写入的数据,十六进制字符串
completeListener (res: ResponseEntity) => void - 操作完成回调,res.databoolean 表示是否写入成功

数据长度要求:

  • Mifare Classic 单块:32 个 hex 字符(16 字节)
  • Mifare Ultralight 单页:8 个 hex 字符(4 字节)

示例 1:写入 Mifare Classic 块

// 向扇区 1 的块 1 写入 16 字节数据
reader.writeBlock({
  block: 1,
  index: 1,
  keyA: "FFFFFFFFFFFF",
  data: "48656C6C6F20576F726C6421212121",  // "Hello World!!!" 的 hex(16 字节)
  completeListener(res) {
    if (res.success && res.data == true) {
      console.log("写块成功")
    } else {
      console.log(`写块失败: ${res.message}`)
    }
  }
})

示例 2:写入 Mifare Ultralight 页

// 向第 6 页写入 4 字节数据
reader.writeBlock({
  block: 6,
  data: "DEADBEEF",
  completeListener(res) {
    if (res.success && res.data == true) {
      console.log("写页成功")
    } else {
      console.log(`写页失败: ${res.message}`)
    }
  }
})

示例 3:写入 ISO15693 块

reader.writeBlock({
  block: 2,
  iso15693Flags: 0x20,
  iso15693ExtendedMode: false,
  data: "AABBCCDD",
  completeListener(res) {
    if (res.success) {
      console.log("ISO15693 写块成功")
    } else {
      console.log(`写块失败: ${res.message}`)
    }
  }
})

closeNFC

结束当前 NFC 会话,释放资源。建议每次完成读卡、透传、NDEF 读写或块操作后调用,避免会话残留影响下次使用。

参数:CloseNFCParams

参数 类型 默认值 平台 说明
iosAlertMessage string 仅 iOS 会话结束时系统弹窗显示的提示文字
completeListener (res: ResponseEntity) => void - 全部 操作完成回调

示例:

reader.closeNFC({
  iosAlertMessage: "读取完成",
  completeListener(res) {
    console.log("NFC 会话已关闭")
  }
})

平台差异说明

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

API Android iOS HarmonyOS 说明
initNFC 支持 支持 支持 三端都支持初始化,但 timeout/androidSound 等参数仅在特定平台生效
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 对 MiFareISO15693 的能力支持。

HarmonyOS 特别说明

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

Android 特别说明

  1. androidSoundandroidCheckNDEFandroidReaderModeFlagsextraReaderPresenceCheckDelay 仅 Android 平台生效。
  2. readBlock / writeBlock 在 Android 侧对 Mifare ClassicMifare UltralightISO15693 的支持相对更完整。

快速开始

1. 引入插件

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

2. 初始化并读卡

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) {
        if (res.success) {
          console.log(`NDEF 结果: ${JSON.stringify(res.data)}`)
        }
        reader.closeNFC({})
      }
    })
  }
})

5. APDU 透传

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

完整示例

以下是一个完整的读卡流程示例,包含初始化 → 读卡 → 判断卡类型 → 按类型执行不同操作的完整调用链。

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({})
              }
            }
          })
        }
      })
    }
  }
}

常用场景示例

场景:Mifare Classic 扇区读写

function readAndWriteSector() {
  reader.readCard({
    completeListener(cardRes) {
      if (!cardRes.success || cardRes.data == null) return

      const tag = cardRes.data as reader.NfcTag
      if (tag.type != "mifare_classic") {
        console.log("非 Mifare Classic 卡片")
        reader.closeNFC({})
        return
      }

      // 步骤1:认证扇区 1
      reader.authenticateSector({
        index: 1,
        keyA: "FFFFFFFFFFFF",
        completeListener(authRes) {
          if (!authRes.success || authRes.data != true) {
            console.log(`认证失败: ${authRes.message}`)
            reader.closeNFC({})
            return
          }

          // 步骤2:读取扇区 1 的块 0
          reader.readBlock({
            block: 0,
            index: 1,
            keyA: "FFFFFFFFFFFF",
            completeListener(readRes) {
              console.log(`块数据: ${readRes.data}`)

              // 步骤3:写入数据到扇区 1 的块 1
              reader.writeBlock({
                block: 1,
                index: 1,
                keyA: "FFFFFFFFFFFF",
                data: "00112233445566778899AABBCCDDEEFF",  // 16 字节
                completeListener(writeRes) {
                  console.log(`写入结果: ${writeRes.success}`)
                  reader.closeNFC({})
                }
              })
            }
          })
        }
      })
    }
  })
}

场景:NDEF 写入并设为只读

function writeNdefAndMakeReadOnly() {
  reader.readCard({
    completeListener(cardRes) {
      if (!cardRes.success || cardRes.data == null) return

      const tag = cardRes.data as reader.NfcTag
      if (!tag.ndefWritable) {
        console.log("卡片不支持 NDEF 写入")
        reader.closeNFC({})
        return
      }

      // 构建一条 Text 记录
      const records: reader.NdefInfo[] = [{
        identifier: "",
        payload: "02656E48656C6C6F",    // "en" + "Hello"
        type: "54",                      // Text
        typeNameFormat: "1"              // Well Known
      }]

      reader.writeNDEFRecords({
        data: JSON.stringify(records),
        completeListener(writeRes) {
          if (!writeRes.success) {
            console.log(`写入失败: ${writeRes.message}`)
            reader.closeNFC({})
            return
          }
          console.log("NDEF 写入成功")

          // 设为只读(不可逆)
          reader.makeNdefReadOnly({
            completeListener(roRes) {
              if (roRes.success) {
                console.log("已设为只读")
              } else {
                console.log(`设只读失败: ${roRes.message}`)
              }
              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 及更早版本上,如果调用轮询前启用了 readIso18092readIso15693,那么必须先正确配置上面的 FeliCa / ISO7816 相关项。否则受 CoreNFC 已知问题影响,设备 NFC 可能在重启前都不可用。
  6. 如果是原生工程联调,打开 Runner.xcworkspace,进入 Xcode 的项目设置页面,在 Signing & Capabilities 中为目标应用添加 Near Field Communication Tag Reading 能力。

HarmonyOS

插件已声明:

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

注意事项

  1. 仅支持手机真机调试,不支持模拟器。
  2. 使用 transceivereadNDEFRecordswriteNDEFRecordsreadBlockwriteBlock 前,必须先成功调用 readCard
  3. 调用完成后建议执行 closeNFC 关闭会话,避免影响下次读卡。
  4. makeNdefReadOnly 为不可逆操作,请谨慎使用。
  5. 不同平台、不同卡型支持能力不同,业务接入前建议先通过 readCard 返回的 typestandardndefAvailable 等字段进行判断。
  6. 所有 API 的 data 字段中涉及二进制数据的(如 APDU 指令、块数据、NDEF 负载等),均使用十六进制字符串表示,业务层需自行处理 hex ↔ 文本/字节的转换。
  7. Mifare Classic 密钥 keyA / keyB 必须是 12 个十六进制字符(6 字节),如 "FFFFFFFFFFFF" 表示全 F 的默认密钥。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

参见插件使用说明文档配置

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: