@trustchex/react-native-sdk 1.362.6 → 1.381.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 (58) hide show
  1. package/TrustchexSDK.podspec +3 -3
  2. package/android/build.gradle +3 -3
  3. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +64 -19
  4. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
  5. package/ios/Camera/TrustchexCameraView.swift +166 -119
  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/Shared/Components/DebugOverlay.js +541 -0
  11. package/lib/module/Shared/Components/FaceCamera.js +1 -0
  12. package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
  13. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
  14. package/lib/module/Shared/Components/IdentityDocumentCamera.js +708 -1593
  15. package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
  16. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
  17. package/lib/module/Shared/Components/QrCodeScannerCamera.js +1 -8
  18. package/lib/module/Shared/Libs/mrz.utils.js +202 -9
  19. package/lib/module/Translation/Resources/en.js +0 -4
  20. package/lib/module/Translation/Resources/tr.js +0 -4
  21. package/lib/module/version.js +1 -1
  22. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
  23. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
  24. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  25. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
  26. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
  27. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
  28. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
  30. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
  31. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
  32. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
  33. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
  34. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
  35. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  36. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +1 -0
  37. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
  38. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +8 -0
  39. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  40. package/lib/typescript/src/Translation/Resources/en.d.ts +0 -4
  41. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  42. package/lib/typescript/src/Translation/Resources/tr.d.ts +0 -4
  43. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  44. package/lib/typescript/src/version.d.ts +1 -1
  45. package/package.json +1 -1
  46. package/src/Shared/Components/DebugOverlay.tsx +656 -0
  47. package/src/Shared/Components/FaceCamera.tsx +1 -0
  48. package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
  49. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
  50. package/src/Shared/Components/IdentityDocumentCamera.tsx +1105 -2324
  51. package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
  52. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
  53. package/src/Shared/Components/QrCodeScannerCamera.tsx +1 -9
  54. package/src/Shared/Components/TrustchexCamera.tsx +1 -0
  55. package/src/Shared/Libs/mrz.utils.ts +238 -26
  56. package/src/Translation/Resources/en.ts +0 -4
  57. package/src/Translation/Resources/tr.ts +0 -4
  58. package/src/version.ts +1 -1
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Document scanning flow handlers
3
+ *
4
+ * This file contains the logic for handling different document type flows:
5
+ *
6
+ * KEY DISTINGUISHING FEATURES:
7
+ * =============================
8
+ * PASSPORT: Has MRZ on FRONT with pattern P<XXX (e.g., P<TUR, P<USA)
9
+ * ID CARD: Has NO MRZ on front; MRZ only on BACK with code 'I'
10
+ *
11
+ * IMPORTANT: Both passports and ID cards have signature sections!
12
+ * Signature presence is NOT a distinguishing feature between document types.
13
+ * The ONLY reliable distinguisher is the passport MRZ pattern (P<XXX).
14
+ *
15
+ * PASSPORT FLOW:
16
+ * 1. SCAN_ID_FRONT_OR_PASSPORT: Detect face + MRZ with code 'P' (passport MRZ pattern)
17
+ * 2. SCAN_HOLOGRAM (optional, skipped if onlyMRZScan): Collect hologram images
18
+ * 3. COMPLETED: No back side for passports
19
+ *
20
+ * ID CARD FLOW:
21
+ * 1. SCAN_ID_FRONT_OR_PASSPORT: Detect face + NO passport MRZ pattern
22
+ * 2. SCAN_HOLOGRAM: Collect hologram images from front
23
+ * 3. SCAN_ID_BACK: Detect MRZ with code 'I' + barcode
24
+ * 4. COMPLETED
25
+ *
26
+ * ID BACK DETECTION:
27
+ * - Only occurs during SCAN_ID_BACK step
28
+ * - No face, has MRZ with code 'I'
29
+ */
30
+
31
+ import type { Face } from './IdentityDocumentCamera.types';
32
+ import type { MRZFields } from '../Types/mrzFields';
33
+ import { PASSPORT_MRZ_PATTERN } from './IdentityDocumentCamera.constants';
34
+ import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
35
+
36
+ /**
37
+ * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
38
+ * even if signature text OCR fails. This prevents blocking users due to OCR limitations.
39
+ */
40
+ const SIGNATURE_RETRY_THRESHOLD = 10;
41
+
42
+ export interface PassportFlowResult {
43
+ shouldProceed: boolean;
44
+ reason?: string;
45
+ nextAction?:
46
+ | 'WAIT_FOR_ELEMENTS'
47
+ | 'WAIT_FOR_MRZ'
48
+ | 'PROCEED_TO_HOLOGRAM'
49
+ | 'PROCEED_TO_COMPLETED';
50
+ }
51
+
52
+ export interface IDFrontFlowResult {
53
+ shouldProceed: boolean;
54
+ reason?: string;
55
+ nextAction?:
56
+ | 'WAIT_FOR_ELEMENTS'
57
+ | 'REJECT_AS_PASSPORT'
58
+ | 'PROCEED_TO_HOLOGRAM';
59
+ }
60
+
61
+ export interface IDBackFlowResult {
62
+ shouldProceed: boolean;
63
+ reason?: string;
64
+ nextAction?: 'WAIT_FOR_MRZ' | 'WAIT_FOR_BARCODE' | 'PROCEED_TO_COMPLETED';
65
+ }
66
+
67
+ /**
68
+ * Handle PASSPORT flow during SCAN_ID_FRONT_OR_PASSPORT step
69
+ *
70
+ * Requirements:
71
+ * - Face must be detected AND captured
72
+ * - MRZ must be present with document code 'P'
73
+ * - MRZ must be stable (consistent reads) even in normal mode
74
+ * - All required MRZ fields must be present
75
+ */
76
+ export function handlePassportFlow(
77
+ faces: Face[],
78
+ mrzText: string | null,
79
+ mrzFields: MRZFields | undefined,
80
+ mrzStableAndValid: boolean,
81
+ onlyMRZScan: boolean,
82
+ hasRequiredFields: boolean,
83
+ faceCaptured: boolean
84
+ ): PassportFlowResult {
85
+ const hasFace = faces.length > 0;
86
+ const hasMRZ = !!mrzText;
87
+
88
+ // Check all required elements are in frame
89
+ if (!hasFace || !hasMRZ) {
90
+ return {
91
+ shouldProceed: false,
92
+ reason: 'Waiting for face + MRZ in frame',
93
+ nextAction: 'WAIT_FOR_ELEMENTS',
94
+ };
95
+ }
96
+
97
+ // Verify MRZ definitively identifies this as passport
98
+ if (!mrzFields?.documentCode || mrzFields.documentCode !== 'P') {
99
+ return {
100
+ shouldProceed: false,
101
+ reason: `MRZ not confirmed as passport (code: ${mrzFields?.documentCode || 'none'})`,
102
+ nextAction: 'WAIT_FOR_MRZ',
103
+ };
104
+ }
105
+
106
+ // Check required MRZ fields
107
+ if (!hasRequiredFields) {
108
+ return {
109
+ shouldProceed: false,
110
+ reason: 'Waiting for complete MRZ fields',
111
+ nextAction: 'WAIT_FOR_MRZ',
112
+ };
113
+ }
114
+
115
+ // Check MRZ stability - required for both modes
116
+ if (!mrzStableAndValid) {
117
+ return {
118
+ shouldProceed: false,
119
+ reason: 'Waiting for stable MRZ reads',
120
+ nextAction: 'WAIT_FOR_MRZ',
121
+ };
122
+ }
123
+
124
+ // For onlyMRZScan mode, skip face capture and hologram
125
+ if (onlyMRZScan) {
126
+ return {
127
+ shouldProceed: true,
128
+ reason: 'Passport MRZ validated',
129
+ nextAction: 'PROCEED_TO_COMPLETED',
130
+ };
131
+ }
132
+
133
+ // Normal mode: ensure face is captured before proceeding to hologram
134
+ if (!faceCaptured) {
135
+ return {
136
+ shouldProceed: false,
137
+ reason: 'Waiting for face to be captured',
138
+ nextAction: 'WAIT_FOR_ELEMENTS',
139
+ };
140
+ }
141
+
142
+ // All requirements met - proceed to hologram
143
+ return {
144
+ shouldProceed: true,
145
+ reason: 'Passport confirmed with face and valid MRZ',
146
+ nextAction: 'PROCEED_TO_HOLOGRAM',
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Handle ID CARD FRONT flow during SCAN_ID_FRONT_OR_PASSPORT step
152
+ *
153
+ * CRITICAL: The KEY distinguishing feature is the ABSENCE of passport MRZ pattern.
154
+ * Both passports and ID cards have signatures, so signature is NOT used to distinguish
155
+ * between document types. Signature is only checked to ensure we're viewing the front
156
+ * side of the document (not the back).
157
+ *
158
+ * Requirements:
159
+ * - Face must be detected (front side indicator)
160
+ * - Must NOT have passport MRZ pattern (P<XXX) - this is the CRITICAL check
161
+ * - Signature text should be present (confirms front side, or retry threshold reached)
162
+ * - If MRZ code is present, it must be 'I' (ID card) not 'P' (passport)
163
+ */
164
+ export function handleIDFrontFlow(
165
+ faces: Face[],
166
+ text: string,
167
+ mrzText: string | null,
168
+ mrzFields: MRZFields | undefined,
169
+ retryCount: number
170
+ ): IDFrontFlowResult {
171
+ // ============================================================================
172
+ // STEP 1: Check for PASSPORT MRZ pattern - THE CRITICAL DISTINGUISHER
173
+ // ============================================================================
174
+ // If passport MRZ pattern (P<XXX) is detected, this is a PASSPORT not an ID card
175
+ // This check must happen FIRST before any other validation
176
+
177
+ if (mrzFields?.documentCode === 'P') {
178
+ // Parsed MRZ shows passport code
179
+ return {
180
+ shouldProceed: false,
181
+ reason: 'Detected as ID_FRONT but MRZ shows passport (code P)',
182
+ nextAction: 'REJECT_AS_PASSPORT',
183
+ };
184
+ }
185
+
186
+ if (mrzText && PASSPORT_MRZ_PATTERN.test(mrzText)) {
187
+ // Passport MRZ pattern visible even if not fully parsed
188
+ return {
189
+ shouldProceed: false,
190
+ reason: 'Passport MRZ pattern visible - not an ID card',
191
+ nextAction: 'REJECT_AS_PASSPORT',
192
+ };
193
+ }
194
+
195
+ // ============================================================================
196
+ // STEP 2: Verify face present (confirms we're viewing front side)
197
+ // ============================================================================
198
+ const hasFace = faces.length > 0;
199
+ if (!hasFace) {
200
+ return {
201
+ shouldProceed: false,
202
+ reason: 'Waiting for face detection (front side)',
203
+ nextAction: 'WAIT_FOR_ELEMENTS',
204
+ };
205
+ }
206
+
207
+ // ============================================================================
208
+ // STEP 3: Check for signature (optional front side confirmation)
209
+ // ============================================================================
210
+ // Note: Both passports and ID cards have signatures! This is NOT used to
211
+ // distinguish document types. It only confirms we're viewing the front side.
212
+ // After retry threshold, we proceed with face alone (signature OCR may fail).
213
+ const hasSignature = /signature|imza|İmza/i.test(text);
214
+ const allowFaceOnly = retryCount > SIGNATURE_RETRY_THRESHOLD;
215
+
216
+ if (!hasSignature && !allowFaceOnly) {
217
+ return {
218
+ shouldProceed: false,
219
+ reason: 'Waiting for signature text (front side confirmation)',
220
+ nextAction: 'WAIT_FOR_ELEMENTS',
221
+ };
222
+ }
223
+
224
+ // ============================================================================
225
+ // STEP 4: Final MRZ code check (if present, must be 'I' not 'P')
226
+ // ============================================================================
227
+ // ID cards should NOT have MRZ on front, but if detected, verify it's not passport
228
+ if (mrzFields?.documentCode && mrzFields.documentCode !== 'I') {
229
+ return {
230
+ shouldProceed: false,
231
+ reason: `MRZ shows unexpected document code: ${mrzFields.documentCode}`,
232
+ nextAction: 'REJECT_AS_PASSPORT',
233
+ };
234
+ }
235
+
236
+ return {
237
+ shouldProceed: true,
238
+ reason: 'ID card front validated',
239
+ nextAction: 'PROCEED_TO_HOLOGRAM',
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Handle ID CARD BACK flow during SCAN_ID_BACK step
245
+ *
246
+ * Requirements:
247
+ * - MRZ must be present, valid, stable, and have all required fields
248
+ * - Barcode must be in frame (unless onlyMRZScan)
249
+ * - Barcode must match MRZ optional1 serial number (lenient comparison)
250
+ */
251
+ export function handleIDBackFlow(
252
+ mrzText: string | null,
253
+ mrzFields: MRZFields | undefined,
254
+ mrzValid: boolean,
255
+ mrzStableAndValid: boolean,
256
+ hasRequiredFields: boolean,
257
+ barcodeValue: string | undefined,
258
+ onlyMRZScan: boolean
259
+ ): IDBackFlowResult {
260
+ const hasMRZ = !!mrzText;
261
+ const hasBarcode = !!barcodeValue;
262
+
263
+ // Both MRZ and barcode must be in frame (unless MRZ-only mode)
264
+ if (!hasMRZ || (!hasBarcode && !onlyMRZScan)) {
265
+ return {
266
+ shouldProceed: false,
267
+ reason: !hasMRZ ? 'Waiting for MRZ' : 'Waiting for barcode in frame',
268
+ nextAction: !hasMRZ ? 'WAIT_FOR_MRZ' : 'WAIT_FOR_BARCODE',
269
+ };
270
+ }
271
+
272
+ // MRZ validation: valid checksum + required fields + stable reads
273
+ const mrzAccepted = mrzValid && hasRequiredFields && mrzStableAndValid;
274
+ if (!mrzAccepted) {
275
+ return {
276
+ shouldProceed: false,
277
+ reason: !hasRequiredFields
278
+ ? 'Incomplete MRZ fields'
279
+ : !mrzValid
280
+ ? 'MRZ checksum invalid'
281
+ : 'Waiting for stable MRZ',
282
+ nextAction: 'WAIT_FOR_MRZ',
283
+ };
284
+ }
285
+
286
+ // In MRZ-only mode, skip barcode cross-validation
287
+ if (onlyMRZScan) {
288
+ return {
289
+ shouldProceed: true,
290
+ reason: 'MRZ validated (MRZ-only mode)',
291
+ nextAction: 'PROCEED_TO_COMPLETED',
292
+ };
293
+ }
294
+
295
+ // Validate barcode matches MRZ optional1 (serial number)
296
+ // Accept exact match OR partial containment (handles encoding differences)
297
+ const trimmedBarcode = barcodeValue?.trim() || '';
298
+ const trimmedOptional1 = mrzFields?.optional1?.trim() || '';
299
+ const barcodeAccepted =
300
+ trimmedBarcode === trimmedOptional1 ||
301
+ (!!trimmedBarcode &&
302
+ (trimmedBarcode.includes(trimmedOptional1) ||
303
+ trimmedOptional1.includes(trimmedBarcode)));
304
+
305
+ if (!barcodeAccepted) {
306
+ return {
307
+ shouldProceed: false,
308
+ reason: 'Barcode does not match MRZ optional1',
309
+ nextAction: 'WAIT_FOR_BARCODE',
310
+ };
311
+ }
312
+
313
+ return {
314
+ shouldProceed: true,
315
+ reason: 'ID card back validated',
316
+ nextAction: 'PROCEED_TO_COMPLETED',
317
+ };
318
+ }
319
+
320
+ /**
321
+ * Determine next step after SCAN_HOLOGRAM based on document type
322
+ *
323
+ * PASSPORT: → COMPLETED (no back side)
324
+ * ID CARD: → SCAN_ID_BACK
325
+ */
326
+ export function getNextStepAfterHologram(
327
+ detectedDocumentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN',
328
+ currentFrameDocType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN',
329
+ mrzDocCode: string | undefined
330
+ ): 'COMPLETED' | 'SCAN_ID_BACK' {
331
+ // Check multiple sources to determine if it's a passport
332
+ const isPassport =
333
+ detectedDocumentType === 'PASSPORT' ||
334
+ currentFrameDocType === 'PASSPORT' ||
335
+ mrzDocCode === 'P';
336
+
337
+ if (isPassport) {
338
+ return 'COMPLETED';
339
+ }
340
+
341
+ return 'SCAN_ID_BACK';
342
+ }