@finos_sdk/sdk-ekyc 1.4.6 → 1.4.7

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.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  React Native SDK for eKYC (electronic Know Your Customer) and eSign. Features include Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, C06 residence verification, SMS OTP verification, and Electronic Signature (eSign) capabilities.
7
7
 
8
- **Version**: 1.4.2
8
+ **Version**: 1.4.7
9
9
 
10
10
  ## Features
11
11
 
@@ -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.4.6.1"
68
+ def sdkVersion = "1.4.7"
69
69
  implementation("finos.sdk.ekyc:ekyc:$sdkVersion")
70
70
  implementation("finos.sdk.ekyc:ekycui:$sdkVersion")
71
71
  implementation("finos.sdk.ekyc:nfc:$sdkVersion")
@@ -97,6 +97,46 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
97
97
  return Pair(eventMap, promiseMap)
98
98
  }
99
99
 
100
+ /** Recursively convert org.json.JSONObject → WritableMap (preserves key names as-is). */
101
+ private fun jsonObjectToWritableMap(jsonObject: JSONObject): WritableMap {
102
+ val map = Arguments.createMap()
103
+ val keys = jsonObject.keys()
104
+ while (keys.hasNext()) {
105
+ val key = keys.next()
106
+ when (val value = jsonObject.get(key)) {
107
+ is JSONObject -> map.putMap(key, jsonObjectToWritableMap(value))
108
+ is JSONArray -> map.putArray(key, jsonArrayToWritableArray(value))
109
+ is Boolean -> map.putBoolean(key, value)
110
+ is Int -> map.putInt(key, value)
111
+ is Long -> map.putDouble(key, value.toDouble())
112
+ is Double -> map.putDouble(key, value)
113
+ is String -> map.putString(key, value)
114
+ JSONObject.NULL -> map.putNull(key)
115
+ else -> map.putString(key, value.toString())
116
+ }
117
+ }
118
+ return map
119
+ }
120
+
121
+ /** Recursively convert org.json.JSONArray → WritableArray. */
122
+ private fun jsonArrayToWritableArray(jsonArray: JSONArray): WritableArray {
123
+ val array = Arguments.createArray()
124
+ for (i in 0 until jsonArray.length()) {
125
+ when (val value = jsonArray.get(i)) {
126
+ is JSONObject -> array.pushMap(jsonObjectToWritableMap(value))
127
+ is JSONArray -> array.pushArray(jsonArrayToWritableArray(value))
128
+ is Boolean -> array.pushBoolean(value)
129
+ is Int -> array.pushInt(value)
130
+ is Long -> array.pushDouble(value.toDouble())
131
+ is Double -> array.pushDouble(value)
132
+ is String -> array.pushString(value)
133
+ JSONObject.NULL -> array.pushNull()
134
+ else -> array.pushString(value.toString())
135
+ }
136
+ }
137
+ return array
138
+ }
139
+
100
140
  /** Convert ESignOpenSessionResult to WritableMap for RN. */
101
141
  private fun eSignOpenSessionResultToWritableMap(result: ESignModels.ESignOpenSessionResult): WritableMap =
102
142
  Arguments.createMap().apply {
@@ -493,24 +533,25 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
493
533
  eKYCFinOSLiveness.startEkyc(
494
534
  ekycConfigSDK = ekycConfig,
495
535
  callbackSuccess = { event, data ->
496
- // LOG_SUCCESS = internal submit result event (v1.4.6.1), don't resolve promise
497
- if (event == EKYCEvent.LOG_SUCCESS) {
498
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: (data as? SDKEkycResult)?.checkLivenessResponse?.let { Gson().toJson(it) } ?: Gson().toJson(data)
499
- Log.d(TAG, "LOG_SUCCESS [Liveness]: $logDataStr")
500
- val logMap = Arguments.createMap().apply {
501
- putString("event", event.name.toString())
502
- putString("data", logDataStr)
503
- }
504
- sendEvent("onLivenessSuccess", logMap)
505
- return@startEkyc
506
- }
507
- Log.d(TAG, "✅ startLiveness() success")
508
- val (eventMap, promiseMap) = createSeparateMaps { map ->
509
- map.putString("event", event.name.toString())
510
- map.putString("data", Gson().toJson((data as SDKEkycResult).checkLivenessResponse))
511
- }
512
- sendEvent("onLivenessSuccess", eventMap)
513
- promise.resolve(promiseMap)
536
+ Log.d(TAG, "🔥 NATIVE CALLBACK (Liveness): event=${event.name}")
537
+ val response = (data as? SDKEkycResult)?.checkLivenessResponse
538
+ val jsonStr = data?.customData?.let { Gson().toJson(it) } ?: Gson().toJson(response ?: data)
539
+
540
+ // Helper to create fresh data map to avoid 'Map already consumed' error
541
+ fun createWritableData() = try { jsonObjectToWritableMap(JSONObject(jsonStr)) } catch (e: Exception) { Arguments.createMap() }
542
+
543
+ // Send the event directly to React Native
544
+ sendEvent("onLivenessSuccess", Arguments.createMap().apply {
545
+ putString("event", event.name)
546
+ putMap("data", createWritableData())
547
+ })
548
+
549
+ // Always attempt to resolve the promise.
550
+ // RN safely ignores subsequent resolves if the native SDK fires multiple events.
551
+ promise.resolve(Arguments.createMap().apply {
552
+ putString("event", event.name)
553
+ putMap("data", createWritableData())
554
+ })
514
555
  },
515
556
  callbackError = { event, errorResult ->
516
557
  Log.e(TAG, "❌ startLiveness() failed - Event: $event, Code: ${errorResult.code}, Message: ${errorResult.message}")
@@ -577,40 +618,21 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
577
618
  eKYCFinOSFaceService.startEkyc(
578
619
  ekycConfigSDK = ekycConfig,
579
620
  callbackSuccess = { event, data ->
580
- // LOG_SUCCESS = internal submit result event (v1.4.6.1), don't resolve promise
581
- if (event == EKYCEvent.LOG_SUCCESS) {
582
- val faceResponse = (data as? SDKEkycResult)?.checkFaceResponse
583
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: faceResponse?.let { Gson().toJson(it) } ?: "null"
584
- Log.d(TAG, "LOG_SUCCESS [FaceCompare]: $logDataStr")
585
- val logMap = Arguments.createMap().apply {
586
- putString("event", event.name.toString())
587
- putString("data", logDataStr)
588
- }
589
- sendEvent("onFaceCompareSuccess", logMap)
590
- return@startEkyc
591
- }
592
- Log.d(TAG, "✅ startFaceCompare() success")
593
- val faceResponse = (data as? SDKEkycResult)?.checkFaceResponse
594
- val (eventMap, promiseMap) = createSeparateMaps { map ->
595
- map.putString("event", event.name.toString())
596
- val resultMap = Arguments.createMap().apply {
597
- putDouble("conf", faceResponse?.result?.conf?.toString()?.toDoubleOrNull() ?: 0.0)
598
- putString("match", faceResponse?.result?.match?.toString())
599
- putInt("matchScore", faceResponse?.result?.matchScore?.toString()?.toIntOrNull() ?: 0)
600
- putString("toBeReviewed", faceResponse?.result?.toBeReviewed?.toString())
601
- }
602
- val faceMap = Arguments.createMap().apply {
603
- putString("requestId", faceResponse?.requestId?.toString())
604
- putMap("result", resultMap)
605
- putString("status", faceResponse?.status?.toString())
606
- putInt("statusCode", faceResponse?.statusCode?.toString()?.toIntOrNull() ?: 0)
607
- putString("error", faceResponse?.error?.toString())
608
- putString("type", faceResponse?.type?.toString())
609
- }
610
- map.putMap("data", faceMap)
611
- }
612
- sendEvent("onFaceCompareSuccess", eventMap)
613
- promise.resolve(promiseMap)
621
+ Log.d(TAG, "🔥 NATIVE CALLBACK (Face Compare): event=${event.name}")
622
+ val response = (data as? SDKEkycResult)?.checkFaceResponse
623
+ val jsonStr = data?.customData?.let { Gson().toJson(it) } ?: Gson().toJson(response ?: data)
624
+ fun createWritableData() = try { jsonObjectToWritableMap(JSONObject(jsonStr)) } catch (e: Exception) { Arguments.createMap() }
625
+
626
+ sendEvent("onFaceCompareSuccess", Arguments.createMap().apply {
627
+ putString("event", event.name)
628
+ putMap("data", createWritableData())
629
+ })
630
+
631
+ // Always attempt to resolve the promise.
632
+ promise.resolve(Arguments.createMap().apply {
633
+ putString("event", event.name)
634
+ putMap("data", createWritableData())
635
+ })
614
636
  },
615
637
  callbackError = { event, errorResult ->
616
638
  Log.e(TAG, "❌ startFaceCompare() failed - Event: $event, Code: ${errorResult.code}, Message: ${errorResult.message}")
@@ -835,41 +857,55 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
835
857
  activity = currentActivity,
836
858
  ekycConfigSDK = ekycConfigSDK,
837
859
  callbackSuccess = { event, data ->
838
- // LOG_SUCCESS = internal submit result event (v1.4.5), emit event only, don't resolve promise
839
- if (event == EKYCEvent.LOG_SUCCESS) {
840
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: Gson().toJson(data)
841
- Log.d(TAG, "LOG_SUCCESS [EkycUI]: $logDataStr")
842
- sendEvent("onEkycUISuccess", Arguments.createMap().apply {
843
- putString("status", "log")
844
- putString("event", event.name.toString())
845
- putString("data", logDataStr)
846
- })
847
- return@startEkyc
848
- }
849
- Log.d(TAG, "✅ startEkycUI() callback - event=${event.name}")
860
+ Log.d(TAG, "🔥 NATIVE CALLBACK (EkycUI): event=${event.name}")
861
+
850
862
  val result = data as? SDKEkycResult
851
- val hasRealData = result?.ekycStateModel != null
852
- // Chỉ resolve promise khi flow hoàn thành có data (SDK_END_SUCCESS); SDK_START_SUCCESS = activity vừa mở, bỏ qua
853
- if (event == EKYCEvent.SDK_START_SUCCESS && !hasRealData) {
854
- Log.d(TAG, "⏳ startEkycUI() SDK_START_SUCCESS chờ SDK_END_SUCCESS")
855
- sendEvent("onEkycUISuccess", Arguments.createMap().apply {
856
- putString("status", "started")
857
- putString("event", event.name.toString())
858
- })
859
- } else {
863
+ val response = result?.ekycStateModel
864
+ val jsonStr = data?.customData?.let { Gson().toJson(it) } ?: Gson().toJson(response ?: data)
865
+
866
+ // Helper to create fresh data map to avoid 'Map already consumed' error
867
+ fun createWritableData() = try { jsonObjectToWritableMap(JSONObject(jsonStr)) } catch (e: Exception) { Arguments.createMap() }
868
+
869
+ val statusStr = when (event) {
870
+ EKYCEvent.LOG_SUCCESS -> "log"
871
+ EKYCEvent.SDK_START_SUCCESS -> "started"
872
+ else -> "success"
873
+ }
874
+
875
+ // Send EVERY event directly to React Native Event Listener (Pass-through)
876
+ sendEvent("onEkycUISuccess", Arguments.createMap().apply {
877
+ putString("status", statusStr)
878
+ putString("event", event.name)
879
+
880
+ if (event == EKYCEvent.SDK_START_SUCCESS && response == null) {
881
+ // Bỏ qua data nếu là SDK_START_SUCCESS (chỉ có event/status) để giống code cũ
882
+ } else {
883
+ putMap("data", createWritableData())
884
+ }
885
+
886
+ // Retain the convenient root properties cho backward compatibility
860
887
  val ekycFiles = result?.ekycStateModel?.eKYCFileModel
861
888
  val ekycTransactionId = result?.ekycStateModel?.transactionId ?: ""
862
- val (eventMap, promiseMap) = createSeparateMaps { map ->
863
- map.putString("status", "success")
864
- map.putString("event", event.name.toString())
865
- map.putString("data", Gson().toJson(data))
866
- map.putString("transactionId", ekycTransactionId)
867
- ekycFiles?.imageFace?.absolutePath?.let { map.putString("imageFacePath", it) }
868
- ekycFiles?.imageOcrFront?.absolutePath?.let { map.putString("imageOcrFrontPath", it) }
869
- ekycFiles?.imageOcrBack?.absolutePath?.let { map.putString("imageOcrBackPath", it) }
870
- }
871
- sendEvent("onEkycUISuccess", eventMap)
872
- promise.resolve(promiseMap)
889
+ if (ekycTransactionId.isNotEmpty()) putString("transactionId", ekycTransactionId)
890
+ ekycFiles?.imageFace?.absolutePath?.let { putString("imageFacePath", it) }
891
+ ekycFiles?.imageOcrFront?.absolutePath?.let { putString("imageOcrFrontPath", it) }
892
+ ekycFiles?.imageOcrBack?.absolutePath?.let { putString("imageOcrBackPath", it) }
893
+ })
894
+
895
+ // IMPORTANT: Chỉ resolve promise ở SDK_END_SUCCESS để `await FinosEKYC.startEkycUI()` không bị kết thúc sớm
896
+ if (event == EKYCEvent.SDK_END_SUCCESS) {
897
+ promise.resolve(Arguments.createMap().apply {
898
+ putString("status", "success")
899
+ putString("event", event.name)
900
+ putMap("data", createWritableData())
901
+
902
+ val ekycFiles = result?.ekycStateModel?.eKYCFileModel
903
+ val ekycTransactionId = result?.ekycStateModel?.transactionId ?: ""
904
+ if (ekycTransactionId.isNotEmpty()) putString("transactionId", ekycTransactionId)
905
+ ekycFiles?.imageFace?.absolutePath?.let { putString("imageFacePath", it) }
906
+ ekycFiles?.imageOcrFront?.absolutePath?.let { putString("imageOcrFrontPath", it) }
907
+ ekycFiles?.imageOcrBack?.absolutePath?.let { putString("imageOcrBackPath", it) }
908
+ })
873
909
  }
874
910
  },
875
911
  callbackError = { event, errorResult ->
@@ -881,14 +917,14 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
881
917
  putString("customMessage", errorResult.message)
882
918
  putString("message", errorResult.message)
883
919
  }
920
+ sendEvent("onEkycUIError", errorMap)
921
+
884
922
  val promiseErrorMap = Arguments.createMap().apply {
885
923
  putString("status", "error")
886
924
  putString("event", event.name.toString())
887
925
  putString("customCode", errorResult.code)
888
926
  putString("customMessage", errorResult.message)
889
- putString("message", errorResult.message)
890
927
  }
891
- sendEvent("onEkycUIError", errorMap)
892
928
  promise.reject(event.name.toString(), errorResult.message, null, promiseErrorMap)
893
929
  }
894
930
  )
@@ -2174,4 +2210,13 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
2174
2210
  }
2175
2211
  }
2176
2212
 
2213
+ @ReactMethod
2214
+ fun addListener(eventName: String) {
2215
+ // Keep: Required for RN built-in Event Emitter Calls.
2216
+ }
2217
+
2218
+ @ReactMethod
2219
+ fun removeListeners(count: Int) {
2220
+ // Keep: Required for RN built-in Event Emitter Calls.
2221
+ }
2177
2222
  }
@@ -3,8 +3,8 @@ import { NfcConfig, NfcError } from './src/types/ekycNFCType';
3
3
  import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent, StartEkycUIResult } from './src/types/ekycType';
4
4
  import { C06Config } from './src/types/ekycC06Type';
5
5
  import { OcrConfig } from './src/types/ekycOCRType';
6
- import { LivenessConfig, SDKFaceDetectStatus } from './src/types/ekycLivenessType';
7
- import { FaceServiceConfig } from './src/types/ekycFaceType';
6
+ import { LivenessConfig, SDKFaceDetectStatus, LivenessSuccessEvent } from './src/types/ekycLivenessType';
7
+ import { FaceServiceConfig, FaceCompareSuccessEvent } from './src/types/ekycFaceType';
8
8
  import { SmsOtpConfig, SmsOtpResult, SmsOtpError } from './src/types/ekycSmsOtpType';
9
9
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignError, AuthorizeInfo, ESignPdfResult, ESignApiResponse } from './src/types/ekycESignType';
10
10
  export declare const SDK_VERSION: string;
@@ -29,8 +29,8 @@ declare class SDKeKYC {
29
29
  startOcr(config: OcrConfig): Promise<SDKEkycResultStringWithEvent>;
30
30
  startNfcScan(config: NfcConfig): Promise<SDKEkycResultStringWithEvent>;
31
31
  checkC06(config: C06Config): Promise<SDKEkycResultStringWithEvent>;
32
- startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent>;
33
- startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent>;
32
+ startLiveness(config: LivenessConfig): Promise<LivenessSuccessEvent>;
33
+ startFaceCompare(config: FaceServiceConfig): Promise<FaceCompareSuccessEvent>;
34
34
  onResume(): void;
35
35
  onPause(): void;
36
36
  onNfcScanStart(callback: (data: SDKEkycResultWithEvent) => void): EmitterSubscription | null;
@@ -40,9 +40,9 @@ declare class SDKeKYC {
40
40
  onC06Error(callback: (error: NfcError) => void): EmitterSubscription | null;
41
41
  onOcrSuccess(callback: (data: SDKEkycResultStringWithEvent) => void): EmitterSubscription | null;
42
42
  onOcrError(callback: (error: any) => void): EmitterSubscription | null;
43
- onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void): EmitterSubscription | null;
43
+ onLivenessSuccess(callback: (data: LivenessSuccessEvent) => void): EmitterSubscription | null;
44
44
  onLivenessError(callback: (error: any) => void): EmitterSubscription | null;
45
- onFaceCompareSuccess(callback: (data: SDKEkycResultWithEvent) => void): EmitterSubscription | null;
45
+ onFaceCompareSuccess(callback: (data: FaceCompareSuccessEvent) => void): EmitterSubscription | null;
46
46
  onFaceCompareError(callback: (error: any) => void): EmitterSubscription | null;
47
47
  onEkycUISuccess(callback: (data: any) => void): EmitterSubscription | null;
48
48
  onEkycUIError(callback: (error: any) => void): EmitterSubscription | null;
package/dist/index.d.ts CHANGED
@@ -9,9 +9,9 @@ export { SDKFlowType, SDK_FLOW_OPTIONS, flowToStrings } from './src/types/ekycFl
9
9
  export type { NfcConfig, NFCData, NfcInfo } from './src/types/ekycNFCType';
10
10
  export type { C06Config } from './src/types/ekycC06Type';
11
11
  export type { OcrConfig } from './src/types/ekycOCRType';
12
- export type { LivenessConfig } from './src/types/ekycLivenessType';
12
+ export type { LivenessConfig, LivenessSuccessEvent, CheckLivenessResponse, Metadata, LivenessCheckResult, Details, QualityChecks, QualityCheck, AgeRange, LiveFace } from './src/types/ekycLivenessType';
13
13
  export { SDKFaceDetectStatus, SDK_LIVENESS_ACTIONS, customActionsToStrings } from './src/types/ekycLivenessType';
14
- export type { FaceServiceConfig } from './src/types/ekycFaceType';
14
+ export type { FaceServiceConfig, FaceCompareSuccessEvent, CheckFaceResponse, FaceVerifyResult } from './src/types/ekycFaceType';
15
15
  export { EKYCEvent, EKYCErrorEvent } from './src/types/EKYCEvent';
16
16
  export type { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent, StartEkycUIResult, EKYCError } from './src/types/ekycType';
17
17
  export { getEkycError, AppIDType } from './src/types/ekycType';
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
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",
@@ -3,8 +3,8 @@ import { NfcConfig } from '../types/ekycNFCType';
3
3
  import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent, EKYCError, AppIDType } from '../types/ekycType';
4
4
  import { C06Config } from '../types/ekycC06Type';
5
5
  import { OcrConfig } from '../types/ekycOCRType';
6
- import { LivenessConfig, SDKFaceDetectStatus } from '../types/ekycLivenessType';
7
- import { FaceServiceConfig } from '../types/ekycFaceType';
6
+ import { LivenessConfig, SDKFaceDetectStatus, LivenessSuccessEvent } from '../types/ekycLivenessType';
7
+ import { FaceServiceConfig, FaceCompareSuccessEvent } from '../types/ekycFaceType';
8
8
  import { SmsOtpConfig, SmsOtpResult } from '../types/ekycSmsOtpType';
9
9
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignPdfResult, ESignApiResponse } from '../types/ekycESignType';
10
10
  import { SDKFlowType } from '../types/ekycFlowType';
@@ -77,12 +77,12 @@ export declare class FinosEKYCModule {
77
77
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
78
78
  * @param config.switchFrontCamera - Use front camera (default: false)
79
79
  */
80
- startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent>;
80
+ startLiveness(config: LivenessConfig): Promise<LivenessSuccessEvent>;
81
81
  /**
82
82
  * Start face comparison (Face Service)
83
83
  * @param config Face service configuration
84
84
  */
85
- startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent>;
85
+ startFaceCompare(config: FaceServiceConfig): Promise<FaceCompareSuccessEvent>;
86
86
  /**
87
87
  * Extract NFC data for C06 verification
88
88
  * @param nfcResultJson NFC result JSON string
@@ -134,7 +134,7 @@ export declare class FinosEKYCModule {
134
134
  /**
135
135
  * Listen for liveness success events
136
136
  */
137
- onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void): import("react-native").EmitterSubscription | null;
137
+ onLivenessSuccess(callback: (data: LivenessSuccessEvent) => void): import("react-native").EmitterSubscription | null;
138
138
  /**
139
139
  * Listen for liveness error events
140
140
  */
@@ -143,7 +143,7 @@ export declare class FinosEKYCModule {
143
143
  /**
144
144
  * Listen for face compare success events
145
145
  */
146
- onFaceCompareSuccess(callback: (data: SDKEkycResultWithEvent) => void): import("react-native").EmitterSubscription | null;
146
+ onFaceCompareSuccess(callback: (data: FaceCompareSuccessEvent) => void): import("react-native").EmitterSubscription | null;
147
147
  /** Payload là EKYCError: { event, code, message }. */
148
148
  onFaceCompareError(callback: (error: EKYCError) => void): import("react-native").EmitterSubscription | null;
149
149
  /**
@@ -1,9 +1,9 @@
1
1
  import { SDK_VERSION, SDK_NAME } from '../../EKYCModule';
2
2
  import { SmsOtpConfig, SmsOtpResult } from '../types/ekycSmsOtpType';
3
3
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, AuthorizeInfo, ESignPdfResult, ESignApiResponse } from '../types/ekycESignType';
4
- import { LivenessConfig } from '../types/ekycLivenessType';
5
- import { FaceServiceConfig } from '../types/ekycFaceType';
6
- import { SDKEkycResultStringWithEvent, SDKEkycResultWithEvent, EKYCError } from '../types/ekycType';
4
+ import { LivenessConfig, LivenessSuccessEvent } from '../types/ekycLivenessType';
5
+ import { FaceServiceConfig, FaceCompareSuccessEvent } from '../types/ekycFaceType';
6
+ import { EKYCError } from '../types/ekycType';
7
7
  /**
8
8
  * Finos eSign SDK Module
9
9
  *
@@ -255,16 +255,16 @@ export declare class FinosESignModule {
255
255
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
256
256
  * @param config.switchFrontCamera - Use front camera (default: false)
257
257
  */
258
- startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent>;
259
- onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void): import("react-native").EmitterSubscription | null;
258
+ startLiveness(config: LivenessConfig): Promise<LivenessSuccessEvent>;
259
+ onLivenessSuccess(callback: (data: LivenessSuccessEvent) => void): import("react-native").EmitterSubscription | null;
260
260
  /** Payload là EKYCError: { event, code, message }. */
261
261
  onLivenessError(callback: (error: EKYCError) => void): import("react-native").EmitterSubscription | null;
262
262
  /**
263
263
  * Face matching (Face Service)
264
264
  * @param config Face service configuration
265
265
  */
266
- startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent>;
267
- onFaceCompareSuccess(callback: (data: SDKEkycResultWithEvent) => void): import("react-native").EmitterSubscription | null;
266
+ startFaceCompare(config: FaceServiceConfig): Promise<FaceCompareSuccessEvent>;
267
+ onFaceCompareSuccess(callback: (data: FaceCompareSuccessEvent) => void): import("react-native").EmitterSubscription | null;
268
268
  /** Payload là EKYCError: { event, code, message }. */
269
269
  onFaceCompareError(callback: (error: EKYCError) => void): import("react-native").EmitterSubscription | null;
270
270
  /**
@@ -12,6 +12,15 @@ export interface FaceVerifyResult {
12
12
  matchScore?: number;
13
13
  toBeReviewed?: string;
14
14
  }
15
+ /**
16
+ * Payload emitted by onFaceCompareSuccess listener.
17
+ * event = "FACE_SUCCESS" | "LOG_SUCCESS"
18
+ * data = CheckFaceResponse object (bridge serialises individual fields, not a JSON string)
19
+ */
20
+ export interface FaceCompareSuccessEvent {
21
+ event: string;
22
+ data: CheckFaceResponse;
23
+ }
15
24
  export interface FaceServiceConfig {
16
25
  /**
17
26
  * Backward/forward compatible:
@@ -97,3 +97,12 @@ export interface QualityCheck {
97
97
  }
98
98
  /** Liveness error – dùng chung EKYCError (event, code, message). */
99
99
  export type LivenessError = import('./ekycType').EKYCError;
100
+ /**
101
+ * Payload emitted by onLivenessSuccess listener.
102
+ * event = "LIVENESS_SUCCESS" | "LOG_SUCCESS"
103
+ * data = CheckLivenessResponse object (bridge serialises fields directly, not a JSON string)
104
+ */
105
+ export interface LivenessSuccessEvent {
106
+ event: string;
107
+ data: CheckLivenessResponse;
108
+ }
@@ -184,8 +184,16 @@ class EKYCModule: RCTEventEmitter {
184
184
  let ekycConfig = EKYCConfigSDK(appKey: AppKeyConfig(appKey: appKey), sdkType: .liveness, livenessConfig: livenessConfig)
185
185
  SdkEkycLiveness.startEkyc(from: vc, ekycConfigSDK: ekycConfig,
186
186
  callbackSuccess: { event, data in
187
- let livenessResponse = (data as? SDKEkycResult)?.checkLivenessResponse?.description ?? ""
188
- let body: [String: Any] = ["event": event.description, "data": livenessResponse]
187
+ let livenessResponse = (data as? SDKEkycResult)?.checkLivenessResponse
188
+ let livenessDict: Any
189
+ if let resp = livenessResponse,
190
+ let encoded = try? JSONEncoder().encode(resp),
191
+ let dict = try? JSONSerialization.jsonObject(with: encoded) as? [String: Any] {
192
+ livenessDict = dict
193
+ } else {
194
+ livenessDict = [String: Any]()
195
+ }
196
+ let body: [String: Any] = ["event": event.description, "data": livenessDict]
189
197
  self?.emit("onLivenessSuccess", body: body)
190
198
  resolve(body)
191
199
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
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",
@@ -4,8 +4,8 @@ import { NfcConfig, NfcError } from '../types/ekycNFCType';
4
4
  import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent, EKYCError, getEkycError, AppIDType } from '../types/ekycType';
5
5
  import { C06Config } from '../types/ekycC06Type';
6
6
  import { OcrConfig, OcrError } from '../types/ekycOCRType';
7
- import { LivenessConfig, SDKFaceDetectStatus, LivenessError } from '../types/ekycLivenessType';
8
- import { FaceServiceConfig, FaceCompareError } from '../types/ekycFaceType';
7
+ import { LivenessConfig, SDKFaceDetectStatus, LivenessError, LivenessSuccessEvent } from '../types/ekycLivenessType';
8
+ import { FaceServiceConfig, FaceCompareError, FaceCompareSuccessEvent } from '../types/ekycFaceType';
9
9
  import { SmsOtpConfig, SmsOtpResult, SmsOtpError } from '../types/ekycSmsOtpType';
10
10
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignError, ESignAuthenticateResult, ESignPdfResult, ESignApiResponse } from '../types/ekycESignType';
11
11
  import { SDKFlowType, flowToStrings } from '../types/ekycFlowType';
@@ -194,7 +194,7 @@ export class FinosEKYCModule {
194
194
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
195
195
  * @param config.switchFrontCamera - Use front camera (default: false)
196
196
  */
197
- public async startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent> {
197
+ public async startLiveness(config: LivenessConfig): Promise<LivenessSuccessEvent> {
198
198
  this.validateSDKReady();
199
199
 
200
200
  try {
@@ -221,7 +221,7 @@ export class FinosEKYCModule {
221
221
  * Start face comparison (Face Service)
222
222
  * @param config Face service configuration
223
223
  */
224
- public async startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent> {
224
+ public async startFaceCompare(config: FaceServiceConfig): Promise<FaceCompareSuccessEvent> {
225
225
  this.validateSDKReady();
226
226
 
227
227
  const normalized: FaceServiceConfig = {
@@ -337,7 +337,7 @@ export class FinosEKYCModule {
337
337
  /**
338
338
  * Listen for liveness success events
339
339
  */
340
- public onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void) {
340
+ public onLivenessSuccess(callback: (data: LivenessSuccessEvent) => void) {
341
341
  return this.sdk.onLivenessSuccess(callback);
342
342
  }
343
343
 
@@ -352,7 +352,7 @@ export class FinosEKYCModule {
352
352
  /**
353
353
  * Listen for face compare success events
354
354
  */
355
- public onFaceCompareSuccess(callback: (data: SDKEkycResultWithEvent) => void) {
355
+ public onFaceCompareSuccess(callback: (data: FaceCompareSuccessEvent) => void) {
356
356
  return this.sdk.onFaceCompareSuccess(callback);
357
357
  }
358
358
 
@@ -2,8 +2,8 @@ import { Platform } from 'react-native';
2
2
  import sdkEKYC, { SDKeKYC, SDK_VERSION, SDK_NAME } from '../../EKYCModule';
3
3
  import { SmsOtpConfig, SmsOtpResult, SmsOtpError } from '../types/ekycSmsOtpType';
4
4
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignError, ESignAuthenticateResult, AuthorizeInfo, ESignPdfResult, ESignApiResponse } from '../types/ekycESignType';
5
- import { LivenessConfig, LivenessError } from '../types/ekycLivenessType';
6
- import { FaceServiceConfig, FaceCompareError } from '../types/ekycFaceType';
5
+ import { LivenessConfig, LivenessError, LivenessSuccessEvent } from '../types/ekycLivenessType';
6
+ import { FaceServiceConfig, FaceCompareError, FaceCompareSuccessEvent } from '../types/ekycFaceType';
7
7
  import { SDKEkycResultStringWithEvent, SDKEkycResultWithEvent, EKYCError, getEkycError } from '../types/ekycType';
8
8
 
9
9
  /**
@@ -565,14 +565,14 @@ export class FinosESignModule {
565
565
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
566
566
  * @param config.switchFrontCamera - Use front camera (default: false)
567
567
  */
568
- public async startLiveness(config: LivenessConfig): Promise<SDKEkycResultStringWithEvent> {
568
+ public async startLiveness(config: LivenessConfig): Promise<LivenessSuccessEvent> {
569
569
  this.validateSDKReady();
570
570
  // Pass through to SDK - error handling is done in EKYCModule.ts
571
571
  return await this.sdk.startLiveness(config);
572
572
  }
573
573
 
574
574
  // Liveness Event Listeners
575
- public onLivenessSuccess(callback: (data: SDKEkycResultStringWithEvent) => void) {
575
+ public onLivenessSuccess(callback: (data: LivenessSuccessEvent) => void) {
576
576
  return this.sdk.onLivenessSuccess(callback);
577
577
  }
578
578
 
@@ -587,7 +587,7 @@ export class FinosESignModule {
587
587
  * Face matching (Face Service)
588
588
  * @param config Face service configuration
589
589
  */
590
- public async startFaceCompare(config: FaceServiceConfig): Promise<SDKEkycResultStringWithEvent> {
590
+ public async startFaceCompare(config: FaceServiceConfig): Promise<FaceCompareSuccessEvent> {
591
591
  this.validateSDKReady();
592
592
  const normalized: FaceServiceConfig = {
593
593
  ...config,
@@ -598,7 +598,7 @@ export class FinosESignModule {
598
598
  }
599
599
 
600
600
  // Face Compare Event Listeners
601
- public onFaceCompareSuccess(callback: (data: SDKEkycResultWithEvent) => void) {
601
+ public onFaceCompareSuccess(callback: (data: FaceCompareSuccessEvent) => void) {
602
602
  return this.sdk.onFaceCompareSuccess(callback);
603
603
  }
604
604
 
@@ -14,6 +14,16 @@ export interface FaceVerifyResult {
14
14
  toBeReviewed?: string;
15
15
  }
16
16
 
17
+ /**
18
+ * Payload emitted by onFaceCompareSuccess listener.
19
+ * event = "FACE_SUCCESS" | "LOG_SUCCESS"
20
+ * data = CheckFaceResponse object (bridge serialises individual fields, not a JSON string)
21
+ */
22
+ export interface FaceCompareSuccessEvent {
23
+ event: string;
24
+ data: CheckFaceResponse;
25
+ }
26
+
17
27
  export interface FaceServiceConfig {
18
28
  /**
19
29
  * Backward/forward compatible:
@@ -131,3 +131,13 @@ export interface QualityCheck {
131
131
 
132
132
  /** Liveness error – dùng chung EKYCError (event, code, message). */
133
133
  export type LivenessError = import('./ekycType').EKYCError;
134
+
135
+ /**
136
+ * Payload emitted by onLivenessSuccess listener.
137
+ * event = "LIVENESS_SUCCESS" | "LOG_SUCCESS"
138
+ * data = CheckLivenessResponse object (bridge serialises fields directly, not a JSON string)
139
+ */
140
+ export interface LivenessSuccessEvent {
141
+ event: string;
142
+ data: CheckLivenessResponse;
143
+ }