@finos_sdk/sdk-ekyc 1.3.2 → 1.3.3

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.2"
68
+ def sdkVersion = "1.3.3.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")
@@ -38,6 +38,7 @@ import finos.sdk.smsotp.SdkSmsOtpService
38
38
  import finos.sdk.core.model.sdk.config.SmsOtpConfig
39
39
  import vn.softdreams.easyca.sdk.SdkeSign
40
40
 
41
+ import org.json.JSONArray
41
42
  import org.json.JSONObject
42
43
  import java.io.File
43
44
  import java.io.FileOutputStream
@@ -102,8 +103,20 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
102
103
  promise.resolve(true)
103
104
  }
104
105
  } catch (e: Exception) {
105
- Log.e(TAG, "❌ initSdkEkyc() failed: ${e.message}", e)
106
- promise.reject("INIT_ERROR", "Failed to initialize SDK EKYC: ${e.message}")
106
+ val msg = e.message?.lowercase() ?: ""
107
+ // SDK đã được khởi tạo (Koin/DI already started) → coi là thành công, cho icon pass xanh / text xanh
108
+ if (msg.contains("already been started") || msg.contains("already started") || msg.contains("already initialized")) {
109
+ Log.d(TAG, "✅ initSdkEkyc() – SDK đã khởi tạo, trả success (không coi là lỗi)")
110
+ val params = Arguments.createMap().apply {
111
+ putString("status", "success")
112
+ putString("message", "SDK EKYC already initialized")
113
+ }
114
+ sendEvent("EKYCInitEvent", params)
115
+ promise.resolve(true)
116
+ } else {
117
+ Log.e(TAG, "❌ initSdkEkyc() failed: ${e.message}", e)
118
+ promise.reject("INIT_ERROR", "Failed to initialize SDK EKYC: ${e.message}")
119
+ }
107
120
  }
108
121
  }
109
122
 
@@ -535,6 +548,20 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
535
548
  }
536
549
  }
537
550
 
551
+ /**
552
+ * Convert File to base64 string for passing to React Native.
553
+ * Mirrors Android SDK: data?.ekycStateModel?.eKYCFileModel?.imageFace
554
+ */
555
+ private fun fileToBase64(file: File?): String? {
556
+ if (file == null || !file.exists()) return null
557
+ return try {
558
+ Base64.encodeToString(file.readBytes(), Base64.NO_WRAP)
559
+ } catch (e: Exception) {
560
+ Log.e(TAG, "fileToBase64 error: ${e.message}", e)
561
+ null
562
+ }
563
+ }
564
+
538
565
  @ReactMethod
539
566
  fun startEkycUI(
540
567
  appKey: String,
@@ -574,15 +601,26 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
574
601
  // Parse StyleConfig JSON from React Native
575
602
  val styleConfig = parseStyleConfig(styleConfigJson)
576
603
 
577
- // Extract switchFrontCamera from optionConfigJson
604
+ // LivenessConfig theo SDKeKYCActivity (157-169): isActiveLiveness, autoCapture, forceCaptureTimeout, isShowCameraFont, customActions, activeActionCount
578
605
  val switchFrontCamera = extractBooleanValue(optionConfigJson, "switchFrontCamera") ?: false
606
+ val isActiveLiveness = extractBooleanValue(optionConfigJson, "isActiveLiveness") ?: true
607
+ val autoCapture = extractBooleanValue(optionConfigJson, "autoCapture") ?: true
608
+ val forceCaptureTimeoutSec = extractIntValue(optionConfigJson, "forceCaptureTimeout") ?: 30
609
+ val customActionsList = parseCustomActionsFromJson(optionConfigJson)
610
+ val activeActionCount = extractIntValue(optionConfigJson, "activeActionCount") ?: customActionsList?.size ?: 2
579
611
 
580
- // Create LivenessConfig if LIVENESS is in the flow and switchFrontCamera is set
581
- val livenessConfig = if (finalFlow.contains(SDKType.LIVENESS) && switchFrontCamera) {
582
- LivenessConfig(isShowCameraFont = true)
612
+ val livenessConfig = if (finalFlow.contains(SDKType.LIVENESS)) {
613
+ LivenessConfig(
614
+ isActiveLiveness = isActiveLiveness,
615
+ autoCapture = autoCapture,
616
+ forceCaptureTimeout = (forceCaptureTimeoutSec * 1000L).coerceAtLeast(0),
617
+ isShowCameraFont = switchFrontCamera,
618
+ customActions = customActionsList?.takeIf { it.isNotEmpty() },
619
+ activeActionCount = activeActionCount,
620
+ )
583
621
  } else null
584
622
 
585
- // Create EKYCConfigSDK like MainActivity.kt
623
+ // Create EKYCConfigSDK like SDKeKYCActivity / MainActivity.kt
586
624
  val ekycConfigSDK = EKYCConfigSDK(
587
625
  appKey = appKeyConfig,
588
626
  optionConfig = optionConfig.copy(language = if (language == "en") "en" else "vi"),
@@ -596,14 +634,31 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
596
634
  activity = currentActivity,
597
635
  ekycConfigSDK = ekycConfigSDK,
598
636
  callbackSuccess = { event, data ->
599
- Log.d(TAG, "✅ startEkycUI() success")
600
- val (eventMap, promiseMap) = createSeparateMaps { map ->
601
- map.putString("status", "success")
602
- map.putString("event", event.name.toString())
603
- map.putString("data", data.toString())
637
+ Log.d(TAG, "✅ startEkycUI() callback - event=${event.name}")
638
+ val result = data as? SDKEkycResult
639
+ val hasRealData = result?.ekycStateModel != null
640
+ // Chỉ resolve promise khi flow hoàn thành có data (SDK_END_SUCCESS); SDK_START_SUCCESS = activity vừa mở, bỏ qua
641
+ if (event == EKYCEvent.SDK_START_SUCCESS && !hasRealData) {
642
+ Log.d(TAG, "⏳ startEkycUI() SDK_START_SUCCESS – chờ SDK_END_SUCCESS")
643
+ sendEvent("onEkycUISuccess", Arguments.createMap().apply {
644
+ putString("status", "started")
645
+ putString("event", event.name.toString())
646
+ })
647
+ } else {
648
+ val ekycFiles = result?.ekycStateModel?.eKYCFileModel
649
+ val ekycTransactionId = result?.ekycStateModel?.transactionId ?: ""
650
+ val (eventMap, promiseMap) = createSeparateMaps { map ->
651
+ map.putString("status", "success")
652
+ map.putString("event", event.name.toString())
653
+ map.putString("data", data.toString())
654
+ map.putString("transactionId", ekycTransactionId)
655
+ ekycFiles?.imageFace?.absolutePath?.let { map.putString("imageFacePath", it) }
656
+ ekycFiles?.imageOcrFront?.absolutePath?.let { map.putString("imageOcrFrontPath", it) }
657
+ ekycFiles?.imageOcrBack?.absolutePath?.let { map.putString("imageOcrBackPath", it) }
658
+ }
659
+ sendEvent("onEkycUISuccess", eventMap)
660
+ promise.resolve(promiseMap)
604
661
  }
605
- sendEvent("onEkycUISuccess", eventMap)
606
- promise.resolve(promiseMap)
607
662
  },
608
663
  callbackError = { event, errorResult ->
609
664
  Log.e(TAG, "❌ startEkycUI() failed - Event: $event, Message: ${errorResult.message}")
@@ -703,6 +758,37 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
703
758
  }
704
759
  }
705
760
 
761
+ /** Parse customActions array from optionConfig JSON. Maps như SDKeKYCActivity getSelectedCustomActions. */
762
+ private fun parseCustomActionsFromJson(optionConfigJson: String): List<SDKFaceDetectStatus>? {
763
+ return try {
764
+ val obj = JSONObject(optionConfigJson)
765
+ val arr = obj.optJSONArray("customActions") ?: return null
766
+ if (arr.length() == 0) return null
767
+ val actions = mutableListOf<SDKFaceDetectStatus>()
768
+ for (i in 0 until arr.length()) {
769
+ val s = arr.optString(i, "")
770
+ when (s) {
771
+ "LEFT" -> actions.add(SDKFaceDetectStatus.LEFT)
772
+ "RIGHT" -> actions.add(SDKFaceDetectStatus.RIGHT)
773
+ "UP" -> actions.add(SDKFaceDetectStatus.UP)
774
+ "DOWN" -> actions.add(SDKFaceDetectStatus.DOWN)
775
+ "SMILE" -> actions.add(SDKFaceDetectStatus.SMILE)
776
+ "BLINK" -> actions.add(SDKFaceDetectStatus.BLINK)
777
+ "TILT_LEFT" -> actions.add(SDKFaceDetectStatus.TILT_LEFT)
778
+ "TILT_RIGHT" -> actions.add(SDKFaceDetectStatus.TILT_RIGHT)
779
+ "WINK_LEFT" -> actions.add(SDKFaceDetectStatus.WINK_LEFT)
780
+ "WINK_RIGHT" -> actions.add(SDKFaceDetectStatus.WINK_RIGHT)
781
+ "STRAIGHT" -> actions.add(SDKFaceDetectStatus.STRAIGHT)
782
+ else -> if (s.isNotEmpty()) Log.w(TAG, "parseCustomActionsFromJson: unknown action=$s")
783
+ }
784
+ }
785
+ if (actions.isNotEmpty()) actions else null
786
+ } catch (e: Exception) {
787
+ Log.w(TAG, "parseCustomActionsFromJson: ${e.message}")
788
+ null
789
+ }
790
+ }
791
+
706
792
  private fun parseAppKeyConfig(appKeyConfigJson: String): AppKeyConfig {
707
793
  return try {
708
794
  if (appKeyConfigJson.isBlank() || appKeyConfigJson == "{}") {
@@ -1019,14 +1105,26 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1019
1105
  promise.resolve(promiseMap)
1020
1106
  },
1021
1107
  callbackError = { event, error ->
1022
- Log.e(TAG, "❌ initializeESign() failed - Event: $event, Message: ${error.message}")
1023
- val errorMap = Arguments.createMap().apply {
1024
- putString("event", event.name.toString())
1025
- putString("message", error.message ?: "")
1026
- putString("code", error.code.toString())
1108
+ val msg = (error.message ?: "").lowercase()
1109
+ // eSign đã được khởi tạo → coi là thành công, cho icon pass xanh / text xanh
1110
+ if (msg.contains("đã được khởi tạo") || msg.contains("already") || msg.contains("already initialized")) {
1111
+ Log.d(TAG, "✅ initializeESign() – eSign đã khởi tạo, trả success (không coi là lỗi)")
1112
+ val (eventMap, promiseMap) = createSeparateMaps { map ->
1113
+ map.putString("code", "0")
1114
+ map.putString("message", "eSign already initialized")
1115
+ }
1116
+ sendEvent("onESignInitSuccess", eventMap)
1117
+ promise.resolve(promiseMap)
1118
+ } else {
1119
+ Log.e(TAG, "❌ initializeESign() failed - Event: $event, Message: ${error.message}")
1120
+ val errorMap = Arguments.createMap().apply {
1121
+ putString("event", event.name.toString())
1122
+ putString("message", error.message ?: "")
1123
+ putString("code", error.code.toString())
1124
+ }
1125
+ sendEvent("onESignError", errorMap)
1126
+ promise.reject(event.name.toString(), error.message ?: "Unknown Error")
1027
1127
  }
1028
- sendEvent("onESignError", errorMap)
1029
- promise.reject(event.name.toString(), error.message ?: "Unknown Error")
1030
1128
  }
1031
1129
  )
1032
1130
  } catch (e: Exception) {
@@ -1053,9 +1151,14 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1053
1151
  promise.resolve(token)
1054
1152
  },
1055
1153
  callbackError = { event, error ->
1056
- Log.e(TAG, "❌ getSdkToken() failed: $error")
1057
- // error might be String or Object, toString() covers both
1058
- promise.reject("GET_SDK_TOKEN_ERROR", error.toString())
1154
+ Log.e(TAG, "❌ getSdkToken() failed - Event: $event, Message: ${error.message}")
1155
+ val errorMap = Arguments.createMap().apply {
1156
+ putString("event", event.name.toString())
1157
+ putString("message", error.message ?: "")
1158
+ putString("code", error.code.toString())
1159
+ }
1160
+ sendEvent("onESignError", errorMap)
1161
+ promise.reject(event.name.toString(), error.message ?: "Get SDK Token failed")
1059
1162
  }
1060
1163
  )
1061
1164
  } catch (e: Exception) {
@@ -1420,26 +1523,14 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1420
1523
  callbackError = { event, error ->
1421
1524
  val errorMessage = error.message ?: ""
1422
1525
  val errorCode = error.code.toString()
1423
-
1424
- // Check if message contains "thành công" or code == "0" -> treat as success
1425
- if (errorMessage.contains("thành công", ignoreCase = true) || errorCode == "0") {
1426
- Log.d(TAG, "✅ registerRemoteSigning() success (async operation) - Message: $errorMessage")
1427
- val (eventMap, promiseMap) = createSeparateMaps { map ->
1428
- map.putString("response", "{\"status\":1,\"msg\":\"$errorMessage\"}")
1429
- map.putString("message", errorMessage)
1430
- }
1431
- sendEvent("onESignRegisterRemoteSigningSuccess", eventMap)
1432
- promise.resolve(promiseMap)
1433
- } else {
1434
- Log.e(TAG, "❌ registerRemoteSigning() failed - Event: $event, Message: $errorMessage")
1435
- val errorMap = Arguments.createMap().apply {
1436
- putString("event", event.name.toString())
1437
- putString("message", errorMessage)
1438
- putString("code", errorCode)
1439
- }
1440
- sendEvent("onESignError", errorMap)
1441
- promise.reject(event.name.toString(), errorMessage)
1526
+ Log.e(TAG, "❌ registerRemoteSigning() failed - Event: $event, Message: $errorMessage")
1527
+ val errorMap = Arguments.createMap().apply {
1528
+ putString("event", event.name.toString())
1529
+ putString("message", errorMessage)
1530
+ putString("code", errorCode)
1442
1531
  }
1532
+ sendEvent("onESignError", errorMap)
1533
+ promise.reject(event.name.toString(), errorMessage)
1443
1534
  }
1444
1535
  )
1445
1536
  } catch (e: Exception) {
@@ -1448,6 +1539,48 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1448
1539
  }
1449
1540
  }
1450
1541
 
1542
+ /**
1543
+ * Composite API: Register Remote Signing + Send Confirmation Document
1544
+ * Align với SdkeSignImpl.registerAndConfirm: gọi registerRemoteSigning -> parse sessionId -> sendConfirmationDocument
1545
+ */
1546
+ @ReactMethod
1547
+ fun registerAndConfirm(
1548
+ requestJson: String,
1549
+ confirmationDocBase64: String,
1550
+ promise: Promise
1551
+ ) {
1552
+ Log.d(TAG, "▶️ registerAndConfirm() called")
1553
+ try {
1554
+ SdkeSign.registerAndConfirm(
1555
+ requestJson = requestJson,
1556
+ confirmationDocBase64 = confirmationDocBase64,
1557
+ callbackSuccess = { rawResponse ->
1558
+ Log.d(TAG, "✅ registerAndConfirm() success")
1559
+ val (eventMap, promiseMap) = createSeparateMaps { map ->
1560
+ map.putString("response", rawResponse)
1561
+ }
1562
+ sendEvent("onESignRegisterAndConfirmSuccess", eventMap)
1563
+ promise.resolve(promiseMap)
1564
+ },
1565
+ callbackError = { event, error ->
1566
+ val errorMessage = error.message ?: ""
1567
+ val errorCode = error.code.toString()
1568
+ Log.e(TAG, "❌ registerAndConfirm() failed - Event: $event, Message: $errorMessage")
1569
+ val errorMap = Arguments.createMap().apply {
1570
+ putString("event", event.name.toString())
1571
+ putString("message", errorMessage)
1572
+ putString("code", errorCode)
1573
+ }
1574
+ sendEvent("onESignError", errorMap)
1575
+ promise.reject(event.name.toString(), errorMessage)
1576
+ }
1577
+ )
1578
+ } catch (e: Exception) {
1579
+ Log.e(TAG, "❌ registerAndConfirm() exception: ${e.message}", e)
1580
+ promise.reject("ESIGN_EXCEPTION", e.message, e)
1581
+ }
1582
+ }
1583
+
1451
1584
  @ReactMethod
1452
1585
  fun signPdf(
1453
1586
  requestJson: String,
@@ -1514,25 +1647,13 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1514
1647
  callbackError = { event, error ->
1515
1648
  val errorMessage = error.message ?: ""
1516
1649
  val errorCode = error.code.toString()
1517
-
1518
- // Check if message contains "thành công" or code == "0" -> treat as success
1519
- if (errorMessage.contains("thành công", ignoreCase = true) || errorCode == "0") {
1520
- Log.d(TAG, "✅ sendConfirmationDocument() success (async operation) - Message: $errorMessage")
1521
- val (eventMap, promiseMap) = createSeparateMaps { map ->
1522
- map.putString("response", "{\"status\":1,\"msg\":\"$errorMessage\"}")
1523
- map.putString("message", errorMessage)
1524
- }
1525
- sendEvent("onESignSendConfirmationDocumentSuccess", eventMap)
1526
- promise.resolve(promiseMap)
1527
- } else {
1528
- val errorMap = Arguments.createMap().apply {
1529
- putString("event", event.name.toString())
1530
- putString("message", errorMessage)
1531
- putString("code", errorCode)
1532
- }
1533
- sendEvent("onESignError", errorMap)
1534
- promise.reject(event.name.toString(), errorMessage)
1650
+ val errorMap = Arguments.createMap().apply {
1651
+ putString("event", event.name.toString())
1652
+ putString("message", errorMessage)
1653
+ putString("code", errorCode)
1535
1654
  }
1655
+ sendEvent("onESignError", errorMap)
1656
+ promise.reject(event.name.toString(), errorMessage)
1536
1657
  }
1537
1658
  )
1538
1659
  } catch (e: Exception) {
@@ -1,6 +1,6 @@
1
1
  import { EmitterSubscription } from 'react-native';
2
2
  import { NfcConfig, NfcError } from './src/types/ekycNFCType';
3
- import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent } from './src/types/ekycType';
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
6
  import { LivenessConfig, SDKFaceDetectStatus } from './src/types/ekycLivenessType';
@@ -82,6 +82,13 @@ declare class SDKeKYC {
82
82
  sendConfirmationDocument(requestJson: string): Promise<{
83
83
  response: string;
84
84
  }>;
85
+ /**
86
+ * Composite API: Register Remote Signing + Send Confirmation Document
87
+ * Align với SdkeSignImpl.registerAndConfirm
88
+ */
89
+ registerAndConfirm(requestJson: string, confirmationDocBase64: string): Promise<{
90
+ response: string;
91
+ }>;
85
92
  onESignInitSuccess(callback: (data: ESignInitResult) => void): EmitterSubscription | null;
86
93
  onESignOpenSessionSuccess(callback: (data: ESignOpenSessionResult) => void): EmitterSubscription | null;
87
94
  onESignRegisterDeviceSuccess(callback: (data: {
@@ -111,7 +118,15 @@ declare class SDKeKYC {
111
118
  onESignSendConfirmationDocumentSuccess(callback: (data: {
112
119
  response: string;
113
120
  }) => void): EmitterSubscription | null;
121
+ onESignRegisterAndConfirmSuccess(callback: (data: {
122
+ response: string;
123
+ }) => void): EmitterSubscription | null;
114
124
  onESignError(callback: (error: ESignError) => void): EmitterSubscription | null;
125
+ /**
126
+ * Start eKYC UI flow. On success, result contains the same data as Android:
127
+ * data.ekycStateModel.eKYCFileModel.imageFace → result.imageFace (base64),
128
+ * result.imageFacePath (file path), result.imageOcrFront / imageOcrBack, result.transactionId.
129
+ */
115
130
  startEkycUI(appKey: string, flowSDK: string[], language: string, transactionId: string, appKeyConfig: {
116
131
  appKey: string;
117
132
  appKeyNfc: string;
@@ -159,7 +174,7 @@ declare class SDKeKYC {
159
174
  textFont?: string;
160
175
  textColor?: number;
161
176
  };
162
- }): Promise<any>;
177
+ }): Promise<StartEkycUIResult>;
163
178
  }
164
179
  declare const sdkEKYC: SDKeKYC;
165
180
  export { SDKeKYC };
@@ -700,6 +700,21 @@ class SDKeKYC {
700
700
  throw error;
701
701
  }
702
702
  }
703
+ /**
704
+ * Composite API: Register Remote Signing + Send Confirmation Document
705
+ * Align với SdkeSignImpl.registerAndConfirm
706
+ */
707
+ async registerAndConfirm(requestJson, confirmationDocBase64) {
708
+ const nativeModule = this.ensureNativeModule();
709
+ try {
710
+ const result = await nativeModule.registerAndConfirm(requestJson, confirmationDocBase64);
711
+ return result;
712
+ }
713
+ catch (error) {
714
+ console.error('eSign Register And Confirm Error:', error);
715
+ throw error;
716
+ }
717
+ }
703
718
  // eSign Event Listeners
704
719
  onESignInitSuccess(callback) {
705
720
  try {
@@ -861,6 +876,22 @@ class SDKeKYC {
861
876
  return null;
862
877
  }
863
878
  }
879
+ onESignRegisterAndConfirmSuccess(callback) {
880
+ try {
881
+ const emitter = this.ensureEventEmitter();
882
+ if (!emitter) {
883
+ console.error('❌ Event emitter not available for onESignRegisterAndConfirmSuccess');
884
+ return null;
885
+ }
886
+ const listener = emitter.addListener('onESignRegisterAndConfirmSuccess', callback);
887
+ this.listeners.push(listener);
888
+ return listener;
889
+ }
890
+ catch (error) {
891
+ console.error('Failed to add onESignRegisterAndConfirmSuccess listener:', error);
892
+ return null;
893
+ }
894
+ }
864
895
  onESignError(callback) {
865
896
  try {
866
897
  const emitter = this.ensureEventEmitter();
@@ -877,7 +908,11 @@ class SDKeKYC {
877
908
  return null;
878
909
  }
879
910
  }
880
- // SdkEkycUI method
911
+ /**
912
+ * Start eKYC UI flow. On success, result contains the same data as Android:
913
+ * data.ekycStateModel.eKYCFileModel.imageFace → result.imageFace (base64),
914
+ * result.imageFacePath (file path), result.imageOcrFront / imageOcrBack, result.transactionId.
915
+ */
881
916
  async startEkycUI(appKey, flowSDK, language, transactionId, appKeyConfig, optionConfig, styleConfig) {
882
917
  if (!this.isInitialized) {
883
918
  throw new Error('SDK is not initialized. Please call initSdkEkyc() first.');
@@ -892,7 +927,7 @@ class SDKeKYC {
892
927
  const appKeyConfigJson = JSON.stringify(appKeyConfig);
893
928
  const styleConfigJson = styleConfig ? JSON.stringify(styleConfig) : '{}';
894
929
  const nativeModule = this.ensureNativeModule();
895
- const result = await nativeModule.startEkycUI(appKey, flowSDKJson, language, transactionId, optionConfigJson, appKeyConfigJson, styleConfigJson);
930
+ const result = (await nativeModule.startEkycUI(appKey, flowSDKJson, language, transactionId, optionConfigJson, appKeyConfigJson, styleConfigJson));
896
931
  console.log('✅ SdkEkycUI started successfully');
897
932
  return result;
898
933
  }
package/dist/index.d.ts CHANGED
@@ -5,13 +5,14 @@ export default sdkEKYC;
5
5
  export { sdkEKYC, SDKeKYC };
6
6
  export { FinosEKYC, FinosEKYCModule };
7
7
  export { FinosESign, FinosESignModule };
8
+ export { SDKFlowType, SDK_FLOW_OPTIONS, flowToStrings } from './src/types/ekycFlowType';
8
9
  export type { NfcConfig, NFCData, NfcInfo } from './src/types/ekycNFCType';
9
10
  export type { C06Config } from './src/types/ekycC06Type';
10
11
  export type { OcrConfig } from './src/types/ekycOCRType';
11
12
  export type { LivenessConfig } from './src/types/ekycLivenessType';
12
- export { SDKFaceDetectStatus } from './src/types/ekycLivenessType';
13
+ export { SDKFaceDetectStatus, SDK_LIVENESS_ACTIONS, customActionsToStrings } from './src/types/ekycLivenessType';
13
14
  export type { FaceServiceConfig } from './src/types/ekycFaceType';
14
- export type { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent } from './src/types/ekycType';
15
+ export type { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent, StartEkycUIResult } from './src/types/ekycType';
15
16
  export type { NfcError } from './src/types/ekycNFCType';
16
17
  export type { SDKTransactionResponse } from './src/types/ekycTransactionType';
17
18
  export type { SmsOtpConfig, SmsOtpResult, SmsOtpError } from './src/types/ekycSmsOtpType';
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.SDK_NAME = exports.SDK_VERSION = exports.parseNfcResponse = exports.SDKFaceDetectStatus = exports.FinosESignModule = exports.FinosESign = exports.FinosEKYCModule = exports.FinosEKYC = exports.SDKeKYC = exports.sdkEKYC = void 0;
26
+ exports.SDK_NAME = exports.SDK_VERSION = exports.parseNfcResponse = exports.customActionsToStrings = exports.SDK_LIVENESS_ACTIONS = exports.SDKFaceDetectStatus = exports.flowToStrings = exports.SDK_FLOW_OPTIONS = exports.SDKFlowType = exports.FinosESignModule = exports.FinosESign = exports.FinosEKYCModule = exports.FinosEKYC = exports.SDKeKYC = exports.sdkEKYC = void 0;
27
27
  const EKYCModule_1 = __importStar(require("./EKYCModule"));
28
28
  exports.sdkEKYC = EKYCModule_1.default;
29
29
  Object.defineProperty(exports, "SDKeKYC", { enumerable: true, get: function () { return EKYCModule_1.SDKeKYC; } });
@@ -38,8 +38,15 @@ Object.defineProperty(exports, "FinosESignModule", { enumerable: true, get: func
38
38
  console.log('✅ SDK modules loaded successfully');
39
39
  // Export main SDK instance and class (legacy)
40
40
  exports.default = EKYCModule_1.default;
41
+ // Flow type enum – bên sử dụng truyền enum vào startEkycUI(flowSDK: SDKFlowType[])
42
+ var ekycFlowType_1 = require("./src/types/ekycFlowType");
43
+ Object.defineProperty(exports, "SDKFlowType", { enumerable: true, get: function () { return ekycFlowType_1.SDKFlowType; } });
44
+ Object.defineProperty(exports, "SDK_FLOW_OPTIONS", { enumerable: true, get: function () { return ekycFlowType_1.SDK_FLOW_OPTIONS; } });
45
+ Object.defineProperty(exports, "flowToStrings", { enumerable: true, get: function () { return ekycFlowType_1.flowToStrings; } });
41
46
  var ekycLivenessType_1 = require("./src/types/ekycLivenessType");
42
47
  Object.defineProperty(exports, "SDKFaceDetectStatus", { enumerable: true, get: function () { return ekycLivenessType_1.SDKFaceDetectStatus; } });
48
+ Object.defineProperty(exports, "SDK_LIVENESS_ACTIONS", { enumerable: true, get: function () { return ekycLivenessType_1.SDK_LIVENESS_ACTIONS; } });
49
+ Object.defineProperty(exports, "customActionsToStrings", { enumerable: true, get: function () { return ekycLivenessType_1.customActionsToStrings; } });
43
50
  // Export utility functions
44
51
  var utils_1 = require("./src/utils/utils");
45
52
  Object.defineProperty(exports, "parseNfcResponse", { enumerable: true, get: function () { return utils_1.parseNfcResponse; } });
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
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,10 +3,11 @@ import { NfcConfig, NfcError } from '../types/ekycNFCType';
3
3
  import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent } from '../types/ekycType';
4
4
  import { C06Config } from '../types/ekycC06Type';
5
5
  import { OcrConfig } from '../types/ekycOCRType';
6
- import { LivenessConfig } from '../types/ekycLivenessType';
6
+ import { LivenessConfig, SDKFaceDetectStatus } from '../types/ekycLivenessType';
7
7
  import { FaceServiceConfig } from '../types/ekycFaceType';
8
8
  import { SmsOtpConfig, SmsOtpResult, SmsOtpError } from '../types/ekycSmsOtpType';
9
9
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignError } from '../types/ekycESignType';
10
+ import { SDKFlowType } from '../types/ekycFlowType';
10
11
  /**
11
12
  * Finos eKYC SDK Module
12
13
  *
@@ -72,7 +73,7 @@ export declare class FinosEKYCModule {
72
73
  * @param config.isActiveLiveness - Enable active liveness detection (default: false)
73
74
  * @param config.autoCapture - Enable auto capture (default: true)
74
75
  * @param config.isShowCameraFont - Show camera font (default: true)
75
- * @param config.customActions - Custom actions array (LEFT, RIGHT, STRAIGHT). If provided, uses these actions instead of random
76
+ * @param config.customActions - SDKFaceDetectStatus[] enum (LEFT, RIGHT, SMILE, BLINK, ...). Bên sử dụng truyền enum thay string
76
77
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
77
78
  * @param config.switchFrontCamera - Use front camera (default: false)
78
79
  */
@@ -282,14 +283,14 @@ export declare class FinosEKYCModule {
282
283
  /**
283
284
  * Start eKYC UI with flow (like MainActivity.kt)
284
285
  * @param appKey Main app key
285
- * @param flowSDK Array of SDK types
286
+ * @param flowSDK Array of SDKFlowType enum (OCR, NFC, LIVENESS) – bên sử dụng truyền enum
286
287
  * @param language Language code (vi/en)
287
288
  * @param transactionId Transaction ID
288
289
  * @param appKeyConfig Optional app key configuration
289
290
  * @param optionConfig Optional configuration settings (includes switchFrontCamera for camera control)
290
291
  * @param styleConfig Optional style configuration
291
292
  */
292
- startEkycUI(appKey: string, flowSDK: string[], language: string, transactionId: string, appKeyConfig: {
293
+ startEkycUI(appKey: string, flowSDK: SDKFlowType[], language: string, transactionId: string, appKeyConfig: {
293
294
  appKey: string;
294
295
  appKeyNfc: string;
295
296
  appKeyOcr: string;
@@ -301,6 +302,13 @@ export declare class FinosEKYCModule {
301
302
  countMaxRetry?: number;
302
303
  language?: string;
303
304
  switchFrontCamera?: boolean;
305
+ /** LivenessConfig – SDKeKYCActivity (157-169) */
306
+ isActiveLiveness?: boolean;
307
+ autoCapture?: boolean;
308
+ forceCaptureTimeout?: number;
309
+ /** Bên sử dụng truyền enum – SDKFaceDetectStatus[] thay vì string[] */
310
+ customActions?: SDKFaceDetectStatus[];
311
+ activeActionCount?: number;
304
312
  }, styleConfig?: {
305
313
  textSize?: number;
306
314
  textFont?: string;
@@ -28,6 +28,7 @@ const react_native_1 = require("react-native");
28
28
  const EKYCModule_1 = __importStar(require("../../EKYCModule"));
29
29
  Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return EKYCModule_1.SDK_VERSION; } });
30
30
  Object.defineProperty(exports, "SDK_NAME", { enumerable: true, get: function () { return EKYCModule_1.SDK_NAME; } });
31
+ const ekycFlowType_1 = require("../types/ekycFlowType");
31
32
  /**
32
33
  * Finos eKYC SDK Module
33
34
  *
@@ -184,7 +185,7 @@ class FinosEKYCModule {
184
185
  * @param config.isActiveLiveness - Enable active liveness detection (default: false)
185
186
  * @param config.autoCapture - Enable auto capture (default: true)
186
187
  * @param config.isShowCameraFont - Show camera font (default: true)
187
- * @param config.customActions - Custom actions array (LEFT, RIGHT, STRAIGHT). If provided, uses these actions instead of random
188
+ * @param config.customActions - SDKFaceDetectStatus[] enum (LEFT, RIGHT, SMILE, BLINK, ...). Bên sử dụng truyền enum thay string
188
189
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
189
190
  * @param config.switchFrontCamera - Use front camera (default: false)
190
191
  */
@@ -631,7 +632,7 @@ class FinosEKYCModule {
631
632
  /**
632
633
  * Start eKYC UI with flow (like MainActivity.kt)
633
634
  * @param appKey Main app key
634
- * @param flowSDK Array of SDK types
635
+ * @param flowSDK Array of SDKFlowType enum (OCR, NFC, LIVENESS) – bên sử dụng truyền enum
635
636
  * @param language Language code (vi/en)
636
637
  * @param transactionId Transaction ID
637
638
  * @param appKeyConfig Optional app key configuration
@@ -648,7 +649,7 @@ class FinosEKYCModule {
648
649
  }
649
650
  console.log('🔑 AppKeyConfig:', appKeyConfig);
650
651
  console.log('🎨 StyleConfig:', styleConfig);
651
- const result = await this.sdk.startEkycUI(appKey, flowSDK, language, transactionId, appKeyConfig, optionConfig, styleConfig);
652
+ const result = await this.sdk.startEkycUI(appKey, (0, ekycFlowType_1.flowToStrings)(flowSDK), language, transactionId, appKeyConfig, optionConfig, styleConfig);
652
653
  console.log('✅ eKYC UI started successfully');
653
654
  return result;
654
655
  }
@@ -151,6 +151,15 @@ export declare class FinosESignModule {
151
151
  sendConfirmationDocument(requestJson: string): Promise<{
152
152
  response: string;
153
153
  }>;
154
+ /**
155
+ * Composite API: Register Remote Signing + Send Confirmation Document
156
+ * Align với SdkeSignImpl.registerAndConfirm
157
+ * @param requestJson JSON request body for registerRemoteSigning
158
+ * @param confirmationDocBase64 PDF base64 for acceptanceDocs
159
+ */
160
+ registerAndConfirm(requestJson: string, confirmationDocBase64: string): Promise<{
161
+ response: string;
162
+ }>;
154
163
  /**
155
164
  * Remove all event listeners
156
165
  */
@@ -184,6 +193,9 @@ export declare class FinosESignModule {
184
193
  onESignSendConfirmationDocumentSuccess(callback: (data: {
185
194
  response: string;
186
195
  }) => void): import("react-native").EmitterSubscription | null;
196
+ onESignRegisterAndConfirmSuccess(callback: (data: {
197
+ response: string;
198
+ }) => void): import("react-native").EmitterSubscription | null;
187
199
  onESignError(callback: (error: ESignError) => void): import("react-native").EmitterSubscription | null;
188
200
  /**
189
201
  * Start liveness detection
@@ -281,6 +281,16 @@ class FinosESignModule {
281
281
  // Pass through to SDK - error handling is done in EKYCModule.ts
282
282
  return await this.sdk.sendConfirmationDocument(requestJson);
283
283
  }
284
+ /**
285
+ * Composite API: Register Remote Signing + Send Confirmation Document
286
+ * Align với SdkeSignImpl.registerAndConfirm
287
+ * @param requestJson JSON request body for registerRemoteSigning
288
+ * @param confirmationDocBase64 PDF base64 for acceptanceDocs
289
+ */
290
+ async registerAndConfirm(requestJson, confirmationDocBase64) {
291
+ this.validateSDKReady();
292
+ return await this.sdk.registerAndConfirm(requestJson, confirmationDocBase64);
293
+ }
284
294
  /**
285
295
  * Remove all event listeners
286
296
  */
@@ -359,6 +369,13 @@ class FinosESignModule {
359
369
  }
360
370
  return listener;
361
371
  }
372
+ onESignRegisterAndConfirmSuccess(callback) {
373
+ const listener = this.sdk.onESignRegisterAndConfirmSuccess(callback);
374
+ if (!listener) {
375
+ console.warn('⚠️ onESignRegisterAndConfirmSuccess: Event emitter not ready.');
376
+ }
377
+ return listener;
378
+ }
362
379
  onESignError(callback) {
363
380
  const listener = this.sdk.onESignError(callback);
364
381
  if (!listener) {
@@ -491,6 +508,7 @@ const isMethod = (prop) => {
491
508
  prop === 'listSignRequest' ||
492
509
  prop === 'confirmSign' ||
493
510
  prop === 'registerRemoteSigning' ||
511
+ prop === 'registerAndConfirm' ||
494
512
  prop === 'signPdf' ||
495
513
  prop === 'sendConfirmationDocument' ||
496
514
  prop === 'startLiveness' ||
@@ -547,7 +565,7 @@ const createFinosESignStub = () => {
547
565
  'onESignInitSuccess', 'onESignOpenSessionSuccess', 'onESignRegisterDeviceSuccess',
548
566
  'onESignListCertsSuccess', 'onESignVerifyCertSuccess', 'onESignListSignRequestSuccess',
549
567
  'onESignConfirmSignSuccess', 'onESignRegisterRemoteSigningSuccess',
550
- 'onESignSignPdfSuccess', 'onESignSendConfirmationDocumentSuccess', 'onESignError',
568
+ 'onESignSignPdfSuccess', 'onESignSendConfirmationDocumentSuccess', 'onESignRegisterAndConfirmSuccess', 'onESignError',
551
569
  'onLivenessSuccess', 'onLivenessError', 'onFaceCompareSuccess', 'onFaceCompareError'
552
570
  ];
553
571
  eventListenerMethods.forEach(method => {
@@ -561,7 +579,7 @@ const createFinosESignStub = () => {
561
579
  'initialize', 'startNfcScan', 'checkC06', 'startOcr', 'startLiveness', 'startFaceCompare',
562
580
  'startEkycUI', 'sendOtp', 'verifyOtp', 'resendOtp', 'initializeESign', 'getSdkToken', 'openSessionId',
563
581
  'registerDevice', 'listCerts', 'verifyCert', 'listSignRequest', 'confirmSign',
564
- 'registerRemoteSigning', 'signPdf', 'sendConfirmationDocument',
582
+ 'registerRemoteSigning', 'registerAndConfirm', 'signPdf', 'sendConfirmationDocument',
565
583
  'onResume', 'onPause', 'isSDKReady', 'getSDKInfo'
566
584
  ];
567
585
  otherMethods.forEach(method => {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Các bước trong flow eKYC – map với Android SDKType (OCR, NFC, LIVENESS).
3
+ * Bên sử dụng truyền enum vào SDK thay vì string.
4
+ *
5
+ * @example
6
+ * import { FinosEKYC, SDKFlowType } from '@finos_sdk/sdk-ekyc';
7
+ * await FinosEKYC.startEkycUI(appKey, [SDKFlowType.OCR, SDKFlowType.NFC, SDKFlowType.LIVENESS], ...);
8
+ */
9
+ export declare enum SDKFlowType {
10
+ OCR = "OCR",
11
+ NFC = "NFC",
12
+ LIVENESS = "LIVENESS"
13
+ }
14
+ /** Mảng đầy đủ các bước (dùng cho picker / default flow). */
15
+ export declare const SDK_FLOW_OPTIONS: readonly SDKFlowType[];
16
+ /** Chuyển flow enum[] sang string[] khi gửi xuống native (giá trị enum đã là 'OCR'|'NFC'|'LIVENESS'). */
17
+ export declare function flowToStrings(flow: SDKFlowType[]): string[];
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flowToStrings = exports.SDK_FLOW_OPTIONS = exports.SDKFlowType = void 0;
4
+ /**
5
+ * Các bước trong flow eKYC – map với Android SDKType (OCR, NFC, LIVENESS).
6
+ * Bên sử dụng truyền enum vào SDK thay vì string.
7
+ *
8
+ * @example
9
+ * import { FinosEKYC, SDKFlowType } from '@finos_sdk/sdk-ekyc';
10
+ * await FinosEKYC.startEkycUI(appKey, [SDKFlowType.OCR, SDKFlowType.NFC, SDKFlowType.LIVENESS], ...);
11
+ */
12
+ var SDKFlowType;
13
+ (function (SDKFlowType) {
14
+ SDKFlowType["OCR"] = "OCR";
15
+ SDKFlowType["NFC"] = "NFC";
16
+ SDKFlowType["LIVENESS"] = "LIVENESS";
17
+ })(SDKFlowType = exports.SDKFlowType || (exports.SDKFlowType = {}));
18
+ /** Mảng đầy đủ các bước (dùng cho picker / default flow). */
19
+ exports.SDK_FLOW_OPTIONS = [
20
+ SDKFlowType.OCR,
21
+ SDKFlowType.NFC,
22
+ SDKFlowType.LIVENESS,
23
+ ];
24
+ /** Chuyển flow enum[] sang string[] khi gửi xuống native (giá trị enum đã là 'OCR'|'NFC'|'LIVENESS'). */
25
+ function flowToStrings(flow) {
26
+ return flow.map(f => f);
27
+ }
28
+ exports.flowToStrings = flowToStrings;
@@ -1,9 +1,25 @@
1
1
  import { CheckSummaryResponse } from "./ekycOCRType";
2
+ /**
3
+ * Custom actions cho liveness – map Android SDKFaceDetectStatus.
4
+ * Bên sử dụng truyền enum vào optionConfig.customActions thay vì string.
5
+ */
2
6
  export declare enum SDKFaceDetectStatus {
3
7
  LEFT = "LEFT",
4
8
  RIGHT = "RIGHT",
9
+ UP = "UP",
10
+ DOWN = "DOWN",
11
+ SMILE = "SMILE",
12
+ BLINK = "BLINK",
13
+ TILT_LEFT = "TILT_LEFT",
14
+ TILT_RIGHT = "TILT_RIGHT",
15
+ WINK_LEFT = "WINK_LEFT",
16
+ WINK_RIGHT = "WINK_RIGHT",
5
17
  STRAIGHT = "STRAIGHT"
6
18
  }
19
+ /** Mảng đầy đủ các action (trừ STRAIGHT thường được SDK tự thêm cuối) – dùng cho picker / default. */
20
+ export declare const SDK_LIVENESS_ACTIONS: readonly SDKFaceDetectStatus[];
21
+ /** Chuyển customActions enum[] sang string[] khi gửi xuống native. */
22
+ export declare function customActionsToStrings(actions: SDKFaceDetectStatus[]): string[];
7
23
  export interface LivenessConfig {
8
24
  appKey: string;
9
25
  transactionId?: string;
@@ -1,9 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SDKFaceDetectStatus = void 0;
3
+ exports.customActionsToStrings = exports.SDK_LIVENESS_ACTIONS = exports.SDKFaceDetectStatus = void 0;
4
+ /**
5
+ * Custom actions cho liveness – map Android SDKFaceDetectStatus.
6
+ * Bên sử dụng truyền enum vào optionConfig.customActions thay vì string.
7
+ */
4
8
  var SDKFaceDetectStatus;
5
9
  (function (SDKFaceDetectStatus) {
6
10
  SDKFaceDetectStatus["LEFT"] = "LEFT";
7
11
  SDKFaceDetectStatus["RIGHT"] = "RIGHT";
12
+ SDKFaceDetectStatus["UP"] = "UP";
13
+ SDKFaceDetectStatus["DOWN"] = "DOWN";
14
+ SDKFaceDetectStatus["SMILE"] = "SMILE";
15
+ SDKFaceDetectStatus["BLINK"] = "BLINK";
16
+ SDKFaceDetectStatus["TILT_LEFT"] = "TILT_LEFT";
17
+ SDKFaceDetectStatus["TILT_RIGHT"] = "TILT_RIGHT";
18
+ SDKFaceDetectStatus["WINK_LEFT"] = "WINK_LEFT";
19
+ SDKFaceDetectStatus["WINK_RIGHT"] = "WINK_RIGHT";
8
20
  SDKFaceDetectStatus["STRAIGHT"] = "STRAIGHT";
9
21
  })(SDKFaceDetectStatus = exports.SDKFaceDetectStatus || (exports.SDKFaceDetectStatus = {}));
22
+ /** Mảng đầy đủ các action (trừ STRAIGHT thường được SDK tự thêm cuối) – dùng cho picker / default. */
23
+ exports.SDK_LIVENESS_ACTIONS = [
24
+ SDKFaceDetectStatus.LEFT,
25
+ SDKFaceDetectStatus.RIGHT,
26
+ SDKFaceDetectStatus.UP,
27
+ SDKFaceDetectStatus.DOWN,
28
+ SDKFaceDetectStatus.SMILE,
29
+ SDKFaceDetectStatus.BLINK,
30
+ SDKFaceDetectStatus.TILT_LEFT,
31
+ SDKFaceDetectStatus.TILT_RIGHT,
32
+ SDKFaceDetectStatus.WINK_LEFT,
33
+ SDKFaceDetectStatus.WINK_RIGHT,
34
+ SDKFaceDetectStatus.STRAIGHT,
35
+ ];
36
+ /** Chuyển customActions enum[] sang string[] khi gửi xuống native. */
37
+ function customActionsToStrings(actions) {
38
+ return actions.map(a => a);
39
+ }
40
+ exports.customActionsToStrings = customActionsToStrings;
@@ -20,3 +20,33 @@ export interface SDKEkycResultStringWithEvent {
20
20
  data: string;
21
21
  event: string;
22
22
  }
23
+ /**
24
+ * Result from startEkycUI() – maps Android SDK data.ekycStateModel.eKYCFileModel.
25
+ * Use the same shape as Android: data?.ekycStateModel?.eKYCFileModel?.imageFace.
26
+ *
27
+ * @example
28
+ * const result = await sdkEKYC.startEkycUI(...);
29
+ * const selfieBase64 = result?.imageFace; // base64 selfie (liveness/face)
30
+ * const frontBase64 = result?.imageOcrFront; // base64 CCCD/CMND mặt trước
31
+ * const backBase64 = result?.imageOcrBack; // base64 CCCD/CMND mặt sau
32
+ * const selfiePath = result?.imageFacePath; // file path (e.g. for FormData)
33
+ * const transactionId = result?.transactionId;
34
+ */
35
+ export interface StartEkycUIResult {
36
+ status: 'success';
37
+ event: string;
38
+ data?: string;
39
+ transactionId?: string;
40
+ /** Base64 selfie (liveness) – same as Android data.ekycStateModel.eKYCFileModel.imageFace */
41
+ imageFace?: string;
42
+ /** Base64 OCR front – same as Android eKYCFileModel.imageOcrFront */
43
+ imageOcrFront?: string;
44
+ /** Base64 OCR back – same as Android eKYCFileModel.imageOcrBack */
45
+ imageOcrBack?: string;
46
+ /** Absolute path to selfie file (e.g. for react-native-fs / FormData) */
47
+ imageFacePath?: string;
48
+ /** Absolute path to OCR front image file */
49
+ imageOcrFrontPath?: string;
50
+ /** Absolute path to OCR back image file */
51
+ imageOcrBackPath?: string;
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
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,10 +4,11 @@ import { NfcConfig, NfcError } from '../types/ekycNFCType';
4
4
  import { SDKEkycResultWithEvent, SDKEkycResultStringWithEvent } from '../types/ekycType';
5
5
  import { C06Config } from '../types/ekycC06Type';
6
6
  import { OcrConfig } from '../types/ekycOCRType';
7
- import { LivenessConfig } from '../types/ekycLivenessType';
7
+ import { LivenessConfig, SDKFaceDetectStatus } from '../types/ekycLivenessType';
8
8
  import { FaceServiceConfig } from '../types/ekycFaceType';
9
9
  import { SmsOtpConfig, SmsOtpResult, SmsOtpError } from '../types/ekycSmsOtpType';
10
10
  import { ESignInitResult, ESignOpenSessionResult, ESignCertificate, ESignSignRequest, ESignError, ESignAuthenticateResult } from '../types/ekycESignType';
11
+ import { SDKFlowType, flowToStrings } from '../types/ekycFlowType';
11
12
 
12
13
  /**
13
14
  * Finos eKYC SDK Module
@@ -189,7 +190,7 @@ export class FinosEKYCModule {
189
190
  * @param config.isActiveLiveness - Enable active liveness detection (default: false)
190
191
  * @param config.autoCapture - Enable auto capture (default: true)
191
192
  * @param config.isShowCameraFont - Show camera font (default: true)
192
- * @param config.customActions - Custom actions array (LEFT, RIGHT, STRAIGHT). If provided, uses these actions instead of random
193
+ * @param config.customActions - SDKFaceDetectStatus[] enum (LEFT, RIGHT, SMILE, BLINK, ...). Bên sử dụng truyền enum thay string
193
194
  * @param config.activeActionCount - Number of random actions (1-10), only used when customActions is null (default: 2)
194
195
  * @param config.switchFrontCamera - Use front camera (default: false)
195
196
  */
@@ -721,7 +722,7 @@ export class FinosEKYCModule {
721
722
  /**
722
723
  * Start eKYC UI with flow (like MainActivity.kt)
723
724
  * @param appKey Main app key
724
- * @param flowSDK Array of SDK types
725
+ * @param flowSDK Array of SDKFlowType enum (OCR, NFC, LIVENESS) – bên sử dụng truyền enum
725
726
  * @param language Language code (vi/en)
726
727
  * @param transactionId Transaction ID
727
728
  * @param appKeyConfig Optional app key configuration
@@ -730,7 +731,7 @@ export class FinosEKYCModule {
730
731
  */
731
732
  public async startEkycUI(
732
733
  appKey: string,
733
- flowSDK: string[],
734
+ flowSDK: SDKFlowType[],
734
735
  language: string,
735
736
  transactionId: string,
736
737
  appKeyConfig: {
@@ -746,6 +747,13 @@ export class FinosEKYCModule {
746
747
  countMaxRetry?: number;
747
748
  language?: string;
748
749
  switchFrontCamera?: boolean;
750
+ /** LivenessConfig – SDKeKYCActivity (157-169) */
751
+ isActiveLiveness?: boolean;
752
+ autoCapture?: boolean;
753
+ forceCaptureTimeout?: number; // seconds, native uses ms
754
+ /** Bên sử dụng truyền enum – SDKFaceDetectStatus[] thay vì string[] */
755
+ customActions?: SDKFaceDetectStatus[];
756
+ activeActionCount?: number;
749
757
  },
750
758
  styleConfig?: {
751
759
  textSize?: number;
@@ -798,7 +806,7 @@ export class FinosEKYCModule {
798
806
 
799
807
  const result = await this.sdk.startEkycUI(
800
808
  appKey,
801
- flowSDK,
809
+ flowToStrings(flowSDK),
802
810
  language,
803
811
  transactionId,
804
812
  appKeyConfig,
@@ -333,6 +333,20 @@ export class FinosESignModule {
333
333
  return await this.sdk.sendConfirmationDocument(requestJson);
334
334
  }
335
335
 
336
+ /**
337
+ * Composite API: Register Remote Signing + Send Confirmation Document
338
+ * Align với SdkeSignImpl.registerAndConfirm
339
+ * @param requestJson JSON request body for registerRemoteSigning
340
+ * @param confirmationDocBase64 PDF base64 for acceptanceDocs
341
+ */
342
+ public async registerAndConfirm(
343
+ requestJson: string,
344
+ confirmationDocBase64: string
345
+ ): Promise<{ response: string }> {
346
+ this.validateSDKReady();
347
+ return await this.sdk.registerAndConfirm(requestJson, confirmationDocBase64);
348
+ }
349
+
336
350
  /**
337
351
  * Remove all event listeners
338
352
  */
@@ -424,6 +438,14 @@ export class FinosESignModule {
424
438
  return listener;
425
439
  }
426
440
 
441
+ public onESignRegisterAndConfirmSuccess(callback: (data: { response: string }) => void) {
442
+ const listener = this.sdk.onESignRegisterAndConfirmSuccess(callback);
443
+ if (!listener) {
444
+ console.warn('⚠️ onESignRegisterAndConfirmSuccess: Event emitter not ready.');
445
+ }
446
+ return listener;
447
+ }
448
+
427
449
  public onESignError(callback: (error: ESignError) => void) {
428
450
  const listener = this.sdk.onESignError(callback);
429
451
  if (!listener) {
@@ -633,6 +655,7 @@ const isMethod = (prop: string | symbol): boolean => {
633
655
  prop === 'listSignRequest' ||
634
656
  prop === 'confirmSign' ||
635
657
  prop === 'registerRemoteSigning' ||
658
+ prop === 'registerAndConfirm' ||
636
659
  prop === 'signPdf' ||
637
660
  prop === 'sendConfirmationDocument' ||
638
661
  prop === 'startLiveness' ||
@@ -689,8 +712,8 @@ const createFinosESignStub = (): FinosESignModule => {
689
712
  'onSmsOtpSendSuccess', 'onSmsOtpVerifySuccess', 'onSmsOtpResendSuccess', 'onSmsOtpError',
690
713
  'onESignInitSuccess', 'onESignOpenSessionSuccess', 'onESignRegisterDeviceSuccess',
691
714
  'onESignListCertsSuccess', 'onESignVerifyCertSuccess', 'onESignListSignRequestSuccess',
692
- 'onESignConfirmSignSuccess', 'onESignRegisterRemoteSigningSuccess',
693
- 'onESignSignPdfSuccess', 'onESignSendConfirmationDocumentSuccess', 'onESignError',
715
+ 'onESignConfirmSignSuccess', 'onESignRegisterRemoteSigningSuccess',
716
+ 'onESignSignPdfSuccess', 'onESignSendConfirmationDocumentSuccess', 'onESignRegisterAndConfirmSuccess', 'onESignError',
694
717
  'onLivenessSuccess', 'onLivenessError', 'onFaceCompareSuccess', 'onFaceCompareError'
695
718
  ];
696
719
 
@@ -706,7 +729,7 @@ const createFinosESignStub = (): FinosESignModule => {
706
729
  'initialize', 'startNfcScan', 'checkC06', 'startOcr', 'startLiveness', 'startFaceCompare',
707
730
  'startEkycUI', 'sendOtp', 'verifyOtp', 'resendOtp', 'initializeESign', 'getSdkToken', 'openSessionId',
708
731
  'registerDevice', 'listCerts', 'verifyCert', 'listSignRequest', 'confirmSign',
709
- 'registerRemoteSigning', 'signPdf', 'sendConfirmationDocument',
732
+ 'registerRemoteSigning', 'registerAndConfirm', 'signPdf', 'sendConfirmationDocument',
710
733
  'onResume', 'onPause', 'isSDKReady', 'getSDKInfo'
711
734
  ];
712
735
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Các bước trong flow eKYC – map với Android SDKType (OCR, NFC, LIVENESS).
3
+ * Bên sử dụng truyền enum vào SDK thay vì string.
4
+ *
5
+ * @example
6
+ * import { FinosEKYC, SDKFlowType } from '@finos_sdk/sdk-ekyc';
7
+ * await FinosEKYC.startEkycUI(appKey, [SDKFlowType.OCR, SDKFlowType.NFC, SDKFlowType.LIVENESS], ...);
8
+ */
9
+ export enum SDKFlowType {
10
+ OCR = 'OCR',
11
+ NFC = 'NFC',
12
+ LIVENESS = 'LIVENESS',
13
+ }
14
+
15
+ /** Mảng đầy đủ các bước (dùng cho picker / default flow). */
16
+ export const SDK_FLOW_OPTIONS: readonly SDKFlowType[] = [
17
+ SDKFlowType.OCR,
18
+ SDKFlowType.NFC,
19
+ SDKFlowType.LIVENESS,
20
+ ] as const;
21
+
22
+ /** Chuyển flow enum[] sang string[] khi gửi xuống native (giá trị enum đã là 'OCR'|'NFC'|'LIVENESS'). */
23
+ export function flowToStrings(flow: SDKFlowType[]): string[] {
24
+ return flow.map(f => f as string);
25
+ }
@@ -1,9 +1,41 @@
1
1
  import { CheckSummaryResponse } from "./ekycOCRType";
2
2
 
3
+ /**
4
+ * Custom actions cho liveness – map Android SDKFaceDetectStatus.
5
+ * Bên sử dụng truyền enum vào optionConfig.customActions thay vì string.
6
+ */
3
7
  export enum SDKFaceDetectStatus {
4
8
  LEFT = "LEFT",
5
9
  RIGHT = "RIGHT",
6
- STRAIGHT = "STRAIGHT"
10
+ UP = "UP",
11
+ DOWN = "DOWN",
12
+ SMILE = "SMILE",
13
+ BLINK = "BLINK",
14
+ TILT_LEFT = "TILT_LEFT",
15
+ TILT_RIGHT = "TILT_RIGHT",
16
+ WINK_LEFT = "WINK_LEFT",
17
+ WINK_RIGHT = "WINK_RIGHT",
18
+ STRAIGHT = "STRAIGHT",
19
+ }
20
+
21
+ /** Mảng đầy đủ các action (trừ STRAIGHT thường được SDK tự thêm cuối) – dùng cho picker / default. */
22
+ export const SDK_LIVENESS_ACTIONS: readonly SDKFaceDetectStatus[] = [
23
+ SDKFaceDetectStatus.LEFT,
24
+ SDKFaceDetectStatus.RIGHT,
25
+ SDKFaceDetectStatus.UP,
26
+ SDKFaceDetectStatus.DOWN,
27
+ SDKFaceDetectStatus.SMILE,
28
+ SDKFaceDetectStatus.BLINK,
29
+ SDKFaceDetectStatus.TILT_LEFT,
30
+ SDKFaceDetectStatus.TILT_RIGHT,
31
+ SDKFaceDetectStatus.WINK_LEFT,
32
+ SDKFaceDetectStatus.WINK_RIGHT,
33
+ SDKFaceDetectStatus.STRAIGHT,
34
+ ] as const;
35
+
36
+ /** Chuyển customActions enum[] sang string[] khi gửi xuống native. */
37
+ export function customActionsToStrings(actions: SDKFaceDetectStatus[]): string[] {
38
+ return actions.map(a => a as string);
7
39
  }
8
40
 
9
41
  export interface LivenessConfig {
@@ -22,4 +22,35 @@ export interface SDKEkycResultWithEvent {
22
22
  export interface SDKEkycResultStringWithEvent {
23
23
  data: string;
24
24
  event: string;
25
+ }
26
+
27
+ /**
28
+ * Result from startEkycUI() – maps Android SDK data.ekycStateModel.eKYCFileModel.
29
+ * Use the same shape as Android: data?.ekycStateModel?.eKYCFileModel?.imageFace.
30
+ *
31
+ * @example
32
+ * const result = await sdkEKYC.startEkycUI(...);
33
+ * const selfieBase64 = result?.imageFace; // base64 selfie (liveness/face)
34
+ * const frontBase64 = result?.imageOcrFront; // base64 CCCD/CMND mặt trước
35
+ * const backBase64 = result?.imageOcrBack; // base64 CCCD/CMND mặt sau
36
+ * const selfiePath = result?.imageFacePath; // file path (e.g. for FormData)
37
+ * const transactionId = result?.transactionId;
38
+ */
39
+ export interface StartEkycUIResult {
40
+ status: 'success';
41
+ event: string;
42
+ data?: string;
43
+ transactionId?: string;
44
+ /** Base64 selfie (liveness) – same as Android data.ekycStateModel.eKYCFileModel.imageFace */
45
+ imageFace?: string;
46
+ /** Base64 OCR front – same as Android eKYCFileModel.imageOcrFront */
47
+ imageOcrFront?: string;
48
+ /** Base64 OCR back – same as Android eKYCFileModel.imageOcrBack */
49
+ imageOcrBack?: string;
50
+ /** Absolute path to selfie file (e.g. for react-native-fs / FormData) */
51
+ imageFacePath?: string;
52
+ /** Absolute path to OCR front image file */
53
+ imageOcrFrontPath?: string;
54
+ /** Absolute path to OCR back image file */
55
+ imageOcrBackPath?: string;
25
56
  }