NAV Navbar
cURL JSON CodeSnippet

1. Introduction

Veri5Digital VideoIdKyc Solution offers Android SDK that can be seamlessly integrated with clients business application to do the KYC processing of the user.

This document explains technical aspects and steps to integrate Veri5Digital VideoIdKyc Android SDK with clients application.

Note:

Please refer attached Veri5Digital - VideoIdKyc Android SDK Technical Specification Companion Guide for sample code snippets.

2. Steps To Integrate VideoIdKyc Solution

Clients should follow below steps for integrating Veri5Digital VideoIdKyc Android SDK with their native android application.

  1. Client should get on boarded or registered with Veri5Digital Platform and get client credentials and other parameters required for integration.

  2. Clients Android Application should integrate VideoIdKyc Android SDK.

  3. Integrate fetchKYCInfo API to retrieve the KYC Info processed by VideoIdKyc Android SDK.

Veri5Digital Platform provides Sandbox Environment for Integration and Production Environment for actual production purpose.

We recommend clients to first integrate their application with Veri5Digital VideoIdKyc Android SDK in Sandbox environment and then move to production post testing and certification in Sandbox Environment.

Below sections explain these steps in detail.

2.1 Client Registration

For a client to get access to SDK and do integration, client registration is mandatory.

As part of registration , Client has to obtain values for following params for both Sandbox and Production Environments.

Note :

2.2 How to integrate Android SDK in Client’s Application

Client should have received the credential information mentioned in Section 2.1 before starting integration.

2.2.1 Step 1 : Retrieve VideoIdKyc SDK from maven repository

Client applications build system should pull VideoIdKyc SDK from Veri5Digital Platforms hosted repository.Please refer below for details.

1. Add​ ​the​ ​following​ ​dependency​ ​in​ client application level gradle file located at  project/build.gradle . You should replace username and password with the maven credentials you have received during onboarding.


    allprojects { 
           ...
           repositories {
                   ...
                 maven { url "https://jitpack.io" }
                 maven {
                 url "https://repo.aadhaarbridge.com/repository/android-sdk/"
                 credentials {
                              username '<<your username>>’
                              password '<<your password>>'
                            }
                      }
                  ...
                 }
               ...
              }

Sample Code Snippet [ Project Level build.gradle ]:
-------------------------------------------------

private void handleFetchKycInfoRes(FetchKycInfo.Res apiResponse)
            throws Exception {

        String hash = calculateResponseHash(
                getIntent().getStringExtra("client_code"),
                getIntent().getStringExtra("user_id"),
                apiResponse.getKycInfo(),
                getIntent().getStringExtra("api_key"),
                getIntent().getStringExtra("salt")
        );

        Log.i(
                TAG,
                "handleFetchKycInfoRes: Api hash = "
                        + apiResponse.getHash() + ", Calculated hash = " + hash + "."
        );

        if (hash != null && !hash.equalsIgnoreCase(apiResponse.getHash())) {
            throw new IllegalArgumentException("Invalid hash!");
        }

        byte[] decodedResponse = Base64.decode(apiResponse.getKycInfo(), Base64.DEFAULT);

        if (apiResponse.getEncrypted().equalsIgnoreCase("yes")) {
            showPasswordDialog(decodedResponse);
        } else {
            refreshUi(decodedResponse);
        }
}

2. Add the following dependency in client application’s module level gradle file app/build.gradle

android{
        ...
packagingOptions {
        pickFirst 'lib/x86_64/libopencv_java3.so'
        pickFirst 'lib/x86/libopencv_java3.so'
        pickFirst 'lib/armeabi-v7a/libopencv_java3.so'
        pickFirst 'lib/arm64-v8a/libopencv_java3.so'
    }
defaultConfig{
ndk{
abiFilters “arm64-v8a”, “armeabi-v7a”, “x86”,
vectorDrawables.useSupportLibrary true
    renderscriptTargetApi 21
    renderscriptSupportModeEnabled true
    }
 ...
 }
 ...
}

Sample Code Snippet [ App Level build.gradle ]:
----------------------------------------------

apply plugin: 'com.android.application'

android {
    packagingOptions {
        pickFirst 'lib/x86_64/libopencv_java3.so'
        pickFirst 'lib/x86/libopencv_java3.so'
        pickFirst 'lib/armeabi-v7a/libopencv_java3.so'
        pickFirst 'lib/arm64-v8a/libopencv_java3.so'
    }
    compileSdkVersion 28
    //buildToolsVersion "29.0.1"
    defaultConfig {
        applicationId "com.example.vikycsampleapp"
        minSdkVersion 21
        targetSdkVersion 28

        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            abiFilters "arm64-v8a", "armeabi-v7a", "x86", "x86_64"
        }

        vectorDrawables.useSupportLibrary true
        renderscriptTargetApi 21
        renderscriptSupportModeEnabled true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation 'com.jakewharton:butterknife:10.0.0'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'

    implementation "org.bouncycastle:bcprov-jdk16:$bouncyCastleVersion"

    implementation 'com.khoslalabs.sdk:videoidkyc:4.2.0-SB'
    implementation 'com.khoslalabs.sdk:base:4.2.0-SB'
    implementation 'com.khoslalabs.sdk:ocrsdk:4.2.0-SB'
    implementation 'com.khoslalabs.sdk:facesdk:4.2.0-SB'
}

Note :
- If client is using other build systems like maven, they should incorporate these dependencies accordingly.

2.2.2 Step 2 : Configure VideoIdKyc Module Dependencies

In this step you should add different sdk modules into your module level gradle file depending on the functionalities like Document Scan , Liveness Check etc that you have chosen.

Add following dependencies in app/build.gradle :


dependencies {
    ...
    implementation 'com.khoslalabs.sdk:videoidkyc:<<latest version>>'
    implementation 'com.khoslalabs.sdk:base:<<latest version>>'
    // <<1>> Add if Document Scan  functionality is required
    implementation 'com.khoslalabs.sdk:ocrsdk:<<latest version>>'
    // <<2>> Add if Livenes functionality is required
    implementation 'com.khoslalabs.sdk:facesdk:<<latest version>>'
    ...
}

Note:
You should replace latest version with actual version received during onboarding. Please refer above code snippet for reference, where version is <<4.2.0-SB>>


2.2.3 Step 3 : Initiation of VideoIdKyc SDK from Clients Application

Inside client application’s calling activity include below code to initiate VideoIdKyc SDK. Refer the below code snippet.

- VideoIdKycInitRequest videoIdKycInitRequest =new VideoIdKycInitRequest.Builder(
                    <<your client code>>,
                    <<your api key>>,
                    <<purpose>>,
                    <<your request id>>,
                    <<calculated hash>>
                    )
        // if watermarking of selfie and document is required, pass plmareq = "YES" else "NO"    
        .plmaRequired(plmaReq)              (mandatory)

        //pass CAF No, agent name and agent id if watermarking is required  as accordingly-      (optional) 
        .cafNumber(cafNo)
        .agentName(agentName)
        .agentId(agentId)

        // pass customer_id if required-            (optional)  
        .customerId(customerId)

        .moduleFactory(OcrSdkModuleFactory.newInstance())
        .moduleFactory(FaceSdkModuleFactory.newInstance())
        .build();

Table : VideoIdKycInitRequest Parameters

Name Value Comments Example
client_code String, Mandatory Your client code received during onboarding a1b2c3
request_id String, Mandatory Unique transaction identifier Client app has to generate the same.For each request it should be unique.This is used for uniquely identifying the transaction. 12345678910111
api_key String, Mandatory Pre-shared api key controls the access to various Api’s. Api Key will be shared during onboarding.There will be separate api_key for SandBox and Production Environment. 1A3b5c7D910111
hash String, Mandatory This should be a SHA-256 value of Hash Sequence defined for _init request. Purpose of this is to ensure the integrity of the request. Refer Appendix B for hash generation logic. 9780cd0d2ce77eef8f64942f54e0281a0e220ff6bbcce0a03df27a2b15575f58
purpose String, Mandatory String that specifies the actual intention of SDK invocation. Eg : “For customer Onboarding”. You can pass “” ( Empty String ) in case you do not need to pass any specific value..

// your_init_request_code refers to the unique code that calling application is passing while invoking SDK, and this will be later used for retrieving the results from SDK using onActivityResult Method.

Refer Section 4.1 in Companion Guide for supported customization capabilities and details.

Refer Section 5 in Companion Guide for sample Android Code Snippet.

2.2.4 Step 4 : SDK Response Processing

Response from SDK will be received via onActivityResult method of the activity that invoked VideoIdKyc SDK.

Client Application will receive following three parameters via onActivityResult method :

If SDK processing results in a successful transaction, client will get a parameter user_id, that client app can later pass in the fetchKYC API to retrieve the KYC data and results.

If SDK processing results in an unsuccessful transaction,client will get appropriate error_code and error_message.

Below snippet of code demonstrates the same :

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (requestCode == `<your request code>`) {
        if (resultCode == ViKycResults.RESULT_OK || resultCode == ViKycResults.RESULT_DOC_COMPLETE) {
                      if (data != null) {
                      // use the user id
                      String userId = data.getStringExtra("user_id");
      }
} else {
    if (data != null) {
      // use the error code and error message
      int code = data.getIntExtra(“error_code”, 333);
      String msg = data.getStringExtra(“error_message”);
      }
    }
  }
}

Refer Section 6 in Companion Guide for sample Android Code Snippet.

2.3 Fetch KYC Request

On successful response from SDK is received, client application should invoke fetchKYCInfo API to retrieve the processed KYC info.

API Name : fetchKYCInfo

Method : POST

Sandbox URL : https://sandbox.veri5digital.com/video-id-kyc/api/1.0/fetchKYCInfo

Production URL : https://prod.veri5digital.com/video-id-kyc/api/1.0/fetchKYCInfo

2.3.1 fetchKYCInfo API Request Details

Calling application should form the Request Payload in the following Json format.

Request Header
        Content-Type: application/json

Request Body
Please note “headers” tag below is part of Https Request Body.
{
    "headers":{  
        "client_code":"<your client_code>",
        "sub_client_code":"<should be same as client_code>",
        "actor_type":"NA",
        "channel_code":"ANDROID_SDK",
        "stan":"<System Trace Number>",
        "user_handle_type":"<>",
        "user_handle_value":"<>",
        "location":"",
        "transmission_datetime":"<<System Time In Milli Seconds", 
        "run_mode":"REAL",
        "client_ip":"<<Your IP Address>>",
        "operation_mode":"SELF",
        "channel_version":"",
        "function_code":"REVISED",
        "function_sub_code":"DEFAULT"
    },
    "request":{
        "api_key":"<api-key>",
        "user_id":"<received in response from android-sdk>",
        “hash” : “###########”
    }
}
Field Name Type Comments Example
client_code String, Mandatory Unique code assigned to a client.This will be assigned to the client during onboarding. client123
sub_client_code String, Not Mandatory Its useful in case calling client needs any customization w.r.to different departments. subclient123
actor_type String, Not Mandatory Type of user who is making the request from the application. CUSTOMER or AGENT
channel_code String, Not Mandatory Channel code used by the client to initiate request. ANDROID_SDK
stan String, Mandatory A unique number generated per request by the client. It is used to uniquely identify a request from a client and can be used for troubleshooting.Max length can be 64 characters long. 98321892319
user_handle_type String, Not Mandatory Indicates the type of the user identifier, who has initiated the request. Eg : UUID, MSISDN, EMAIL. EMAIL
user_handle_value String, Not Mandatory Value of user_handle_type. If user_handle_type is EMAIL, then user_handle_value is actual email. a@b.com
location String, Not Mandatory Location of the device that initiated the request. Can be empty.
transmission_datetime String, Not Mandatory Time in System milli seconds at which request was initiated from client. 1553255080298
run_mode String, Not Mandatory Execution Mode in which transaction is being done.Possible values are REAL or TRIAL. REAL
client_ip String, Not Mandatory IP Address of the client device.
operation_mode String, Not Mandatory The way in which the transaction is executed.Example values can be “SELF”,”ASSISTED”,”SYSTEM” etc. SELF
channel_version String, Not Mandatory It can be Android SDK Version 3.1.7
function_code String, Mandatory Defines the behaviour of this api.Value should be ‘REVISED’ REVISED
function_sub_code String, Mandatory Indicates default behaviour for this api. If no customized behaviour requirements, then value will be “DEFAULT” DEFAULT
user_id String, Mandatory This is same as user_id passed on to client application as part of SDK response. userid123
hash String, Mandatory Hash to be calculated by client to verify integrity of request. Please refer APPENDIX C to see how to generate the hash.
api_key String, Mandatory api_key shared with client during onboarding. samplekey123

Refer Section 7 in Companion Guide for details and sample Code Snippet.

Points to Note :

2.3.2 fetchKYCInfo API Response Details

fetchKYCInfo API Response Payload structure is given below :

Response Payload

Response Payload holds the following :
{
    “response_status”:{
    “status”    : ”<<status of API Execution>>”,
    “code”      : ”<<status code of API Execution>>”,   
    “message”   :”<<message based on the status and code>>”
},
“response_data”:{
    “is_encrypted”      :”<<YES|NO>>”,
    “kyc_info”          :”<<base64 encoded KYC info>>”,
    “hash”              : <<sha256 of hash sequence defined for fetch response>>,
    "Location"          : <<latitude and longitude location of place where transaction is done">> // if location permission is granted.
 }
}
Response Payload Example :

Below given is an example for a successful response.

{
    "response_status": {
    "status": "SUCCESS",
    "code": "000",
    "message": "Kyc data fetched successfully"
    },
    "response_data": {
        "is_encrypted": "NO",
        "kyc_info": "Base64 Encoded kyc_info block",
        "hash": "5eea1888a2920b6fddf25f6c12ec2aba5e7ce2bff6d9391c18fd1b2749a4ba47"
    }
}

2.3.2.1 Detailed Structure of kyc_info block

Details of kyc_info block of response payload is given below :

Case 1 : is_encrypted flag is “NO”

If encrypted is NO then kyc_info block is just base64 encoded. Client application should just decode the Base64 encoded kyc_info block to get the kyc data.By default value of is_encrypted flag will be ‘NO’.

Case 2 : is_encrypted flag is “YES”

This means client has chosen the option to have the kyc_info data to be encrypted.If encrypted is YES then base64 encoded kyc_info block is encrypted.

In this case client application should:

Please refer to Section 3 in Companion Guide to understand the requirements for decryption in case data encryption is chosen, ie if “is_encrypted” is “YES”.

kyc_info Data Structure

Structure of kyc_info block post decoding of Base64 encoded data is given below. Please refer the Json and table given below for more field level details.

There are mainly following blocks :

Points to Note :

{  
    // original_kyc_info tag encapsulates info  extracted from the Original ID  Document through OCR
    "original_kyc_info":{  
    "address":"<address>",
    "gender":"M",
    "dob":"17/12/1993",
    "name":"John Doe",
    “father_name”:””,
    "mother_name":"",
    "expiry_date":"",
    "email":"",
    // Value will be Input Mobile#, in case Mobile Validation flow is chosen and hash of input mobile# matches with hash of mobile present in UIDAI Aadhaar XML.
    "mobile":"", 
    "doc_type":"AADHAAR",
    "document_id":"XXXXXXXX7059",
    "dist": ""
    "house":"",
    "loc":"" ,
    "pc":"" ,
    "po":"" ,
    "state":"" ,
    "street":"" ,
    "subdist":"" ,
    "vtc":"" ,
    "country":"",
    “verified_by”:”UIDAI”,
    “verified_using”:”DIGITAL_SIGNATURE”,
    “document_verification_status”:”SUCCESS|FAIL”
    “document_verification_code”:””
    “document_verification_message”:””
 },
    // This tag encapsulates info that is declared or edited by the user after reviewing data extracted from the Original ID Document

    "declared_kyc_info":{  
       "address":"NULL",
       "gender":"M",
       "dob":"17/12/1993",
       "name":"John Doe",
       “father_name”:””,
       "mother_name":"",
       "expiry_date":"",
       "Email":"",
    //  Value will be Input Mobile#, in case Mobile Validation flow is chosen.
       "mobile":"",
       "doc_type":"AADHAAR",
       "document_id":"XXXXXXXX7059",
       “Verified_by”:””, 
       “verified_using”:””,
       “document_verification_status”:”SUCCESS|FAIL”
       “document_verification_code”:””
       “document_verification_message”:””
   },
   "photo":{  
       "match_rate":””,
       "match_status":"SUCCESS|FAIL|UNCERTAIN", 
       "document_image":"<Base64 encoded image>",
       "live_image":"<base64 encoded image>"
   },
   "doc_image":{  
      "pan":"<Base64 encoded image>",
      "aadhaar_front":"<Base64 encoded image>",
      "aadhaar_back":"<Base64 encoded image>",
      "passport_front":"<Base64 encoded image>",
      "passport_back":"<Base64 encoded image>"
   }
}
Field Name Block name Comments
address original_kyc_info Address info extracted from document as a result of OCR. Address info consists of all address fields as a single string.
gender original_kyc_info Gender Information extracted from document if available in document.
dob original_kyc_info Date Of Birth information extracted from document if available in document.
name original_kyc_info Name extracted from document if available in document.
father_name original_kyc_info Father Name extracted from document if available in document.
mother_name original_kyc_info Mother’s Name extracted from document if available in document.
expiry_date original_kyc_info Expiry Date extracted from document if available in document.
email original_kyc_info Email extracted from document if available in document.
mobile original_kyc_info Mobile number user input in client application. It’s an optional field. If no mobile number is given as input, value will be an empty string. Its present in original_kyc_block means hash of input Mobile# matches with mobile hash present in UIDAI Aadhaar XML in case document type is ‘AADHAAR’
doc_type original_kyc_info Specifies the type of the document.
document_id original_kyc_info Is the unique Document Identifier extracted from the document. For example, if doc_type is PAN then it will be PAN#. If doc_type is Aadhaar, it will be Aadhaar Number with first 8 digits masked.
dist original_kyc_info District info extracted from Address in case of Aadhaar XML.
house original_kyc_info House Name field extracted from Address in Aadhaar XML.
loc original_kyc_info Locality field extracted from Aadhaar XML.
pc original_kyc_info Pincode field extracted from Address in Aadhaar XML.
po original_kyc_info Post Office field extracted from Address in Aadhaar XML.
state original_kyc_info State field extracted from Address in Aadhaar XML.
street original_kyc_info Street field extracted from Address in Aadhaar XML.
subdist original_kyc_info Sub District field extracted from Address in Aadhaar XML.
vtc original_kyc_info Village/Town/City field extracted from Address in Aadhaar XML.
country original_kyc_info Street field extracted from Address in Aadhaar XML.
verified_by original_kyc_info This is applicable in case of Aadhaar XML. Value will be “UIDAI” in this case.
verified_using original_kyc_info This is applicable in case of Aadhaar XML. Value will be “DIGITAL_SIGNATURE” in this case.
document_verification_status original_kyc_info In case the client has opted for verification of the Id doc against government database, this indicates the document verification status. Possible values are SUCCESS
document_verification_code original_kyc_info In case the client has opted for verification of the Id doc (Verification API) against government database, this indicates the document verification status based on declared info. Please refer to Section 4.4 in Companion Guide for more details. If this service is not opted value will be an Empty String.
document_verification_message original_kyc_info In case the client has opted for verification of the Id doc (Verification API) against government database, this indicates the document verification status based on declared info. Please refer to Section 4.4 in Companion Guide for more details. If this service is not opted value will be an Empty String.
address declared_kyc_info Address info that is declared by the user after reviewing data extracted from the Original ID Document. There is a review page where user can edit the info.
gender declared_kyc_info Gender Information info that is declared by the user after reviewing data extracted from the Original ID Document
dob declared_kyc_info Date Of Birth information info that is declared by the user after reviewing data extracted from the Original ID Document
name declared_kyc_info Name info that is declared by the user after reviewing data extracted from the Original ID Document.
father_name declared_kyc_info Father Name info that is declared by the user after reviewing data extracted from the Original ID Document
mother_name declared_kyc_info Mother’s Name info that is declared by the user after reviewing data extracted from the Original ID Document
expiry_date declared_kyc_info Expiry Date info that is declared by the user after reviewing data extracted from the Original ID Document
email declared_kyc_info Email info that is declared by the user after reviewing data extracted from the Original ID Document
mobile declared_kyc_info Mobile number user input in client application. It’s an optional field. If no mobile number is given as input, value will be an empty string. Its present in declared_kyc_info means hash of input Mobile# does not match with mobile hash present in UIDAI Aadhaar XML in case document type is ‘AADHAAR’
doc_type declared_kyc_info Specifies the type of the document.
document_id declared_kyc_info Is the unique Document Identifier info that is declared by the user after reviewing data extracted from the Original ID Document For example, if doc_type is PAN then it will be PAN#. If doc_type is Aadhaar, it will be Aadhaar Number .
verified_by declared_kyc_info This is applicable in case of Aadhaar XML. Value will be “UIDAI” in this case.
verified_using declared_kyc_info This is applicable in case of Aadhaar XML. Value will be “DIGITAL_SIGNATURE” in this case. document_verification_status
document_verification_code declared_kyc_info In case the client has opted for verification of the Id doc (Verification API) against government database, this indicates the document verification status based on declared info. Please refer to Section 4.4 in Companion Guide for more details. If this service is not opted value will be an Empty String.
document_verification_message declared_kyc_info In case the client has opted for verification of the Id doc (Verification API) against government database, this indicates the document verification status based on declared info. Please refer to Section 4.4 in Companion Guide for more details. If this service is not opted value will be an Empty String.
match_rate photo Face Match Confidence Percentage.
match_status photo Possible values are SUCCESS/FAIL/UNCERTAIN. If face match confidence rate is above True Positive (TP) threshold rate configured, then status will be SUCCESS. If face match confidence rate is below True Negative (TN) threshold rate configured, then status will be FAIL. If face match confidence rate is between True Positive (TP) Threshold and True Negative (TN) Threshold, then status will be UNCERTAIN.
document_image photo Base64 Encoded String of face image of the user that is extracted from document captured by SDK.
live_image photo Base64 Encoded String of Live Face captured by SDK for liveness check.
pan doc_image Base64 Encoded String of PAN Document captured by SDK.
aadhaar_front doc_image Base64 Encoded String of Aadhaar Front Document captured by SDK.
aadhaar_back doc_image Base64 Encoded String of Aadhaar Back Document captured by SDK.
passport_front doc_image Base64 Encoded String of Passport Front Page Document captured by SDK.
passport_back doc_image Base64 Encoded String of Passport Back Page Document captured by SDK.

Appendix A: Error Codes

Error Code Scenario Message
333 SDK There seems to be an unknown issue. Please try again later.
777 SDK Transaction Expired
111 SDK Invalid otp type.
222 SDK Aadhaar Card and Aadhaar QR Details do not match. Please initiate process with the same Aadhaar Card for OCR Scan and QR.
444 SDK Document not supported yet!
555 SDK Transaction cancelled!
666 SDK Dependencies required for your KYC flow were not found.
888 SDK Please check your internet connection and retry.
999 SDK There seems to be an issue (UIDAI). Please try again later.
1111 SDK Seems like you might not have received OTP (UIDAI). Please try again later.
380001 SDK api_key is mandatory
380002 SDK request_id is mandatory.
380003 SDK purpose is mandatory.
380036 SDK Session error occurred.
380037 SDK Session error occurred.
380039 SDK Session error occurred.
380040 SDK Session error occurred.
380048 SDK Invalid api_key or client_code
380049 SDK Invalid api_key or client_code
380050 SDK Invalid api_key configuration.
380092 SDK Invalid api_key configuration.
380090 SDK Hash is mandatory.
380091 SDK Hash validation failed.
240002 SDK Insufficient Balance.
240003 SDK Subscribed Service is inactive.
240004 SDK Subscription of the client is inactive.
240005 SDK Sorry, You have exceeded the consumption limit. Please contact customer.support@aadhaarbridge.com
240006 SDK Subscription info not found.
380024 fetchKYC user_id is mandatory
380025 fetchKYC hash is mandatory
380026 fetchKYC api_key is mandatory
380048 fetchKYC Invalid api_key or client_code
380049 fetchKYC Invalid api_key or client_code
380091 fetchKYC Hash validation failed
380076 fetchKYC No such user-id exists
380114 fetchKYC Session does not exist.
380116 fetchKYC Transaction does not exist.
333 fetchKYC Unexpected Error Occured

Appendix B: Hash generation

--> Initiate SDK Request : client_code|request_id|api_key|salt

--> fetchKYCInfo Request : client_code|user_id|api_key|salt

--> fetchKYCInfo Response : client_code|user_id|kyc_info|api_key|salt

Example for _init request, If your 
         client_code=a1b2c3, 
         api_key=123,
         requestId=1234567890101112,
         salt=e1d2c3b4a,
         then 
         Hash-Sequence=a1b2c3|1234567890101112|123|e1d2c3b4a
         hash =SHA-256(Hash-Sequence)

For validation:

Sample Code Snippet _init Request Hash Calculation

private String calculateHash(String clientCode, String requestId, String apiKey, String salt)
       throws NoSuchAlgorithmException {
   MessageDigest digest;
   digest = MessageDigest.getInstance("SHA-256");
   if (digest != null) {
       byte[] hash = digest.digest((clientCode + "|" + requestId + "|" + apiKey + "|" + salt).getBytes());
       return bytesToHex(hash);
   }
   return null;
}

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String bytesToHex(byte[] bytes) {
   char[] hexChars = new char[bytes.length * 2];
   for (int j = 0; j < bytes.length; j++) {
       int v = bytes[j] & 0xFF;
       hexChars[j * 2] = hexArray[v >>> 4];
       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
   }
   return new String(hexChars);}

Appendix C : GitHub Link For Sample Application

Below is the github repository link of codebase of sample App integrated to our video-id-kyc android SDK alongwith implementation of fetchKYCInfo API.

https://github.com/dev-kl/video-id-kyc-trial-app

Companion Guide

1. Introduction

Veri5Digital VideoIdKyc Solution offers Android SDK that can be seamlessly integrated with clients business application to do the KYC processing of the user.

This document serves as a companion guide to Veri5Digital VideoIdKyc Product Technical Integration Document. This document demonstrates sample code snippets relevant for integration steps as applicable is given.

2. Hash Generation

--> Initiate SDK Request :

client_code|request_id|api_key|salt

--> fetchKYCInfo Request :

client_code|user_id|api_key|salt

--> fetchKYCInfo Response :

client_code|user_id|kyc_info|api_key|salt

     Example for _init request, If your
     client_code=a1b2c3,
     api_key=123,
     requestId=1234567890101112,
     salt=e1d2c3b4a,
then
     Hash-Sequence=a1b2c3|1234567890101112|123|e1d2c3b4a
     hash =SHA-256(Hash-Sequence)

For validation:

Sample Code Snippet _init Request Hash Calculation

private String calculateHash(String clientCode, String requestId, String apiKey, String salt) throws NoSuchAlgorithmException {
  MessageDigest digest;
  digest = MessageDigest.getInstance("SHA-256");
  if (digest != null) {
  byte[] hash = digest.digest((clientCode + "|" + requestId + "|" + apiKey + "|"+ salt).getBytes());
  return bytesToHex(hash);
  }
return null;
}

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String bytesToHex(byte[] bytes) {
  char[] hexChars = new char[bytes.length * 2];
  for (int j = 0; j < bytes.length; j++) {
  int v = bytes[j] & 0xFF;
  hexChars[j * 2] = hexArray[v >>> 4];
  hexChars[j * 2 + 1] = hexArray[v & 0x0F];
  }
return new String(hexChars);
}

3. Data Decryption Sample Code

This section demonstrates the process and sample code for implementation of decrypting kyc response in case client has opted for encryption of response data.

Client has to :

Details of these steps are explained in below sections.

3.1 Key Pair Generation Process

If client has opted for kyc data encryption, client should create the public-private key pair that will be used for encryption and decryption. Generated public key should be shared with KL.

SDK will encrypt kyc_data using the public key shared with KL. Client application should decrypt the response using the private key.

This section explains the steps to generate the public-private key pair.

Steps to generate a CSR file using OpenSSL.

  1. Create private key with a validity

keytool -genkey -alias ALIAS_NAME -keyalg RSA -keystore COMP_NAME.jks -keysize 2048 -dname "CN=COMMON_NAME, EMAILADDRESS=sample@abc.com, C=IN, OU=Khosla Labs, O=ORGANIZATION_NAME"

Note :

ALIAS_NAME can be replaced with any string of your choice.

COMP_NAME can be replaced by your company name..

CN : Should give a string value.This should be the same as the contact person’s name.

EMAILADDRESS :Should give a string value. This should be some valid email id.Eg sample@abc.com

ORG_UNIT: Value should be “Khosla Labs”

ORGANIZATION_NAME: This should be the same as the organization name.

  1. Create CSR for above key

    keytool -certreq -alias ALIAS_NAME -file COMP_NAME.csr -keystore COMP_NAME.jks

  2. Convert jks store to p12

    keytool -importkeystore -srckeystore COMP_NAME.jks -destkeystore COMP_NAME.p12 -srcstoretype jks -deststoretype pkcs12

  3. Get private key from .p12 file

    openssl pkcs12 -in COMP_NAME.p12 -nodes -out COMP_NAME.key -nocerts

  4. Self sign .csr file using the private key to get .cer file

    openssl x509 -req -in COMP_NAME.csr -signkey COMP_NAME.key -out COMP_NAME.cer

Provide us the .cer file (public key certificate) generated above using which KYC response will be encrypted and use .p12 (private key) for decryption.

3.2 Code Snippet : kyc_info Data Block Decryption

Below given is Java Code Snippet that can be used as a reference for decryption using your private key.

Snippet 1: Response Reading and Decryption

Below code will read the Base64 encoded response string returned by the API. First it will Base64 decoding and then followed by decryption using your private key.This uses few utility classes given in Snippet 2 and Snippet 3.

// byte array of the base64 decoded response
byte[] decodeResponse = `<base64_decode_response>`;

// open your p12 file
// to generate your p12 file
InputStream inputStream = new FileInputStream("`<your_p12_file>.p12`");

// your keystore credentials
char[] keystorePassword = "`<your_keystore_password>`".toCharArray();
char[] aliasPassword = "`<your_alias_password>`".toCharArray();
String alias = "`<your_alias>`";

KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(inputStream, keystorePassword);

// get the private key
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, aliasPassword);

ResponseByteArraySpliter splitter = new ResponseByteArraySpliter(decodeResponse);

// get the symmetric key
byte[] sKey = DecryptionUtil.decryptUsingPrivateKey(
  privateKey, splitter.getEncryptedSymmetricKey()
);

// byte array of the decrypted data
byte[] decryptedData =
DecryptionUtil.decryptUsingSymmetricKey(
sKey, splitter.getEncryptedData()
);

Snippet 2 : ResponseByteArraySplitter Class.

// ResponseByteArraySplitter class
// Note: Please alter the access modifiers according to your packaging strategy.

public class ResponseByteArraySplitter {

private static final int SYMMETRIC_KEY_SIZE = 256;
private final byte[] encryptedSymmetricKey;
private final byte[] encryptedData;
public ResponseByteArraySplitter(byte[] data) throws Exception {
    int offset = 0;
    encryptedSymmetricKey = new byte[SYMMETRIC_KEY_SIZE];
    copyByteArray(
    data,
    offset,
    encryptedSymmetricKey.length,
    encryptedSymmetricKey
    );

    offset = offset + SYMMETRIC_KEY_SIZE;
    encryptedData = new byte[data.length - offset];
    copyByteArray(data, offset, encryptedData.length, encryptedData);
}

byte[] getEncryptedSymmetricKey() {
    return encryptedSymmetricKey;
}

byte[] getEncryptedData() {
    return encryptedData;
}

private void copyByteArray(byte[] src, int offset, int length, byte[] dest) throws Exception {
    try {
          System.arraycopy(src, offset, dest, 0, length);
        } catch (Exception e) {
              throw new Exception("Decryption failed, Corrupted packet!", e);
          }
        }
}

Snippet 3 : Decryption Util Class.

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import javax.crypto.Cipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

public class DecryptionUtil {

 private static final String ASYMMETRIC_ALGO = "RSA/ECB/PKCS1Padding";

 public static byte[] decryptUsingPrivateKey( PrivateKeyprivateKey, byte[] data) throws IOException,  GeneralSecurityException {

 Cipher pkCipher = Cipher.getInstance(ASYMMETRIC_ALGO); 
 pkCipher.init(Cipher.DECRYPT_MODE, privateKey);
 return pkCipher.doFinal(data);
 }

 public static byte[] decryptUsingSymmetricKey( byte[] symmetricKey, byte[] data) throws InvalidCipherTextException {
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher( new AESEngine(), new PKCS7Padding());

    cipher.init(false, new KeyParameter(symmetricKey));

    int outputSize = cipher.getOutputSize(data.length);

    byte[] tempOP = new byte[outputSize];
    int processLen = cipher.processBytes(data, 0, data.length, tempOP, 0);

    int outputLen = cipher.doFinal(tempOP, processLen);

    byte[] result = new byte[processLen + outputLen];
    System.arraycopy(tempOP, 0, result, 0, result.length);
    return result;
   }
}

4. Customization Capabilities

VideoIdKyc SDK provide the client application ability to do different customizations.Supported customization capabilities and how to implement these customisations are explained below.

Note :

Clients should specify these customizations to the onboarding team during onboarding process. This is required as some configurations has to be done accordingly.

4.1 SDK Initialization And Customization

SDK provides the option to choose Mobile Validation using OTP. If client has opted for OTP flow then following highlighted lines has to be added in VideoIdKycInitiRequest initiation as shown below. If this flow is enabled, there will be an OTP Input screen and client has to pass mobile number and Otp Type as ‘MOB_NO’ in the request.

// Add In Case OTP Validation is chosen, specify OTP Tye.

.otpType()

// Add In Case OTP Validation is chosen and channel is MOBILE

.mobile(<mobile number>) .moduleFactory(OcrSdkModuleFactory.newInstance()) .moduleFactory(FaceSdkModuleFactory.newInstance()) .build();

Table : VideoIdKycInitRequest Parameters

Name Value Comments Example
client_code String, Mandatory Your client code received during onboarding a1b2c3
request_id String, Mandatory Unique transaction identifier Client app has to generate the same.For each request it should be unique.This is used for uniquely identifying the transaction. 12345678910111
api_key String, Mandatory Pre-shared api key controls the access to various Api’s. Api Key will be shared during onboarding.There will be separate api_key for SandBox and Production Environment. 1A3b5c7D910111
hash String, Mandatory This should be a SHA-256 value of Hash Sequence defined for _init request. Purpose of this is to ensure the integrity of the request. Refer Hash generation for hash generation logic. 9780cd0d2ce77eef8f6 4942f54e0281a0e220f f6bbcce0a03df27a2b1 5575f58
purpose String, Mandatory String that specifies the actual intention of SDK invocation. Eg : “For customer Onboarding”. If there is no need to pass purpose, please pass “” ( empty ) string.

4.2 Application Obfuscation And Proguard Rules

If the client requires obfuscation of application code, please add the below proguard rules in your app’s proguard-rules.pro file.These rules specify the exclusion list that will not impact VideoIdKyc SDK which is already obfuscated.

    # Preserve all annotations.
      -keepattributes *Annotation*

    # Preserve R.*.* things.
      -keepclassmembers class **.R$* {
        public static <fields>;
       }

    # Preserve things required for parcelable classes.
    -keepclassmembers class * implements android.os.Parcelable {
      public static final android.os.Parcelable$Creator CREATOR;
      }

      # Preserve serializable things.
      -keepclassmembers class * implements java.io.Serializable {
      static final long serialVersionUID;
      static final java.io.ObjectStreamField[] serialPersistentFields;
      private void writeObject(java.io.ObjectOutputStream);
      private void readObject(java.io.ObjectInputStream);
      java.lang.Object writeReplace();
      java.lang.Object readResolve();
      }

     # Preserve enums.
     -keepclassmembers class * extends java.lang.Enum {
      public static **[] values();
      public static ** valueOf(java.lang.String);
    }

     # Preserve all .class method names.
    -keepclassmembernames class * {
    java.lang.Class class$(java.lang.String);
    java.lang.Class class$(java.lang.String, boolean);
    }

    # Preserve all native method names and the names of their classes.
    -keepclasseswithmembernames class * {
    native <methods>;
    }

    # Ignore support lib warnings.
    -dontwarn android.support.**

    # Preserve support libs.
    -keep interface android.support.v4.** { *; }
    -keep interface android.support.v7.** { *; }
    -keep class android.support.** { *; }


    # Preserve khosla labs libs.
    -keep public class com.khoslalabs.** { *; }
    -keep class com.khoslalabs.vikycapi.** { *; }

Note :

Proguard is a delicate tool, make sure you test your app thoroughly after enabling it.

4.3 User Interface And Customization

VideoIdKyc SDK can be customized using Android Theming/Styles framework by adding styles in Client Application’s styles.xml file.

Following customization capabilities are supported :

4.3.1 Customizing Basic Colors

To change basic color theme of the sdk, add the following in client applications styles.xml file. This facilitates to change Primary Color, PrimaryDark color and Accent Color.

The style elements include :

<style>
name="VideoIdKycSdkTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary"> your primary color </item>
<item name="colorPrimaryDark"> your primary dark color </item>
<item name="colorAccent"> your accent color here </item>
</style>


4.3.2 Action Bar Background Color

To change the background color of the action bar, add the following in the client app’s styles.xml file.

<style>
  name="VideoIdKycSdkTheme.Widget.Toolbar"
  parent="Widget.AppCompat.Toolbar">
  <item name="android:background"> your background color </item>
  <item name="titleTextAppearance">
  @style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse
</item>
 </style>


If the required action bar background color is light, change the titleTextAppearance and appropriate dark icons as given below.


<style
  name="VideoIdKycSdkTheme.Widget.Toolbar"
  parent="Widget.AppCompat.Toolbar">
  <item name="android:background">
  @color/colorVikycSdkActionBarBackground
</item>
<item name="titleTextAppearance">
@style/VideoIdKycSdkTheme.TextAppearance.Toolbar

</item>
</style>
<style
name="VideoIdKycSdkTheme.TextAppearance.Toolbar"
parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
</style>
<style name="VideoIdKycSdkTheme.Widget.Toolbar.Back">
<item name="navigationIcon"> appropriate dark back icon </item>
</style>
<style name="VideoIdKycSdkTheme.Widget.Toolbar.Close">
<item name="navigationIcon"> appropriate dark close icon </item>
</style>
4.3.3 Font Style Customization

To change the font in the whole SDK:

1. Add the .otf/.ttf file of the required font in the font resource folder (res/font/)
2. Set the font as the value for fontFamily param in VideoIdKycSdkTheme in the client’s styles.xml file.


<style
    name="VideoIdKycSdkTheme"
    parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:fontFamily">@font/your_font_name</item>
</style>

3. Pass the same font as the value of font parameter of the VideoIdKycInitRequest.

CodeSnippet
VideoIdKycInitRequest.Builder(...)

.
.
.
.font(<res id of the font>)


4. Make sure to pass the font in both places as mentioned in points 2 and 3.

If the required action bar color is different than the app’s font, add the following in the client app’s styles.xml file.

```CodeSnippet
<style
    name="VideoIdKycSdkTheme.Widget.Toolbar"
    parent="Widget.AppCompat.Toolbar">
    <item name="android:background"> you action bar background color </item>
    <item name="titleTextAppearance">
    @style/VideoIdKycSdkTheme.TextAppearance.Toolbar
  </item>
</style>

<style
  name="VideoIdKycSdkTheme.TextAppearance.Toolbar"
  parent="TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse">
  <item name="android:fontFamily">@font/your_font_name</item>
</style>

4.3.4 Action Button Text Style Customization

To change the action button text color, add the following in the client app’s styles.xml file.

<style
    name="VideoIdKycSdkTheme.TextAppearance.ActionButton"
    parent="TextAppearance.AppCompat.Widget.Button">
    <item name="android:textColor"> your action button text color </item>
</style>

4.4 Verification API

VideoIdKyc Solution provides the capability to verify document details ( PAN and Aadhaar ) against government databases like income tax database for PAN document and UIDAI database for Aadhaar Document. Client has to opt for this service during onboarding. Accordingly the extracted data will be verified against the actual database source.Following table summarizes different Verification Status, Verification Code and Verification Message that can be returned.

SI# Verified By Verification Status Verification Code Verification Message
1 PAN SUCCESS 000 Verification done Successfully
2 PAN FAIL 400101 PAN format is correct and PAN number is correct matching against DOB. However, Name is Incorrect.
3 PAN FAIL 400102 PAN format is correct but PAN or Name or DOB is incorrect.
4 PAN FAIL 400103 Invalid PAN
5 PAN FAIL 410007 Verification Service Not Available.
6 PAN FAIL 400099 Unknown Error Occurred.Please try after some time.
7 Aadhaar SUCCESS 000 Verification done Successfully and details are matching with the PAN Database.
8 Aadhaar FAIL 400301 Aadhaar number is not present.
9 Aadhaar FAIL 400302 Verification is not complete. Please Try again.
10 Aadhaar FAIL 400303 Not a valid Aadhaar number.
11 Aadhaar FAIL 400304 UIDAI website not reachable.
12 Aadhaar FAIL 333 Unknown Error Occurred.Please try after some time.

5. SDK Invocation - Reference Android Code Snippet

This section demonstrates reference Android Code that invokes the SDK.

/**
* Reference code that can be used Android Application developer for integrating Veri5Digital
* VideoIdKyc SDK into hosting application
*
*/

package com.demo.basics;
/**
* This is a reference Activity class that can be used by clients to implement their own Activity to
invoke VideoIdKyc SDK.
* MainActivity can be replaced by clients own Activity Name.
* @author Aditya Upadhay
*
*/

public class MainActivity extends AppCompatActivity {
  private static final String TAG = MainActivity.class.getSimpleName();
  private static final int VI_KYC_SDK_REQ_CODE = 7879;

  @BindView(R.id.smtButton)
  Button buton;

  @BindView(R.id.client_code)
  EditText etClientcode;

  @BindView(R.id.apiKey)
  EditText etAPIkey;

  @BindView(R.id.client_salt)
  EditText etSalt;

  @BindView(R.id.mob_No)
  EditText etMobNo;

  @BindView(R.id.email_Id)
  EditText etEmail;

  @BindView(R.id.progressBar)
  ContentLoadingProgressBar progressBar;
  String salt;

  /**
    * Dummy function to demonstrate the required params for invoking SDK. 
    *
    * @param savedInstanceState
  */

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    buton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    salt = (etSalt.getText().toString().equals(""))? "BYPASS" : etSalt.getText().toString();
    startSdk(
    etClientcode.getText().toString(),
    etAPIkey.getText().toString(),
    salt,
    getOtpType(), //Optional
    etMobNo.getText().toString(), // Optional
    null,
    true,
    false,
    false,
    false
    );
  }
});
}
/**
* This function is used to invoke the VideoIDKyc SDK.
* For invoking SDK critical params like clientCode, apiKey,otpType( if OTP is not opted pass
* NULL), and other params.
**/

public void startSdk(
String clientCode, // Client Code shared during Onboarding
String apiKey, // Api Key shared during Onboarding
String salt, // Salt shared during Onboarding
String otpType, // OTP Type option opted by client it can be null, "MOB_NO""
String mobNo, //Moble No for getting OTP if otpType is MOB_NO
boolean ocrSdk,
boolean faceSdk,

) {
try {
 /**
 * ReuestId is a unique Id that should be passed for each transaction request.
 * Clients can use their own mechanism for generating requestId. Below given is just for sample.
 */

String requestId = clientCode + "-" + TimeUtil.getCurTimeMilisec() + "";
// For request integrity check Hash Digest is also passed. Refer below how to do the same.
String hash = calculateHash(clientCode, requestId, apiKey, salt);
if (hash == null) {
  throw new Exception("Hash cannot be generated.");
}

Intent myIntent = new Intent(MainActivity.this, VideoIdKycInitActivity.class);
// Forming VideoIdKyc Request for invoking SDK.
VideoIdKycInitRequest.Builder videoIdKycInitRequestBuilder =
                              new VideoIdKycInitRequest
                                .Builder(
                                clientCode,
                                apiKey,
                                "Testing Demo Application",
                                requestId,
                                hash
                                )
                            .otpType(otpType)
                            .email(email)
                            .mobile(mobNo)
                            .screenTitle(screenTitle)
                            .salt(salt);

        videoIdKycInitRequestBuilder.moduleFactory(OcrSdkModuleFactory.newInstance());
        videoIdKycInitRequestBuilder.moduleFactory(FaceSdkModuleFactory.newInstance());
        myIntent.putExtra("init_request", videoIdKycInitRequestBuilder.build());
        startActivityForResult(myIntent, VI_KYC_SDK_REQ_CODE);
        } catch (Exception e) {
    Log.e(TAG, "startSdk: ", e);
    }
}

/**
* This function handles response from SDK post processing UserId will be returned from veri5Digital
* android SDK result, which then client will use for getting the
* KYCinfo of user by calling fetchKycInfo API.
*
*/

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
 if (requestCode == VI_KYC_SDK_REQ_CODE) {
    if (resultCode == ViKycResults.RESULT_OK || resultCode == ViKycResults.RESULT_DOC_COMPLETE) {
    if (data != null) {
    String userId = data.getStringExtra(ViKycConstants.KEY_USER_ID); //here user id is taken, will be used by the client to get the KYCinfo block of user
    if (userId != null) {
    /**
    * here we have are calling one activity method(startkycDataActivity) as demo to process the KYC info block
    */

    startKycDataActivity(
    userId,
    etClientcode.getText().toString(),
    etAPIkey.getText().toString(),
    salt
    );
    }
} 
else {
  showToast("Video Id KYC was not completed!");
 }
} else {
  if (data != null) {
     int code = data.getIntExtra(ViKycConstants.KEY_ERROR_CODE, 333);
     String msg = data.getStringExtra(ViKycConstants.KEY_ERROR_MESSAGE);
     if (msg != null) {
     showToast(code + " : " + msg);
    }
} else {
showToast("Video Id KYC was not completed!");
   }
  } 
 }
}

/**
* using userId client can implement their own mechanism to get KYCinfo block
* here we have just implemented one activity method(startkycDataActivity) as demo
*/
private void startKycDataActivity(String userId,
String clientCode,
String apiKey,
String salt) {

Intent i = new Intent(this, KycDataActivity.class);
i.putExtra("user_id", userId);
i.putExtra("client_code", clientCode);
i.putExtra("api_key", apiKey);
i.putExtra("salt", salt);
startActivity(i);
}
private void showToast(String s) {
Toast.makeText(MainActivity.this, s + "", Toast.LENGTH_LONG).show();
}

}
/**
*
* Function to calculate Hash Digest. It uses sequence of clientCode|requestId|apiKey|salt
*
* @param clientCode
* @param requestId
* @param apiKey
* @param salt
* @return
* @throws NoSuchAlgorithmException
*/
private String calculateHash(String clientCode, String requestId, String apiKey, String salt) throws NoSuchAlgorithmException {

    MessageDigest digest;
    digest = MessageDigest.getInstance("SHA-256");
    if (digest != null) {
    byte[] hash =
    digest
    .digest(
    (clientCode + "|" + requestId + "|" + apiKey + "|" + salt)
    .getBytes()
    );
    return bytesToHex(hash);
    }
    return null;
}

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
    int v = bytes[j] & 0xFF;
    hexChars[j * 2] = hexArray[v >>> 4];
    hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

6. SDK Response Processing - Reference Android Code Snippet

/**
* This is a reference Activity class that can be used by clients to implement their own Activity to
* invoke VideoIdKyc SDK.
* MainActivity can be replaced by clients own Activity Name.
* @author Aditya Upadhay
*
*/
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int VI_KYC_SDK_REQ_CODE = 7879;

    @BindView(R.id.smtButton)
    Button buton;

    @BindView(R.id.client_code)
    EditText etClientcode;

    @BindView(R.id.apiKey)
    EditText etAPIkey;

    @BindView(R.id.client_salt)
    EditText etSalt;

    @BindView(R.id.mob_No)
    EditText etMobNo;

    @BindView(R.id.email_Id)
    EditText etEmail;

    @BindView(R.id.progressBar)
    ContentLoadingProgressBar progressBar;
    String salt;
    /**
    * This function handles response from SDK post processing
    * UserId will be returned from veri5Digital android SDK result, which then client will use for
    * getting the KYCinfo of user by calling fetchKycInfo API.
    *
    */

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (requestCode == VI_KYC_SDK_REQ_CODE) {   
    if (resultCode == ViKycResults.RESULT_OK || resultCode == ViKycResults.RESULT_DOC_COMPLETE) {
    if (data != null) {
    String userId = data.getStringExtra(ViKycConstants.KEY_USER_ID); //here user id is taken, will be used by client to get the KYCinfo block of user

    if (userId != null) {
    /**
    * here we are calling one activity method(startkycDataActivity) as demo to process
    *the KYCinfo block.
    */
    startKycDataActivity(
    userId,
    etClientcode.getText().toString(),
    etAPIkey.getText().toString(),
    salt
    );
    }
} else {
    showToast("Video Id KYC was not completed!");
    }
} else {
if (data != null) {
    int code = data.getIntExtra(ViKycConstants.KEY_ERROR_CODE, 333);
    String msg = data.getStringExtra(ViKycConstants.KEY_ERROR_MESSAGE);
    if (msg != null) {
    showToast(code + " : " + msg);
}
} else {
    showToast("Video Id KYC was not completed!");
    }
   }
  }
}

/**
* using userId client can implement their own mechanism to get KYCinfo block
* here we have just implemented one activity method(startkycDataActivity) as demo
*/
private void startKycDataActivity(String userId,
String clientCode,
String apiKey,
String salt) {

Intent i = new Intent(this, KycDataActivity.class);
i.putExtra("user_id", userId);
i.putExtra("client_code", clientCode);
i.putExtra("api_key", apiKey);
i.putExtra("salt", salt);
startActivity(i);
}
private void showToast(String s) {
Toast.makeText(MainActivity.this, s + "", Toast.LENGTH_LONG).show();
}
}

7. Invoking fetchKYCInfo API - Reference Android Code Snippet

7.1 fetcKYCInfo API Request
---------------------------

This code snippet demonstrates the Android RxJava Code with Retrofit for making fetchKYCInfo API.

Disposable d = Util.apiService()
                .fetchKycInfo(apiRequest)
                .flatMap(
                        new Function<ApiResponse<FetchKycInfo.Res>, ObservableSource<ApiResponse<FetchKycInfo.Res>>>() {
                            @Override
                            public ObservableSource<ApiResponse<FetchKycInfo.Res>> apply(ApiResponse<FetchKycInfo.Res> resApiResponse) {
                                return Util.apiResponseInterceptor(resApiResponse);
                            }
                        }
                )
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(
                        new Consumer<Disposable>() {
                            @Override
                            public void accept(Disposable disposable) {
                                showLoading("Fetching KYC Info...");
                            }
                        }
                )
                .subscribe(
                        new Consumer<ApiResponse<FetchKycInfo.Res>>() {
                            @Override
                            public void accept(ApiResponse<FetchKycInfo.Res> resApiResponse)
                                    throws Exception {
                                handleFetchKycInfoRes(resApiResponse.getResponseData());
                                hideLoading();
                            }
                        },
                        new Consumer<Throwable>() {
                            @SuppressWarnings("RedundantCast")
                            @Override
                            public void accept(Throwable throwable) {
                                throwable.printStackTrace();
                                hideLoading();
                                if (throwable instanceof ApiException) {
                                    showToast(((ApiException) throwable).getMessage());
                                    matchStatusTv.setText(((ApiException) throwable).getMessage());
                                } else {
                                    showToast(throwable.getMessage());
                                    matchStatusTv.setText(throwable.getMessage());
                                }

                                finish();

                            }
                        }
                );
    }
7.2 Decoding Base64 Encoded fetchKYCInfo API Response
-----------------------------------------------------

API response data contains Base64 Encoded kyc_info block that contains actual Kyc data elements.Hence you need to decode the response data to get kyc_info block.

private void handleFetchKycInfoRes(FetchKycInfo.Res apiResponse)
            throws Exception {

        String hash = calculateResponseHash(
                getIntent().getStringExtra("client_code"),
                getIntent().getStringExtra("user_id"),
                apiResponse.getKycInfo(),
                getIntent().getStringExtra("api_key"),
                getIntent().getStringExtra("salt")
        );

        Log.i(
                TAG,
                "handleFetchKycInfoRes: Api hash = "
                        + apiResponse.getHash() + ", Calculated hash = " + hash + "."
        );

        if (hash != null && !hash.equalsIgnoreCase(apiResponse.getHash())) {
            throw new IllegalArgumentException("Invalid hash!");
        }

        byte[] decodedResponse = Base64.decode(apiResponse.getKycInfo(), Base64.DEFAULT);

        if (apiResponse.getEncrypted().equalsIgnoreCase("yes")) {
            showPasswordDialog(decodedResponse);
        } else {
            refreshUi(decodedResponse);
        }
    }

7.3 POJO Class fetchKYCInfo API Response

class Res {
    private String encrypted;
    @SerializedName("kyc_info")
    private String kycInfo;
    private String hash;

    public String getEncrypted() {
        return encrypted;
    }

    public String getKycInfo() {
        return kycInfo;
    }

    public String getHash() {
        return hash;
    }

    public static class KycInfo {
        private Photo photo;
        @SerializedName("original_kyc_info")
        private KycData originalKycData;
        @SerializedName("declared_kyc_info")
        private KycData declaredKycData;
        @SerializedName("doc_image")
        private Document document;
        @SerializedName("verified_agent")
        private VerifiedAgent verifiedAgent;

        public Photo getPhoto() {
            return photo;
        }

        public KycData getOriginalKycData() {
            return originalKycData;
        }

        public KycData getDeclaredKycData() {
            return declaredKycData;
        }

        public Document getDocument() {
            return document;
        }

        public VerifiedAgent getVerifiedAgent() {
            return verifiedAgent;
        }
    }

    public enum VerifiedAgentStatus {
        PENDING, APPROVED, REJECTED, NA
    }

    public static class VerifiedAgent {
        @SerializedName("status")
        public VerifiedAgentStatus status;

        public VerifiedAgentStatus getStatus() {
            return status;
        }
    }

    public static class Document {
        public String pan;
        @SerializedName("aadhaar_front")
        public String aadhaarFront;
        @SerializedName("aadhaar_back")
        public String aadhaarBack;
        @SerializedName("passport_front")
        public String passportFront;
        @SerializedName("passport_back")
        public String passportBack;
        @SerializedName("voter_id_front")
        public String voterIdFront;
        @SerializedName("voter_id_back")
        public String voterIdBack;

        public String getPan() {
            return pan;
        }

        public String getAadhaarFront() {
            return aadhaarFront;
        }

        public String getAadhaarBack() {
            return aadhaarBack;
        }

        public String getPassportFront() {
            return passportFront;
        }

        public String getPassportBack() {
            return passportBack;
        }

        public String getVoterIdFront() {
            return voterIdFront;
        }

        public String getVoterIdBack() {
            return voterIdBack;
        }
    }

    public enum MatchStatus {
        SUCCESS, UNCERTAIN, FAIL
    }

    public static class Photo {
        @SerializedName("document_image")
        private String docFace;
        @SerializedName("live_image")
        private String liveFace;
        @SerializedName("match_rate")
        private Double matchRate;
        @SerializedName("match_status")
        private MatchStatus matchStatus;

        public String getDocFace() {
            return docFace;
        }

        public String getLiveFace() {
            return liveFace;
        }

        public Double getMatchRate() {
            return matchRate;
        }

        public MatchStatus getMatchStatus() {
            return matchStatus;
        }
    }

public static class KycData {
    private String name;
    @SerializedName("father_name")
    private String fatherName;
    @SerializedName("mother_name")
    private String motherName;
    private String gender;
    private String dob;
    private String email;
    private String mobile;
    private String address;
    @SerializedName("doc_type")
    private String docType;
    @SerializedName("document_id")
    private String documentId;
    @SerializedName("verified_by")
    private String verifiedBy;
    @SerializedName("verified_using")
    private String verifiedUsing;
    @SerializedName("document_verification_status")
    private String documentVerificationStatus;
    @SerializedName("document_verification_code")
    private String documentVerificationCode;
    @SerializedName("document_verification_message")
    private String documentVerificationMessage;
    public String getDocumentVerificationCode() {
    return documentVerificationCode;
    }
    public String getDocumentVerificationMessage() {
    return documentVerificationMessage;
    }
    public String getName() {
    return name;
    }
    public String getFatherName() {
    return fatherName;
    }
    public String getMotherName() {
    return motherName;
    }
    public String getGender() {
    return gender;
    }
    public String getDob() {
    return dob;
    }
    public String getEmail() {
    return email;
    }
    public String getMobile() {
    return mobile;
    }
    public String getAddress() {
    return address;
    }
    public String getDocType() {
    return docType;
    }
    public String getDocumentId() {
    return documentId;  
    }
    public String getVerifiedBy() {
    return verifiedBy;
    }
    public String getVerifiedUsing() {
    return verifiedUsing;
    }
    public String getDocumentVerificationStatus() {
        return documentVerificationStatus;
    }
  }
}

Technical Specification Extension

1. Introduction

This document is an extension of the Technical Specification document. This contains following add-on functionalities that are being offered as part of video-id-kyc solution.

This API offers following functionality:

2. executeAadhaarXMLService API

Post processing of VideoIdKyc SDK processing,this api can be used to download or delete aadhaar xml/zip.

Note :

This Api is relevant only in case the user had chosen Offline Aadhaar XML Option for completing the Kyc processing.

2.1 API Request Details

API Name : executeAadhaarXMLService

Method : POST

Sandbox URL :

https://sandbox.veri5digital.com/video-id-kyc/api/1.0/executeAadhaarXMLService

Production URL :

https://prod.veri5digital.com/video-id-kyc/api/1.0/executeAadhaarXMLService

2.1.1 executeAadhaarXMLService API Request Details
--------------------------------------------------

Calling application should form the Request Payload in the following Json format.This should be passed as http Request Body.
{
"headers": {
        "client_code": "<<your client_code>>",
        "sub_client_code": "<<your sub client code>>",
        "actor_type": "NA",
        "channel_code": "SDK",
        "stan": "<<System Trace Number>>",
        "user_handle_type": "",
        "user_handle_value": "",
        "location": "",
        "transmission_datetime": "<<System Time In MilliSeconds">>,
        "run_mode": "REAL",
        "client_ip": "<<Your IP Address>>",
        "operation_mode": "SELF",
        "channel_version": "",
        "function_code": "<<can be DOWNLOAD or NO_ACTION>>",
        "function_sub_code": "<<can be DELETE or NO_ACTION>>"
        },
"request": {
        "api_key": "<api-key>",
        "request_id": "<<Unique Request Id Passed by client>>",
        "user_id": "<<received in response from android-sdk>>",
        "hash": "<<Generated Hash>>"
        }
}

Table : executeAadhaarXMLService API Request Body Parameters

Please refer below table for details of Request Field information.

Field Name Type Comments Example
client_code String, Mandatory Unique code assigned to a client.This will be assigned to the client during onboarding. client123
sub_client_code String, Not Mandatory Its useful in case calling client needs any customization w.r.to different departments. subclient123
actor_type String, Not Mandatory Type of user who is making the request from the application. CUSTOMER or AGENT
channel_code String, Not Mandatory Channel code used by the client to initiate request. ANDROID_SDK
stan String, Mandatory A unique number generated per request by the client. It is used to uniquely identify a request from a client and can be used for troubleshooting.Max length can be 64 characters long. 98321892319
user_handle_type String, Not Mandatory Indicates the type of the user identifier, who has initiated the request. Eg : UUID, MSISDN, EMAIL. EMAIL
user_handle_value String, Not Mandatory Value of user_handle_type. If user_handle_type is EMAIL, then user_handle_value is actual email. a@b.com
location String, Not Mandatory Location of the device that initiated the request. Can be empty.
transmission_datetime String, Not Mandatory Time in System milli seconds at which request was initiated from client. 1553255080298
run_mode String, Not Mandatory Execution Mode in which transaction is being done.Possible values are REAL or TRIAL. REAL
client_ip String, Not Mandatory IP Address of the client device.
operation_mode String, Not Mandatory The way in which the transaction is executed.Example values can be “SELF”,”ASSISTED”,”SYSTEM” etc. SELF
channel_version String, Not Mandatory It can be Android SDK Version 3.1.7
function_code String, Mandatory Defines the behaviour of this api.Value should be ‘REVISED’ REVISED
function_sub_code String, Mandatory Indicates default behaviour for this api. If no customized behaviour requirements, then value will be “DEFAULT” DEFAULT
user_id String, Mandatory This is same as user_id passed on to client application as part of SDK response. userid123
request_id String, Conditional Client generated unique Id for referencing the transaction that is passed while invoking SDK. Refer Section 2.1.1.2 for the usage. requestid123
user_id String, Conditional This is the unique user identifier returned by SDK post successful processing. Refer Section 2.1.1.2 for the usage. userid123
hash String, Mandatory Hash to be calculated by client to verify integrity of request. Please refer APPENDIX A to see how to generate the hash.
api_key String, Mandatory api_key shared with client during onboarding. samplekey123

2.1.1.1 Function Code & Function Sub Code Map

Table : Action Matrix Based on function_code and function_sub_code

function_code function_sub_code Action
DOWNLOAD NO_ACTION Aadhaar XML Zip File for the requested user_id will be downloaded
DOWNLOAD DELETE Aadhaar XML Zip File for the requested user_id will be downloaded and deleted from server.
NO_ACTION DELETE Aadhaar XML Zip File for the requested user_id will be deleted from server.

2.1.1.2 request Id & user_id Usage

Table : Action Matrix Based on function_code and function_sub_code

request_id user_id Action
PRESENT NOT PRESENT Aadhaar XML Zip File for the request_id will be returned
NOT PRESENT PRESENT Aadhaar XML Zip File for the given user_id will be downloaded.
PRESENT PRESENT Aadhaar XML Zip File for the given user_id will be downloaded. Please note user_id will get preference and error will be returned if user_id does not exist.

2.1.2 executeAadhaarXMLService API Response Details

Response data contains processed KYC information in “response_data” tag.Response Data is comprised of doc_format, doc_content and password.

  {
    “response_status”:{
    “status” : ”<<status of API Execution>>”,
    “code” : ”<<status code of API Execution>>”,
    “message” :”<<message based on the status and code>>”
  },
  “response_data”:{
      “doc_format”:”<<ZIP or XML>>”,
      “doc_content”:”<<base64 encoded document>>”
      “password”:”<<password to open zip file>>”
  },
}

Note - Refer Appendix B for possible code and messages in response_status.

Appendix A: Hash generation

_sub_code>
If your
    client_code=”a1b2c3”,
    api_key=”123”,
    request_id=”1234567890101112”,
    user_id =””,
    salt=”e1d2c3b4a”,
    function_code=”DOWNLOAD”,
    function_sub_code=”DELETE”,

then
    Hash-Sequence=
    a1b2c3|1234567890101112||123|e1d2c3b4a|DOWNLOAD|DELETE
    hash =SHA-256(Hash-Sequence)

For validation:

Sample Code Snippet _init Request Hash Calculation

private String calculateHash(String clientCode, String requestId, String apiKey, String salt)
       throws NoSuchAlgorithmException {
   MessageDigest digest;
   digest = MessageDigest.getInstance("SHA-256");
   if (digest != null) {
       byte[] hash = digest.digest((clientCode + "|" + requestId + "|" + apiKey + "|" + salt).getBytes());
       return bytesToHex(hash);
   }
   return null;
}

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String bytesToHex(byte[] bytes) {
   char[] hexChars = new char[bytes.length * 2];
   for (int j = 0; j < bytes.length; j++) {
       int v = bytes[j] & 0xFF;
       hexChars[j * 2] = hexArray[v >>> 4];
       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
   }
   return new String(hexChars);}

Appendix B: Error Codes

Error Code Message
000 Success
380145 please provide either request_id or user_id
380146 hash is mandatory
380147 api_key is mandatory
380148 function_code is mandatory
380149 function_sub_code is mandatory
380150 Invalid function_code
380151 Invalid function_sub_code
380152 user_id doesn't exist
380153 Required details not found
380154 Required details not available
380155 request_id doesn't exist
380048 Invalid api_key
380049 Invalid client_code
380091 Hash validation failed.

Appendix C : Sample Postman Request Response

https://sandbox.veri5digital.com/video-id-kyc/api/1.0/executeAadhaarXMLService

https://prod.veri5digital.com/video-id-kyc/api/1.0/executeAadhaarXMLService

Content-Type : application/json

- HTTP Request Body:

{
  "headers":{
        "client_code":"your-code",
        "sub_client_code":"your-code",
        "actor_type":"NA",
        "channel_code":"ANDROID_SDK",
        "stan":"stanwedfsdfqaaaawerqwer",
        "user_handle_type":"EMAIL",
        "user_handle_value":"abs@gmail.com",
        "location":"",
        "transmission_datetime":"1533123525716",
        "run_mode":"REAL",
        "client_ip":"",
        "operation_mode":"SELF",
        "channel_version":"0.0.1",
        "function_code":"DOWNLOAD",
        "function_sub_code":"DELETE"
  },
  "request":{
        "request_id":"dummy-codea-1564137837052",
        "api_key":"your-api-key1",
        "hash":"dd02c40fe612066bbdff72a0e93200a08866380e726dc31dd15806898cf541de",
        "user_id":""
  }
}


- Response Payload :

{
  "response_status": {
            "status ": "SUCCESS ",
            "code": "000",
            "message": "document details fetched successfully"
  },
  "response_data": {
            "doc_format ": "ZIP ",
            "doc_content": "...encoded document string.... >> ",
            "password": "3962"
  }
}