@finos_sdk/sdk-ekyc 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,7 +65,7 @@ dependencies {
65
65
  implementation 'com.facebook.react:react-android'
66
66
 
67
67
  // Finos eKYC SDK dependencies from GitHub Packages Maven repository
68
- def sdkVersion = "1.3.3.1"
68
+ def sdkVersion = "1.3.5.1"
69
69
  implementation("finos.sdk.ekyc:ekyc:$sdkVersion")
70
70
  implementation("finos.sdk.ekyc:ekycui:$sdkVersion")
71
71
  implementation("finos.sdk.ekyc:nfc:$sdkVersion")
@@ -1,6 +1,9 @@
1
1
  package finos.sdk.ekyc
2
2
 
3
3
  import android.R
4
+ import android.net.Uri
5
+ import android.os.Handler
6
+ import android.os.Looper
4
7
  import android.util.Base64
5
8
  import android.util.Log
6
9
  import com.facebook.react.bridge.Arguments
@@ -42,6 +45,8 @@ import org.json.JSONArray
42
45
  import org.json.JSONObject
43
46
  import java.io.File
44
47
  import java.io.FileOutputStream
48
+ import java.io.FileInputStream
49
+ import java.io.InputStream
45
50
  import java.util.Date
46
51
 
47
52
  @ReactModule(name = EKYCModule.NAME)
@@ -446,48 +451,58 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
446
451
  ) {
447
452
  Log.d(TAG, "▶️ startFaceCompare() called")
448
453
  try {
449
- val currentActivity = reactApplicationContext.currentActivity
450
- if (currentActivity == null) {
451
- Log.e(TAG, "❌ startFaceCompare() failed: Activity not available")
452
- promise.reject("NO_ACTIVITY", "Activity not available")
454
+ // React Native có thể gọi khi currentActivity = null (background / app state).
455
+ // FaceService không cần Activity, nên không block ở đây.
456
+ val selfieFile = resolveImageInputToFile(selfieImage, "selfie")
457
+ val idImageFile = resolveImageInputToFile(idImage, "id")
458
+
459
+ if (!selfieFile.exists() || selfieFile.length() <= 0L) {
460
+ promise.reject("SELFIE_IMAGE_INVALID", "Selfie image is invalid or not found")
453
461
  return
454
462
  }
455
- val imageFile = File(selfieImage.replace("file://", ""))
456
- if (!imageFile.exists()) {
457
- promise.reject("FILE_NOT_FOUND", "Image file does not exist")
463
+ if (!idImageFile.exists() || idImageFile.length() <= 0L) {
464
+ promise.reject("ID_IMAGE_INVALID", "ID image is invalid or not found")
458
465
  return
459
466
  }
460
- val idImageFile = base64ToImageFile(idImage)
461
467
 
462
468
  val faceServiceConfig =
463
469
  FaceServiceConfig(
464
470
  transactionId = transactionId,
465
- selfieImage = imageFile,
471
+ selfieImage = selfieFile,
466
472
  idImage = idImageFile
467
473
  )
468
- val ekycConfig = EKYCConfigSDK(faceServiceConfig = faceServiceConfig)
469
- SdkEkycFaceService.startEkyc(
470
- ekycConfigSDK = ekycConfig,
471
- callbackSuccess = { event, data ->
472
- Log.d(TAG, "✅ startFaceCompare() success")
473
- val (eventMap, promiseMap) = createSeparateMaps { map ->
474
- map.putString("event", event.name.toString())
475
- map.putString("data", data.toString())
476
- }
477
- sendEvent("onFaceCompareSuccess", eventMap)
478
- promise.resolve(promiseMap)
479
- },
480
- callbackError = { event, message ->
481
- Log.e(TAG, "❌ startFaceCompare() failed - Event: $event, Message: $message")
482
- val errorMap =
483
- Arguments.createMap().apply {
484
- putString("event", event.name.toString())
485
- putString("message", message?.toString() ?: "Unknown error")
486
- }
487
- sendEvent("onFaceCompareError", errorMap)
488
- promise.reject(event.name.toString(), message?.toString() ?: "Unknown FaceService error")
489
- }
474
+ // FaceService cần appKeyFaceService. Trước đây RN chỉ truyền 1 key string,
475
+ // nên map key này vào appKeyFaceService (và set appKey luôn để tương thích).
476
+ val ekycConfig = EKYCConfigSDK(
477
+ appKey = AppKeyConfig(appKey = appKey, appKeyFaceService = appKey),
478
+ faceServiceConfig = faceServiceConfig
490
479
  )
480
+
481
+ // Đảm bảo chạy trên main thread để tránh crash trên một số devices/SDK versions.
482
+ Handler(Looper.getMainLooper()).post {
483
+ SdkEkycFaceService.startEkyc(
484
+ ekycConfigSDK = ekycConfig,
485
+ callbackSuccess = { event, data ->
486
+ Log.d(TAG, "✅ startFaceCompare() success")
487
+ val (eventMap, promiseMap) = createSeparateMaps { map ->
488
+ map.putString("event", event.name.toString())
489
+ map.putString("data", data.toString())
490
+ }
491
+ sendEvent("onFaceCompareSuccess", eventMap)
492
+ promise.resolve(promiseMap)
493
+ },
494
+ callbackError = { event, message ->
495
+ Log.e(TAG, "❌ startFaceCompare() failed - Event: $event, Message: $message")
496
+ val errorMap =
497
+ Arguments.createMap().apply {
498
+ putString("event", event.name.toString())
499
+ putString("message", message?.toString() ?: "Unknown error")
500
+ }
501
+ sendEvent("onFaceCompareError", errorMap)
502
+ promise.reject(event.name.toString(), message?.toString() ?: "Unknown FaceService error")
503
+ }
504
+ )
505
+ }
491
506
  } catch (e: Exception) {
492
507
  Log.e(TAG, "❌ startFaceCompare() exception: ${e.message}", e)
493
508
  promise.reject("FACE_COMPARE_ERROR", e.message)
@@ -515,7 +530,56 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
515
530
  }
516
531
  }
517
532
 
518
- private fun base64ToImageFile(base64Image: String): File {
533
+ /**
534
+ * Resolve input image string to a local file.
535
+ * - Supports: file path (`/...`), file URI (`file://...`), content URI (`content://...`), base64 (with/without data URI).
536
+ */
537
+ private fun resolveImageInputToFile(input: String, prefix: String): File {
538
+ val value = input.trim()
539
+ if (value.isEmpty()) {
540
+ // Return empty file to trigger validation error upstream
541
+ return File(reactApplicationContext.cacheDir, "empty_${prefix}_${Date().time}.jpg").apply {
542
+ createNewFile()
543
+ }
544
+ }
545
+
546
+ // file://...
547
+ if (value.startsWith("file://")) {
548
+ val file = File(value.removePrefix("file://"))
549
+ if (file.exists()) return file
550
+ }
551
+
552
+ // content://...
553
+ if (value.startsWith("content://")) {
554
+ val copied = copyContentUriToCacheFile(value, prefix)
555
+ if (copied != null && copied.exists()) return copied
556
+ }
557
+
558
+ // raw path
559
+ val rawFile = File(value)
560
+ if (rawFile.exists()) return rawFile
561
+
562
+ // fallback: treat as base64
563
+ return base64ToImageFile(value, prefix)
564
+ }
565
+
566
+ private fun copyContentUriToCacheFile(uriString: String, prefix: String): File? {
567
+ return try {
568
+ val uri = Uri.parse(uriString)
569
+ val resolver = reactApplicationContext.contentResolver
570
+ val inputStream: InputStream = resolver.openInputStream(uri) ?: return null
571
+ val outFile = File(reactApplicationContext.cacheDir, "${prefix}_${Date().time}.jpg")
572
+ FileOutputStream(outFile).use { out ->
573
+ inputStream.use { ins -> ins.copyTo(out) }
574
+ }
575
+ outFile
576
+ } catch (e: Exception) {
577
+ Log.w(TAG, "copyContentUriToCacheFile failed: ${e.message}")
578
+ null
579
+ }
580
+ }
581
+
582
+ private fun base64ToImageFile(base64Image: String, prefix: String = "image"): File {
519
583
  try {
520
584
  // Remove data URI prefix if present (like "data:image/png;base64,")
521
585
  var processedBase64 = base64Image
@@ -528,7 +592,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
528
592
 
529
593
  // Create a unique filename with timestamp
530
594
  val timestamp = Date().time.toString()
531
- val fileName = "image_$timestamp.jpg"
595
+ val fileName = "${prefix}_$timestamp.jpg"
532
596
 
533
597
  // Get temporary directory and create file path
534
598
  val directory = reactApplicationContext.cacheDir
@@ -540,9 +604,9 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
540
604
 
541
605
  return imageFile
542
606
  } catch (e: Exception) {
543
- Log.e("LivenessFragment", "Error converting base64 to image file: ${e.message}", e)
607
+ Log.e(TAG, "Error converting base64 to image file: ${e.message}", e)
544
608
  // In case of error, create an empty file to avoid null returns
545
- val errorFile = File(reactApplicationContext.cacheDir, "error_image_${Date().time}.jpg")
609
+ val errorFile = File(reactApplicationContext.cacheDir, "error_${prefix}_${Date().time}.jpg")
546
610
  errorFile.createNewFile()
547
611
  return errorFile
548
612
  }
@@ -221,10 +221,12 @@ class SDKeKYC {
221
221
  }
222
222
  async startFaceCompare(config) {
223
223
  var _a;
224
- this.validateConfig(config, ['appKey', 'selfieImage', 'idImage']);
224
+ // Support both `appKey` and docs' `appKeyFaceService`
225
+ const normalized = Object.assign(Object.assign({}, config), { appKey: (config.appKeyFaceService || config.appKey) });
226
+ this.validateConfig(normalized, ['appKey', 'selfieImage', 'idImage']);
225
227
  const nativeModule = this.ensureNativeModule();
226
228
  try {
227
- return await nativeModule.startFaceCompare(config.appKey, (_a = config.transactionId) !== null && _a !== void 0 ? _a : '', config.selfieImage, config.idImage);
229
+ return await nativeModule.startFaceCompare(normalized.appKey, (_a = normalized.transactionId) !== null && _a !== void 0 ? _a : '', normalized.selfieImage, normalized.idImage);
228
230
  }
229
231
  catch (error) {
230
232
  console.error('Face Compare Error:', error);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -42,30 +42,11 @@
42
42
  "typescript"
43
43
  ],
44
44
  "scripts": {
45
- "android": "npm run build && npm run bundle:android && npx react-native run-android",
46
- "bundle:android": "mkdir -p android/app/src/main/assets && curl -s \"http://localhost:8081/index.bundle?platform=android&dev=false&minify=false\" -o android/app/src/main/assets/index.android.bundle || echo \"⚠️ Metro không chạy, bỏ qua bundle\"",
47
- "android:log": "npm run build && npm run bundle:android && npx react-native run-android && sleep 8 && npm run log:all",
48
- "reload": "npm run log:reload",
49
- "ios": "react-native run-ios",
50
- "start": "react-native start",
51
- "reset": "react-native start --reset-cache",
52
- "clean": "cd android && ./gradlew clean && cd ..",
53
- "npm-install": "npm install",
45
+ "clear": "rm -rf node_modules && npm cache clean --force && cd android && ./gradlew clean",
54
46
  "build": "npx --package=typescript tsc",
55
- "prepare": "npm run build",
56
- "publish-sdk-local": "npm pack",
57
- "publish-sdk": "npm run build && npm publish --access public",
58
- "publish-sdk-version": "npm run build && npm version patch --force && npm publish --access public",
59
- "prepublishOnly": "npm run build",
60
- "unpublish:1.2.2": "node -e \"if(process.env.CONFIRM_UNPUBLISH==='YES'){process.exit(0)}else{console.error('Set CONFIRM_UNPUBLISH=YES to confirm');process.exit(1)}\" && npm unpublish @finos_sdk/sdk-ekyc@1.2.2",
61
- "log:android": "npx react-native log-android",
62
- "log:ios": "npx react-native log-ios",
63
- "log:sdk": "./watch-sdk-logs.sh",
64
- "log:buffer": "./watch-logs-with-buffer.sh",
65
- "log:native": "./watch-native-logs.sh",
66
- "log:all": "./watch-all-logs.sh",
67
- "log:reload": "./reload-and-watch.sh",
68
- "log:clear": "adb logcat -c && echo '✅ Logs cleared'"
47
+ "publish-sdk-local": "npm install && npm run build &&npm pack",
48
+ "publish-sdk": "npm install && npm run build && npm publish --access public",
49
+ "publish-sdk-version": "npm run build && npm version patch --force && npm publish --access public"
69
50
  },
70
51
  "dependencies": {
71
52
  "react": "18.3.1",
@@ -79,7 +79,7 @@ export declare class FinosEKYCModule {
79
79
  */
80
80
  startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent>;
81
81
  /**
82
- * Start face comparison
82
+ * Start face comparison (Face Service)
83
83
  * @param config Face service configuration
84
84
  */
85
85
  startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent>;
@@ -215,19 +215,20 @@ class FinosEKYCModule {
215
215
  }
216
216
  }
217
217
  /**
218
- * Start face comparison
218
+ * Start face comparison (Face Service)
219
219
  * @param config Face service configuration
220
220
  */
221
221
  async startFaceCompare(config) {
222
222
  this.validateSDKReady();
223
+ const normalized = Object.assign(Object.assign({}, config), { appKey: (config.appKeyFaceService || config.appKey) });
223
224
  try {
224
- console.log('👤 Starting face comparison...');
225
- const result = await this.sdk.startFaceCompare(config);
226
- console.log('✅ Face comparison completed:', result.event);
225
+ console.log('👤 Starting face compare...');
226
+ const result = await this.sdk.startFaceCompare(normalized);
227
+ console.log('✅ Face compare completed:', result.event);
227
228
  return result;
228
229
  }
229
230
  catch (error) {
230
- console.error('❌ Face comparison failed:', error);
231
+ console.error('❌ Face compare failed:', error);
231
232
  throw error;
232
233
  }
233
234
  }
@@ -211,7 +211,7 @@ export declare class FinosESignModule {
211
211
  onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void): import("react-native").EmitterSubscription | null;
212
212
  onLivenessError(callback: (error: any) => void): import("react-native").EmitterSubscription | null;
213
213
  /**
214
- * Start face comparison
214
+ * Face matching (Face Service)
215
215
  * @param config Face service configuration
216
216
  */
217
217
  startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent>;
@@ -408,13 +408,14 @@ class FinosESignModule {
408
408
  }
409
409
  // ==================== Face Service Methods ====================
410
410
  /**
411
- * Start face comparison
411
+ * Face matching (Face Service)
412
412
  * @param config Face service configuration
413
413
  */
414
414
  async startFaceCompare(config) {
415
415
  this.validateSDKReady();
416
+ const normalized = Object.assign(Object.assign({}, config), { appKey: (config.appKeyFaceService || config.appKey) });
416
417
  // Pass through to SDK - error handling is done in EKYCModule.ts
417
- return await this.sdk.startFaceCompare(config);
418
+ return await this.sdk.startFaceCompare(normalized);
418
419
  }
419
420
  // Face Compare Event Listeners
420
421
  onFaceCompareSuccess(callback) {
@@ -36,6 +36,14 @@ export interface ESignSignRequest {
36
36
  documentName?: string;
37
37
  status?: string;
38
38
  }
39
+ /**
40
+ * eSign Sign Request Info
41
+ */
42
+ export interface ESignSignRequestConfirm {
43
+ requestId: string;
44
+ authId?: string;
45
+ authData?: string;
46
+ }
39
47
  /**
40
48
  * eSign Error Result
41
49
  */
@@ -13,7 +13,14 @@ export interface FaceVerifyResult {
13
13
  toBeReviewed?: string;
14
14
  }
15
15
  export interface FaceServiceConfig {
16
+ /**
17
+ * Backward/forward compatible:
18
+ * - SDK code uses `appKey`
19
+ * - Some integration docs use `appKeyFaceService`
20
+ * Provide either; `startFaceCompare()` will normalize.
21
+ */
16
22
  appKey: string;
23
+ appKeyFaceService?: string;
17
24
  transactionId?: string;
18
25
  selfieImage: string;
19
26
  idImage: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -42,30 +42,11 @@
42
42
  "typescript"
43
43
  ],
44
44
  "scripts": {
45
- "android": "npm run build && npm run bundle:android && npx react-native run-android",
46
- "bundle:android": "mkdir -p android/app/src/main/assets && curl -s \"http://localhost:8081/index.bundle?platform=android&dev=false&minify=false\" -o android/app/src/main/assets/index.android.bundle || echo \"⚠️ Metro không chạy, bỏ qua bundle\"",
47
- "android:log": "npm run build && npm run bundle:android && npx react-native run-android && sleep 8 && npm run log:all",
48
- "reload": "npm run log:reload",
49
- "ios": "react-native run-ios",
50
- "start": "react-native start",
51
- "reset": "react-native start --reset-cache",
52
- "clean": "cd android && ./gradlew clean && cd ..",
53
- "npm-install": "npm install",
45
+ "clear": "rm -rf node_modules && npm cache clean --force && cd android && ./gradlew clean",
54
46
  "build": "npx --package=typescript tsc",
55
- "prepare": "npm run build",
56
- "publish-sdk-local": "npm pack",
57
- "publish-sdk": "npm run build && npm publish --access public",
58
- "publish-sdk-version": "npm run build && npm version patch --force && npm publish --access public",
59
- "prepublishOnly": "npm run build",
60
- "unpublish:1.2.2": "node -e \"if(process.env.CONFIRM_UNPUBLISH==='YES'){process.exit(0)}else{console.error('Set CONFIRM_UNPUBLISH=YES to confirm');process.exit(1)}\" && npm unpublish @finos_sdk/sdk-ekyc@1.2.2",
61
- "log:android": "npx react-native log-android",
62
- "log:ios": "npx react-native log-ios",
63
- "log:sdk": "./watch-sdk-logs.sh",
64
- "log:buffer": "./watch-logs-with-buffer.sh",
65
- "log:native": "./watch-native-logs.sh",
66
- "log:all": "./watch-all-logs.sh",
67
- "log:reload": "./reload-and-watch.sh",
68
- "log:clear": "adb logcat -c && echo '✅ Logs cleared'"
47
+ "publish-sdk-local": "npm install && npm run build &&npm pack",
48
+ "publish-sdk": "npm install && npm run build && npm publish --access public",
49
+ "publish-sdk-version": "npm run build && npm version patch --force && npm publish --access public"
69
50
  },
70
51
  "dependencies": {
71
52
  "react": "18.3.1",
@@ -223,19 +223,24 @@ export class FinosEKYCModule {
223
223
  }
224
224
 
225
225
  /**
226
- * Start face comparison
226
+ * Start face comparison (Face Service)
227
227
  * @param config Face service configuration
228
228
  */
229
229
  public async startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent> {
230
230
  this.validateSDKReady();
231
231
 
232
+ const normalized: FaceServiceConfig = {
233
+ ...config,
234
+ appKey: (config.appKeyFaceService || config.appKey),
235
+ };
236
+
232
237
  try {
233
- console.log('👤 Starting face comparison...');
234
- const result = await this.sdk.startFaceCompare(config);
235
- console.log('✅ Face comparison completed:', result.event);
238
+ console.log('👤 Starting face compare...');
239
+ const result = await this.sdk.startFaceCompare(normalized);
240
+ console.log('✅ Face compare completed:', result.event);
236
241
  return result;
237
242
  } catch (error) {
238
- console.error('❌ Face comparison failed:', error);
243
+ console.error('❌ Face compare failed:', error);
239
244
  throw error;
240
245
  }
241
246
  }
@@ -484,13 +484,17 @@ export class FinosESignModule {
484
484
  // ==================== Face Service Methods ====================
485
485
 
486
486
  /**
487
- * Start face comparison
487
+ * Face matching (Face Service)
488
488
  * @param config Face service configuration
489
489
  */
490
490
  public async startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent> {
491
491
  this.validateSDKReady();
492
+ const normalized: FaceServiceConfig = {
493
+ ...config,
494
+ appKey: (config.appKeyFaceService || config.appKey),
495
+ };
492
496
  // Pass through to SDK - error handling is done in EKYCModule.ts
493
- return await this.sdk.startFaceCompare(config);
497
+ return await this.sdk.startFaceCompare(normalized);
494
498
  }
495
499
 
496
500
  // Face Compare Event Listeners
@@ -124,7 +124,7 @@ try {
124
124
  }
125
125
  ```
126
126
 
127
- ### Face Comparison
127
+ ### Face Comparison (Face Matching)
128
128
 
129
129
  ```typescript
130
130
  import { finosEKYC, FaceServiceConfig } from '@finos_sdk/sdk-ekyc';
@@ -43,6 +43,15 @@ export interface ESignSignRequest {
43
43
  status?: string;
44
44
  }
45
45
 
46
+ /**
47
+ * eSign Sign Request Info
48
+ */
49
+ export interface ESignSignRequestConfirm {
50
+ requestId: string;
51
+ authId?: string;
52
+ authData?: string;
53
+ }
54
+
46
55
  /**
47
56
  * eSign Error Result
48
57
  */
@@ -15,7 +15,14 @@ export interface FaceVerifyResult {
15
15
  }
16
16
 
17
17
  export interface FaceServiceConfig {
18
+ /**
19
+ * Backward/forward compatible:
20
+ * - SDK code uses `appKey`
21
+ * - Some integration docs use `appKeyFaceService`
22
+ * Provide either; `startFaceCompare()` will normalize.
23
+ */
18
24
  appKey: string,
25
+ appKeyFaceService?: string,
19
26
  transactionId?: string,
20
27
  selfieImage: string,
21
28
  idImage: string,