prism_ekyc 0.1.0
prism_ekyc: ^0.1.0 copied to clipboard
Prism eKYC – NFC + Camera + Face Verification Flutter package for Japanese ID documents.
prism_ekyc #
A Flutter package for end-to-end electronic KYC (eKYC) of Japanese IC identity cards. The package presents a self-contained UI flow, reads the card chip over NFC and returns a strongly-typed result.
Supported cards
| Card | Authentication input |
|---|---|
| Residence Card (在留カード) | Card number (e.g. UH73843905EA) |
| Driver's License (運転免許証) | PIN 1 + PIN 2 |
| My Number Card (マイナンバーカード) | 4-digit PIN |
How it works — end to end #
Welcome
└─ Nationality selector
└─ ID card instructions
└─ NFC scan ← card number / PIN entered here
│
├─ flutter_libjeid reads chip over NFC
│ • card front PNG image
│ • holder face photo
│ • chip-direct text fields (DL / My Number)
│ • runs on card front PNG
│ • extracts: name, nationality, DOB, gender,
│ address, visa status, dates
│
└─ NfcScanResult built (chip data takes priority; OCR fills gaps)
│
├─ Backside capture (camera — photo of card back)
├─ Face verification (liveness check + face match)
└─ Registration form (pre-filled, user confirms)
│
└─ onComplete(EkycResult)
NFC layer — flutter_libjeid #
Card type is auto-detected from the number the user types:
| Input format | Detected type | Scan call |
|---|---|---|
2 letters + 8 digits + 2 letters (e.g. UH73843905EA) |
Residence Card | scanResidentCard(cardNumber:) |
| 12 digits | Driver's License | scanDriverLicenseCard(pin1:, pin2:) |
| Anything else | My Number Card | scanMyNumberCard(pin:) |
The NFC scan screen shows PIN input fields automatically when the input matches a Driver's License or My Number format.
Form auto-fill #
Once NfcScanResult is built, UserFormData.fromNfc() pre-populates the
Registration Form screen. The user can review and edit every field before
submitting.
Platform requirements #
| Platform | Minimum | Notes |
|---|---|---|
| iOS | 13.0 | Physical device only — NFC unavailable on Simulator |
| Android | API 24 (7.0) | Requires NFC hardware |
Installation #
dependencies:
prism_ekyc:
path: ../prism_ekyc # local
# or git / pub.dev reference when published
iOS setup #
1 — NFC entitlement #
In Xcode: Signing & Capabilities → + Capability → Near Field Communication Tag Reading
This adds to your .entitlements file:
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
2 — Info.plist usage descriptions #
<key>NFCReaderUsageDescription</key>
<string>Used to read your ID card chip for identity verification.</string>
<key>NSCameraUsageDescription</key>
<string>Used to photograph your ID card and verify your face.</string>
3 — Podfile #
No extra pods are required. flutter_libjeid ships its own iOS implementation
and prism_ekyc links only Apple's Vision framework (for face comparison).
Android setup #
1 — AndroidManifest.xml #
The plugin manifest already declares NFC permission. Confirm your
android/app/src/main/AndroidManifest.xml has:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
Set required="false" if you want the app installable on non-NFC devices
(the scan step will return an error at runtime on those devices).
Your <activity> tag must use singleTop launch mode so NFC intents are
delivered to the running activity:
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
...>
2 — MainActivity #
No changes required. flutter_libjeid registers its own ActivityAware
plugin and handles foreground NFC dispatch internally.
Quick start #
import 'package:prism_ekyc/prism_ekyc.dart';
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PrismEkyc.screen(
PrismEkycConfig(
onComplete: (EkycResult result) {
print(result.formData.fullName);
print(result.nfcData?.dateOfBirth);
print(result.nfcData?.nationality);
},
),
),
),
);
Configuration — PrismEkycConfig #
PrismEkycConfig({
void Function(EkycResult)? onComplete,
PrismLanguage defaultLanguage = PrismLanguage.english,
List<PrismLanguage> availableLanguages = const [...],
List<SupportedCountry> supportedCountries = const [...],
bool debugBypassNfc = false,
})
| Parameter | Type | Description |
|---|---|---|
onComplete |
void Function(EkycResult)? |
Called with the full result when the user submits the form. If omitted the package silently pops. |
defaultLanguage |
PrismLanguage |
Initial UI language. Defaults to english. |
availableLanguages |
List<PrismLanguage> |
Languages shown in the switcher. Defaults to both. |
supportedCountries |
List<SupportedCountry> |
Countries in the nationality picker. Defaults to all 9. |
debugBypassNfc |
bool |
Skip NFC and inject mock data. Useful during UI development. |
Available languages #
PrismLanguage.english
PrismLanguage.japanese
Supported countries #
SupportedCountry.brazil SupportedCountry.indonesia
SupportedCountry.philippines SupportedCountry.vietnam
SupportedCountry.southKorea SupportedCountry.china
SupportedCountry.malaysia SupportedCountry.singapore
SupportedCountry.thailand
Result types #
EkycResult #
class EkycResult {
final NfcScanResult? nfcData; // chip + OCR extracted data
final Uint8List? backsideImage; // card back photo (JPEG bytes)
final bool faceVerified; // liveness challenges passed
final double? faceMatchConfidence; // 0.0–1.0, null if no face photo
final UserFormData formData; // confirmed form values
}
NfcScanResult — chip + OCR extracted fields #
| Field | Source | Notes |
|---|---|---|
cardNumber |
User input | Uppercased |
cardType |
Detected | "1" RC · "dl" DL · "mn" My Number |
name |
OCR (RC) / chip (DL, MN) | Romaji, ALL CAPS |
nationality |
OCR (RC) / chip (DL, MN) | Katakana or English |
dateOfBirth |
OCR (RC) / chip (DL, MN) | yyyy-MM-dd |
gender |
OCR (RC) / chip (MN) | "male" · "female" · "" |
address |
Chip (all) | Japanese address string |
status |
OCR (RC) | Visa / residence status in Japanese |
permissionToLand |
OCR (RC) | yyyy-MM-dd or "" |
issuanceDate |
OCR (RC) / chip (DL) | yyyy-MM-dd or "" |
expiryDate |
OCR (RC) / chip (DL, MN) | yyyy-MM-dd or "" |
chipAddress |
Chip (RC) | Address directly from chip data files |
addressCode |
Chip (RC) | JIS address code |
comprehensivePermission |
Chip (RC) | Activity permission string |
individualPermission |
Chip (RC) | Individual permission string |
updateStatus |
Chip (RC) | Update status code |
cardFrontPng |
Chip (all) | PNG image of card front, used for OCR |
facePhotoJpeg2000 |
Chip (all) | Holder face photo for face matching |
RC = Residence Card fields are populated via OCR because the free-tier libjeid API does not expose personal text fields (name, DOB, nationality) directly from the chip. The card front image is read from the chip and OCR is run on it.
UserFormData — confirmed form values #
| Field | Type |
|---|---|
fullName |
String |
firstNameRomaji |
String |
middleNameRomaji |
String |
lastNameRomaji |
String |
address |
String |
dateOfBirth |
DateTime? |
nationality |
String |
gender |
FormGender — male · female · notSpecified |
cardNumber |
String |
expiryDate |
DateTime? |
visaStatus |
String |
permissionToLand |
DateTime? |
permissionToStay |
DateTime? |
isAutoFilled |
bool — true when pre-filled from NFC + OCR |
Face match confidence #
faceMatchConfidence is derived from the feature-print distance between the
chip face photo and the live selfie captured during face verification:
| Value | Meaning |
|---|---|
>= 0.7 |
Strong match |
0.5 – 0.69 |
Match |
null |
No chip face photo, or comparison was skipped |
faceVerified is always true when onComplete fires — a mismatch blocks
progression and the user must retake the selfie.
Debug mode #
Set debugBypassNfc: true to skip the NFC step entirely and inject a mock
NfcScanResult. This is useful for testing the UI flow and form auto-fill
without a physical card or NFC device.
PrismEkyc.screen(
PrismEkycConfig(
debugBypassNfc: true,
onComplete: (result) { ... },
),
)
Full example #
import 'package:flutter/material.dart';
import 'package:prism_ekyc/prism_ekyc.dart';
class VerifyPage extends StatelessWidget {
const VerifyPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _start(context),
child: const Text('Start Identity Verification'),
),
),
);
}
void _start(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PrismEkyc.screen(
PrismEkycConfig(
defaultLanguage: PrismLanguage.english,
availableLanguages: const [
PrismLanguage.english,
PrismLanguage.japanese,
],
supportedCountries: const [
SupportedCountry.vietnam,
SupportedCountry.philippines,
SupportedCountry.indonesia,
],
onComplete: (EkycResult result) {
_submit(result);
},
),
),
),
);
}
Future<void> _submit(EkycResult result) async {
final payload = {
'name': result.formData.fullName,
'cardNumber': result.formData.cardNumber,
'dob': result.nfcData?.dateOfBirth,
'nationality': result.nfcData?.nationality,
'visaStatus': result.nfcData?.status,
'expiryDate': result.nfcData?.expiryDate,
'faceConfidence': result.faceMatchConfidence,
// Images — send as base64 or multipart
'facePhoto': result.nfcData?.facePhotoJpeg2000,
'cardFront': result.nfcData?.cardFrontPng,
'cardBack': result.backsideImage,
};
// ... POST to your API
}
}
Error handling #
All errors during NFC, OCR, camera, and face verification are handled internally. The user gets a descriptive message and up to 3 retry attempts per step. After exhausting retries a Return to Start button appears.
onComplete is only invoked on full success. If the user backs out or quits
at any step, onComplete is never called — treat its absence as abandonment.
Package dependencies #
| Package | Purpose |
|---|---|
flutter_libjeid |
NFC reading of Japanese IC cards (free tier) |
camera |
Backside card capture + face selfie |
google_mlkit_text_recognition |
OCR on card front image (Japanese script) |
google_mlkit_face_detection |
Face detection for liveness + face match |
Known limitations #
- Residence Card personal fields via OCR: OCR accuracy depends on card condition, lighting during NFC capture, and ML Kit model quality on the device. Fields that fail to parse are returned as empty strings; the user can fill them manually in the Registration Form.
- iOS Simulator: NFC is unavailable. Use
debugBypassNfc: trueduring simulator development. - Android NFC sensitivity: Some Android devices require the card to be held
very still during the ~3-second read. A
TAG_LOSTerror triggers a retry.
License #
Proprietary — RubiLabs. See LICENSE for details.