Skip to content

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:

Text Only
[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:

Text Only
implementation(libs.enqualify.plus.nfc)

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:

Text Only
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:

Text Only
<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:

Text Only
class EnQualifyPlusActivity : AppCompatActivity(), NFCCallbacks

Overriding Callbacks:

Text Only
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.

Text Only
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.

Text Only
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:

Text Only
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:

Text Only
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

  1. 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:

Text Only
override fun nfcSaveStarted() {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
  1. When Data Transmission is Completed

When the data is successfully sent to the back office, nfcCompleted is triggered:

Text Only
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:

Text Only
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:

Text Only
override fun onNfcError(error: String) {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} : $error")
    if (isAdded) setCurrentIndicator(-1, R.string.empty)
}

Adding Integration Data

Text Only
val integrationModel = IntegrationModel(
    idRegistration = IDRegistration(),
    addressRegistration = AddressRegistration(),
    data = ""
)
enQualifyNFC.addIntegration(integrationModel)
Text Only
override fun integrationAddCompleted() {
    Log.i(TAG, "integrationAddCompleted")
}

override fun integrationAddFailed(errorMessage: String) {
    Log.i(TAG, "integrationAddFailed: $errorMessage")
}

Closing Session and Clearing

Text Only
// 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

Text Only
// Function returning SessionUId
enQualifyNFC.getSessionUId()

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

Text Only
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:

Text Only
-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:

Text Only
CustomerChip.getInstance()

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)