@trustchex/react-native-sdk 1.409.0 → 1.472.0

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.
Files changed (171) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +102 -4
  3. package/ios/Camera/TrustchexCameraView.swift +151 -18
  4. package/ios/Permission/PermissionModule.m +22 -0
  5. package/ios/Permission/PermissionModule.swift +67 -0
  6. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  7. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  8. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  9. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +125 -25
  10. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  11. package/lib/module/Screens/Dynamic/VideoCallScreen.js +10 -13
  12. package/lib/module/Screens/Static/ResultScreen.js +131 -22
  13. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  14. package/lib/module/Shared/Animations/recording.json +1 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  16. package/lib/module/Shared/Components/EIDScanner.js +222 -115
  17. package/lib/module/Shared/Components/FaceCamera.js +14 -8
  18. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  19. package/lib/module/Shared/Components/IdentityDocumentCamera.js +60 -42
  20. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  21. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  22. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  23. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  24. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  25. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  26. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  27. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  28. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  29. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  30. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  31. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  32. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  33. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  34. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  37. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  38. package/lib/module/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  39. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  40. package/lib/module/Shared/Libs/contains.js +1 -40
  41. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  42. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  43. package/lib/module/Shared/Libs/index.js +20 -0
  44. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  45. package/lib/module/Shared/Libs/permissions.utils.js +199 -0
  46. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  47. package/lib/module/Shared/Types/analytics.types.js +2 -0
  48. package/lib/module/Translation/Resources/en.js +42 -2
  49. package/lib/module/Translation/Resources/tr.js +42 -2
  50. package/lib/module/Trustchex.js +54 -20
  51. package/lib/module/version.js +1 -1
  52. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  53. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  54. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  58. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  59. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  72. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  74. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  75. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  76. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  78. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  79. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  82. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  83. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  84. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  85. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  86. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  87. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  88. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  89. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  90. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  92. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  94. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  96. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  97. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  99. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  102. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  103. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  104. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  105. package/lib/typescript/src/Shared/Libs/index.d.ts +20 -0
  106. package/lib/typescript/src/Shared/Libs/index.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  108. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts +58 -0
  109. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts.map +1 -0
  110. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  112. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  114. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  115. package/lib/typescript/src/Translation/Resources/en.d.ts +41 -1
  116. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  117. package/lib/typescript/src/Translation/Resources/tr.d.ts +41 -1
  118. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  119. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  120. package/lib/typescript/src/version.d.ts +1 -1
  121. package/package.json +7 -4
  122. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  123. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  124. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  125. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +165 -24
  126. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  127. package/src/Screens/Dynamic/VideoCallScreen.tsx +11 -20
  128. package/src/Screens/Static/ResultScreen.tsx +187 -31
  129. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  130. package/src/Shared/Animations/recording.json +1 -0
  131. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  132. package/src/Shared/Components/EIDScanner.tsx +280 -119
  133. package/src/Shared/Components/FaceCamera.tsx +19 -16
  134. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  135. package/src/Shared/Components/IdentityDocumentCamera.tsx +206 -191
  136. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  137. package/src/Shared/Components/NavigationManager.tsx +27 -18
  138. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  139. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  140. package/src/Shared/EIDReader/bacKey.ts +24 -8
  141. package/src/Shared/EIDReader/eidReader.ts +398 -12
  142. package/src/Shared/EIDReader/eidService.ts +49 -1
  143. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  144. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  145. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  146. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  147. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  148. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  149. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  150. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  151. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  152. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  153. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  154. package/src/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  155. package/src/Shared/Libs/analytics.utils.ts +4 -0
  156. package/src/Shared/Libs/contains.ts +0 -53
  157. package/src/Shared/Libs/country-display.utils.ts +55 -0
  158. package/src/Shared/Libs/crypto.utils.ts +2 -2
  159. package/src/Shared/Libs/demo.utils.ts +10 -0
  160. package/src/Shared/Libs/http-client.ts +12 -4
  161. package/src/Shared/Libs/index.ts +63 -0
  162. package/src/Shared/Libs/mrz.utils.ts +3 -2
  163. package/src/Shared/Libs/permissions.utils.ts +251 -0
  164. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  165. package/src/Shared/Services/VideoSessionService.ts +1 -1
  166. package/src/Shared/Types/analytics.types.ts +2 -0
  167. package/src/Shared/Types/identificationInfo.ts +11 -0
  168. package/src/Translation/Resources/en.ts +64 -3
  169. package/src/Translation/Resources/tr.ts +63 -3
  170. package/src/Trustchex.tsx +53 -17
  171. package/src/version.ts +1 -1
@@ -4,56 +4,3 @@ export interface Rect {
4
4
  width: number;
5
5
  height: number;
6
6
  }
7
-
8
- interface Contains {
9
- outside: Rect;
10
- inside: Rect;
11
- }
12
-
13
- export function contains({ outside, inside }: Contains) {
14
- const outsideMaxX = outside.minX + outside.width;
15
- const insideMaxX = inside.minX + inside.width;
16
-
17
- const outsideMaxY = outside.minY + outside.height;
18
- const insideMaxY = inside.minY + inside.height;
19
-
20
- if (inside.minX < outside.minX) {
21
- return false;
22
- }
23
- if (insideMaxX > outsideMaxX) {
24
- return false;
25
- }
26
- if (inside.minY < outside.minY) {
27
- return false;
28
- }
29
- if (insideMaxY > outsideMaxY) {
30
- return false;
31
- }
32
-
33
- return true;
34
- }
35
-
36
- export function contains2({ outside, inside }: Contains) {
37
- const outsideMaxX = outside.minX + outside.width;
38
- const insideMaxX = inside.minX + inside.width;
39
-
40
- const outsideMaxY = outside.minY + outside.height;
41
- const insideMaxY = inside.minY + inside.height;
42
-
43
- const xIntersect = Math.max(
44
- 0,
45
- Math.min(insideMaxX, outsideMaxX) - Math.max(inside.minX, outside.minX)
46
- );
47
- const yIntersect = Math.max(
48
- 0,
49
- Math.min(insideMaxY, outsideMaxY) - Math.max(inside.minY, outside.minY)
50
- );
51
- const intersectArea = xIntersect * yIntersect;
52
-
53
- const insideArea = inside.width * inside.height;
54
- const outsideArea = outside.width * outside.height;
55
-
56
- const unionArea = insideArea + outsideArea - intersectArea;
57
-
58
- return unionArea === outsideArea;
59
- }
@@ -0,0 +1,55 @@
1
+ import { ISOCountry } from '../EIDReader/data/isoCountry';
2
+ import { UnicodeCountry } from '../EIDReader/data/unicodeCountry';
3
+
4
+ const COUNTRY_NAME_OVERRIDES: Record<string, string> = {
5
+ TUR: 'Türkiye',
6
+ };
7
+
8
+ const findCountryByAlpha3 = (alpha3Code: string) => {
9
+ return [...UnicodeCountry.VALUES, ...ISOCountry.VALUES].find(
10
+ (country) => country.toAlpha3Code() === alpha3Code
11
+ );
12
+ };
13
+
14
+ export const getLocalizedCountryName = (
15
+ alpha3Code?: string | null,
16
+ _language?: string | null
17
+ ): string => {
18
+ if (!alpha3Code) return '';
19
+
20
+ const normalizedCode = alpha3Code.trim().toUpperCase();
21
+ const overrideName = COUNTRY_NAME_OVERRIDES[normalizedCode];
22
+
23
+ if (overrideName) {
24
+ return overrideName;
25
+ }
26
+
27
+ const country = findCountryByAlpha3(normalizedCode);
28
+ if (!country) {
29
+ return normalizedCode;
30
+ }
31
+
32
+ if (
33
+ typeof Intl !== 'undefined' &&
34
+ 'Locale' in Intl &&
35
+ typeof Intl.Locale === 'function' &&
36
+ 'DisplayNames' in Intl &&
37
+ typeof Intl.DisplayNames === 'function'
38
+ ) {
39
+ const nativeLanguage = new Intl.Locale(
40
+ `und-${country.toAlpha2Code()}`
41
+ ).maximize().language;
42
+
43
+ if (nativeLanguage) {
44
+ const nativeName = new Intl.DisplayNames([nativeLanguage], {
45
+ type: 'region',
46
+ }).of(country.toAlpha2Code());
47
+
48
+ if (nativeName) {
49
+ return nativeName;
50
+ }
51
+ }
52
+ }
53
+
54
+ return country.getName();
55
+ };
@@ -9,8 +9,8 @@ const getSessionKey = async (apiUrl: string, sessionId: string) => {
9
9
  const clientPrivateKey = secp256k1.utils.randomPrivateKey();
10
10
  const clientPublicKey = secp256k1.getPublicKey(clientPrivateKey);
11
11
  const serverResponse = await httpClient.post<
12
- { serverPublicKey: String },
13
- { clientPublicKey: String }
12
+ { serverPublicKey: string },
13
+ { clientPublicKey: string }
14
14
  >(`${apiUrl}/verification-sessions/${sessionId}/auth`, {
15
15
  clientPublicKey: Buffer.from(clientPublicKey).toString('hex'),
16
16
  });
@@ -36,6 +36,16 @@ const demoSession = {
36
36
  },
37
37
  },
38
38
  },
39
+ {
40
+ type: 'VERBAL_CONSENT',
41
+ required: false,
42
+ data: {
43
+ voiceGuidanceActive: true,
44
+ verbalConsentTitle: 'Verbal Consent',
45
+ verbalConsentText:
46
+ 'I hereby provide my verbal consent for identity verification purposes. I confirm that all information provided is accurate and complete.',
47
+ },
48
+ },
39
49
  {
40
50
  type: 'IDENTITY_DOCUMENT_SCAN',
41
51
  required: false,
@@ -66,7 +66,10 @@ const request = async <TResponse, TBody>(
66
66
 
67
67
  console.log(`[HTTP] ${httpMethod} ${url}`);
68
68
  if (body) {
69
- console.log('[HTTP] Request body:', JSON.stringify(body).substring(0, 200) + '...');
69
+ console.log(
70
+ '[HTTP] Request body:',
71
+ JSON.stringify(body).substring(0, 200) + '...'
72
+ );
70
73
  }
71
74
 
72
75
  try {
@@ -80,14 +83,19 @@ const request = async <TResponse, TBody>(
80
83
 
81
84
  statusCode = response.status;
82
85
  success = response.ok;
83
- console.log(`[HTTP] Response status: ${statusCode} ${response.ok ? '✓' : '✗'}`);
86
+ console.log(
87
+ `[HTTP] Response status: ${statusCode} ${response.ok ? '✓' : '✗'}`
88
+ );
84
89
 
85
90
  let responseJson = null;
86
91
 
87
92
  try {
88
93
  responseJson = await response.json();
89
94
  if (responseJson) {
90
- console.log('[HTTP] Response body:', JSON.stringify(responseJson).substring(0, 200) + '...');
95
+ console.log(
96
+ '[HTTP] Response body:',
97
+ JSON.stringify(responseJson).substring(0, 200) + '...'
98
+ );
91
99
  }
92
100
  } catch (error) {
93
101
  // Invalid JSON response
@@ -214,7 +222,7 @@ const post = <TResponse, TBody>(
214
222
 
215
223
  const put = <TResponse, TBody>(
216
224
  url: string,
217
- body?: any,
225
+ body?: TBody,
218
226
  simulatedResponse?: TResponse
219
227
  ) => {
220
228
  return request<TResponse, TBody>('PUT', url, body, simulatedResponse);
@@ -0,0 +1,63 @@
1
+ // Export utility modules with validated symbols only.
2
+ export { default as PermissionManager } from './permissions.utils';
3
+ export type { PermissionType } from './permissions.utils';
4
+ export { SignalingClient } from './SignalingClient';
5
+
6
+ export {
7
+ setDebugMode,
8
+ setLogLevel,
9
+ isDebugEnabled,
10
+ getLogLevel,
11
+ traceLog,
12
+ debugLog,
13
+ infoLog,
14
+ debugWarn,
15
+ debugError,
16
+ logError,
17
+ logWarn,
18
+ } from './debug.utils';
19
+
20
+ export {
21
+ default as HTTPClient,
22
+ HttpClientError,
23
+ UnauthorizedError,
24
+ ForbiddenError,
25
+ NotFoundError,
26
+ BadRequestError,
27
+ TooManyRequestsError,
28
+ InternalServerError,
29
+ } from './http-client';
30
+
31
+ export * from './analytics.utils';
32
+ export type { Rect } from './contains';
33
+ export { getLocalizedCountryName } from './country-display.utils';
34
+ export { getSessionKey, encryptWithAes, decryptWithAes } from './crypto.utils';
35
+ export { default as debounce } from './debounce.utils';
36
+ export { handleDeepLink } from './deeplink.utils';
37
+ export { getSimulatedDemoData, isDemoSession } from './demo.utils';
38
+ export { default as MRZUtils } from './mrz.utils';
39
+ export { NativeDeviceInfo } from './native-device-info.utils';
40
+ export { default as getDeviceInfo } from './native-device-info.utils';
41
+
42
+ export {
43
+ useKeepAwake,
44
+ enableKeepAwake,
45
+ disableKeepAwake,
46
+ isKeepAwakeCurrentlyActive,
47
+ getKeepAwakeStats,
48
+ } from './native-keep-awake.utils';
49
+
50
+ export { runWithRetry } from './promise.utils';
51
+
52
+ export {
53
+ configureStatusBarForWhiteBackground,
54
+ useStatusBarWhiteBackground,
55
+ } from './status-bar.utils';
56
+
57
+ export {
58
+ speak,
59
+ speakWithDebounce,
60
+ stopSpeaking,
61
+ resetLastMessage,
62
+ initializeTTS,
63
+ } from './tts.utils';
@@ -114,8 +114,9 @@ const fixMRZ = (rawText: string): string => {
114
114
  // Must contain filler characters
115
115
  if (!/<+/.test(line)) return false;
116
116
 
117
- // Prioritize lines starting with I< (document code)
118
- if (line.startsWith('I<')) return true;
117
+ // Lines starting with a valid MRZ document code (ICAO 9303):
118
+ // I (ID cards), A (residence permits, admin docs), C (crew), P (passport), V (visa)
119
+ if (/^[IACPV][<A-Z]/.test(line)) return true;
119
120
 
120
121
  // Or lines with numbers and fillers (second line of TD1: birth date, expiry, etc.)
121
122
  if (/\d{5,}/.test(line) && /<{3,}/.test(line)) return true;
@@ -0,0 +1,251 @@
1
+ import {
2
+ Platform,
3
+ NativeModules,
4
+ PermissionsAndroid,
5
+ type Permission,
6
+ } from 'react-native';
7
+ import { debugLog, debugError, debugWarn } from './debug.utils';
8
+
9
+ const { PermissionModule } = NativeModules;
10
+
11
+ export type PermissionType = 'camera' | 'microphone' | 'nfc';
12
+
13
+ class PermissionManager {
14
+ private static permissionCache: Map<PermissionType, boolean> = new Map();
15
+
16
+ /**
17
+ * Request a single permission
18
+ */
19
+ static async requestPermission(permission: PermissionType): Promise<boolean> {
20
+ try {
21
+ if (Platform.OS === 'android') {
22
+ return await this.requestAndroidPermission(permission);
23
+ } else {
24
+ return await this.requestIOSPermission(permission);
25
+ }
26
+ } catch (error) {
27
+ debugError(
28
+ 'PermissionManager',
29
+ `Failed to request ${permission}:`,
30
+ error
31
+ );
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Request multiple permissions at once
38
+ */
39
+ static async requestPermissions(
40
+ permissions: PermissionType[]
41
+ ): Promise<Map<PermissionType, boolean>> {
42
+ const results = new Map<PermissionType, boolean>();
43
+
44
+ if (Platform.OS === 'android') {
45
+ const androidPermissions = permissions.map((p) =>
46
+ this.permissionTypeToAndroid(p)
47
+ );
48
+ const granted =
49
+ await PermissionsAndroid.requestMultiple(androidPermissions);
50
+
51
+ permissions.forEach((p, i) => {
52
+ const isGranted =
53
+ granted[androidPermissions[i]] === PermissionsAndroid.RESULTS.GRANTED;
54
+ results.set(p, isGranted);
55
+ });
56
+ } else {
57
+ for (const permission of permissions) {
58
+ const isGranted = await this.requestIOSPermission(permission);
59
+ results.set(permission, isGranted);
60
+ }
61
+ }
62
+
63
+ return results;
64
+ }
65
+
66
+ /**
67
+ * Check if a permission is granted (without requesting)
68
+ */
69
+ static async checkPermission(permission: PermissionType): Promise<boolean> {
70
+ try {
71
+ if (Platform.OS === 'android') {
72
+ const androidPerm = this.permissionTypeToAndroid(permission);
73
+ const result = await PermissionsAndroid.check(androidPerm);
74
+ return result;
75
+ } else {
76
+ return await this.checkIOSPermission(permission);
77
+ }
78
+ } catch (error) {
79
+ debugError(
80
+ 'PermissionManager',
81
+ `Failed to check ${permission} permission:`,
82
+ error
83
+ );
84
+ return false;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Request permission with user-friendly error handling
90
+ */
91
+ static async requestWithFallback(
92
+ permission: PermissionType,
93
+ onDenied?: () => void
94
+ ): Promise<boolean> {
95
+ const isGranted = await this.requestPermission(permission);
96
+
97
+ if (!isGranted) {
98
+ debugLog('PermissionManager', `${permission} permission denied`);
99
+ onDenied?.();
100
+ return false;
101
+ }
102
+
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * Convenience method: Request camera and microphone together (for video recording)
108
+ */
109
+ static async requestCameraAndMicrophone(): Promise<boolean> {
110
+ const permissions = await this.requestPermissions(['camera', 'microphone']);
111
+
112
+ const hasCamera = permissions.get('camera') ?? false;
113
+ const hasMicrophone = permissions.get('microphone') ?? false;
114
+
115
+ return hasCamera && hasMicrophone;
116
+ }
117
+
118
+ /**
119
+ * Convenience method: Request camera only
120
+ */
121
+ static async requestCamera(): Promise<boolean> {
122
+ return this.requestPermission('camera');
123
+ }
124
+
125
+ /**
126
+ * Convenience method: Request microphone only
127
+ */
128
+ static async requestMicrophone(): Promise<boolean> {
129
+ return this.requestPermission('microphone');
130
+ }
131
+
132
+ /**
133
+ * Convenience method: Request NFC only
134
+ */
135
+ static async requestNFC(): Promise<boolean> {
136
+ return this.requestPermission('nfc');
137
+ }
138
+
139
+ /**
140
+ * Check if camera and microphone are both accessible
141
+ */
142
+ static async hasCameraAndMicrophone(): Promise<boolean> {
143
+ const camera = await this.checkPermission('camera');
144
+ const microphone = await this.checkPermission('microphone');
145
+ return camera && microphone;
146
+ }
147
+
148
+ /**
149
+ * Check if camera is accessible
150
+ */
151
+ static async hasCamera(): Promise<boolean> {
152
+ return this.checkPermission('camera');
153
+ }
154
+
155
+ /**
156
+ * Check if microphone is accessible
157
+ */
158
+ static async hasMicrophone(): Promise<boolean> {
159
+ return this.checkPermission('microphone');
160
+ }
161
+
162
+ /**
163
+ * Check if NFC is accessible
164
+ */
165
+ static async hasNFC(): Promise<boolean> {
166
+ return this.checkPermission('nfc');
167
+ }
168
+
169
+ // Private helper methods
170
+
171
+ private static permissionTypeToAndroid(permission: PermissionType): Permission {
172
+ const map: Record<PermissionType, Permission> = {
173
+ camera: PermissionsAndroid.PERMISSIONS.CAMERA,
174
+ microphone: PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
175
+ nfc: 'android.permission.NFC' as Permission,
176
+ };
177
+ return map[permission];
178
+ }
179
+
180
+ private static async requestAndroidPermission(
181
+ permission: PermissionType
182
+ ): Promise<boolean> {
183
+ const androidPerm = this.permissionTypeToAndroid(permission);
184
+ const result = await PermissionsAndroid.request(androidPerm);
185
+ return result === PermissionsAndroid.RESULTS.GRANTED;
186
+ }
187
+
188
+ private static async requestIOSPermission(
189
+ permission: PermissionType
190
+ ): Promise<boolean> {
191
+ if (!PermissionModule) {
192
+ debugLog(
193
+ 'PermissionManager',
194
+ 'PermissionModule not available, assuming permission granted'
195
+ );
196
+ return true;
197
+ }
198
+
199
+ try {
200
+ switch (permission) {
201
+ case 'camera':
202
+ return await PermissionModule.requestCameraPermission();
203
+ case 'microphone':
204
+ return await PermissionModule.requestMicrophonePermission();
205
+ case 'nfc':
206
+ // NFC permission on iOS is automatically handled when NFCManager is initialized
207
+ // No explicit permission request needed
208
+ return true;
209
+ default:
210
+ return false;
211
+ }
212
+ } catch (error) {
213
+ debugError(
214
+ 'PermissionManager',
215
+ `iOS permission request failed for ${permission}:`,
216
+ error
217
+ );
218
+ return false;
219
+ }
220
+ }
221
+
222
+ private static async checkIOSPermission(
223
+ permission: PermissionType
224
+ ): Promise<boolean> {
225
+ if (!PermissionModule) {
226
+ return true;
227
+ }
228
+
229
+ try {
230
+ switch (permission) {
231
+ case 'camera':
232
+ return await PermissionModule.checkCameraPermission?.();
233
+ case 'microphone':
234
+ return await PermissionModule.checkMicrophonePermission?.();
235
+ case 'nfc':
236
+ return true;
237
+ default:
238
+ return false;
239
+ }
240
+ } catch (error) {
241
+ debugWarn(
242
+ 'PermissionManager',
243
+ `iOS permission check failed for ${permission}`,
244
+ error
245
+ );
246
+ return false;
247
+ }
248
+ }
249
+ }
250
+
251
+ export default PermissionManager;
@@ -1,4 +1,4 @@
1
- import { StatusBar } from 'react-native';
1
+ import { Platform, StatusBar } from 'react-native';
2
2
  import { useEffect } from 'react';
3
3
 
4
4
  /**
@@ -6,7 +6,9 @@ import { useEffect } from 'react';
6
6
  */
7
7
  export const configureStatusBarForWhiteBackground = (): void => {
8
8
  StatusBar.setBarStyle('dark-content', true);
9
- StatusBar.setBackgroundColor('#ffffff', true);
9
+ if (Platform.OS === 'android') {
10
+ StatusBar.setBackgroundColor('#ffffff', true);
11
+ }
10
12
  };
11
13
 
12
14
  /**
@@ -127,7 +127,7 @@ export class VideoSessionService {
127
127
  console.log('[VideoSessionService] Creating SSE connection to:', url);
128
128
 
129
129
  const es = new EventSource(url);
130
- let heartbeatInterval: NodeJS.Timeout | null = null;
130
+ let heartbeatInterval: ReturnType<typeof setInterval> | null = null;
131
131
 
132
132
  es.addEventListener('open', () => {
133
133
  console.log('[VideoSessionService] Queue SSE connected');
@@ -82,6 +82,8 @@ export enum AnalyticsEventName {
82
82
  IDENTITY_DOCUMENT_EID_SCAN_COMPLETED = 'identity_document_eid_scan_completed',
83
83
  LIVENESS_CHECK_STARTED = 'liveness_check_started',
84
84
  LIVENESS_CHECK_COMPLETED = 'liveness_check_completed',
85
+ VERBAL_CONSENT_VIDEO_STARTED = 'verbal_consent_video_started',
86
+ VERBAL_CONSENT_VIDEO_COMPLETED = 'verbal_consent_video_completed',
85
87
  VIDEO_CALL_STARTED = 'video_call_started',
86
88
  VIDEO_CALL_COMPLETED = 'video_call_completed',
87
89
 
@@ -14,6 +14,14 @@ export interface IdentificationInfo {
14
14
  authToken?: string;
15
15
  videoSessionId?: string;
16
16
  mediaUploadedDuringVideoCall?: boolean;
17
+ verbalConsentVideos?: VerbalConsentVideo[];
18
+ }
19
+
20
+ export interface VerbalConsentVideo {
21
+ title: string;
22
+ text: string;
23
+ videoPath: string;
24
+ recordedSeconds?: number;
17
25
  }
18
26
 
19
27
  export interface ScannedIdentityDocument {
@@ -42,6 +50,7 @@ export interface WorkflowStep {
42
50
  | 'IDENTITY_DOCUMENT_SCAN'
43
51
  | 'IDENTITY_DOCUMENT_EID_SCAN'
44
52
  | 'LIVENESS_CHECK'
53
+ | 'VERBAL_CONSENT'
45
54
  | 'AML_CHECK'
46
55
  | 'VIDEO_CALL';
47
56
  data?: {
@@ -61,6 +70,8 @@ export interface WorkflowStep {
61
70
  allowedCountries?: 'TUR'[] | null;
62
71
  allowedLivenessInstructionTypes?: LivenessInstructionType[] | null;
63
72
  voiceGuidanceActive?: boolean;
73
+ verbalConsentTitle?: string | null;
74
+ verbalConsentText?: string | null;
64
75
  };
65
76
  required: boolean;
66
77
  }
@@ -49,6 +49,9 @@ export default {
49
49
  'resultScreen.demoImageHologram': 'Hologram',
50
50
  'resultScreen.demoLivenessDetection': 'Liveness Detection',
51
51
  'resultScreen.demoVideo': 'Video',
52
+ 'resultScreen.demoVerbalConsent': 'Verbal Consent',
53
+ 'resultScreen.demoVerbalConsentTitle': 'Title',
54
+ 'resultScreen.demoVerbalConsentText': 'Text',
52
55
  'resultScreen.demoStartOver': 'Start Over',
53
56
  'livenessDetectionScreen.guideHeader': 'Face Verification',
54
57
  'livenessDetectionScreen.guideText':
@@ -83,10 +86,8 @@ export default {
83
86
  'When prompted, hold your document flat against the back of your device near the NFC antenna.',
84
87
  'eidScannerScreen.invalidMRZFields':
85
88
  'Document information could not be read. Please try again.',
86
- 'eidScannerScreen.imageCannotBeShown':
87
- 'Photo data found but cannot be displayed.',
88
89
  'eidScannerScreen.faceImageNotFound': 'Photo data not found on document.',
89
- 'eidScannerScreen.documentCode': 'Document Code',
90
+ 'eidScannerScreen.documentCode': 'Document Type',
90
91
  'eidScannerScreen.nationality': 'Nationality',
91
92
  'eidScannerScreen.personalNumber': 'Personal Number',
92
93
  'eidScannerScreen.documentNumber': 'Document Number',
@@ -94,7 +95,15 @@ export default {
94
95
  'eidScannerScreen.surname': 'Surname',
95
96
  'eidScannerScreen.birthDate': 'Date of Birth',
96
97
  'eidScannerScreen.gender': 'Gender',
98
+ 'eidScannerScreen.genderMale': 'Male',
99
+ 'eidScannerScreen.genderFemale': 'Female',
100
+ 'eidScannerScreen.genderUnspecified': 'Unspecified',
101
+ 'eidScannerScreen.docTypePassport': 'Passport',
102
+ 'eidScannerScreen.docTypeID': 'ID Card',
103
+ 'eidScannerScreen.docTypeResidence': 'Residence Permit',
104
+ 'eidScannerScreen.docTypeVisa': 'Visa',
97
105
  'eidScannerScreen.expirationDate': 'Expiration Date',
106
+ 'eidScannerScreen.issuingState': 'Issuing State',
98
107
  'eidScannerScreen.mrzText': 'MRZ Text',
99
108
  'eidScannerScreen.nfcNotSupported':
100
109
  'This device does not support NFC functionality.',
@@ -183,6 +192,58 @@ export default {
183
192
  'videoCallScreen.agentInstructions': 'Instructions from Agent',
184
193
  'videoCallScreen.callNotCompleted':
185
194
  'Video call was not completed. This step is required to proceed.',
195
+ 'verbalConsentVideoScreen.title': 'Verbal Consent',
196
+ 'verbalConsentVideoScreen.description':
197
+ 'Read the text below clearly while recording your video.',
198
+ 'verbalConsentVideoScreen.guideHeader':
199
+ 'Prepare for verbal consent recording',
200
+ 'verbalConsentVideoScreen.guideText':
201
+ 'Before you start recording, please ensure the following:',
202
+ 'verbalConsentVideoScreen.guidePoint1':
203
+ 'Stay in a quiet environment with minimal background noise',
204
+ 'verbalConsentVideoScreen.guidePoint2':
205
+ 'Keep your full face visible in good lighting',
206
+ 'verbalConsentVideoScreen.guidePoint3':
207
+ 'Read the consent text clearly and without rushing',
208
+ 'verbalConsentVideoScreen.guidePoint4':
209
+ 'Keep your phone steady until recording completes',
210
+ 'verbalConsentVideoScreen.defaultConsentText':
211
+ 'I confirm my identity and give my explicit consent to the processing of my personal data for digital identity verification. I acknowledge that my biometric data will be used solely for this purpose. I understand that I can withdraw this consent at any time.',
212
+ 'verbalConsentVideoScreen.startRecording': 'Start Recording',
213
+ 'verbalConsentVideoScreen.stopRecording': 'Stop Recording',
214
+ 'verbalConsentVideoScreen.recordAgain': 'Record Again',
215
+ 'verbalConsentVideoScreen.tryAgain': 'Try Again',
216
+ 'verbalConsentVideoScreen.submit': 'Continue',
217
+ 'verbalConsentVideoScreen.recordingProgress': 'Recording: {{current}}s',
218
+ 'verbalConsentVideoScreen.readNowPrompt':
219
+ 'Read the text aloud — touch screen to pause.',
220
+ 'verbalConsentVideoScreen.scrollReadStopHint':
221
+ 'Scroll to the end to enable Stop Recording.',
222
+ 'verbalConsentVideoScreen.scrollCompletedStopHint':
223
+ 'Great. You reached the end. You can stop recording after finishing the full text.',
224
+ 'verbalConsentVideoScreen.recordingIndicator': 'Recording',
225
+ 'verbalConsentVideoScreen.recordingStartedBanner':
226
+ 'Recording started. Read the full text and scroll to the end.',
227
+ 'verbalConsentVideoScreen.keepHeadInCirclePrompt':
228
+ 'Place your head inside the circle and keep it there until recording is complete.',
229
+ 'verbalConsentVideoScreen.readThenStopPrompt':
230
+ 'Read the text shown on screen, then stop recording.',
231
+ 'verbalConsentVideoScreen.readProgress':
232
+ 'Read progress (estimated): {{current}}/{{target}}s',
233
+ 'verbalConsentVideoScreen.stopHintPending':
234
+ 'Keep reading the full text before stopping.',
235
+ 'verbalConsentVideoScreen.stopHintReady':
236
+ 'Looks complete. You can stop recording now.',
237
+ 'verbalConsentVideoScreen.faceAligned':
238
+ 'Face aligned. You can start recording.',
239
+ 'verbalConsentVideoScreen.previewTitle':
240
+ 'Review your recording and make sure the text is clear before continuing.',
241
+ 'verbalConsentVideoScreen.analytics.recordingFailed':
242
+ 'Video recording failed',
243
+ 'verbalConsentVideoScreen.analytics.identificationIdMissing':
244
+ 'Identification ID is missing before upload',
245
+ 'verbalConsentVideoScreen.analytics.uploadFailed':
246
+ 'Consent video upload failed',
186
247
  'navigationManager.skipStepWarning':
187
248
  'Completing this step is recommended for successful verification. Skip anyway?',
188
249
  'navigationManager.skipStepLabel': 'Skip This Step',