@trustchex/react-native-sdk 1.374.0 → 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.
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +0 -9
- package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
- package/ios/Camera/TrustchexCameraView.swift +8 -8
- package/ios/OpenCV/OpenCVHelper.h +0 -7
- package/ios/OpenCV/OpenCVHelper.mm +0 -60
- package/ios/OpenCV/OpenCVModule.h +0 -4
- package/ios/OpenCV/OpenCVModule.mm +440 -358
- package/lib/module/Shared/Components/DebugOverlay.js +541 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +679 -1701
- package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Shared/Components/DebugOverlay.tsx +656 -0
- package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
- package/src/Shared/Components/IdentityDocumentCamera.tsx +1065 -2462
- package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
- 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
|
+
}
|