Skip to content

nfc: add support for NFC Forum Type 1/2 tags (NTAG213/215/216)#809

Open
AxelHamburch wants to merge 1 commit into
ACINQ:masterfrom
AxelHamburch:fix/nfc-ntag21x-support
Open

nfc: add support for NFC Forum Type 1/2 tags (NTAG213/215/216)#809
AxelHamburch wants to merge 1 commit into
ACINQ:masterfrom
AxelHamburch:fix/nfc-ntag21x-support

Conversation

@AxelHamburch
Copy link
Copy Markdown

Problem

The NFC reader in Phoenix only works with ISO-DEP (NFC Forum Type 4) tags such as NTAG424-DNA and Bolt Cards. Tags from the widely-used NTAG21x family (NTAG213, NTAG215, NTAG216) are silently dropped without any payment being initiated.

What happens today

  1. Android OS detects the tag → plays the system NFC discovery sound ("bling")
  2. NfcReaderCallback.onTagDiscovered() is called
  3. IsoDep.get(tag) returns null because NTAG21x tags implement NFC Forum Type 2 (ISO 14443-3A), not ISO-DEP (ISO 14443-4)
  4. The callback logs "aborting tag discovery: tag does not support iso-dep" and returns
  5. No payment flow is triggered

NTAG21x tags are extremely common — they are used by many Lightning payment terminals, NFC stickers, and payment devices to encode LNURL-pay / Lightning invoice URIs as plain NDEF records.

Other Lightning wallets already handle this

The following Lightning wallets read NTAG21x tags without issues:

  • ZEUS
  • Wallet of Satoshi (WoS)
  • Blink
  • Electrum
  • Alby Go
  • BitBanana
  • Misty Breez

Phoenix is currently the only major Android Lightning wallet where NTAG21x tags do not trigger a payment.


Root cause

NfcReaderCallback exclusively calls IsoDep.get(tag). If that returns null the entire tag is rejected. There is no fallback for the Android Ndef technology class, which is the correct API for NFC Forum Type 1/2 tags.


Fix

Add a fallback path in NfcReaderCallback that uses Ndef.get(tag) when a tag is not ISO-DEP capable:

onTagDiscovered(tag)
  ├─ IsoDep.get(tag) != null  →  readIsoDepTag()   (existing APDU flow, unchanged)
  └─ Ndef.get(tag)   != null  →  readNdefTag()     (new: direct NDEF read via Android API)

The readNdefTag() method calls ndef.ndefMessage directly — no APDU commands needed. The NDEF record is then parsed by the existing NdefParser, so all supported URI/text schemes (LNURL, Lightning, Bitcoin, …) work out of the box.

The existing ISO-DEP path is completely unchanged. Only one file is modified.

Changed file

phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/nfc/NfcReaderCallback.kt

  • import android.nfc.tech.Ndef added
  • KDoc updated to document both tag paths
  • onTagDiscovered refactored: isReading() guard moved to top, two tech branches added
  • Existing APDU logic extracted into readIsoDepTag(isoDep: IsoDep)
  • New method readNdefTag(ndef: Ndef) added

Testing

Tested with:

  • NTAG213 encoding lnurlp://... — payment screen opens correctly
  • NTAG424-DNA (Bolt Card) — existing ISO-DEP path continues to work as before

No changes to AndroidManifest.xml are required: FLAG_READER_NFC_A is already set in startNfcReader(), which covers both Type 2 and Type 4 NFC-A tags.

The NFC reader previously only accepted ISO-DEP (NFC Forum Type 4) tags
such as NTAG424-DNA and Bolt Cards. Tags based on the NFC Forum Type 2
spec, like the widely-used NTAG213, NTAG215 and NTAG216 family, were
silently dropped because IsoDep.get(tag) returns null for them.

Add a fallback path in NfcReaderCallback that uses the Android Ndef API
to read NDEF messages directly from Type 1/2 tags whenever a tag is not
ISO-DEP capable. The existing APDU-based ISO-DEP flow is unchanged.

The two paths were also refactored into separate private methods
(readIsoDepTag / readNdefTag) to improve readability.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants