Card91 NSDL AADHAAR KYC Android Library Integration with Flutter Android Apps
Description
Purpose of this document is to illustrate the method to add Card91 ‘Card91AadhaarKYC’ android library to any flutter android application written in java and dart.
Card91 Card91AadhaarKYC library allows developers to launch the Card91KYCActivity activity from their application to process and complete the aadhaar XML full KYC for NSDL cards.
Developers need to launch Card91KYCActivity from their android native class on the flutter application with some required parameters mentioned below for completing the aadhaar KYC for the NSDL cards.
Installation (Gradle and Manifest Configuration) Android
Android Minimum Requirements :
Gradle Version - 7.3.3
minSdkVersion - 26
compileSdkVersion - 33
targetSdkVersion - 33
Android Permission on Manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
Reference gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
Gradle dependencies
Add the provided "aadhaarsdk2-1.0.7.aar","nsdlkyc_card91.aar" aar (AAR files will be provide by the Card91 team) file SDK on the given location of the react application (android folder).
Path: src/main/libs (This path must be same as that defined in the project)
dependencies {
//CARD SDK
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
/*SDK DEPENDENCY*/
//HTTP CALL
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.3'
//JSON CONVERTOR
implementation('com.squareup.moshi:moshi-kotlin:1.12.0')
//kotlin reflect
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.6.10'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation platform('com.google.firebase:firebase-bom:29.3.1')
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
implementation "com.google.firebase:firebase-auth:17.0.0"
implementation 'com.google.firebase:firebase-iid'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.4.1'
implementation(name: 'aadhaarsdk2-1.0.7', ext: 'aar' ) /// NSDL SDK
implementation(name: 'nsdlkyc_card91', ext: 'aar' ) // Card91 NSDL Aadhaar KYC SDK
implementation 'com.scottyab:rootbeer-lib:0.1.0'
implementation "androidx.room:room-runtime:2.4.3"
annotationProcessor "androidx.room:room-compiler:2.4.3"
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
/// required for SDK
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.github.bastiaanjansen:jwt-java:1.2.0'
implementation 'com.auth0:java-jwt:4.0.0'
implementation 'com.auth0.android:jwtdecode:2.0.1'
implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'
}
Note
Clean and Build the android app to get the dependencies added on to the application
And minifyEnable is true on you app build.gradle and add below line on your ProGuard rules file-keep class com.card91.nsdlkyc.model.** { *; }
USAGE
Launch Card91KYCActivity (Aadhaar SDK) from flutter application android native module class with the help of MethodChannel (https://api.flutter.dev/flutter/services/MethodChannel-class.html)
MethodChannel is used to communicate between Dart widget and Android Native Activity Class.
First create a stateful widget on flutter application from where you want to invoke the SDK (aar class).
Declare the MethodChannel on the widget.
static const platform = MethodChannel('flutter.native/intent');
Then invoke the function of the native android activity which has been exposed with a matching key.
var dataRespForGetToken = await platform.invokeMethod('invoke_card91SDK', {
'MOBILE_NUMBER': '91805**\*\***', //Should start with 91 (12 digit)
'AUTH_TOKEN': "C91CHVdNSU1jWVQWxIoXWoDZgWcBAp7U5cfrE**\***",// Card holder auth token
'ENV': 'PROD_SANDBOX', // "PROD_SANDBOX" for production sandbox and "PROD" for PRODUCTION environment
'PAN': 'AJHPL**\*\***' /// User real PAN needs to be passed here
'ORG_ID':'230901080404946ID1OID909***' ///Card org id
});
Dart widget example from where the native android activity is called .
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InvokeAadhaarSDK extends StatefulWidget {
const InvokeAadhaarSDK({Key? key}) : super(key: key);
@override
State<InvokeAadhaarSDK> createState() => _InvokeAadhaarSDKState();
}
class _InvokeAadhaarSDKState extends State<InvokeAadhaarSDK> {
static const platform = MethodChannel('flutter.native/intent');
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: ElevatedButton(
onPressed: () async {
var dataRespForGetToken = await platform.invokeMethod('invoke_card91SDK', {
'MOBILE_NUMBER': '91805******', //Should start with 91 (12 digit)
'AUTH_TOKEN': "C91CHVdNSU1jWVQWxIoXWoDZgWcBAp7U5cfrE*****",// Card holder auth token
'ENV': 'PROD_SANDBOX', // "PROD_SANDBOX" for production sandbox and "PROD" for PRODUCTION environment
'PAN': 'AJHPL******' /// User real PAN needs to be passed here
'ORG_ID':'230901080404946ID1OID909***' ///Card org id
});
print(
"dataRespForGetToken--->${dataRespForGetToken.toString()}");
},
child: Text("Invoke Aadhaar SDK")),
),
),
);
}
}
After setting up the dart file with MethodChannel while calling invokeMethod with key "invoke_card91SDK", now we have to setup the listener on the MainActivity.
Then invoke the function of the native android activity MainActivity which extends FlutterFragmentActivity and has been exposed with a matching key.
package com.card91.mobile.customerapp.new_flow_cust_app
import android.app.Activity
import android.content.Intent
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import org.json.JSONObject
import java.util.*
import android.widget.Toast
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import com.card91.nsdlkyc.activities.Card91KYCActivity
import java.io.UnsupportedEncodingException
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.*
import java.util.Base64.Encoder
import android.os.Build
import androidx.annotation.RequiresApi
class MainActivity: FlutterFragmentActivity() {
companion object {
private const val CHANNEL = "flutter.native/intent"
}
var resp: MethodChannel.Result? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler
{ call: MethodCall, result: MethodChannel.Result? ->
///Card from Dart file will be listened here and based on the invoke key "invoke_card91SDK"
//consition is put to launch the SDK
if (call.method == "invoke_card91SDK") {
val intent = Intent(this@MainActivity, Card91KYCActivity::class.java)
intent.putExtra("MOBILE", Objects.requireNonNull(call.argument<Any>("MOBILE_NUMBER")).toString()) // User registered mobile number
intent.putExtra("AUTH_TOKEN", Objects.requireNonNull(call.argument<Any>("AUTH_TOKEN")).toString()) // Auth token of card holder
intent.putExtra("ENV", Objects.requireNonNull(call.argument<Any>("ENV")).toString()) // "PROD_SANDBOX" for sandbox and "PROD" production environment
intent.putExtra("PAN", Objects.requireNonNull(call.argument<Any>("PAN")).toString())// card holder PAN
intent.putExtra("ORG_ID",Objects.requireNonNull(call.argument<Any>("ORG_ID")).toString())// card orgnization ID
//start intent
aadhaarLauncher.launch(intent) // registerForActivityResult is added on the same
//activity to listen the callbacks evens
resp = result // This will send the result back to dart file.
}
}
}
private val aadhaarLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { res ->
if (res.resultCode == Activity.RESULT_OK) {
val data: Intent? = res.data
print("res.data"+res.data);
Log.e("res1", "error_msg---${data?.getStringExtra("COMPLETE_KYC_SUCCESS")}")
resp?.success( data?.getStringExtra("COMPLETE_KYC_SUCCESS")) // SUCCESS on success result and "FAILED" when API failed
Log.e("res1", "KYC_RESPONSE_DATA---${data?.getStringExtra("KYC_RESPONSE_DATA")}") // Response data from the SDK
Log.e("res1", "KYC_RESPONSE_CODE---${data?.getStringExtra("KYC_RESPONSE_CODE")}") // Response code
}
if(res.resultCode === Activity.RESULT_CANCELED) {
/// When operation is cancelled from the SDK
Toast.makeText(this.applicationContext,
"Operation Cancelled", Toast.LENGTH_SHORT).show()
}
}
}
In case there is following runtime exception:
java.lang.RuntimeException: Unable to start activity ComponentInfo{...../com.karza.aadhaarsdk.AadharActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Add a following line in AndroidManifest.xml under the application tag:
android:theme=“@style/Theme.AppCompat.Light”
INPUT PARAMETER DETAILS WHICH LAUNCHING THE SDK ACTIVITY
Name | Type | Required | Description |
---|---|---|---|
MOBILE | string | true | Registered card holder mobile number (12 digit) with 91 |
AUTH_TOKEN | string | true | Card Holder auth token (Card91 API) |
ENV | string | true | "PROD_SANDBOX" for sandbox and "PROD" production environment |
PAN | string | false | Registered card holder valid pan number |
ORG_ID | string | true | Card Organisation ID |
DIFFERENT CALLBACK EVENTS
The events are as follows:
Event Name | Description |
---|---|
SUCCESS | On successful aadhaar KYC completion, developer needs to parse "bundle.getString("COMPLETE_KYC_SUCCESS")" for the same. In this scenario "KYC_RESPONSE_CODE" will comes as 00 and "COMPLETE_KYC_SUCCESS" key value will be "SUCCESS" |
FAILED | On un successful aadhaar KYC completion, developer needs to parse the key "COMPLETE_KYC_SUCCESS" from the bundle, value be "FAILED". In this scenario "KYC_RESPONSE_CODE" will not comes as 00 and detail reason will on the key "KYC_RESPONSE_DATA" |
KYC_RESPONSE_DATA | API service response body will be send back on this param |
KYC_RESPONSE_CODE | API service response code will be send back on this param |