prism_ekyc

A Flutter plugin for eKYC (electronic Know Your Customer) of Japanese IC cards. Reads Residence Cards (在留カード) via NFC, captures the card backside via camera, sends images to the Prism eKYC backend for OCR and face matching, then presents an auto-filled registration form.

Platform support: Android · iOS


Features

Feature Android iOS
NFC read (Residence Card) ✅ libjeid ✅ libjeid.xcframework
Card front image (PNG)
Face photo from chip ✅ JPEG ✅ JPEG2000
Chip address / permissions
Card backside camera capture
OCR (name, DOB, visa status, …) ✅ server-side ✅ server-side
Face matching (selfie vs chip photo) ✅ server-side ✅ server-side
Liveness challenge (blink / smile / turn) ✅ on-device ✅ on-device

Installation

Add to your pubspec.yaml:

dependencies:
  prism_ekyc: 0.3.0

Getting an API Key

OCR and face matching are powered by the Prism eKYC backend, hosted by RubiLabs. To obtain a baseUrl and apiKey for your integration, contact:

RubiLabsdev@rubilabs.io https://rubilabs.io

You will receive:

  • A hosted backend URL (baseUrl)
  • An x-api-key value (apiKey)

Both are passed directly into PrismEkycConfig — no additional setup needed on the client side.


Android Setup

1. Maven repository

Add the libjeid repository to your root android/build.gradle (or build.gradle.kts):

// android/build.gradle.kts
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://cdn.osstech.co.jp/android") }
    }
}

2. App-level dependencies

In android/app/build.gradle.kts:

dependencies {
    // libjeid — NFC reading of Japanese IC cards
    implementation("jp.co.osstech:libjeid-free:20251031@aar")
    // BouncyCastle — required by libjeid for BAC/DES crypto
    implementation("org.bouncycastle:bcprov-lts8on:2.73.9")
    // ML Kit Japanese text recognition (kept for offline OCR fallback)
    implementation("com.google.mlkit:text-recognition-japanese:16.0.1")
}

3. Permissions

In android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

Set launchMode="singleTop" on your MainActivity:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    ...>

4. ProGuard rules

In android/app/proguard-rules.pro:

-keep class org.bouncycastle.** { *; }
-dontwarn org.bouncycastle.**
-keep class jp.co.osstech.libjeid.** { *; }
-dontwarn jp.co.osstech.libjeid.**

iOS Setup

1. NFC capability

In Xcode, enable the Near Field Communication Tag Reading capability for your app target.

2. Info.plist

<!-- ios/Runner/Info.plist -->
<key>NFCReaderUsageDescription</key>
<string>NFC is used to read your IC card for identity verification.</string>

<key>NSCameraUsageDescription</key>
<string>Camera is used to capture your ID card for identity verification.</string>

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>

3. Entitlements

Add to Runner.entitlements:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>

4. libjeid.xcframework

libjeid.xcframework is bundled inside the plugin at ios/Frameworks/libjeid.xcframework. No additional CocoaPod or manual linking is required — the plugin's podspec handles it.


Usage

import 'package:prism_ekyc/prism_ekyc.dart';

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (_) => PrismEkyc.screen(
      PrismEkycConfig(
        // API credentials — obtain from RubiLabs (dev@rubilabs.com)
        // Pass the server host only — no trailing slash, no /api suffix.
        baseUrl: 'https://your-server.com',
        apiKey: 'your-api-key-from-rubilabs',

        onComplete: (EkycResult result) {
          print('Name: ${result.formData.fullName}');
          print('Card #: ${result.formData.cardNumber}');
          print('NFC address: ${result.nfcData?.chipAddress}');
          print('Face verified: ${result.faceVerified}');
        },
      ),
    ),
  ),
);

PrismEkycConfig

Parameter Type Required Description
baseUrl String? Recommended Backend server URL — host only, no trailing slash, no /api (e.g. https://your-server.com). The /api prefix is added automatically. When null, falls back to on-device OCR with no face comparison.
apiKey String? Recommended x-api-key header value. Required when baseUrl is set. Obtain from RubiLabs.
onComplete void Function(EkycResult)? No Called with the final result when the user submits the form.
licenseKey String No libjeid commercial license key. Leave empty for the free version.
defaultLanguage PrismLanguage No Initial UI language (english or japanese). Default: english.
supportedCountries List<SupportedCountry> No Countries shown in the nationality picker.

EkycResult

Field Type Description
formData UserFormData User-entered form data
nfcData NfcScanResult? Chip-read data (address, images, etc.)
backsideImage Uint8List? Card backside JPEG bytes
faceVerified bool Whether face matched the chip photo
faceMatchConfidence double? 0.0–1.0 confidence (null if not run)

eKYC Screen Flow

Welcome → NationalitySelector → IdInstruction → NfcScan →
BacksideCapture → FaceVerification → RegistrationForm → Completion
  • NfcScan — reads the NFC chip; extracts card front image and face photo
  • BacksideCapture — camera captures card back → sends all images to backend for OCR
  • FaceVerification — on-device liveness (blink / smile / turn) → sends selfie to backend for face matching
  • RegistrationForm — auto-filled from OCR data; user confirms and submits

NFC Method Channel Reference

The plugin exposes com.rubilabs.prism_ekyc/nfc for direct use if needed:

const _nfc = MethodChannel('com.rubilabs.prism_ekyc/nfc');

// Check if NFC is available and enabled
final bool available = await _nfc.invokeMethod('isNfcAvailable');

// Read a Residence Card (blocks until card is physically tapped)
final Map result = await _nfc.invokeMethod('readResidenceCard', {
  'cardNumber': 'AA12345678BC',  // 12-character number printed on the card front
});
// Returned keys:
//   cardFrontImage  — "data:image/png;base64,..."
//   photo           — "data:image/jpeg;base64,..."  (Android) or jp2 (iOS)
//   address         — String
//   addressCode     — String
//   addressDate     — String
//   comprehensivePermission — String
//   individualPermission    — String
//   updateStatus    — String
//   rcCardType      — "1" (regular) or "2" (special permanent)
//   isValid         — bool (chip signature validation)

// Cancel an in-progress read (e.g. when the screen is popped)
await _nfc.invokeMethod('stopRead');

Plugin Architecture

Android

Class Role
PrismEkycPlugin Flutter plugin entry point; ActivityAware; registers NFC channel
AndroidNfcReader NFC via NfcAdapter.enableReaderMode + libjeid-free

iOS

Class Role
PrismEkycPlugin Flutter plugin entry point; registers NFC channel
NfcReader NFC via NFCTagReaderSession + libjeid.xcframework

Troubleshooting

Android: NoClassDefFoundError: org.bouncycastle.* Add -keep class org.bouncycastle.** { *; } to proguard-rules.pro.

Android: app crashes immediately after "Reading data" in NFC dialog R8 is stripping libjeid or BouncyCastle classes in release builds. Add both -keep rules listed in the setup section.

iOS: NFC session never starts

  • Ensure the NFC capability is enabled in Xcode
  • Ensure NFCReaderUsageDescription is in Info.plist
  • NFC only works on physical devices (not simulator)

iOS: card not detected / Invalid tag Hold the card flat against the back/top of the iPhone and do not move either until the session closes.

Face verification always fails Ensure baseUrl and apiKey are set correctly. Face-api.js model weights are bundled inside node_modules/@vladmandic/face-api/model/ and are available automatically after npm install — no separate download needed.


License

Proprietary — RubiLabs © 2026

Libraries

prism_ekyc