@trustchex/react-native-sdk 1.374.0 → 1.409.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 (137) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +1 -21
  2. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
  3. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
  4. package/ios/Camera/TrustchexCameraView.swift +9 -20
  5. package/ios/MLKit/MLKitModule.swift +1 -1
  6. package/ios/OpenCV/OpenCVHelper.h +0 -7
  7. package/ios/OpenCV/OpenCVHelper.mm +0 -60
  8. package/ios/OpenCV/OpenCVModule.h +0 -4
  9. package/ios/OpenCV/OpenCVModule.mm +440 -358
  10. package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
  11. package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
  12. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -29
  13. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -0
  14. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
  15. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +26 -6
  16. package/lib/module/Screens/Dynamic/VideoCallScreen.js +676 -0
  17. package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
  18. package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
  19. package/lib/module/Screens/Static/ResultScreen.js +27 -13
  20. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +51 -51
  21. package/lib/module/Shared/Animations/video-call.json +1 -0
  22. package/lib/module/Shared/Components/DebugNavigationPanel.js +180 -14
  23. package/lib/module/Shared/Components/DebugOverlay.js +541 -0
  24. package/lib/module/Shared/Components/EIDScanner.js +1 -4
  25. package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
  26. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
  27. package/lib/module/Shared/Components/IdentityDocumentCamera.js +702 -1703
  28. package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
  29. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
  30. package/lib/module/Shared/Components/NavigationManager.js +15 -3
  31. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  32. package/lib/module/Shared/Libs/SignalingClient.js +128 -0
  33. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  34. package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
  35. package/lib/module/Shared/Libs/http-client.js +9 -0
  36. package/lib/module/Shared/Libs/promise.utils.js +16 -2
  37. package/lib/module/Shared/Libs/status-bar.utils.js +21 -0
  38. package/lib/module/Shared/Services/DataUploadService.js +294 -0
  39. package/lib/module/Shared/Services/VideoSessionService.js +156 -0
  40. package/lib/module/Shared/Services/WebRTCService.js +510 -0
  41. package/lib/module/Shared/Types/analytics.types.js +2 -0
  42. package/lib/module/Translation/Resources/en.js +20 -0
  43. package/lib/module/Translation/Resources/tr.js +20 -0
  44. package/lib/module/Trustchex.js +10 -0
  45. package/lib/module/version.js +1 -1
  46. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
  47. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
  48. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
  49. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  50. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  51. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  52. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  53. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
  54. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
  55. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  58. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  59. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  60. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
  61. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
  62. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
  64. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
  70. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
  71. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
  72. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  74. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
  75. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  76. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
  77. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
  78. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  79. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
  83. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
  84. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
  85. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
  86. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
  87. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
  88. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
  89. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
  90. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  91. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  92. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +4 -1
  93. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  94. package/lib/typescript/src/Translation/Resources/en.d.ts +20 -0
  95. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  96. package/lib/typescript/src/Translation/Resources/tr.d.ts +20 -0
  97. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  98. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  99. package/lib/typescript/src/version.d.ts +1 -1
  100. package/package.json +29 -2
  101. package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
  102. package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
  103. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +59 -33
  104. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +6 -0
  105. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
  106. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +34 -6
  107. package/src/Screens/Dynamic/VideoCallScreen.tsx +764 -0
  108. package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
  109. package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
  110. package/src/Screens/Static/ResultScreen.tsx +58 -23
  111. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +58 -72
  112. package/src/Shared/Animations/video-call.json +1 -0
  113. package/src/Shared/Components/DebugNavigationPanel.tsx +185 -9
  114. package/src/Shared/Components/DebugOverlay.tsx +656 -0
  115. package/src/Shared/Components/EIDScanner.tsx +1 -5
  116. package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
  117. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
  118. package/src/Shared/Components/IdentityDocumentCamera.tsx +1089 -2465
  119. package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
  120. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
  121. package/src/Shared/Components/NavigationManager.tsx +14 -1
  122. package/src/Shared/Contexts/AppContext.ts +2 -0
  123. package/src/Shared/Libs/SignalingClient.ts +189 -0
  124. package/src/Shared/Libs/analytics.utils.ts +4 -0
  125. package/src/Shared/Libs/deeplink.utils.ts +12 -1
  126. package/src/Shared/Libs/http-client.ts +10 -0
  127. package/src/Shared/Libs/promise.utils.ts +16 -2
  128. package/src/Shared/Libs/status-bar.utils.ts +19 -0
  129. package/src/Shared/Services/DataUploadService.ts +395 -0
  130. package/src/Shared/Services/VideoSessionService.ts +190 -0
  131. package/src/Shared/Services/WebRTCService.ts +636 -0
  132. package/src/Shared/Types/analytics.types.ts +2 -0
  133. package/src/Shared/Types/identificationInfo.ts +5 -1
  134. package/src/Translation/Resources/en.ts +25 -0
  135. package/src/Translation/Resources/tr.ts +27 -0
  136. package/src/Trustchex.tsx +12 -2
  137. package/src/version.ts +1 -1
@@ -0,0 +1,136 @@
1
+ export type SecurityFeature =
2
+ | 'icao_photo' // ICAO uyumlu fotoğraf - ICAO compliant photo
3
+ | 'barcode' // Barkod
4
+ | 'hologram' // DOVID/OVD - hologram
5
+ | 'wet_signature' // Islak imza - wet signature
6
+ | 'guilloche' // Giyoş - complex line patterns
7
+ | 'rainbow_print' // Gökkuşağı baskı - color gradient
8
+ | 'ovi' // Optik değişken mürekkep - color-shifting ink
9
+ | 'latent_image' // Gizli görüntü - visible at angles
10
+ | 'microtext'; // Mikro yazı - tiny text
11
+
12
+ export type DocumentScannedData = {
13
+ documentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
14
+ image: string;
15
+ faceImage?: string;
16
+ secondaryFaceImage?: string;
17
+ hologramImage?: string;
18
+ barcodeValue?: string;
19
+ mrzText?: string;
20
+ mrzFields?: MRZFields;
21
+ // BDDK Madde 7 - Visual security validation (NFC fallback)
22
+ securityFeaturesDetected?: SecurityFeature[];
23
+ documentTiltValidated?: boolean;
24
+ interactiveTouchValidated?: boolean;
25
+ serialNumberVerified?: boolean;
26
+ transitionPointsValidated?: boolean;
27
+ };
28
+
29
+ export type BlockText = {
30
+ blocks: BlocksData[];
31
+ resultText: string;
32
+ mrzOnlyText?: string;
33
+ };
34
+
35
+ export type BlocksData = {
36
+ blockFrame: FrameType;
37
+ blockCornerPoints: CornerPointsType;
38
+ lines: LinesData;
39
+ blockLanguages: string[] | [];
40
+ blockText: string;
41
+ };
42
+
43
+ export type CornerPointsType = [{ x: number; y: number }];
44
+
45
+ export type FrameType = {
46
+ boundingCenterX: number;
47
+ boundingCenterY: number;
48
+ height: number;
49
+ width: number;
50
+ x: number;
51
+ y: number;
52
+ };
53
+
54
+ export type LinesData = [
55
+ lineCornerPoints: CornerPointsType,
56
+ elements: ElementsData,
57
+ lineFrame: FrameType,
58
+ lineLanguages: string[] | [],
59
+ lineText: string,
60
+ ];
61
+
62
+ export type ElementsData = [
63
+ elementCornerPoints: CornerPointsType,
64
+ elementFrame: FrameType,
65
+ elementText: string,
66
+ ];
67
+
68
+ export type PhotoOptions = {
69
+ uri: string;
70
+ orientation?:
71
+ | 'landscapeRight'
72
+ | 'portrait'
73
+ | 'portraitUpsideDown'
74
+ | 'landscapeLeft';
75
+ };
76
+
77
+ export interface IdentityDocumentCameraProps {
78
+ onlyMRZScan: boolean;
79
+ onIdentityDocumentScanned: (scannedData: DocumentScannedData) => void;
80
+ testMode?: boolean;
81
+ }
82
+
83
+ export interface Face {
84
+ bounds: { x: number; y: number; width: number; height: number };
85
+ rollAngle?: number;
86
+ pitchAngle?: number;
87
+ yawAngle?: number;
88
+ leftEyeOpenProbability?: number;
89
+ rightEyeOpenProbability?: number;
90
+ smilingProbability?: number;
91
+ }
92
+
93
+ export interface Barcode {
94
+ rawValue: string;
95
+ displayValue: string;
96
+ format: number;
97
+ boundingBox: { left: number; top: number; right: number; bottom: number };
98
+ cornerPoints: Array<{ x: number; y: number }>;
99
+ value?: string;
100
+ }
101
+
102
+ import type { MRZFields } from '../Types/mrzFields';
103
+
104
+ export type ScanStep =
105
+ | 'SCAN_ID_FRONT_OR_PASSPORT'
106
+ | 'SCAN_ID_BACK'
107
+ | 'SCAN_HOLOGRAM'
108
+ | 'COMPLETED';
109
+
110
+ export type ScanStatus = 'SEARCHING' | 'SCANNING' | 'SCANNED' | 'INCORRECT';
111
+
112
+ export type DocumentType = 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
113
+
114
+ export type CompletedStep =
115
+ | 'SCAN_ID_FRONT_OR_PASSPORT'
116
+ | 'SCAN_ID_BACK'
117
+ | 'SCAN_HOLOGRAM'
118
+ | null;
119
+
120
+ export type BoundsWithRotation = {
121
+ x: number;
122
+ y: number;
123
+ width: number;
124
+ height: number;
125
+ rollAngle?: number;
126
+ cropPadding?: number;
127
+ };
128
+
129
+ export type BoundsWithAngle = {
130
+ x: number;
131
+ y: number;
132
+ width: number;
133
+ height: number;
134
+ angle?: number;
135
+ corners?: Array<{ x: number; y: number }>;
136
+ };
@@ -0,0 +1,364 @@
1
+ import { Dimensions } from 'react-native';
2
+ import type {
3
+ ScanStep,
4
+ ScanStatus,
5
+ DocumentType,
6
+ Face,
7
+ } from './IdentityDocumentCamera.types';
8
+ import type { MRZFields } from '../Types/mrzFields';
9
+ import {
10
+ SIGNATURE_REGEX,
11
+ PASSPORT_MRZ_PATTERN,
12
+ } from './IdentityDocumentCamera.constants';
13
+ import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
14
+
15
+ /**
16
+ * Frame-to-screen coordinate transform using FILL_CENTER scaling
17
+ */
18
+ export function getFrameToScreenTransform(
19
+ frameWidth: number,
20
+ frameHeight: number
21
+ ) {
22
+ const screen = Dimensions.get('window');
23
+ const frameAspect = frameWidth / frameHeight;
24
+ const screenAspect = screen.width / screen.height;
25
+
26
+ let scale: number;
27
+ let offsetX = 0;
28
+ let offsetY = 0;
29
+
30
+ if (frameAspect > screenAspect) {
31
+ scale = screen.height / frameHeight;
32
+ offsetX = (frameWidth * scale - screen.width) / 2;
33
+ } else {
34
+ scale = screen.width / frameWidth;
35
+ offsetY = (frameHeight * scale - screen.height) / 2;
36
+ }
37
+
38
+ return { scale, offsetX, offsetY, screen };
39
+ }
40
+
41
+ /**
42
+ * Compute scan area bounds in frame coordinates
43
+ */
44
+ export function getScanAreaBounds(frameWidth: number, frameHeight: number) {
45
+ const { scale, offsetX, offsetY, screen } = getFrameToScreenTransform(
46
+ frameWidth,
47
+ frameHeight
48
+ );
49
+
50
+ const scanLeft = (screen.width * 0.05 + offsetX) / scale;
51
+ const scanTop = (screen.height * 0.36 + offsetY) / scale;
52
+ const scanRight = (screen.width * 0.95 + offsetX) / scale;
53
+ const scanBottom = (screen.height * 0.64 + offsetY) / scale;
54
+
55
+ const isInsideScan = (x: number, y: number, w: number, h: number) =>
56
+ x >= scanLeft && y >= scanTop && x + w <= scanRight && y + h <= scanBottom;
57
+
58
+ return { scanLeft, scanTop, scanRight, scanBottom, isInsideScan };
59
+ }
60
+
61
+ /**
62
+ * Transform frame-space bounds to screen-space bounds
63
+ */
64
+ export function transformBoundsToScreen(
65
+ bounds: { x: number; y: number; width: number; height: number },
66
+ scale: number,
67
+ offsetX: number,
68
+ offsetY: number
69
+ ) {
70
+ return {
71
+ x: bounds.x * scale - offsetX,
72
+ y: bounds.y * scale - offsetY,
73
+ width: bounds.width * scale,
74
+ height: bounds.height * scale,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Unified status message logic used by both voice guidance and render text.
80
+ * Returns the appropriate i18n key arguments for the current scan state.
81
+ */
82
+ export function getStatusMessage(
83
+ nextStep: ScanStep,
84
+ status: ScanStatus,
85
+ detectedDocumentType: DocumentType,
86
+ isBrightnessLow: boolean,
87
+ isFrameBlurry: boolean,
88
+ allElementsDetected: boolean,
89
+ elementsOutsideScanArea: string[],
90
+ t: (key: string, params?: Record<string, unknown>) => string
91
+ ): string {
92
+ if (nextStep === 'COMPLETED') {
93
+ return t('identityDocumentCamera.scanCompleted');
94
+ }
95
+
96
+ if (status === 'INCORRECT') {
97
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
98
+ return t('identityDocumentCamera.alignIDFront');
99
+ }
100
+ if (nextStep === 'SCAN_ID_BACK') {
101
+ return t('identityDocumentCamera.alignIDBack');
102
+ }
103
+ if (nextStep === 'SCAN_HOLOGRAM') {
104
+ return t('identityDocumentCamera.alignIDFront');
105
+ }
106
+ return t('identityDocumentCamera.alignPhotoSide');
107
+ }
108
+
109
+ if (isBrightnessLow) {
110
+ return t('identityDocumentCamera.lowBrightness');
111
+ }
112
+
113
+ if (isFrameBlurry) {
114
+ return t('identityDocumentCamera.avoidBlur');
115
+ }
116
+
117
+ if (
118
+ status === 'SCANNING' &&
119
+ allElementsDetected &&
120
+ elementsOutsideScanArea.length === 0
121
+ ) {
122
+ if (nextStep === 'SCAN_ID_BACK') {
123
+ return t('identityDocumentCamera.idCardBackDetected');
124
+ }
125
+ if (detectedDocumentType === 'PASSPORT') {
126
+ return t('identityDocumentCamera.passportDetected');
127
+ }
128
+ if (detectedDocumentType === 'ID_FRONT') {
129
+ return t('identityDocumentCamera.idCardFrontDetected');
130
+ }
131
+ if (nextStep === 'SCAN_HOLOGRAM') {
132
+ return t('identityDocumentCamera.alignHologram');
133
+ }
134
+ return t('identityDocumentCamera.readingDocument');
135
+ }
136
+
137
+ if (elementsOutsideScanArea.length > 0) {
138
+ return t('identityDocumentCamera.centerDocument');
139
+ }
140
+
141
+ if (
142
+ (status === 'SCANNING' || status === 'SEARCHING') &&
143
+ !allElementsDetected
144
+ ) {
145
+ if (nextStep === 'SCAN_ID_BACK') {
146
+ return t('identityDocumentCamera.alignIDBack');
147
+ }
148
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
149
+ if (detectedDocumentType === 'PASSPORT') {
150
+ return t('identityDocumentCamera.alignPassport');
151
+ }
152
+ if (detectedDocumentType === 'ID_FRONT') {
153
+ return t('identityDocumentCamera.alignIDFront');
154
+ }
155
+ return t('identityDocumentCamera.alignPhotoSide');
156
+ }
157
+ if (nextStep === 'SCAN_HOLOGRAM') {
158
+ return t('identityDocumentCamera.alignHologram');
159
+ }
160
+ return t('identityDocumentCamera.readingDocument');
161
+ }
162
+
163
+ // Default fallback
164
+ if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
165
+ return status === 'SCANNING'
166
+ ? t('identityDocumentCamera.readingDocument')
167
+ : t('identityDocumentCamera.alignPhotoSide');
168
+ }
169
+ if (nextStep === 'SCAN_HOLOGRAM') {
170
+ return t('identityDocumentCamera.alignHologram');
171
+ }
172
+ if (nextStep === 'SCAN_ID_BACK') {
173
+ return status === 'SCANNING'
174
+ ? t('identityDocumentCamera.readingDocument')
175
+ : t('identityDocumentCamera.alignIDBackSide');
176
+ }
177
+
178
+ return '';
179
+ }
180
+
181
+ /**
182
+ * Calculate angle from two points in degrees
183
+ */
184
+ export function angleBetweenPoints(
185
+ p1: { x: number; y: number },
186
+ p2: { x: number; y: number }
187
+ ): number {
188
+ const dx = p2.x - p1.x;
189
+ const dy = p2.y - p1.y;
190
+ return Math.atan2(dy, dx) * (180 / Math.PI);
191
+ }
192
+
193
+ /**
194
+ * Calculate distance between two points
195
+ */
196
+ export function distanceBetweenPoints(
197
+ p1: { x: number; y: number },
198
+ p2: { x: number; y: number }
199
+ ): number {
200
+ const dx = p2.x - p1.x;
201
+ const dy = p2.y - p1.y;
202
+ return Math.sqrt(dx * dx + dy * dy);
203
+ }
204
+
205
+ /**
206
+ * Detect document type based on faces, OCR text, and MRZ fields
207
+ */
208
+ export function detectDocumentType(
209
+ faces: Face[],
210
+ ocrText: string,
211
+ mrzFields?: MRZFields,
212
+ frameWidth?: number,
213
+ mrzText?: string | null
214
+ ): DocumentType {
215
+ // Relaxed signature detection: matches signature/imza variants and OCR errors
216
+ const hasSignatureMatch = SIGNATURE_REGEX.test(ocrText);
217
+
218
+ if (isDebugEnabled()) {
219
+ debugLog('IdentityDocumentCamera.utils', '[DocType] Detection', {
220
+ faces: faces.length,
221
+ hasMRZ: !!mrzFields,
222
+ hasMRZText: !!mrzText,
223
+ textLength: ocrText?.length,
224
+ hasSignature: hasSignatureMatch,
225
+ });
226
+ }
227
+
228
+ // ID Back: no face + ID MRZ
229
+ if (faces.length === 0 && mrzFields?.documentCode === 'I') {
230
+ return 'ID_BACK';
231
+ }
232
+
233
+ // Passport: face + passport MRZ
234
+ if (faces.length > 0 && mrzFields?.documentCode === 'P') {
235
+ return 'PASSPORT';
236
+ }
237
+
238
+ // Check for PASSPORT MRZ pattern BEFORE classifying as ID_FRONT
239
+ // Both passports and ID cards have face + signature, so we MUST check MRZ first
240
+ // Check BOTH parsed mrzText AND raw ocrText — MRZ parsing can fail while
241
+ // the raw OCR still contains P<TUR/P<USA pattern
242
+ const hasPassportMRZPattern =
243
+ (mrzText && mrzText.length > 20 && PASSPORT_MRZ_PATTERN.test(mrzText)) ||
244
+ PASSPORT_MRZ_PATTERN.test(ocrText);
245
+
246
+ if (hasPassportMRZPattern) {
247
+ // Passport MRZ pattern detected (P<TUR, P<USA, etc.)
248
+ // Even if not fully parsed, this is definitely a passport, not ID card
249
+ return 'PASSPORT';
250
+ }
251
+
252
+ // ID Front: face detected
253
+ // CRITICAL: Only classify as ID_FRONT when we're confident it's NOT a passport
254
+ // This means we must have either:
255
+ // 1. MRZ code 'I' (definitive ID card), OR
256
+ // 2. Face + signature but NO passport MRZ pattern visible
257
+ if (faces.length > 0 && ocrText?.length >= 5) {
258
+ // Filter to card-sized faces only (min 5% of frame width to exclude tiny background faces)
259
+ const cardSizedFaces = frameWidth
260
+ ? faces.filter(
261
+ (face) =>
262
+ face.bounds.width >= frameWidth * 0.05 &&
263
+ face.bounds.height >= frameWidth * 0.05
264
+ )
265
+ : faces;
266
+
267
+ if (cardSizedFaces.length > 0) {
268
+ // If we have MRZ code 'I', it's definitely an ID card
269
+ if (mrzFields?.documentCode === 'I') {
270
+ return 'ID_FRONT';
271
+ }
272
+
273
+ // If signature present and NO passport MRZ pattern, likely ID_FRONT
274
+ // But we can't be 100% sure yet - passport MRZ might appear in next frames
275
+ const hasSignature = hasSignatureMatch;
276
+ if (hasSignature && !hasPassportMRZPattern) {
277
+ return 'ID_FRONT';
278
+ }
279
+ }
280
+ }
281
+
282
+ return 'UNKNOWN';
283
+ }
284
+
285
+ /**
286
+ * Determine the document type to set based on current frame analysis
287
+ * Handles correction of misdetections and passport pattern checking
288
+ */
289
+ export function determineDocumentTypeToSet(
290
+ documentType: DocumentType,
291
+ cardSizedFaces: Face[],
292
+ parsedMRZFields?: MRZFields,
293
+ mrzText?: string | null
294
+ ): DocumentType {
295
+ // CRITICAL: Passport MRZ always takes precedence over other detections
296
+ const hasPassportMRZ =
297
+ (mrzText && mrzText.length > 20 && PASSPORT_MRZ_PATTERN.test(mrzText)) ||
298
+ parsedMRZFields?.documentCode === 'P';
299
+
300
+ if (hasPassportMRZ) {
301
+ return 'PASSPORT';
302
+ }
303
+
304
+ // After passport MRZ check, accept the detected type directly
305
+ if (documentType === 'PASSPORT') return 'PASSPORT';
306
+ if (documentType === 'ID_FRONT') return 'ID_FRONT';
307
+ return 'UNKNOWN';
308
+ }
309
+
310
+ /**
311
+ * Compare MRZ field values (ignore raw text variations)
312
+ */
313
+ export function areMRZFieldsEqual(fields1: any, fields2: any): boolean {
314
+ if (!fields1 || !fields2) return false;
315
+ // Compare critical fields that define document identity
316
+ return (
317
+ fields1.documentNumber === fields2.documentNumber &&
318
+ fields1.birthDate === fields2.birthDate &&
319
+ fields1.expirationDate === fields2.expirationDate &&
320
+ fields1.firstName === fields2.firstName &&
321
+ fields1.lastName === fields2.lastName &&
322
+ fields1.issuingState === fields2.issuingState
323
+ );
324
+ }
325
+
326
+ /**
327
+ * Check if all required MRZ fields are present
328
+ */
329
+ export function hasRequiredMRZFields(fields: any): boolean {
330
+ return (
331
+ !!fields?.firstName &&
332
+ !!fields?.lastName &&
333
+ !!fields?.documentNumber &&
334
+ !!fields?.birthDate
335
+ );
336
+ }
337
+
338
+ /**
339
+ * Validate if face position has changed within acceptable tolerance
340
+ * Returns true if position is valid (within tolerance)
341
+ */
342
+ export function validateFacePosition(
343
+ currentBounds: { x: number; y: number; width: number; height: number },
344
+ referenceBounds: { x: number; y: number; width: number; height: number },
345
+ isHologramStep: boolean
346
+ ): boolean {
347
+ const xDiff = Math.abs(currentBounds.x - referenceBounds.x);
348
+ const yDiff = Math.abs(currentBounds.y - referenceBounds.y);
349
+ const widthDiff = Math.abs(currentBounds.width - referenceBounds.width);
350
+ const heightDiff = Math.abs(currentBounds.height - referenceBounds.height);
351
+
352
+ // Use looser tolerance during hologram step since flash toggling causes position jitter
353
+ const tolerance = isHologramStep ? 0.5 : 0.2;
354
+ const xTolerance = referenceBounds.width * tolerance;
355
+ const yTolerance = referenceBounds.height * tolerance;
356
+ const sizeTolerance = referenceBounds.width * tolerance;
357
+
358
+ return (
359
+ xDiff <= xTolerance &&
360
+ yDiff <= yTolerance &&
361
+ widthDiff <= sizeTolerance &&
362
+ heightDiff <= sizeTolerance
363
+ );
364
+ }
@@ -45,6 +45,7 @@ const NavigationManager = forwardRef(
45
45
  IDENTITY_DOCUMENT_SCAN: 'IdentityDocumentScanningScreen',
46
46
  IDENTITY_DOCUMENT_EID_SCAN: 'IdentityDocumentEIDScanningScreen',
47
47
  LIVENESS_CHECK: 'LivenessDetectionScreen',
48
+ VIDEO_CALL: 'VideoCallScreen',
48
49
  },
49
50
  RESULT: 'ResultScreen',
50
51
  };
@@ -109,6 +110,10 @@ const NavigationManager = forwardRef(
109
110
  return routes.DYNAMIC_ROUTES.LIVENESS_CHECK;
110
111
  }
111
112
 
113
+ if (nextStep.type === 'VIDEO_CALL') {
114
+ return routes.DYNAMIC_ROUTES.VIDEO_CALL;
115
+ }
116
+
112
117
  return routes.VERIFICATION_SESSION_CHECK;
113
118
  },
114
119
  [
@@ -207,7 +212,14 @@ const NavigationManager = forwardRef(
207
212
  deviceInfo: '',
208
213
  },
209
214
  locale: appContext.locale || i18n.language,
215
+ // Explicitly reset collected data fields
216
+ scannedDocument: undefined,
217
+ livenessDetection: undefined,
218
+ authToken: undefined,
219
+ videoSessionId: undefined,
210
220
  };
221
+
222
+ // Reset branding to defaults while preserving any custom values
211
223
  appContext.branding = {
212
224
  logoUrl: appContext.branding?.logoUrl || '',
213
225
  primaryColor: appContext.branding?.primaryColor || '#000000',
@@ -220,6 +232,7 @@ const NavigationManager = forwardRef(
220
232
  appContext.setIsDemoSession?.(false);
221
233
  analyticsService.setDemoSession(false);
222
234
  }
235
+ appContext.isTestVideoSession = false;
223
236
 
224
237
  navigation.dispatch(
225
238
  CommonActions.reset({
@@ -238,7 +251,7 @@ const NavigationManager = forwardRef(
238
251
  }, [appContext, navigation, routes.VERIFICATION_SESSION_CHECK]);
239
252
 
240
253
  usePreventRemove(true, ({ data }) => {
241
- if (data.action.type === 'RESET') {
254
+ if (data?.action?.type === 'RESET') {
242
255
  navigation.dispatch(data.action);
243
256
  }
244
257
  });
@@ -18,6 +18,7 @@ export type AppContextType = {
18
18
  workflowSteps?: WorkflowStep[];
19
19
  currentWorkflowStep?: WorkflowStep;
20
20
  isDebugNavigated?: boolean;
21
+ isTestVideoSession?: boolean;
21
22
  onCompleted?: () => void;
22
23
  onError?: (error: string) => void;
23
24
  setSessionId?: (id: string) => void;
@@ -47,6 +48,7 @@ export default createContext<AppContextType>({
47
48
  workflowSteps: [],
48
49
  currentWorkflowStep: undefined,
49
50
  isDebugNavigated: false,
51
+ isTestVideoSession: false,
50
52
  onCompleted: undefined,
51
53
  onError: undefined,
52
54
  setSessionId: undefined,