Flutter SDK ">=2.12.0 <3.0.0"
iOS 11 and above
Xcode "15.2"
Add to pubspec.yaml
file under dependencies
group:
trust_vision_plugin:
git:
# Replace the below with your specific info
url: to_be_replaced
ref: to_be_replaced
$ flutter pub get
# Add below lines to the end of podfile
post_install do |installer|
installer.pods_project.targets.each do |target|
// you can add more modules which have the error "Undefined symbol" into the list
if ['CocoaLumberjack', 'TensorFlowLiteC', 'TensorFlowLiteSwift', 'PromiseKit', 'CryptoSwift'].include? "#{target}"
target.build_configurations.each do |config|
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
end
end
end
end
Set Build Libraries for Distribution in Build Settings to No
$ pod install
Add to root-level build.gradle file (host app):
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
allprojects {
repositories {
google()
mavenCentral()
/*
Replace the below with your path to `trust_vision_plugin` Android repo which downloaded by `flutter pub get`.
*/
maven { url "$flutterRoot/.pub-cache/git/replace_by_git_repo_name-replace_by_commit_hash/android/repo" }
/*
For example:
maven { url "$flutterRoot/.pub-cache/git/tv_flutter_sdk_xyz-1a5eb61ccb44ffafa4d3557f4d9d8087bc1a4666/android/repo" }
*/
}
}
Add to app/build.gradle:
aaptOptions {
noCompress "tflite"
noCompress "lite"
}
import 'package:trust_vision_plugin/enums.dart';
import 'package:trust_vision_plugin/trust_vision_plugin.dart';
Before using any TV SDK functions, it needs to be successfully initialized once.
Init UIOnly mode
void initUiOnlyModeSDK() async {
try {
var initResult = await TrustVisionPlugin.instance.initialize(
jsonConfigurationByServer: jsonConfigurationByServer,
languageCode: 'vi',
);
} catch (exception) {
print("Handle exception: $exception");
}
}
where:
String
.
It's the setting specialized for each client from TS server.
It's the response json string get by API https://ekyc.trustingsocial.com/api-reference/customer-api/#get-client-settings.
When it's null or unmatched with the expected type then the default setting in the SDK will be used.String
the code of the language that will show and sound to user. E.g Vietnamese (vi), English (en).Init Full mode
void initFullModeSDK() async {
try {
var initResult = await TrustVisionPlugin.instance.initialize(
accessKeyId: "your_key",
accessKeySecret: 'your_key',
endpoint: 'your_endpoint',
xLenderRequestId: "your_id",
languageCode: 'vi',
);
} catch (exception) {
print("Handle exception: $exception");
}
}
Change the following parameters to suit your needs.
void _captureIdCard() async {
try {
TVCardType cardType = TVCardType(
id: "vn.national_id",
name: "CMND cũ / CMND mới / CCCD / Hộ chiếu",
hasBackSide: true,
orientation: TVCardOrientation.HORIZONTAL,
);
CardSide cardSide = CardSide.front; // CardSide.back
Map<String, dynamic> idCardConfig = {
"cardTypes": [cardType.toMap()],
"cardSide": cardSide.name,
"isEnableSound": true,
"isEnableSanityCheck": true,
"isReadBothSide": false,
"skipConfirmScreen": true,
"isEnableDetectingIdCardTampering": true,
"isEnableScanNfc": false,
"isEnableScanQr": false,
};
// Start ID card capturing
TVDetectionResult? result = await TrustVisionPlugin.instance.captureIdCard(idCardConfig);
print("_captureIdCard result: $result");
} catch (exception) {
print("Handle exception: $exception");
}
}
Where:
TVCardType
CardSide
(back, front)bool
. enable/disable guiding soundbool
. Id card sanity checking should be enabled or notbool
. whether or not read both sides of the selected id card at one flowbool
. Whether or not TV SDK should skip its own confirmation screen.bool
. enable/disable ID card tampering detectionbool
. enable/disable scan NFC flowbool
. enable/disable scan QRCode flowTVDetectionResult? result = await TrustVisionPlugin.instance.captureIdCard(idCardConfig);
var idFrontImage = result?.idFrontImage;
// TODO call your api to upload image
var idBackImage = result?.idBackImage;
// TODO call your api to upload image
var frontIdQr = result?.frontIdQr;
// TODO call your api to upload image
Change the following parameters to suit your needs.
void _captureSelfie() async {
try {
LivenessMode livenessMode = LivenessMode.flash;
SelfieCameraMode selfieCameraMode = SelfieCameraMode.back; // or SelfieCameraMode.front, SelfieCameraMode.back
final selfieConfig = {
"cameraOption": selfieCameraMode.name,
"livenessMode": livenessMode.name,
"isEnableSound": true,
"isEnableSanityCheck": false,
"isEnableVerifyLiveness": false,
"skipConfirmScreen": true
};
// Start Selfie capturing
TVDetectionResult? result = await TrustVisionPlugin.instance.captureSelfie(
selfieConfig,
onNewFrameBatch: (TvFrameBatch frameBatch) {
// Handle frame batch
// Refer: https://ekyc.trustingsocial.com/sdks/Android-SDK#303-handle-onnewframebatch-callback
},
);
print("_captureSelfie result: $result");
} catch (exception) {
print("Handle exception: $exception");
}
}
Where:
Function(TvFrameBatch)?
. This callback function can be called multiple times during the capturing process to return frames.SelfieCameraMode
. Camera sidebool
. enable/disable guiding soundbool
. Selfie sanity checking should be enabled or notbool
. Whether or not TV SDK should skip its own confirmation screen.LivenessMode
. Liveness modeenum LivenessMode {
active,
passive,
none,
flash,
flash_edge,
flash_advanced,
flash_8,
flash_16,
flash_32
}
TVDetectionResult? result = await TrustVisionPlugin.instance.captureSelfie(selfieConfig);
Please don't remove any item from result.selfieImages
list
var selfieImages = result?.selfieImages;
var frontalImages = <TVImageClass?>[];
selfieImages?.forEach((item) {
if(item.frontalImage?.rawImageBase64 != null) {
// Get frontal image
frontalImages.add(item.frontalImage);
}
});
// TODO call your api to upload image
var selfieImages = result?.selfieImages;
var finalFrontalImage = selfieImages?.firstWhere((item) => item.gestureType?.toLowerCase() == 'frontal').frontalImage;
var selfieImages = result?.selfieImages;
var gestureImages = <TVImageClass?>[];
selfieImages?.forEach((item) {
if(item.gestureImage?.rawImageBase64 != null) {
// Get gesture image
gestureImages.add(item.gestureImage);
}
});
// TODO call your api to upload image
If you are using liveness modes such as flash, flash_edge, flash_advanced, flash_8, flash_16, or flash_32, the images should be handled as follows:
frontalImages[0]
: Image captured during the close step.frontalImages[1]
: Image captured during the far step.<key>NFCReaderUsageDescription</key>
<string>This app would like to use NFC to scan CCCD chip</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
<string>A0000002472001</string>
<string>00000000000000</string>
</array>
Change the following parameters to suit your needs.
void _readNfc() async {
try {
final nfcConfig = {
nfcCode: "12 digits of CCCD number",
nfcSod: sod,
cardIssueDate: issueDate,
nfcCacheFields: nfcCacheFields,
isRequestReadImageNfc: true,
isRequestCloneDetectionNfc: true,
isRequestIntegrityCheckNfc: true,
nfcMaxRetries: 5,
isEnableCheckNfcData: true,
isEnableVerifyNfc: true,
dateOfBirth: "dd/MM/yyyy",
dateOfExpiry : "dd/MM/yyyy"
};
// Start NFC reader
TVDetectionResult? result = await TrustVisionPlugin.instance.readNfc(nfcConfig);
print("_readNfc result: $result");
} catch (exception) {
print("Handle exception: $exception");
}
}
Where:
String
12 digits of CCCD numberString
(optional) is the hash of SODString
(optional) is the issue date of ID card (dd/MM/yyyy)List<String>
(optional) is the list of fields that was cached from previous scanningBool
. Read image in the chip when scan nfc or notBool
. Check clone of the chip when scanning nfc or notBool
. Check integrity of the chip when scanning nfc or notInt
. The maximum number of times the SDK retries an NFC scanning before giving upBool
. Enable check NFC data or not. If it's true then the SDK will call
the API to get sod and cached fields of the NFC data.Bool
. Enable verify NFC or not. If it's true then the SDK will call
the API to verify the NFC data.String?
(required for iOS) is the date of birth (dd/MM/yyyy)String?
(required for iOS) is the expired of ID card (dd/MM/yyyy). For cards with unlimited expiration, please input: 31/12/9999 TVDetectionResult? result = await TrustVisionPlugin.instance.readNfc(nfcConfig);
bool? isSupported = await TrustVisionPlugin.instance.checkNfcSupport();
Change the following parameters to suit your needs.
void _scanQrCode () async {
try {
var selectedCardType = TVCardType(
id: "vn.national_id",
name: "CMND cũ / CMND mới / CCCD / Hộ chiếu",
hasBackSide: true,
orientation: TVCardOrientation.HORIZONTAL,
frontQr: TVQrSdk(
exist: true,
type: TVQRType.qrCode,
widthHeightRatio: 1,
)
);
final config = {
"cardType": selectedCardType.toMap(),
"cardSide": CardSide.front.name,
"skipConfirmScreen": true,
"isEnableUploadFrames" : false,
"isEnableUploadImages" : false
};
TVDetectionResult? result = await TrustVisionPlugin.instance.scanQrCode(config);
} catch (exception) {
showSnackbar("Error: ${exception.toString()}");
}
}
Where:
TVCardType
. Type of cardTVQrSdk
. Qr settingbool
. Whether or not TV SDK should skip its own confirmation screen.Bool
. Enable upload video frames or not. If it's false then the SDK won't call the API to upload
the frames and the APIs that need the video frames will be skipped or called with empty frames data.Bool
. Enable upload images or not. If it's false then the SDK won't call the API to upload
the images and the APIs that need the image will be skipped.Bool
. Has QR code or not.TVQRType
: Type of code: qrCode
, barCode
Float
: Card ratio TVDetectionResult? result = await TrustVisionPlugin.instance.scanQrCode(config);
If the APIs is called by the SDK, please skip this step.
if result.frontIdQr.isRequired
is true then result.frontIdQr.images
array should be non-empty. Otherwise, clients should be warned to re-scan QR code.
QR images will be uploaded with this api: https://ekyc.trustingsocial.com/api-reference/customer-api/#upload-image
var frontQrImage = result?.frontIdQr?.images[i];
*The same logic will be applied to result.backIdQr
result: .TVDetectionResult
:
TVCardType
. Card typeTVActionMode
. Action ModeTVCardInfoResult
. Card information resultTVLivenessResult
. Liveness resultTVSanityResult
. Id card sanity resultTVSanityResult
. Selfie sanity resultTVSanityResult
. ID tampering result[SelfieImage]
. List of selfie image objectsTVImageClass
. Id front image objectTVImageClass
. Id back image objectTVCardQr
. Card's QR Code image.TVNfcInfoResult
. Card NFC information[Uint8List]
. Liveness video data[String]
. Liveness valid frame batch idSelfieImage:
String
. UP | DOWN | LEFT | RIGHT | FRONTAL
TVImageClass
. Frontal image objectTVImageClass
. Gesture image objectTVImageClass:
String
. Base64 string of image dataString
. Encrypted base64 string of image dataString
. Image idMap<String, dynamic>
. Metadata of the imageTVCardInfoResult:
[Info]
array of InfoString
. Id of requestInfo:
String
. Name of field example name, address...String
. The value of each fieldTVLivenessResult:
float
. The score from 0 to 1bool
. Selfie image is live or notString
. Id of requestTVSanityResult:
bool
. Sanity is good or notfloat
. The score from 0 to 1String
. Id of requestString
. Error message[Detail]
. List of result detailDetail:
String
. Name of fieldString
. The score from 0 to 1String
. Verdict of fieldTVCardQr:
[TVImageClass]
. List of imagebool
.if isRequired
is true then images
array should be non-empty. Otherwise, clients should be warned to re-capture id card photos.
TVNfcInfoResult:
String
. Id of requestString
. Com dataString
. Sod dataString
. Dg1 dataString
. Dg2 dataString
. Dg13 dataString
. Dg14 dataString
. Dg15 data[Info]
. array of InfoTVNfcVerificationStatus
. Verification result dataTVNfcVerificationResult:
String
. Result's verdictTVNfcVerificationStatus
. Clone statusTVNfcVerificationStatus
. Integrity statusTVNfcVerificationStatus
. BCA status (BCA is Bộ Công An)TVNfcVerificationStatus:
TVNfcVerificationStatusVerdict | UNKNOWN,NOT_PRESENT,NOT_CHECKED,ALERT,GOOD,ERROR
. Result's verdictTVDetectionError
. Error detailTvFrameBatch:
String?
. Frame's id[TvFrameClass]
. Batch of framed to be pushedMap<String, dynamic>
. Batch metadata to push[String]
. For debugging purpose onlyTvFrameClass:
int?
. Index of this frame in the list of recorded framesString?
. The data of the frame as a base64 StringString
. Label of this frameTVDetectionError:
int
. Error codeString
. Detail for error codeString
. Error description String? error = result?.idSanityResult?.error;
if (error != null) {
switch (result.idSanityResult.error) {
case 'image_too_blur':
// Image is too blurry
break;
case 'image_too_dark':
// Image is too dark
break;
case 'image_too_bright':
// Image is too bright
break;
case 'image_has_hole':
// Image has holes
break;
case 'image_has_cut':
// Images has been cut
break;
case 'image_has_hole_and_cut':
// Images has holes and has been cut
break;
}
}
String? error = result?.selfieSanityResult?.error;
if (error != null) {
switch (selfieSanityResult["error"]) {
case 'image_too_blur':
// Image is too blurry
break;
case 'image_too_dark':
// Image is too dark
break;
case 'image_too_bright':
// Image is too bright
break;
case 'not_white_background':
// The background is not white enough
break;
case 'not_qualified':
// The face is not qualified, could be occluded, covered or having something unusal
break;
case 'image_has_multiple_faces':
// Image has multiple faces
break;
case 'image_has_no_faces':
// Image does not have any face
break;
case 'right':
// Face is looking to the right
break;
case 'left':
// Face is looking to the left
break;
case 'open_eye,closed_eye':
// Right eye is closed
break;
case 'closed_eye,open_eye':
// Left eye is closed
break;
case 'open_eye,sunglasses':
// Sunglass covers the right eye
break;
case 'sunglasses,open_eye':
// Sunglass covers the left eye
break;
case 'closed_eye,closed_eye':
// Both eyes are closed
break;
case 'closed_eye,sunglasses':
// Left eye is closed, sunglass covers the right eye
break;
case 'sunglasses,closed_eye':
// Sunglass covers the right eye, right eye is closed
break;
case 'sunglasses,sunglasses':
// Sunglasses cover both eyes
break;
}
}
switch (e.code) {
// connection errors
case 'timeout_error'
break;
// Network timeout. Poor internet connection.
case 'network_error':
// Network error. No internet connection
break;
// Id capturing
case 'incorrect_card_type':
// the input image is not same type with selected card
break;
case 'nocard_or_multicard_image':
// the input image is no card or multicard detected
break;
// Selfie capturing
case 'image_has_no_faces':
// face not detected in selfie image
break;
case 'image_has_multipe_faces':
// multiple faces are detected in selfie image
break;
// Common errors
case 'access_denied_exception':
break;
case 'invalid_parameter_exception':
break;
case 'rate_limit_exception':
break;
case 'internal_server_error':
break;
case: 'sdk_canceled':
break;
}