EMV-NFC-Paycard-Enrollment reads what the card volunteers, and nothing else
A Java library used to read and extract data from NFC EMV credit cards (Android/PCSC).
At a glance
- What is it?
- EMV-NFC-Paycard-Enrollment is an Apache-2.0 Java library that talks APDU to a contactless EMV card through a provider you supply, then exposes the public data objects the card returns as typed getters. It has no NFC code of its own, which is both its main portability feature and the reason nothing works until you write the provider.
- Who is it for?
- Adopt this library if you are building an Android acceptance app, a terminal integration or a card wallet and want the EMV tag layer parsed into typed objects rather than raw TLV. Do not adopt it expecting it to work out of the box: the entire transport is a two method interface you must implement, and there is no Android provider in the repository, only a PCSC sample under `sample-pcsc`.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 53 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
You write the transport, the library writes the protocol
The first thing to understand is that this library contains no NFC code. The README's getting started begins with creating a custom Provider to exchange APDU with an NFC EMV credit card, and the interface is two methods:
public class YourProvider implements IProvider {
@Override
public byte[] transceive(final byte[] pCommand) {
// implement this
}
@Override
public byte[] getAt() {
// implement this to get card ATR (Answer To Reset) or ATS (Answer To Select)
}
}`transceive` sends one command and returns the response bytes. `getAt` returns the card's ATR for a contact reader or its ATS after a contactless selection. That is the entire contract, and the README links a PCSC implementation as the sample.
This split is the design decision that matters. Because the library never touches a reader API, the same parsing code runs against Android NFC, a PCSC reader over a USB contact reader, or a test double you wrote yourself, and the parsing does not change. The cost is that on Android you are writing the `transceive` body against `android.nfc.tech.IsoDep` and its response handling, and the repository does not ship that for you.
The library's stated scope is public data. The description says it is used to read and extract public data from NFC EMV credit cards, and the topics include `tlv` and `track2`. The documented API does not expose a PIN, CVV or cryptographic key.
Config decides how much of the card you actually read
After the provider, you build a parser with a config object, and every setting in it changes the amount of exchange with the card:
Config config = EmvTemplate.Config()
.setContactLess(true) // Enable contact less reading (default: true)
.setReadAllAids(true) // Read all aids in card (default: true)
.setReadTransactions(true) // Read all transactions (default: true)
.setReadCplc(false) // Read and extract CPCLC data (default: false)
.setRemoveDefaultParsers(false) // Remove default parsers for GeldKarte and EmvCard (default: false)
.setReadAt(true) // Read and extract ATR/ATS and description
.setReadAllRecords(true) // Read the whole application file, not only up to the card number (default: true)
.setReadExtendedData(false) // Read the optional GET DATA objects: offline balance, last online ATC (default: false)
.setTerminalCountryCode(CountryCodeEnum.FR) // Country the terminal pretends to be located in (default: FR)
;Three of these deserve a note. `setReadAllRecords` being true by default means the library reads the whole application file rather than stopping once it has the card number, which is slower but is what lets you populate the history and the risk objects. `setReadExtendedData` is false by default, and turning it on adds the GET DATA objects for offline balance and last online ATC. `setTerminalCountryCode` sets the country the terminal claims to be in, which affects how country specific behaviour is answered, and the default is `FR`.
There are also two escape hatches. A custom terminal implementation can be supplied through `setTerminal(terminal)` to build the APDUs yourself, and `setRemoveDefaultParsers(true)` drops the built in parsers for GeldKarte and EmvCard, which is what you want when a German girocard parser interferes with what you are reading.
The parser is then built and used:
EmvTemplate parser = EmvTemplate.Builder()
.setProvider(provider)
.setConfig(config)
.build();
EmvCard card = parser.readEmvCard();The returned `EmvCard` holds everything read: the AIDs, the card number, the expiration date, the card type and the transaction history.
The tag table is the real API, and it is long
The most useful page in the README is a table mapping EMV data objects to tags and to getters, repeated per `Application` on the card. It runs from the basics outward.
The basics: Application PAN and sequence number at tags `5A` and `5F34` give `getPan()` and `getPanSequenceNumber()`. Effective and expiration dates at `5F25` and `5F24`. Issuer country at `5F28` and `5F56`. Application currency and exponent at `9F42` and `9F44`. Application preferred name at `9F12`, issuer code table index at `9F11`, language preference at `5F2D`, service code at `5F30`, and issuer URL at `5F50`.
Then the EMV risk objects. Application version number `9F08`. Application Interchange Profile `82`. Application Usage Control `9F07`. The Cardholder Verification Method list `8E` with `getCvmList()`, `getCvmAmountX()` and `getCvmAmountY()`. Issuer Action Codes at `9F0E`, `9F0F` and `9F0D`. Kernel capability data objects at `9F6C`, `9F6E` and `9F5D`. The contactless kernel at `9F2A` with `getKernel()`, `getShortKernelId()` and `isKernelDerived()`. Offline data authentication credentials across `8F`, `90`, `92`, `93`, `9F32`, `9F46`, `9F47`, `9F48`, `9F4A` and `9F4B`. Consecutive offline limits at `9F14` and `9F23`. Application reference currencies at `9F3B` and `9F43`. The data object lists the card carries at `8C`, `8D`, `9F49` and `97`. Application discretionary data at `9F05`, issuer identification number at `42`, and issuer application data at `9F10`.
Two entries are worth pulling out because they answer questions people usually ask out loud. The offline balance at `9F79` and `9F50` is returned as a `BigDecimal` carrying up to 12 digits. The tokenized credential tags `9F19`, `9F24` and `9F25` answer `isTokenized()`, `getPaymentAccountReference()` and `getLast4DigitsOfPan()`, which is what tells a wallet app whether it is talking to a card or to a provisioned device.
Decoded objects where the standard fixes the meaning, raw bytes where it does not
The library distinguishes carefully between data objects it can decode and data objects it deliberately leaves alone.
The Application Interchange Profile, the Application Usage Control and the CVM list are decoded into objects, so you ask questions rather than decode bitmasks yourself:
Application app = card.getApplications().get(0);
app.getInterchangeProfile().isCdaSupported();
app.getUsageControl().isInternationalUsageAllowed();
for (CVM cvm : app.getCvmList()) {
System.out.println(cvm.getMethod() + " - " + cvm.getCondition());
}Those three lines cover the questions an issuer and risk team actually ask: does the card support cardholder data authentication, may it be used abroad, and which verification methods does it accept.
The Issuer Action Codes and the kernel capability data objects are exposed raw, and the README gives the reason twice. The Issuer Action Codes are bitmaps built on the Terminal Verification Results layout from EMV 4.3 Book 3 Annex C5, which is a fixed structure and therefore safe to decode. The meaning of `9F6C` and `9F6E` depends on the payment system, where those tags are Card Transaction Qualifiers and Form Factor Indicator for Visa and Mag Stripe Application Version Number and Third Party Data for MasterCard, so the library does not guess which one a card meant.
That is the right call and it is worth copying into your own integration. A tag whose meaning is scheme dependent cannot be decoded without knowing the scheme, and the card does not tell you which one it is in a field this library can read.
Building it, and the six year gap in the tag history
The repository is a Maven project: `pom.xml` at the root, a `library/` directory holding the library sources and a `sample-pcsc/` directory holding the sample provider. There is no Gradle build and no install command in the README, so consuming it means a dependency entry in your own `pom.xml` or a manual jar.
An Android sample app is published on Google Play under `com.github.devnied.emvnfccard.pro`, linked from the README as the demonstration of the Android path.
The version history is the part to look at carefully. Releases are tagged `parent-3.2.0` on 2026-08-31, `parent-3.1.0` on 2026-02-09 and `parent-3.0.1` on 2020-05-01. The 3.2.0 tag date and the last push are the same day, 2026-08-31, so the current work is released.
The six year gap between 3.0.1 and 3.1.0 is not a dormant project, since there have been two releases in 2026. It does mean the API surface in front of you has had one long stable period and one recent burst, and a CHANGELOG.md at the root is where you check what moved between them before you upgrade a version you last pinned in 2020.
Continuous integration is set up through a GitHub Actions CI badge, and coverage is tracked with Coveralls against the master branch.
Where the boundary is, and what to use instead
The limit of this library is worth stating plainly, because it is a property of the card interface rather than a defect. Everything it returns is what the card chooses to send a contactless reader. A card that does not expose a tag does not have it available, and no configuration setting here changes that. `setReadAllRecords` and `setReadExtendedData` widen what you ask for, not what the card agrees to.
That also means the library is not a card reader for a wallet integration that needs a secure element protocol, and it is not a substitute for an EMV compliant kernel in an acceptance device. It parses and exposes. What the host does with the result, including deciding whether the transaction may proceed, is the host's problem, and the presence of `setTerminalCountryCode` and a custom terminal implementation shows that the library is happy to be told what to claim.
The alternative for Android terminal work is to write the APDU exchange directly against `android.nfc.tech.IsoDep` and parse the TLV yourself. You get full control over transaction flow, and you take on the tag decoding, which is exactly the part this library's table above already enumerates.
For a desktop or server reader, the same library with the PCSC sample is the shorter path, since PCSC is a stable cross platform API and the sample shows the whole exchange.
Whichever path you take, the provider interface is the seam worth getting right first, because it is the only part you will have to write again on the next platform.
Editorial conclusion
Adopt this library if you are building an Android acceptance app, a terminal integration or a card wallet and want the EMV tag layer parsed into typed objects rather than raw TLV. Do not adopt it expecting it to work out of the box: the entire transport is a two method interface you must implement, and there is no Android provider in the repository, only a PCSC sample under `sample-pcsc`. Verify first that your reader and your card expose the tags you need, since `setReadAllRecords` and `setReadExtendedData` change what arrives, and note the last push was on 2026-08-31 while release 3.0.1 predates 3.1.0 and 3.2.0 by six years.
Frequently asked questions
How do I use EMV-NFC-Paycard-Enrollment?
Implement the `IProvider` interface with a `transceive` method for APDU exchange and a `getAt` method returning the card ATR or ATS, then build an `EmvTemplate` with that provider and a `Config`, and call `readEmvCard()`. The repository ships a PCSC sample provider under `sample-pcsc`.
Does EMV-NFC-Paycard-Enrollment read the PIN or CVV?
No. The library is described as reading public data from the card, and the API exposes EMV data objects such as the PAN, expiration date and transaction history. The documented API does not show a PIN or CVV getter.
What does setReadAllRecords do in EMV-NFC-Paycard-Enrollment?
When true, which is the default, the library reads the whole application file instead of stopping once it has reached the card number. That is what populates the transaction history and the risk related data objects.
Which EMV tags does the library decode into objects?
The Application Interchange Profile at tag `82`, the Application Usage Control at `9F07`, and the CVM list at `8E` are decoded into objects. Issuer Action Codes and the kernel capability tags `9F6C` and `9F6E` are left raw because their meaning depends on the payment system.
What licence is EMV-NFC-Paycard-Enrollment released under?
Apache-2.0, with the licence text in `licence.txt` at the repository root.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/devnied-emv-nfc-paycard-enrollment)Was this useful?