Android - NFC Module¶
The NFC module is used to read chip data from identity cards or passports. For the reading process to be successful, specific information obtained from the identification document must be provided in advance.
⚠️ Prerequisite: Before using the NFC Module, Maven access must be provided and the Core Module must be added to the project. The NFC Module will not work by itself without the Core Module.
Required Data¶
The following information must be provided for chip data to be read successfully:
| Data | Format | Example |
|---|---|---|
| Serial Number | Alphanumeric | A11A11111 |
| ID Validity Date | YYMMDD | 280621 |
| Date of Birth | YYMMDD | 950621 |
Without this information, the NFC reading process will fail.
Adding to the Project¶
1. Add to libs.versions.toml:¶
[versions]
...
enqualify-plus = "x.x.x.x"
[libraries]
...
enqualify-plus-nfc = {
group = "com.enqualify.plus",
name = "nfc",
version.ref = "enqualify-plus"
}
2. Add Dependency to build.gradle.kts:¶
3. Gradle Sync¶
Synchronize the Gradle files by clicking on the "Sync Now" option.
Implementation¶
1. Overriding the onNewIntent Function¶
To manage the intents coming to the NFC process, the onNewIntent should be overridden in the Activity where SDK functions are used. This is not a callback from EnQualify, but a system callback coming from AppCompatActivity:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (NfcAdapter.ACTION_TAG_DISCOVERED == intent.action ||
NfcAdapter.ACTION_TECH_DISCOVERED == intent.action) {
setIntent(intent)
} else {
super.onNewIntent(intent)
}
}
2. Activity Layout — FrameLayout¶
A FrameLayout that covers the entire screen should be added to the layout of the Activity where the NFC process will take place. The SDK manages page transitions through this component:
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3. Adding NFCCallbacks Interface¶
The NFCCallbacks interface contains the necessary callback methods to manage NFC operations and must work with an Activity.
Adding to the class:
Overriding Callbacks:
class EnQualifyPlusActivity : AppCompatActivity(), NFCCallbacks {
override fun initializeCompleted(modules: EnModules) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
when (modules) {
EnModules.NFC -> {
// NFC operations can be started
}
}
}
override fun initializeFailed(failureCode: FailureCode, additionalMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun nfcCompleted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun nfcFailed(nfcFailureCode: NFCFailureCode, additionalMessage: String?) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $nfcFailureCode: $additionalMessage")
}
override fun nfcSaveStarted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun integrationAddCompleted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun integrationAddFailed(errorMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
}
4. Creating Custom NFCReadFragment¶
The fragment that performs the NFC reading process should be derived from the EnNFCBaseFragment class. This class provides the basic functions for NFC operations and manages the reading stages.
class NfcReadFragment : EnNFCBaseFragment() {
private var _binding: FragmentNfcReadBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNfcReadBinding.inflate(layoutInflater)
setupIndicators()
return binding.root
}
override fun onNfcReadStart() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
setCurrentIndicator(0, R.string.nfc_chip_scan_start)
}
}
ℹ️ Customizations can be made on the fragment depending on the design.
5. Initializing EnQualifyNFC¶
Before starting NFC operations, EnQualifyNFC.initialize() must be called. If any other operation is attempted before initialization, the SDK will not work.
val sessionModel = SessionModel(
callType = "NewCustomer",
reference = UUID.randomUUID().toString()
)
val baseModel = BaseModel(
baseURL = "https://deveqmapi.enqura.com",
signallingCertificateList = listOf("enqura"),
mapiCertificateList = listOf("enqura"),
mobileUser = "mobile",
countryCode = "TUR",
locale = "TR",
useEmbeddedLocalSound = false
)
EnQualifyNFC.initialize(
context = this,
fragmentManager = supportFragmentManager,
containerID = R.id.fragmentContainer,
sessionModel = sessionModel,
baseModel = baseModel
)
Initialize Parameters¶
| Parameter | Type | Description |
|---|---|---|
context | Context | Runtime context of the application |
sessionModel | SessionModel | Session information to be used during NFC operations |
baseModel | BaseModel | Model carrying the basic data for the verification process |
fragmentManager | FragmentManager | Manages NFC fragments |
containerID | Int | ID of the container where the NFC fragment will be added |
Automatic Flow After Initialization¶
After calling initialize(), the SDK automatically performs the steps in order: Token → Session → Settings. In case of errors, the relevant callbacks are triggered:
override fun tokenCreateFailed(errorMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $errorMessage")
}
override fun sessionAddFailed(errorMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun settingsGetFailed(errorMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
When all steps are completed, initializeCompleted is triggered, and NFC operations can be initiated.
NFC Operations¶
Functions¶
| Function | Description |
|---|---|
addFragment(fragment) | Adds a new fragment on top of the current fragment |
replaceFragment(fragment) | Replaces the current fragment with a new one |
nfcStart(fragment, documentNo, birthDate, expiryDate) | Starts the NFC reading process |
addIntegration(integrationModel) | Adds integration data |
closeSession(isFinished: Boolean) | Closes the session |
clear() | Clears the SDK |
getSessionUId() | Returns the UId of the current session. |
Starting NFC Reading Process¶
ID document data is required for the NFC reading process. This data is generally obtained automatically from the CustomerIDDoc object after the OCR process:
EnQualifyNFC.getInstance().nfcStart(
NfcReadFragment(),
CustomerIDDoc.getInstance().documentNumber,
CustomerIDDoc.getInstance().dateOfBirthDay,
CustomerIDDoc.getInstance().expiryDate
)
Function Parameters¶
| Parameter | Type | Description |
|---|---|---|
fragment | EnNFCBaseFragment | NFC reading fragment derived from EnNFCBaseFragment |
documentNo | String | Document/serial number |
birthDate | String | Date of birth — in YYMMDD format (e.g., 21.06.1995 → 950621) |
expiryDate | String | Validity date — in YYMMDD format (e.g., 21.06.2028 → 280621) |
ℹ️ If the NFC operation is to be performed before OCR, the data should be manually entered in the formats above.
Reading Flow¶
- When Reading is Completed
When the NFC reading is completed, the SDK starts automatically sending the read data to the back office, and nfcSaveStarted is triggered:
override fun nfcSaveStarted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
- When Data Transmission is Completed
When the data is successfully sent to the back office, nfcCompleted is triggered:
override fun nfcCompleted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
fragmentManager.replaceFragment(NfcResultFragment(), false)
}
Error Management¶
Critical Errors: Errors that prevent the continuation of the NFC process are communicated through nfcFailed:
override fun nfcFailed(nfcFailureCode: NFCFailureCode, additionalMessage: String?) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $nfcFailureCode: $additionalMessage")
}
Temporary Errors: Temporary errors, such as contactless issues, are communicated through the onNfcError callback within the fragment derived from EnNFCBaseFragment:
override fun onNfcError(error: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} : $error")
if (isAdded) setCurrentIndicator(-1, R.string.empty)
}
Adding Integration Data¶
val integrationModel = IntegrationModel(
idRegistration = IDRegistration(),
addressRegistration = AddressRegistration(),
data = ""
)
enQualifyNFC.addIntegration(integrationModel)
override fun integrationAddCompleted() {
Log.i(TAG, "integrationAddCompleted")
}
override fun integrationAddFailed(errorMessage: String) {
Log.i(TAG, "integrationAddFailed: $errorMessage")
}
Closing Session and Clearing¶
// Close the session
enQualifyNFC.closeSession(isFinished = true)
override fun sessionCloseCompleted() {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
override fun sessionCloseFailed(errorMessage: String) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $errorMessage")
}
// Clear the SDK
enQualifyNFC.clear()
ℹ️ After the session is closed,
initialize()must be called again to reuse the SDK.
Retrieving Session Information¶
EnNFCBaseFragment¶
EnNFCBaseFragment is the base class from which the custom fragment that performs the NFC reading process will be derived. It contains the necessary functions to manage the reading stages, update progress indicators, and handle error messages.
Callback Functions¶
| Function | Description |
|---|---|
onNfcReadStart() | Called when NFC reading starts |
onNfcTagDetected() | Triggered when the NFC tag is read |
onNfcRead(data: Any?) | Triggered when NFC data is read |
onNfcReadFinish() | Triggered when NFC reading is completed |
onFirsLevelCompleted() | Called when the 1st phase of the reading process is completed |
onSecondLevelCompleted() | Called when the 2nd phase of the reading process is completed |
onThirdLevelCompleted() | Called when the 3rd phase of the reading process is completed |
onFourthLevelCompleted() | Called when the 4th phase of the reading process is completed |
onNfcError(error: String) | Called when a temporary error occurs during reading |
Error Codes¶
NFCFailureCode¶
enum class NFCFailureCode(errorMessage: String) {
NFCReadFailed(""),
NFCStoreFailed(""),
NFCTimeout("Nfc timeout"),
NFCDisable("NFC is disabled on the device."),
PermissionDenied("NFC permission denied"),
MissingNFCKey("Invalid or Null MRZ keys."),
BacFailed("BAC failed"),
EfComNotRead("EF_COM Access File not read"),
SodNotRead("Failed to retrieve EF.SOD file. Possible chip or file corruption"),
SodHashError("Failed to extract hash values from EF.SOD. Missing or unsupported algorithm."),
UnsupportedAlgorithm("Failed to extract hash values from EF.SOD. Missing or unsupported algorithm."),
Dg1NotRead("DG1 File not read"),
Dg11NotRead("DG11 File not read"),
Dg2NotRead("DG2 File not read"),
NotSupport("Device does not support NFC"),
Failed(""),
ChipAuthenticationFailed("Chip Authentication failed"),
ActiveAuthenticationFailed("Active Authentication failed"),
Dg14FileEmpty("DG14 File is empty"),
SodFileEmpty("SOD File is empty"),
StoredHashError(""),
CertificateValidationFailed("Certificate Validation failed"),
FidNotRead(""),
Dg14NotRead(""),
CertificateValidation(""),
HashMatchError("")
}
Proguard Rules¶
To ensure the NFC SDK works correctly in release packages, add the following rules to the proguard-rules.pro file:
-keep class net.sf.scuba.smartcards.** { *; }
-keep class org.bouncycastle.** { *; }
-dontwarn javax.naming.**
-dontwarn org.bouncycastle.cert.dane.fetcher.JndiDANEFetcherFactory
-dontwarn com.enqualify.plus.ocr.OCRCallbacks$DefaultImpls
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
Accessing the Result Data¶
After the NFC reading process is completed, the read data can be accessed through the CustomerChip object:
For the explanation of the CustomerChip model and all its fields, see the Core Module — CustomerChip page.
Callback Reference¶
Token / Session / Settings¶
| Callback | Description |
|---|---|
tokenCreateCompleted(isNewCreatedToken: Boolean) | Triggered when the token is created |
tokenCreateFailed(errorMessage: String) | Token creation error |
sessionAddFailed(errorMessage: String) | Session creation error |
settingsGetFailed(errorMessage: String) | Triggered when settings cannot be retrieved |
sessionCloseCompleted(status: CallSessionTypeStatus) | Triggered when the session is closed |
sessionCloseFailed(errorMessage: String) | Session closing error |
integrationAddCompleted() | Integration data successfully added |
integrationAddFailed(errorMessage: String) | Integration data could not be added |
initializeFailed(failureCode: FailureCode, additionalMessage: String) | Core initialization error |
NFC¶
| Callback | Description |
|---|---|
initializeCompleted(module: EnModules) | Initialization completed, NFC is ready |
nfcSaveStarted() | Data transmission to the back office has begun |
nfcCompleted() | Data successfully sent to the back office |
nfcFailed(nfcFailureCode, additionalMessage) | NFC operation error (critical) |