@trustchex/react-native-sdk 1.475.1 → 1.478.7
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 +19 -21
- package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -0
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +3 -2
- package/lib/module/Screens/Static/QrCodeScanningScreen.js +12 -2
- package/lib/module/Screens/Static/ResultScreen.js +103 -107
- package/lib/module/Shared/Components/DebugOverlay.js +5 -2
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +53 -22
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +8 -1
- package/lib/module/Shared/EIDReader/eidReader.js +15 -14
- package/lib/module/Shared/Libs/MRZ_KNOWN_ISSUES.md +34 -0
- package/lib/module/Shared/Libs/SignalingClient.js +6 -5
- package/lib/module/Shared/Libs/crypto.utils.js +25 -3
- package/lib/module/Shared/Libs/deeplink.utils.js +37 -7
- package/lib/module/Shared/Libs/http-client.js +19 -6
- package/lib/module/Shared/Libs/mrz.utils.js +193 -33
- package/lib/module/Shared/Libs/mrzFrameAggregator.js +45 -1
- package/lib/module/Shared/Libs/promise.utils.js +4 -3
- package/lib/module/Shared/Libs/shuffle.utils.js +17 -0
- package/lib/module/Shared/Services/AnalyticsService.js +8 -2
- package/lib/module/Shared/Services/DataUploadService.js +114 -127
- package/lib/module/Shared/Services/VideoSessionService.js +4 -3
- package/lib/module/Translation/Resources/en.js +1 -0
- package/lib/module/Translation/Resources/tr.js +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/crypto.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts +1 -0
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts +2 -0
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrzFrameAggregator.d.ts +22 -0
- package/lib/typescript/src/Shared/Libs/mrzFrameAggregator.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/shuffle.utils.d.ts +3 -0
- package/lib/typescript/src/Shared/Libs/shuffle.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +1 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +1 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +13 -3
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +4 -3
- package/src/Screens/Static/QrCodeScanningScreen.tsx +19 -2
- package/src/Screens/Static/ResultScreen.tsx +126 -161
- package/src/Shared/Components/DebugOverlay.tsx +5 -2
- package/src/Shared/Components/IdentityDocumentCamera.tsx +55 -22
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +9 -1
- package/src/Shared/EIDReader/eidReader.ts +23 -14
- package/src/Shared/Libs/MRZ_KNOWN_ISSUES.md +34 -0
- package/src/Shared/Libs/SignalingClient.ts +10 -5
- package/src/Shared/Libs/crypto.utils.ts +41 -4
- package/src/Shared/Libs/deeplink.utils.ts +39 -7
- package/src/Shared/Libs/http-client.ts +24 -8
- package/src/Shared/Libs/mrz.utils.ts +200 -34
- package/src/Shared/Libs/mrzFrameAggregator.ts +53 -1
- package/src/Shared/Libs/promise.utils.ts +11 -5
- package/src/Shared/Libs/shuffle.utils.ts +15 -0
- package/src/Shared/Services/AnalyticsService.ts +17 -1
- package/src/Shared/Services/DataUploadService.ts +155 -166
- package/src/Shared/Services/VideoSessionService.ts +15 -3
- package/src/Translation/Resources/en.ts +1 -0
- package/src/Translation/Resources/tr.ts +1 -0
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoSessionService.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Services/VideoSessionService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VideoSessionService.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Services/VideoSessionService.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAS;gBAEtB,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM;IAKtD;;OAEG;IACU,iBAAiB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IA+B3D;;OAEG;IACU,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0BtE;;OAEG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5D;;OAEG;IACU,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3D;;OAEG;IACI,uBAAuB,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAC9B,MAAM,IAAI;CAgFd"}
|
|
@@ -110,6 +110,7 @@ declare const _default: {
|
|
|
110
110
|
'identityDocumentCamera.alignIDFrontSide': string;
|
|
111
111
|
'identityDocumentCamera.alignIDBackSide': string;
|
|
112
112
|
'identityDocumentCamera.scanCompleted': string;
|
|
113
|
+
'identityDocumentCamera.mrzDetected': string;
|
|
113
114
|
'identityDocumentCamera.searchingDocument': string;
|
|
114
115
|
'identityDocumentCamera.faceDetected': string;
|
|
115
116
|
'identityDocumentCamera.readingDocument': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBA0PE"}
|
|
@@ -110,6 +110,7 @@ declare const _default: {
|
|
|
110
110
|
'identityDocumentCamera.alignIDFrontSide': string;
|
|
111
111
|
'identityDocumentCamera.alignIDBackSide': string;
|
|
112
112
|
'identityDocumentCamera.scanCompleted': string;
|
|
113
|
+
'identityDocumentCamera.mrzDetected': string;
|
|
113
114
|
'identityDocumentCamera.searchingDocument': string;
|
|
114
115
|
'identityDocumentCamera.faceDetected': string;
|
|
115
116
|
'identityDocumentCamera.readingDocument': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBA6PE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "1.
|
|
1
|
+
export declare const SDK_VERSION = "1.478.7";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trustchex/react-native-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.478.7",
|
|
4
4
|
"description": "Trustchex mobile app react native SDK for android or ios devices",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@react-navigation/native": "7.1.18",
|
|
62
62
|
"@react-navigation/native-stack": "7.3.27",
|
|
63
63
|
"@react-navigation/elements": "2.6.5",
|
|
64
|
-
"lodash": "4.
|
|
64
|
+
"lodash": "4.18.0",
|
|
65
65
|
"brace-expansion": "1.1.13",
|
|
66
66
|
"minimatch": "3.1.4",
|
|
67
67
|
"qs": "6.15.2",
|
|
@@ -70,7 +70,16 @@
|
|
|
70
70
|
"yaml": "2.8.3",
|
|
71
71
|
"fast-uri": "3.1.2",
|
|
72
72
|
"flatted": "3.4.2",
|
|
73
|
-
"react-native-screens": "4.18.0"
|
|
73
|
+
"react-native-screens": "4.18.0",
|
|
74
|
+
"shell-quote": "1.8.4",
|
|
75
|
+
"handlebars": "4.7.9",
|
|
76
|
+
"node-forge": "1.4.0",
|
|
77
|
+
"@xmldom/xmldom": "0.8.13",
|
|
78
|
+
"basic-ftp": "5.3.1",
|
|
79
|
+
"tmp": "0.2.6",
|
|
80
|
+
"ws": "8.20.1",
|
|
81
|
+
"picomatch": "2.3.2",
|
|
82
|
+
"fast-xml-parser": "5.7.0"
|
|
74
83
|
},
|
|
75
84
|
"scripts": {
|
|
76
85
|
"example": "yarn workspace @trustchex/react-native-sdk-example",
|
|
@@ -135,6 +144,7 @@
|
|
|
135
144
|
"dependencies": {
|
|
136
145
|
"@noble/ciphers": "^1.3.0",
|
|
137
146
|
"@noble/curves": "^1.9.4",
|
|
147
|
+
"@noble/hashes": "^1.8.0",
|
|
138
148
|
"buffer": "^6.0.3",
|
|
139
149
|
"crypto-js": "^4.2.0",
|
|
140
150
|
"elliptic": "^6.6.1",
|
|
@@ -35,6 +35,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
35
35
|
import StyledButton from '../../Shared/Components/StyledButton';
|
|
36
36
|
import LottieView from 'lottie-react-native';
|
|
37
37
|
import { speak, resetLastMessage } from '../../Shared/Libs/tts.utils';
|
|
38
|
+
import { secureShuffle } from '../../Shared/Libs/shuffle.utils';
|
|
38
39
|
import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
|
|
39
40
|
import {
|
|
40
41
|
trackFunnelStep,
|
|
@@ -202,8 +203,8 @@ const LivenessDetectionScreen = () => {
|
|
|
202
203
|
}, [hasGuideShown]);
|
|
203
204
|
|
|
204
205
|
useEffect(() => {
|
|
205
|
-
const il =
|
|
206
|
-
.filter(
|
|
206
|
+
const il = secureShuffle(
|
|
207
|
+
Object.keys(instructions).filter(
|
|
207
208
|
(instruction) =>
|
|
208
209
|
!['START', 'FINISH'].includes(instruction) &&
|
|
209
210
|
// Look straight and blink is always included
|
|
@@ -216,7 +217,7 @@ const LivenessDetectionScreen = () => {
|
|
|
216
217
|
instruction as any
|
|
217
218
|
))
|
|
218
219
|
)
|
|
219
|
-
|
|
220
|
+
);
|
|
220
221
|
il.unshift('START');
|
|
221
222
|
il.push('FINISH');
|
|
222
223
|
setInitialState({
|
|
@@ -4,7 +4,8 @@ import { SafeAreaView, StyleSheet } from 'react-native';
|
|
|
4
4
|
import AppContext from '../../Shared/Contexts/AppContext';
|
|
5
5
|
import QrCodeScannerCamera from '../../Shared/Components/QrCodeScannerCamera';
|
|
6
6
|
import { useNavigation } from '@react-navigation/native';
|
|
7
|
-
import { handleDeepLink } from '../../Shared/Libs/deeplink.utils';
|
|
7
|
+
import { handleDeepLink, getUrlHost } from '../../Shared/Libs/deeplink.utils';
|
|
8
|
+
import { logWarn } from '../../Shared/Libs/debug.utils';
|
|
8
9
|
import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
9
10
|
import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
|
|
10
11
|
|
|
@@ -17,7 +18,23 @@ const QrCodeScanningScreen = () => {
|
|
|
17
18
|
useStatusBarWhiteBackground();
|
|
18
19
|
|
|
19
20
|
const onQrCodeScanned = (data: string) => {
|
|
20
|
-
|
|
21
|
+
let [bUrl, sId] = handleDeepLink({ url: data });
|
|
22
|
+
|
|
23
|
+
// A scanned QR may not repoint an already-configured backend to another
|
|
24
|
+
// host — uploads carry identity documents and biometrics. Keep the
|
|
25
|
+
// configured baseUrl and only take the session id from the QR.
|
|
26
|
+
const configuredBaseUrl = appContext.baseUrl;
|
|
27
|
+
if (
|
|
28
|
+
bUrl &&
|
|
29
|
+
configuredBaseUrl &&
|
|
30
|
+
getUrlHost(bUrl) !== getUrlHost(configuredBaseUrl)
|
|
31
|
+
) {
|
|
32
|
+
logWarn(
|
|
33
|
+
'QrCodeScanningScreen',
|
|
34
|
+
'Ignoring QR-provided baseUrl with mismatched host'
|
|
35
|
+
);
|
|
36
|
+
bUrl = configuredBaseUrl;
|
|
37
|
+
}
|
|
21
38
|
|
|
22
39
|
if (bUrl && sId) {
|
|
23
40
|
if (appContext.setBaseUrl) {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from 'react-native';
|
|
18
18
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
19
19
|
import AppContext from '../../Shared/Contexts/AppContext';
|
|
20
|
-
import httpClient from '../../Shared/Libs/http-client';
|
|
20
|
+
import httpClient, { getSessionToken } from '../../Shared/Libs/http-client';
|
|
21
21
|
import RNFS from 'react-native-fs';
|
|
22
22
|
import NativeProgressBar from '../../Shared/Components/NativeProgressBar';
|
|
23
23
|
import LottieView from 'lottie-react-native';
|
|
@@ -285,11 +285,15 @@ const ResultScreen = () => {
|
|
|
285
285
|
return;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
const uploadSessionToken = getSessionToken();
|
|
288
289
|
const uploadFileOptions: RNFS.UploadFileOptions = {
|
|
289
290
|
toUrl: `${apiUrl}/identifications/${identificationId}/media`,
|
|
290
291
|
method: 'POST',
|
|
291
292
|
headers: {
|
|
292
293
|
Accept: 'application/json',
|
|
294
|
+
...(uploadSessionToken
|
|
295
|
+
? { 'X-Session-Token': uploadSessionToken }
|
|
296
|
+
: {}),
|
|
293
297
|
},
|
|
294
298
|
files: [],
|
|
295
299
|
progress: (res) => {
|
|
@@ -299,186 +303,143 @@ const ResultScreen = () => {
|
|
|
299
303
|
},
|
|
300
304
|
} satisfies RNFS.UploadFileOptions;
|
|
301
305
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
// Plaintext identity images/video staged on disk for the multipart
|
|
307
|
+
// upload; everything tracked here is deleted in the finally below.
|
|
308
|
+
const tempPaths: string[] = [];
|
|
309
|
+
|
|
310
|
+
const writeTempImage = async (
|
|
311
|
+
fileName: string,
|
|
312
|
+
base64Data: string,
|
|
313
|
+
filetype: string = 'image/jpeg'
|
|
314
|
+
) => {
|
|
315
|
+
const filePath = decodeURIComponent(
|
|
316
|
+
`${RNFS.TemporaryDirectoryPath}/${fileName}`
|
|
310
317
|
);
|
|
318
|
+
await RNFS.writeFile(filePath, base64Data, 'base64');
|
|
319
|
+
tempPaths.push(filePath);
|
|
311
320
|
uploadFileOptions.files.push({
|
|
312
321
|
name: 'files',
|
|
313
|
-
filename:
|
|
314
|
-
filepath:
|
|
315
|
-
|
|
316
|
-
),
|
|
317
|
-
filetype: 'image/jpeg',
|
|
322
|
+
filename: fileName,
|
|
323
|
+
filepath: filePath,
|
|
324
|
+
filetype,
|
|
318
325
|
});
|
|
319
|
-
|
|
326
|
+
return filePath;
|
|
327
|
+
};
|
|
320
328
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
),
|
|
327
|
-
backImage,
|
|
328
|
-
'base64'
|
|
329
|
-
);
|
|
330
|
-
uploadFileOptions.files.push({
|
|
331
|
-
name: 'files',
|
|
332
|
-
filename: 'DOCUMENT_BACK_IMAGE.jpg',
|
|
333
|
-
filepath: decodeURIComponent(
|
|
334
|
-
`${RNFS.TemporaryDirectoryPath}/DOCUMENT_BACK_IMAGE.jpg`
|
|
335
|
-
),
|
|
336
|
-
filetype: 'image/jpeg',
|
|
337
|
-
});
|
|
338
|
-
}
|
|
329
|
+
try {
|
|
330
|
+
const frontImage = scannedIdentityDocument?.frontImage;
|
|
331
|
+
if (frontImage && frontImage !== '') {
|
|
332
|
+
await writeTempImage('DOCUMENT_FRONT_IMAGE.jpg', frontImage);
|
|
333
|
+
}
|
|
339
334
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
scannedIdentityDocument.faceImageMimeType === 'image/jp2'
|
|
345
|
-
? 'jp2'
|
|
346
|
-
: scannedIdentityDocument.faceImageMimeType === 'image/png'
|
|
347
|
-
? 'png'
|
|
348
|
-
: 'jpg';
|
|
349
|
-
await RNFS.writeFile(
|
|
350
|
-
decodeURIComponent(
|
|
351
|
-
`${RNFS.TemporaryDirectoryPath}/FACE_IMAGE.${extension}`
|
|
352
|
-
),
|
|
353
|
-
faceImage,
|
|
354
|
-
'base64'
|
|
355
|
-
);
|
|
356
|
-
uploadFileOptions.files.push({
|
|
357
|
-
name: 'files',
|
|
358
|
-
filename: `FACE_IMAGE.${extension}`,
|
|
359
|
-
filepath: decodeURIComponent(
|
|
360
|
-
`${RNFS.TemporaryDirectoryPath}/FACE_IMAGE.${extension}`
|
|
361
|
-
),
|
|
362
|
-
filetype: scannedIdentityDocument.faceImageMimeType,
|
|
363
|
-
});
|
|
364
|
-
}
|
|
335
|
+
const backImage = scannedIdentityDocument?.backImage;
|
|
336
|
+
if (backImage && backImage !== '') {
|
|
337
|
+
await writeTempImage('DOCUMENT_BACK_IMAGE.jpg', backImage);
|
|
338
|
+
}
|
|
365
339
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
filetype: 'image/jpeg',
|
|
382
|
-
});
|
|
383
|
-
}
|
|
340
|
+
const faceImage = scannedIdentityDocument?.faceImage;
|
|
341
|
+
if (faceImage && faceImage !== '') {
|
|
342
|
+
scannedIdentityDocument.faceImageMimeType ||= 'image/jpeg';
|
|
343
|
+
const extension =
|
|
344
|
+
scannedIdentityDocument.faceImageMimeType === 'image/jp2'
|
|
345
|
+
? 'jp2'
|
|
346
|
+
: scannedIdentityDocument.faceImageMimeType === 'image/png'
|
|
347
|
+
? 'png'
|
|
348
|
+
: 'jpg';
|
|
349
|
+
await writeTempImage(
|
|
350
|
+
`FACE_IMAGE.${extension}`,
|
|
351
|
+
faceImage,
|
|
352
|
+
scannedIdentityDocument.faceImageMimeType
|
|
353
|
+
);
|
|
354
|
+
}
|
|
384
355
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
'base64'
|
|
393
|
-
);
|
|
394
|
-
uploadFileOptions.files.push({
|
|
395
|
-
name: 'files',
|
|
396
|
-
filename: 'DOCUMENT_HOLOGRAM_IMAGE.jpg',
|
|
397
|
-
filepath: decodeURIComponent(
|
|
398
|
-
`${RNFS.TemporaryDirectoryPath}/DOCUMENT_HOLOGRAM_IMAGE.jpg`
|
|
399
|
-
),
|
|
400
|
-
filetype: 'image/jpeg',
|
|
401
|
-
});
|
|
402
|
-
}
|
|
356
|
+
const secondaryFaceImage = scannedIdentityDocument?.secondaryFaceImage;
|
|
357
|
+
if (secondaryFaceImage && secondaryFaceImage !== '') {
|
|
358
|
+
await writeTempImage(
|
|
359
|
+
'DOCUMENT_SECONDARY_FACE_IMAGE.jpg',
|
|
360
|
+
secondaryFaceImage
|
|
361
|
+
);
|
|
362
|
+
}
|
|
403
363
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
await RNFS.writeFile(
|
|
408
|
-
decodeURIComponent(
|
|
409
|
-
`${RNFS.TemporaryDirectoryPath}/LIVENESS_${instruction.instruction}_IMAGE.jpg`
|
|
410
|
-
),
|
|
411
|
-
instruction.photo,
|
|
412
|
-
'base64'
|
|
413
|
-
);
|
|
414
|
-
uploadFileOptions.files.push({
|
|
415
|
-
name: 'files',
|
|
416
|
-
filename: `LIVENESS_${instruction.instruction}_IMAGE.jpg`,
|
|
417
|
-
filepath: decodeURIComponent(
|
|
418
|
-
`${RNFS.TemporaryDirectoryPath}/LIVENESS_${instruction.instruction}_IMAGE.jpg`
|
|
419
|
-
),
|
|
420
|
-
filetype: 'image/jpeg',
|
|
421
|
-
});
|
|
422
|
-
if (instruction.instruction === 'LOOK_STRAIGHT_AND_BLINK') {
|
|
423
|
-
uploadFileOptions.files.push({
|
|
424
|
-
name: 'files',
|
|
425
|
-
filename: 'SELFIE_IMAGE.jpg',
|
|
426
|
-
filepath: decodeURIComponent(
|
|
427
|
-
`${RNFS.TemporaryDirectoryPath}/LIVENESS_${instruction.instruction}_IMAGE.jpg`
|
|
428
|
-
),
|
|
429
|
-
filetype: 'image/jpeg',
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
}
|
|
364
|
+
const hologramImage = scannedIdentityDocument?.hologramImage;
|
|
365
|
+
if (hologramImage && hologramImage !== '') {
|
|
366
|
+
await writeTempImage('DOCUMENT_HOLOGRAM_IMAGE.jpg', hologramImage);
|
|
433
367
|
}
|
|
434
368
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
369
|
+
if (livenessDetection) {
|
|
370
|
+
for (const instruction of livenessDetection.instructions) {
|
|
371
|
+
if (instruction.photo !== '') {
|
|
372
|
+
const filePath = await writeTempImage(
|
|
373
|
+
`LIVENESS_${instruction.instruction}_IMAGE.jpg`,
|
|
374
|
+
instruction.photo
|
|
375
|
+
);
|
|
376
|
+
if (instruction.instruction === 'LOOK_STRAIGHT_AND_BLINK') {
|
|
377
|
+
uploadFileOptions.files.push({
|
|
378
|
+
name: 'files',
|
|
379
|
+
filename: 'SELFIE_IMAGE.jpg',
|
|
380
|
+
filepath: filePath,
|
|
381
|
+
filetype: 'image/jpeg',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
444
385
|
}
|
|
445
|
-
await RNFS.copyFile(livenessVideoPath, videoFilePath);
|
|
446
386
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
videoFilePath
|
|
450
|
-
{
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
387
|
+
const livenessVideoPath = livenessDetection.videoPath;
|
|
388
|
+
if (livenessVideoPath) {
|
|
389
|
+
let videoFilePath: string;
|
|
390
|
+
if (Platform.OS === 'ios') {
|
|
391
|
+
const tmpTimestamp = new Date().getTime();
|
|
392
|
+
const tempDir = `${RNFS.TemporaryDirectoryPath}/${tmpTimestamp}`;
|
|
393
|
+
await RNFS.mkdir(tempDir);
|
|
394
|
+
tempPaths.push(tempDir);
|
|
395
|
+
videoFilePath = `${tempDir}/LIVENESS_VIDEO.mp4`;
|
|
396
|
+
} else {
|
|
397
|
+
videoFilePath = `${RNFS.TemporaryDirectoryPath}/LIVENESS_VIDEO.mp4`;
|
|
398
|
+
tempPaths.push(videoFilePath);
|
|
459
399
|
}
|
|
460
|
-
|
|
400
|
+
await RNFS.copyFile(livenessVideoPath, videoFilePath);
|
|
401
|
+
|
|
402
|
+
// Compress video with maximum compression settings
|
|
403
|
+
const compressedVideoPath = await VideoCompressor.compress(
|
|
404
|
+
videoFilePath,
|
|
405
|
+
{
|
|
406
|
+
compressionMethod: 'manual',
|
|
407
|
+
bitrate: 500000, // 500 kbps
|
|
408
|
+
maxSize: 1280, // HD 720p
|
|
409
|
+
minimumFileSizeForCompress: 0, // Always compress
|
|
410
|
+
},
|
|
411
|
+
(compressionProgress) => {
|
|
412
|
+
// Map compression progress (0-1) to main progress (40-60%)
|
|
413
|
+
setProgress(40 + compressionProgress * 20);
|
|
414
|
+
}
|
|
415
|
+
);
|
|
461
416
|
|
|
462
|
-
|
|
463
|
-
|
|
417
|
+
// Convert file:// URI to regular path for RNFS upload
|
|
418
|
+
const normalizedPath = compressedVideoPath.replace('file://', '');
|
|
419
|
+
tempPaths.push(normalizedPath);
|
|
464
420
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
421
|
+
uploadFileOptions.files.push({
|
|
422
|
+
name: 'files',
|
|
423
|
+
filename: 'LIVENESS_VIDEO.mp4',
|
|
424
|
+
filepath: normalizedPath,
|
|
425
|
+
filetype: 'video/mp4',
|
|
426
|
+
});
|
|
427
|
+
}
|
|
471
428
|
}
|
|
472
|
-
}
|
|
473
429
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
430
|
+
if (uploadFileOptions.files.length === 0) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
477
433
|
|
|
478
|
-
|
|
434
|
+
const response = await RNFS.uploadFiles(uploadFileOptions).promise;
|
|
479
435
|
|
|
480
|
-
|
|
481
|
-
|
|
436
|
+
if (![200, 201, 204].includes(response.statusCode)) {
|
|
437
|
+
throw new Error('Media upload failed');
|
|
438
|
+
}
|
|
439
|
+
} finally {
|
|
440
|
+
await Promise.all(
|
|
441
|
+
tempPaths.map((path) => RNFS.unlink(path).catch(() => {}))
|
|
442
|
+
);
|
|
482
443
|
}
|
|
483
444
|
},
|
|
484
445
|
[apiUrl]
|
|
@@ -518,11 +479,15 @@ const ResultScreen = () => {
|
|
|
518
479
|
''
|
|
519
480
|
);
|
|
520
481
|
|
|
482
|
+
const consentSessionToken = getSessionToken();
|
|
521
483
|
const response = await RNFS.uploadFiles({
|
|
522
484
|
toUrl: `${apiUrl}/identifications/${identificationId}/media`,
|
|
523
485
|
method: 'POST',
|
|
524
486
|
headers: {
|
|
525
487
|
Accept: 'application/json',
|
|
488
|
+
...(consentSessionToken
|
|
489
|
+
? { 'X-Session-Token': consentSessionToken }
|
|
490
|
+
: {}),
|
|
526
491
|
},
|
|
527
492
|
fields: {
|
|
528
493
|
consentTitle: consentVideo.title,
|
|
@@ -625,14 +625,17 @@ export const TestModePanel = React.memo(function TestModePanel({
|
|
|
625
625
|
<View style={{ padding: 10 }}>
|
|
626
626
|
<TextView
|
|
627
627
|
style={{
|
|
628
|
-
|
|
628
|
+
// Green "GO" header: the panel only ever receives a STABLE + valid
|
|
629
|
+
// consensus MRZ (the caller withholds it until then), so showing it
|
|
630
|
+
// here IS the reliable-detection signal.
|
|
631
|
+
color: '#4CAF50',
|
|
629
632
|
fontSize: 12,
|
|
630
633
|
fontWeight: 'bold',
|
|
631
634
|
marginBottom: 8,
|
|
632
635
|
textAlign: 'center',
|
|
633
636
|
}}
|
|
634
637
|
>
|
|
635
|
-
MRZ
|
|
638
|
+
✓ MRZ STABLE — GO
|
|
636
639
|
</TextView>
|
|
637
640
|
<TextView
|
|
638
641
|
style={{
|