Skip to content

Android - NFC Module

The NFC module is used to read the chip data on identity cards or passports. In order for the reading process to be successful, certain information obtained from the identity document must be provided in advance.

⚠️ Prerequisite: Maven access must be established and the Core Module must be added to the project before using the NFC Module. The NFC Module does not work alone without the Core Module being added.


Required Data

To successfully read the chip data, the following information must be provided:

Data Format Example
Serial Number Alphanumeric A11A11111
ID Validity Date YYMMDD 280621
Date of Birth YYMMDD 950621

The NFC reading process will fail without providing this information.


Adding to the Project

1. Add to libs.versions.toml file:

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 file:

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

3. Gradle Sync

Synchronize Gradle files by clicking on the "Sync Now" option.


Implementation

1. Override onNewIntent Function

The onNewIntent method should be overridden in the Activity where SDK functions are used to manage intents coming to the NFC process. This is a system callback coming from AppCompatActivity, not a callback from EnQualify:

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 full-screen covering FrameLayout 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 for managing NFC processes and must work with an Activity.

Adding to the class:

Text Only
class EnQualifyPlusActivity : AppCompatActivity(), NFCCallbacks

Overriding the callbacks:

Text Only
class EnQualifyPlusActivity : AppCompatActivity(), NFCCallbacks {

    override fun initializeCompleted(modules: EnModules) {
        Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
        when (modules) {
            EnModules.NFC -> {
                // NFC processes 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 performing the NFC reading process should be derived from the EnNFCBaseFragment class. This class provides 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 processes, EnQualifyNFC.initialize() must be called. If any other process is attempted without 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 The runtime context of the application
sessionModel SessionModel Session information to be used during the NFC process
baseModel BaseModel Model carrying basic data during the verification process
fragmentManager FragmentManager Manages the NFC fragments
containerID Int The ID of the container where the NFC fragment will be added

Automatic Flow After Initialization

After the initialize() call, the SDK automatically executes the Token → Session → Settings steps in order. In case of errors, the respective 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 processes can be started.


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 the NFC Reading Process

Data from the identity document is required for the NFC reading process. This data is typically 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 inherited 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 process is to be performed before OCR, the data must be manually entered in the formats above.


Reading Flow

  1. When Reading is Completed

When the NFC reading is completed, the SDK begins sending the read data to the backoffice automatically, and nfcSaveStarted is triggered:

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

When the data is successfully sent to the backoffice, 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 via nfcFailed:

Text Only
override fun nfcFailed(nfcFailureCode: NFCFailureCode, additionalMessage: String?) {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $nfcFailureCode: $additionalMessage")
}

Momentary Errors: Temporary errors such as contactless issues are communicated via the onNfcError callback inside 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 closing the session, initialize() must be called again to use the SDK again.


Retrieving Session Information

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

EnNFCBaseFragment

EnNFCBaseFragment is the base class from which the special fragment performing the NFC reading process will be derived. It includes the necessary functions to manage reading stages, update progress indicators, and handle error messages.

Callback Functions

Function Description
onNfcReadStart() Called when NFC reading starts
onNfcTagDetected() Triggered when an NFC tag is read
onNfcRead(data: Any?) Triggered when NFC data is read
onNfcReadFinish() Triggered when NFC reading is completed
onFirsLevelCompleted() Called when the first stage of the reading process is completed
onSecondLevelCompleted() Called when the second stage of the reading process is completed
onThirdLevelCompleted() Called when the third stage of the reading process is completed
onFourthLevelCompleted() Called when the fourth stage 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 Result Data

After the NFC reading process is completed, the read data can be accessed through the CustomerChip object:

Text Only
CustomerChip.getInstance()

Refer to the Core Module — CustomerChip page for documentation on the CustomerChip model and all its fields.


Callback Reference

Token / Session / Settings

Callback Description
tokenCreateCompleted(isNewCreatedToken: Boolean) Triggered when a token is created
tokenCreateFailed(errorMessage: String) Token creation error
sessionAddFailed(errorMessage: String) Session creation error
settingsGetFailed(errorMessage: String) Triggered when settings cannot be obtained
sessionCloseCompleted(status: CallSessionTypeStatus) Triggered when the session is closed
sessionCloseFailed(errorMessage: String) Session closing error
integrationAddCompleted() Integration data successfully added
integrationAddFailed(errorMessage: String) Unable to add integration data
initializeFailed(failureCode: FailureCode, additionalMessage: String) Core initialize error

NFC

Callback Description
initializeCompleted(module: EnModules) Initialization completed, NFC ready
nfcSaveStarted() Data has begun sending to the backoffice
nfcCompleted() Data successfully transmitted to the backoffice
nfcFailed(nfcFailureCode, additionalMessage) NFC process error (critical)