# Blusalt Full Identity Verification Flutter Plugin

A Flutter plugin for Blusalt Full Identity Verification SDK that supports both Android and iOS platforms.

## Features

- **Full Identity SDK**: Complete identity verification with document scanning and liveness check
- **Document Absent with Custom Selector**: Identity verification where user selects document type from a list
- **Document Absent with ID Number**: Identity verification by providing document type and number directly
- **Cross-platform**: Works on both Android (API 16+) and iOS (11.0+)
- **Native Integration**: Uses native Blusalt SDKs for optimal performance

## Getting Started

### Prerequisites

#### Android
- Min SDK: 16
- Compile SDK: 31
- Kotlin: 1.7.10+

#### iOS
- iOS 14.0+
- Swift 5.0+
- Xcode 13.0+

### Installation

Add this to your package's `pubspec.yaml` file:

```yaml
dependencies:
  full_identity_verification: ^lastVersion 
```

### Setup

#### Android Setup

#### Step 1

Create a [github.properties] file in root of android folder and put below into content.
Replace values with your github credentials from github and make sure to grant necessary permissions
especially for github packages

```
USERNAME_GITHUB=SampleUsername
TOKEN_GITHUB=SampleClassicToken
```

#### Step 2

Add below to project level gradle file `/android/build.gradle`

```gradle
buildscript {
    ext.kotlin_version = '1.9.+'
    ...

    dependencies {
        classpath 'com.android.tools.build:gradle:7.3.+'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    def githubPropertiesFile = rootProject.file("github.properties")
    def githubProperties = new Properties()
    githubProperties.load(new FileInputStream(githubPropertiesFile))


    repositories {

        maven {
            name "GitHubPackages"
            url 'https://maven.pkg.github.com/Blusalt-FS/Liveness-Only-Android-Package'

            credentials {
                username githubProperties['USERNAME_GITHUB']
                password githubProperties['TOKEN_GITHUB']
            }
        }

        maven {
            name "GitHubPackages"
            url 'https://maven.pkg.github.com/Blusalt-FS/Blusalt_Document_Verification-Android-Package'

            credentials {
                username githubProperties['USERNAME_GITHUB']
                password githubProperties['TOKEN_GITHUB']
            }
        }

        maven {
            name "GitHubPackages"
            url 'https://maven.pkg.github.com/Blusalt-FS/Full-Identity-SDK-Android-Package'

            credentials {
                username githubProperties['USERNAME_GITHUB']
                password githubProperties['TOKEN_GITHUB']
            }
        }
    }
}
```

#### Step 3

Change the minimum Android sdk version to 24 (or higher) in your `/android/app/build.gradle` file.

```

android {
    ...
    defaultConfig {
      ...
      minSdkVersion 24
    }
    ...
}
```

#### Step 4

If you're experiencing crashes or timeouts.
Add below to your progaurd.pro file if using progaurd or minify is enabled
`/android/app/proguard-rules.pro`

```proguard
-keep public class com.megvii.**{*;}
-keep class net.blusalt.liveness_native.** { *; }
-keep class net.blusalt.identityverify.** { *; }
-keep class net.blusalt.fullidentity.** { *; }
```

Enable proguard in `/android/app/build.gradle` file like below.

```

android {
    
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    ...
}
```

## Note on Android:

If you are getting an error on android which says "Unauthorized" when gradle is downloading or
building, generate a new
github token that have access to clone, read and write to repo, access github packages. If you don't
know which to tick, tick all boxes. Cheers

#### iOS Setup

The plugin includes the necessary frameworks. Just run:
```bash
cd ios && pod install
```

```
Minimum iOS Deployment = 14
```

Add one row to the ios/Runner/Info.plist:

with the key Privacy - Camera Usage Description and a usage description.

```
<key>NSCameraUsageDescription</key>
<string>your usage description here</string>
```

## 📱 Note on iOS

1. **Physical Device Requirement**  
   This liveness feature **only works on a physical iOS device**. iOS Simulators are **not supported** and may
   not reflect actual behavior.

2. **Ensure `FullIdentityFramework.xcframework` is Linked**  
   In your Xcode workspace:
  - Navigate to `Pods` > `FullIdentityFramework.xcframework`
  - Confirm that it is properly linked and included in your app’s build target.

3. **Add Required Frameworks to Xcode**  
   If you encounter errors related to `MediaPlayer` or `WebKit`, follow these steps:
  - Open your project in Xcode.
  - Under the `TARGETS` section, click on **Runner**.
  - Go to the **General** tab and scroll to **Frameworks, Libraries, and Embedded Content**.
  - Click the **`+`** button and add the following frameworks:
    - `AVFoundation.framework`
    - `CoreMedia.framework`
    - `CoreMotion.framework`
    - `MediaPlayer.framework`
    - `SystemConfiguration.framework`
    - `WebKit.framework`


## Usage

### Import the package

```dart
import 'package:full_identity_verification/full_identity_verification.dart';
import 'package:full_identity_verification/enums.dart';
```

### Initialize the plugin

```dart
final _fullIdentityPlugin = BlusaltFullIdentityVerification();
```

### 1. Full Identity SDK (Document Present)

Use this when users will physically scan their documents:

```dart
final result = await _fullIdentityPlugin.startFullIdentitySDK(
  apiKey: 'your_api_key',
  appName: 'your_app_name',
  clientId: 'your_client_id',
  isDev: true, // Set to false for production
  reference: 'unique_reference',
  webhookUrl: 'https://your-webhook-url.com',
  documentTypeList: [
    DocumentType.bvn,
    DocumentType.nin,
    DocumentType.passport,
    DocumentType.driverLicense,
    DocumentType.pvc,
  ],
  timeoutDurationInSec: 120,
);

if (result?.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
  // Success - Access data
  final documentData = result?.fullIdentitySuccessData?.extractedDocumentData;
  final livenessData = result?.fullIdentitySuccessData?.livenessSuccessData;
  
  print('First Name: ${documentData?.firstName ?? documentData?.first_name}');
  print('Last Name: ${documentData?.lastName ?? documentData?.last_name}');
  print('Document Number: ${documentData?.documentNumber ?? documentData?.document_number}');
  print('Liveness Passed: ${livenessData?.isPassProcedureValidation}');
} else {
  // Handle error
  print('Error: ${result?.message}');
}
```

### 2. Document Absent with Custom Selector

Use this when document is not available but user can select document type:

```dart
final result = await _fullIdentityPlugin.startDocAbsentWithCustomSelector(
  apiKey: 'your_api_key',
  appName: 'your_app_name',
  clientId: 'your_client_id',
  isDev: true,
  documentTypeList: [
    DocumentType.bvn,
    DocumentType.nin,
  ],
  reference: 'unique_reference',
  webhookUrl: 'https://your-webhook-url.com',
  livenessFacialComparisonType: LivenessFacialComparisonType.motional,
  thresholdInPercent: 85.0, // Optional: 0-100, higher = stricter
  timeoutDurationInSec: 120,
);

// Handle result same as above
```

### 3. Document Absent with ID Number

Use this when you already have the document type and number:

```dart
final result = await _fullIdentityPlugin.startDocAbsentWithIdNumber(
  apiKey: 'your_api_key',
  appName: 'your_app_name',
  clientId: 'your_client_id',
  isDev: true,
  documentType: DocumentType.bvn,
  documentNumber: '12345678901',
  reference: 'unique_reference',
  webhookUrl: 'https://your-webhook-url.com',
  livenessFacialComparisonType: LivenessFacialComparisonType.motional,
  thresholdInPercent: 85.0,
  timeoutDurationInSec: 120,
);

// Handle result same as above
```

## Models

### BlusaltFullIdentityResultResponse

Main response model containing:
- `blusaltFullIdentityProcess`: Status (completed/notImplemented)
- `exception`: Exception if any error occurred
- `code`: Error code if applicable
- `message`: Error message if applicable
- `reference`: Your reference string
- `fullIdentitySuccessData`: Success data containing document and liveness info

### FullIdentitySuccessData

Success data containing:
- `extractedDocumentData`: Document information
- `livenessSuccessData`: Liveness check results

### ExtractedDocumentData

Document information:
- `firstName`: First name from document
- `lastName`: Last name from document
- `middleName`: Middle name from document
- `phoneNumber`: Phone number if available
- `email`: Email if available
- `dateOfBirth`: Date of birth
- `gender`: Gender
- `documentNumber`: Document ID number
- `documentType`: Type of document
- `imageByte`: Base64 encoded image from document

### LivenessSuccessData

Liveness check results:
- `isPassProcedureValidation`: Overall validation status
- `isPassFaceComparison`: Face comparison result (for doc-absent flows)
- `isPassFaceGenuiness`: Face genuineness result
- `livenessImage`: Base64 encoded liveness image
- `originalLivenessImage`: Base64 encoded original liveness image
- `originalImage`: Base64 encoded original image (for comparison)

## Enums

### BlusaltFullIdentityProcess
- `completed`: Process completed successfully
- `notImplemented`: Platform not supported or error occurred

### DocumentType
- `bvn`: Bank Verification Number
- `nin`: National Identification Number
- `passport`: International Passport
- `driverLicense`: Driver's License
- `pvc`: Permanent Voter's Card

### LivenessFacialComparisonType
- `motional`: User performs head movements (more secure)
- `still`: User takes a still photo (faster)

### DocumentAvailabilityType
- `present`: Physical document available for scanning
- `absent`: Document not physically available

## Parameters

### Common Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| apiKey | String | Yes | Your Blusalt API key |
| appName | String | Yes | Your application name |
| clientId | String | Yes | Your Blusalt client ID |
| isDev | bool | Yes | Use development environment |
| reference | String | No | Your unique reference for this transaction |
| webhookUrl | String | No | URL to receive webhook notifications |
| timeoutDurationInSec | int | No | Network timeout in seconds (default: 120) |

### Full Identity SDK Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| documentTypeList | List\<DocumentType> | No | Filter available document types |

### Document Absent Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| documentTypeList | List\<DocumentType> | Yes* | Available document types (*custom selector only) |
| documentType | DocumentType | Yes* | Specific document type (*ID number only) |
| documentNumber | String | Yes* | Document ID number (*ID number only) |
| livenessFacialComparisonType | LivenessFacialComparisonType | No | Liveness check type (default: motional) |
| thresholdInPercent | double | No | Face comparison threshold 0-100 (default: SDK default 90-94) |

## Error Handling

```dart
try {
  final result = await _fullIdentityPlugin.startFullIdentitySDK(...);
  
  if (result != null) {
    if (result.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
      // Success
    } else {
      // Failed
      print('Error Code: ${result.code}');
      print('Error Message: ${result.message}');
    }
  }
} catch (e) {
  print('Exception: $e');
}
```

## Platform-Specific Notes

### Android
- The plugin uses the native Blusalt Full Identity SDK for Android
- Requires camera, storage, and microphone permissions
- Minimum Android version: API 16 (Android 4.1)

### iOS
- The plugin uses the native Blusalt Full Identity Framework for iOS
- Requires camera and photo library permissions
- Minimum iOS version: 11.0

## Permissions

### Android (AndroidManifest.xml)
The plugin automatically requests these permissions:
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
```

### iOS (Info.plist)
Add these keys to your `Info.plist`:
```xml
<key>NSCameraUsageDescription</key>
<string>We need camera access for identity verification</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access for identity verification</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access for liveness detection</string>
```

## Example

See the [example](example/) directory for a complete sample app demonstrating all three SDK modes.

## Support

For issues, questions, or contributions, please contact Blusalt support or visit the Blusalt developer portal.

## License

This plugin is licensed under [LICENSE](LICENSE).
