kyc_workflow 2.0.28
kyc_workflow: ^2.0.28 copied to clipboard
Digio kyc workflow plugin
Digio KYC Workflow Plugin #
A Flutter plugin for running Digio's KYC, eSign, mandate, UPI, and RPD flows on Android and iOS.
The plugin exposes two ways of using the Digio SDK from your app. Pick the one that fits your use case:
| Mode | When to use it | Supported flows | Entry point |
|---|---|---|---|
| Workflow | You want the full Digio-orchestrated journey. Your backend has created a workflow request and issued you a documentId + tokenId. |
KYC, Mandate, UPI, RPD, eSign (via OTP / Fingerprint / Face / Iris) | KycWorkflow.start(...) |
| Stateless | You want a pure native SDK experience for a standalone task. Your backend issues a JWT that carries the Digio credentials — the SDK reads the token and handles the rest. | Selfie (Android + iOS) | KycWorkflow.startStateless(...) |
Table of Contents #
Android setup #
Digio SDK supports Android 7.0 and above (SDK level 24+).
1. Root build.gradle #
Add the required Maven repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
maven {
url = uri("https://pkgs.dev.azure.com/eMudhraTrustService/b81ae285-cb60-4000-8c39-394db16f2a75/_packaging/eMudhraEsignAAR/maven/v1")
}
}
}
2. App-level dependency (android/app/build.gradle) #
implementation 'com.github.digio-tech:protean-esign:v3.12'
3. App build.gradle configuration #
android {
compileSdk = 35
defaultConfig {
minSdk = 26
}
buildTypes {
release {
signingConfig signingConfigs.debug
minifyEnabled true
}
debug {
signingConfig signingConfigs.debug
minifyEnabled true
}
}
buildFeatures {
viewBinding true
dataBinding true
}
dependencies {
implementation 'com.github.digio-tech:protean-esign:v3.12'
}
}
4. Android Gradle Plugin (android/settings.gradle) #
plugins {
...
id 'com.android.application' version '8.7.1' apply false
...
}
5. Project-level build.gradle #
Add the Kotlin plugin at the project level:
buildscript {
ext.kotlin_version = "1.9.24"
dependencies {
classpath("com.android.tools.build:gradle:8.4.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
6. Manifest permissions #
<!-- Required for Video KYC -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Required for geotagging -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Required for ID card analysis, selfie and face match -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
A fintech Android app cannot access these permissions:
READ_EXTERNAL_STORAGE,READ_MEDIA_IMAGES,READ_CONTACTS,ACCESS_FINE_LOCATION,READ_PHONE_NUMBERS,READ_MEDIA_VIDEOS.
Common Android issues #
Build error due to a theme conflict — add tools:replace="android:theme" under your <application> tag:
<application
...
tools:replace="android:label,android:theme"
...>
...
</application>
Crash related to the AppCompat theme while using eSign — set an AppCompat theme on the <application> tag:
<application
...
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
...>
...
</application>
ProGuard rules #
Always test the release build before shipping. Add these rules to your ProGuard config:
Click to expand ProGuard rules
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keepattributes JavascriptInterface
-keepattributes *Annotation*
-keepattributes Signature
-optimizations !method/inlining/*
-keeppackagenames
-keepnames class androidx.navigation.fragment.NavHostFragment
-keep class * extends androidx.fragment.app.Fragment{}
-keepnames class * extends android.os.Parcelable
-keepnames class * extends java.io.Serializable
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-dontwarn androidx.databinding.**
-keep class androidx.databinding.** { *; }
-keepclassmembers class * extends androidx.databinding.** { *; }
-dontwarn org.json.**
-keep class org.json** { *; }
-keep public class org.simpleframework.**{ *; }
-keep class org.simpleframework.xml.**{ *; }
-keep class org.simpleframework.xml.core.**{ *; }
-keep class org.simpleframework.xml.util.**{ *; }
-dontwarn com.google.android.gms.**
-keep class com.google.android.gms.** { *; }
-keep class com.google.android.material.** { *; }
-dontwarn org.simpleframework.**
-keepattributes ElementList, Root
-keepclassmembers class * {
@org.simpleframework.xml.* *;
}
-keep class org.spongycastle.** { *; }
-keep class com.ecs.rdlibrary.request.** { *; }
-keep class com.ecs.rdlibrary.response.** { *; }
-keep class com.ecs.rdlibrary.utils.** { *; }
-keep class com.ecs.rdlibrary.ECSBioCaptureActivity { *; }
-keep class org.simpleframework.xml.** { *; }
-keepattributes Exceptions, InnerClasses
-keep class com.google.android.gms.location.LocationSettingsRequest$Builder { *; }
-keepnames class ** { *; }
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-keepattributes AnnotationDefault
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}
-dontwarn javax.annotation.**
-dontwarn kotlin.Unit
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface * extends <1>
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**
-keep class * extends androidx.databinding.DataBinderMapper
-dontwarn kotlin.jvm.internal.SourceDebugExtension
-dontwarn org.xmlpull.v1.**
-keep class org.xmlpull.v1.** { *; }
-keep class in.digio.sdk.gateway.interfaces.FeatureRegistrationInterface$DefaultImpls { *; }
-keep class javax.xml.bind.annotation.** { *; }
-dontwarn javax.xml.bind.annotation.**
-keep class com.ecs.cdslxsds.ESignProcessorResponse { *; }
-dontwarn org.xmlpull.v1.XmlPullParser
-dontwarn android.content.res.XmlResourceParser
-keep class in.digio.sdk.gateway.interfaces.FeatureRegistrationInterface$DefaultImpls { *; }
-dontwarn java.lang.invoke.StringConcatFactory
-keep public class com.digio.two_way_sdk.** {
public *;
}
-keep class com.digio.two_way_sdk.** { *; }
-keep class androidx.databinding.** { *; }
-keep class androidx.viewbinding.** { *; }
-keep public class * extends androidx.**
-keep public class com.google.android.material.** { *; }
-keep public class androidx.** { public *; }
# emudhra
-keep class com.emudhra.** { *; }
-keep interface com.emudhra.** { *; }
-keepclassmembers class com.emudhra.** {
native <methods>;
}
-dontwarn com.emudhra.**
# Jackson dataformat XML (used by emudhra at runtime via XmlMapper)
-keep class com.fasterxml.jackson.** { *; }
-keepnames class com.fasterxml.jackson.** { *; }
-dontwarn com.fasterxml.jackson.**
# Woodstox - StAX implementation backing jackson-dataformat-xml.
# FactoryFinder uses Class.forName via META-INF/services lookup; the impl
# classes must not be obfuscated or shrunk away.
-keep class com.ctc.wstx.** { *; }
-keep class org.codehaus.stax2.** { *; }
-dontwarn com.ctc.wstx.**
-dontwarn org.codehaus.stax2.**
# StAX API surfaces referenced via reflection
-keep class javax.xml.stream.** { *; }
-dontwarn javax.xml.stream.**
-keep class * implements javax.xml.stream.XMLInputFactory
-keep class * implements javax.xml.stream.XMLOutputFactory
-keep class * implements javax.xml.stream.XMLEventFactory
-keepattributes Signature, *Annotation*, EnclosingMethod, InnerClasses
iOS setup #
No manual SDK installation is required for iOS — CocoaPods handles it. Digio SDK supports Xcode 14.0+, Swift 5.7+, and iOS 15.6+.
Info.plist permissions #
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) would like to access your camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like to access your photo.</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) would like to access your microphone to capture video.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) would like to access your location.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) would like to access your location.</string>
LSApplicationQueriesSchemes (needed for UPI apps) #
<key>LSApplicationQueriesSchemes</key>
<array>
<string>phonepe</string>
<string>gpay</string>
<string>paytmmp</string>
<string>bhim</string>
<string>upi</string>
<string>ppe</string>
</array>
Usage — Workflow mode #
Use this when your backend has created a Digio workflow request and given you a documentId and tokenId. The SDK will drive the entire journey on its own — this includes:
- KYC (identity verification, document capture, video KYC, geotagging)
- Mandate (eNACH / UPI mandate)
- UPI (collect, intent)
- RPD (Reverse Penny Drop — bank account verification)
- eSign — with authentication via OTP, Fingerprint (FP), Face, or Iris
Which flow actually runs is decided by the workflow you configured on the Digio backend — your Flutter code just starts it.
HashMap<String, String> additionalData = HashMap<String, String>();
additionalData["dg_disable_upi_collect_flow"] = "false"; // optional, for mandate
final digioConfig = DigioConfig();
digioConfig.theme.primaryColor = "#32a83a";
digioConfig.logo = "https://your_logo_url";
digioConfig.environment = Environment.SANDBOX;
digioConfig.serviceMode = ServiceMode.OTP; // FP / FACE / IRIS / OTP
digioConfig.global = false;
final kycWorkflow = KycWorkflow(digioConfig);
kycWorkflow.setGatewayEventListener((GatewayEvent? event) {
print("gateway event: $event");
});
final workflowResult = await kycWorkflow.start(
"KID23010416361850266BAKNKNORLP6W", // documentId
"abc@gmail.com", // identifier (customer)
"GWT230104163618520T2Y9IPUT2PBNC8", // tokenId
additionalData,
);
print('workflowResult: $workflowResult');
Usage — Stateless mode #
Stateless mode gives a pure native SDK experience. Your app tells the SDK which task to run (currently only Selfie) and passes a JWT token issued by your backend. That JWT carries the Digio credentials the SDK needs — clientSecret, transactionId, templateName, customerIdentifier, expirySeconds — so your app never has to handle them directly. The SDK decodes the token and runs the task natively.
Supported features today: SELFIE (Android + iOS).
How the JWT works #
- Your backend calls Digio to prepare a task (selfie capture) and gets back the credentials.
- Your backend wraps those credentials into a short-lived JWT and hands it to your Flutter app.
- Your app passes that JWT + the feature list into
startStateless(...). - The SDK opens the native screen, runs the task, and returns a result — no further backend calls from your Flutter code.
Example — Selfie #
import 'package:kyc_workflow/kyc_workflow.dart';
import 'package:kyc_workflow/environment.dart';
import 'package:kyc_workflow/digio_config.dart';
import 'package:kyc_workflow/stateless/stateless_config.dart';
import 'package:kyc_workflow/stateless/stateless_feature.dart';
import 'package:kyc_workflow/stateless/selfie_config.dart';
final config = StatelessConfig(
environment: Environment.SANDBOX,
clientId: 'YOUR_CLIENT_ID',
token: 'JWT_FROM_YOUR_BACKEND',
features: [StatelessFeature.SELFIE],
selfieConfig: SelfieConfig(
autoCapture: false,
showInstruction: false,
hasGeotagging: false,
logoUrl: 'https://your_logo_url',
),
);
final kyc = KycWorkflow(DigioConfig());
kyc.setStatelessEventListener((event) {
print('stateless event: screen=${event.screen}, action=${event.action}');
});
final response = await kyc.startStateless(config);
print('status: ${response.status}');
print('results: ${response.results}');
print('error: ${response.error}');
StatelessConfig fields #
| Field | Required | Description |
|---|---|---|
environment |
Yes (default PRODUCTION) |
Environment.SANDBOX for testing, Environment.PRODUCTION for live. |
clientId |
Yes | Your Digio client ID. |
token |
Yes | The JWT issued by your backend. Encodes clientSecret, transactionId, templateName, customerIdentifier, expirySeconds. |
features |
Yes | List of tasks to run. Currently: SELFIE. |
selfieConfig |
Only if SELFIE is in features |
See below. |
SelfieConfig fields #
| Field | Default | Description |
|---|---|---|
autoCapture |
false |
Automatically capture when the face is aligned. |
showInstruction |
true |
Show the instructions screen before the camera opens. |
hasGeotagging |
false |
Attach the user's location to the capture. |
logoUrl |
null |
Your logo shown on the SDK screens. |
StatelessResponse #
| Field | Description |
|---|---|
status |
FlowStatus.SUCCESS, FAILED, or CANCELLED. |
results |
List of SdkTaskResponse — one per feature you requested. |
error |
Human-readable error string, if any. |
Response codes #
The code field in the workflow response is the single source of truth — do not parse the message string.
| Field | Example | Description |
|---|---|---|
documentId |
DID22040413040490937VNTC6LAP8KWD |
Request ID passed by the parent app. |
message |
Signing Success |
Human-readable status. May be Signing Failure, Failure (SDK crash), or Webpage could not be loaded (network issue after 3 retries). |
code |
1001 |
Machine-readable status — see codes below. |
screen |
document_preview |
Last screen the user was on. |
npciTxnId |
— | NPCI transaction ID (UPI flows). |
stacktrace |
— | Crash log, present only on SDK crash. |
Response code constants:
| Constant | Value |
|---|---|
DigioConstants.RESPONSE_CODE_SUCCESS |
1001 |
RESPONSE_CODE_CANCEL |
-1000 |
RESPONSE_CODE_FAIL |
1002 |
RESPONSE_CODE_WEB_VIEW_CRASH |
1003 |
RESPONSE_CODE_SDK_CRASH |
1004 |