@trustchex/react-native-sdk 1.488.0 → 1.490.1
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/ios/ImageDecoder/ImageDecoderModule.m +12 -0
- package/ios/ImageDecoder/ImageDecoderModule.swift +43 -0
- package/lib/module/Screens/Static/ResultScreen.js +1 -0
- package/lib/module/Shared/Components/DiagnosticReportButton.js +16 -14
- package/lib/module/Shared/Components/EIDScanner.js +5 -1
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +22 -2
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +1 -1
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +7 -7
- package/lib/module/Shared/EIDReader/eidReader.js +15 -3
- package/lib/module/Shared/Libs/diagnosticAuth.js +36 -0
- package/lib/module/Shared/Libs/jp2Decode.js +43 -0
- package/lib/module/Shared/Libs/sendDiagnosticReport.js +83 -103
- package/lib/module/Translation/Resources/en.js +6 -7
- package/lib/module/Translation/Resources/tr.js +6 -7
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts +9 -6
- package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +11 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.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/diagnosticAuth.d.ts +8 -0
- package/lib/typescript/src/Shared/Libs/diagnosticAuth.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/jp2Decode.d.ts +13 -0
- package/lib/typescript/src/Shared/Libs/jp2Decode.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts +26 -10
- package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +4 -5
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +4 -5
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -5
- package/src/Screens/Static/ResultScreen.tsx +1 -0
- package/src/Shared/Components/DiagnosticReportButton.tsx +23 -14
- package/src/Shared/Components/EIDScanner.tsx +5 -1
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +22 -2
- package/src/Shared/Components/IdentityDocumentCamera.tsx +1 -2
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +6 -8
- package/src/Shared/EIDReader/eidReader.ts +19 -4
- package/src/Shared/Libs/diagnosticAuth.ts +47 -0
- package/src/Shared/Libs/jp2Decode.ts +48 -0
- package/src/Shared/Libs/sendDiagnosticReport.ts +91 -118
- package/src/Translation/Resources/en.ts +6 -9
- package/src/Translation/Resources/tr.ts +6 -9
- package/src/version.ts +1 -1
|
@@ -1,58 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Assembles and
|
|
2
|
+
* Assembles and uploads the support diagnostic report to the backend.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* This runs from the SDK's DEMO flow only, which is simulated client-side and
|
|
5
|
+
* has no real verification session or account — so there's no session key to
|
|
6
|
+
* encrypt with and nothing to authenticate against. The report is POSTed
|
|
7
|
+
* unauthenticated to the global demo-diagnostics endpoint over HTTPS (encrypted
|
|
8
|
+
* in transit); the backend encrypts the PII fields at rest. Internal developers
|
|
9
|
+
* view and download the reports from a hidden dashboard page.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* draft regardless of the chosen app.
|
|
11
|
+
* Uploaded payload (one JSON body):
|
|
12
|
+
* - non-PII technical metadata (platform, sdkVersion, documentType,
|
|
13
|
+
* nfcErrorCode, paceFallbackReason, succeeded) for the list/filter,
|
|
14
|
+
* - `diagnosticsJson` — technical signals (device, camera, MRZ, NFC steps),
|
|
15
|
+
* - `scanDataJson` — the scanned identity data (PII),
|
|
16
|
+
* - `images` — the captured document/face images (base64, PII).
|
|
18
17
|
*/
|
|
19
|
-
import
|
|
18
|
+
import 'react-native-get-random-values';
|
|
20
19
|
import { diagnostics } from './diagnostics';
|
|
21
20
|
import { gatherDeviceDetails } from './native-device-info.utils';
|
|
22
21
|
import {
|
|
23
22
|
buildDiagnosticReport,
|
|
24
23
|
type ScanDataForReport,
|
|
25
24
|
} from './diagnosticReport';
|
|
25
|
+
import { signDiagnosticBody } from './diagnosticAuth';
|
|
26
26
|
import { logError } from './debug.utils';
|
|
27
27
|
|
|
28
|
-
// `react-native-share` is OPTIONAL. Load it softly so the SDK bundles and runs
|
|
29
|
-
// without it; only host apps that installed it get the diagnostic feature. A
|
|
30
|
-
// static `import` would make Metro fail to resolve the module when it's absent,
|
|
31
|
-
// so we use a guarded require and keep the reference typed loosely.
|
|
32
|
-
type ShareModule = {
|
|
33
|
-
open: (options: Record<string, unknown>) => Promise<unknown>;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
let shareModule: ShareModule | null | undefined;
|
|
37
|
-
|
|
38
|
-
const loadShare = (): ShareModule | null => {
|
|
39
|
-
if (shareModule !== undefined) return shareModule;
|
|
40
|
-
try {
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
-
const mod = require('react-native-share');
|
|
43
|
-
shareModule = (mod?.default ?? mod) as ShareModule;
|
|
44
|
-
} catch {
|
|
45
|
-
shareModule = null; // not installed — feature unavailable
|
|
46
|
-
}
|
|
47
|
-
return shareModule;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Whether diagnostic sharing is available (i.e. `react-native-share` is
|
|
52
|
-
* installed in the host app). The result-screen button hides itself when false.
|
|
53
|
-
*/
|
|
54
|
-
export const isDiagnosticSharingAvailable = (): boolean => loadShare() != null;
|
|
55
|
-
|
|
56
28
|
export interface DiagnosticImage {
|
|
57
29
|
/** Stable file name without extension, e.g. "mrz-side". */
|
|
58
30
|
name: string;
|
|
@@ -61,105 +33,106 @@ export interface DiagnosticImage {
|
|
|
61
33
|
}
|
|
62
34
|
|
|
63
35
|
export interface SendDiagnosticParams {
|
|
36
|
+
/** Backend base URL (same value the SDK was initialised with). */
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
/** Demo session id (informational; the endpoint is unauthenticated). */
|
|
39
|
+
sessionId?: string;
|
|
64
40
|
scan: ScanDataForReport;
|
|
65
41
|
sdkVersion: string;
|
|
66
|
-
sessionId?: string;
|
|
67
42
|
/** base64 images to attach (MRZ side / front / face / NFC chip face). */
|
|
68
43
|
images?: DiagnosticImage[];
|
|
69
44
|
}
|
|
70
45
|
|
|
71
|
-
|
|
72
|
-
const writeTemp = async (
|
|
73
|
-
dir: string,
|
|
74
|
-
name: string,
|
|
75
|
-
data: string,
|
|
76
|
-
encoding: 'utf8' | 'base64'
|
|
77
|
-
): Promise<string> => {
|
|
78
|
-
const path = `${dir}/${name}`;
|
|
79
|
-
await RNFS.writeFile(path, data, encoding);
|
|
80
|
-
return path.startsWith('file://') ? path : `file://${path}`;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
46
|
export interface SendResult {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
dismissed?: boolean;
|
|
47
|
+
/** True when the report was uploaded and stored. */
|
|
48
|
+
uploaded: boolean;
|
|
87
49
|
}
|
|
88
50
|
|
|
51
|
+
const mimeToType = (mime: 'jpg' | 'png'): string =>
|
|
52
|
+
mime === 'png' ? 'image/png' : 'image/jpeg';
|
|
53
|
+
|
|
89
54
|
/**
|
|
90
|
-
* Build the report
|
|
91
|
-
* {
|
|
55
|
+
* Build the report and upload it (over HTTPS) to the demo-diagnostics endpoint.
|
|
56
|
+
* Returns { uploaded } — false on any error (best-effort; a failed report must
|
|
57
|
+
* never disrupt the user's flow).
|
|
92
58
|
*/
|
|
93
59
|
export const sendDiagnosticReport = async (
|
|
94
60
|
params: SendDiagnosticParams
|
|
95
61
|
): Promise<SendResult> => {
|
|
96
|
-
const
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
return { shared: false };
|
|
62
|
+
const { baseUrl, sessionId, scan, sdkVersion, images = [] } = params;
|
|
63
|
+
if (!baseUrl) {
|
|
64
|
+
return { uploaded: false };
|
|
100
65
|
}
|
|
101
|
-
|
|
66
|
+
|
|
102
67
|
const now = Date.now();
|
|
103
68
|
const iso = new Date(now).toISOString();
|
|
104
69
|
|
|
105
|
-
const device = await gatherDeviceDetails();
|
|
106
|
-
const report = buildDiagnosticReport({
|
|
107
|
-
diag: diagnostics.snapshot(now, iso),
|
|
108
|
-
device,
|
|
109
|
-
scan,
|
|
110
|
-
sdkVersion,
|
|
111
|
-
sessionId,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const dir = `${RNFS.TemporaryDirectoryPath}/trustchex-diagnostics-${now}`;
|
|
115
70
|
try {
|
|
116
|
-
await
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
71
|
+
const device = await gatherDeviceDetails();
|
|
72
|
+
const snapshot = diagnostics.snapshot(now, iso);
|
|
73
|
+
const report = buildDiagnosticReport({
|
|
74
|
+
diag: snapshot,
|
|
75
|
+
device,
|
|
76
|
+
scan,
|
|
77
|
+
sdkVersion,
|
|
78
|
+
sessionId,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Queryable, non-PII technical metadata for the dashboard list/filter.
|
|
82
|
+
const nfc = snapshot.nfc;
|
|
83
|
+
const failingSw = nfc.failureStep
|
|
84
|
+
? nfc.steps.find((s) => s.step === nfc.failureStep && !s.ok)?.sw
|
|
85
|
+
: undefined;
|
|
86
|
+
const nfcErrorCode =
|
|
87
|
+
nfc.attempted && !nfc.succeeded
|
|
88
|
+
? `NFC-${(nfc.errorCategory ?? 'reading_error').toUpperCase()}${failingSw ? `-${failingSw}` : ''}`
|
|
89
|
+
: null;
|
|
90
|
+
|
|
91
|
+
const payload = {
|
|
92
|
+
// metadata (stored in plaintext, no PII)
|
|
93
|
+
platform: device.platform,
|
|
94
|
+
osVersion: device.systemVersion,
|
|
95
|
+
sdkVersion,
|
|
96
|
+
documentType: scan.documentType ?? snapshot.documentType ?? null,
|
|
97
|
+
dataSource: scan.dataSource ?? snapshot.dataSource ?? null,
|
|
98
|
+
nfcErrorCode,
|
|
99
|
+
paceFallbackReason: nfc.paceFallbackReason ?? null,
|
|
100
|
+
succeeded: nfc.attempted ? nfc.succeeded : null,
|
|
101
|
+
// full report (PII, encrypted at rest by the backend)
|
|
102
|
+
diagnosticsJson: report.diagnosticsJson,
|
|
103
|
+
scanDataJson: report.scanDataJson,
|
|
104
|
+
images: images
|
|
105
|
+
.filter((img) => img.base64)
|
|
106
|
+
.map((img) => ({
|
|
107
|
+
name: img.name,
|
|
108
|
+
base64: img.base64,
|
|
109
|
+
mimeType: mimeToType(img.mime),
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Sign the EXACT body bytes so the backend can verify authenticity. Use
|
|
114
|
+
// fetch directly (httpClient doesn't expose custom headers / raw body).
|
|
115
|
+
const rawBody = JSON.stringify(payload);
|
|
116
|
+
const { timestamp, nonce, signature } = signDiagnosticBody(rawBody);
|
|
117
|
+
|
|
118
|
+
const response = await fetch(`${baseUrl}/api/app/mobile/diagnostics`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
'X-Trustchex-Timestamp': timestamp,
|
|
123
|
+
'X-Trustchex-Nonce': nonce,
|
|
124
|
+
'X-Trustchex-Signature': signature,
|
|
125
|
+
},
|
|
126
|
+
body: rawBody,
|
|
127
|
+
});
|
|
130
128
|
|
|
131
|
-
|
|
132
|
-
await share.open({
|
|
133
|
-
title: report.subject,
|
|
134
|
-
subject: report.subject, // used by email targets
|
|
135
|
-
message: report.body,
|
|
136
|
-
urls,
|
|
137
|
-
// Per-file MIME types so targets render attachments correctly.
|
|
138
|
-
filenames: urls.map((u) => u.split('/').pop() ?? 'attachment'),
|
|
139
|
-
failOnCancel: false,
|
|
140
|
-
});
|
|
141
|
-
return { shared: true };
|
|
142
|
-
} catch (shareErr) {
|
|
143
|
-
// react-native-share throws on user cancel unless failOnCancel:false; treat
|
|
144
|
-
// a cancel as a non-error dismissal.
|
|
145
|
-
const msg =
|
|
146
|
-
shareErr instanceof Error ? shareErr.message : String(shareErr);
|
|
147
|
-
if (/cancel|dismiss|user did not share/i.test(msg)) {
|
|
148
|
-
return { shared: false, dismissed: true };
|
|
149
|
-
}
|
|
150
|
-
throw shareErr;
|
|
151
|
-
}
|
|
129
|
+
return { uploaded: response.ok };
|
|
152
130
|
} catch (e) {
|
|
153
131
|
logError(
|
|
154
132
|
'Diagnostics',
|
|
155
|
-
'Failed to
|
|
133
|
+
'Failed to upload diagnostic report',
|
|
156
134
|
e instanceof Error ? e.message : String(e)
|
|
157
135
|
);
|
|
158
|
-
return {
|
|
159
|
-
} finally {
|
|
160
|
-
// Best-effort cleanup — temp files are short-lived.
|
|
161
|
-
setTimeout(() => {
|
|
162
|
-
RNFS.unlink(dir).catch(() => {});
|
|
163
|
-
}, 60_000);
|
|
136
|
+
return { uploaded: false };
|
|
164
137
|
}
|
|
165
138
|
};
|
|
@@ -54,18 +54,15 @@ export default {
|
|
|
54
54
|
'resultScreen.demoVerbalConsentText': 'Text',
|
|
55
55
|
'resultScreen.demoStartOver': 'Start Over',
|
|
56
56
|
'diagnosticReport.button': 'Report a scanning issue',
|
|
57
|
-
'diagnosticReport.noticeTitle': '
|
|
57
|
+
'diagnosticReport.noticeTitle': 'Report a scanning issue',
|
|
58
58
|
'diagnosticReport.noticeBody':
|
|
59
|
-
'This
|
|
59
|
+
'This sends your scan data, document images and technical details to support so they can help resolve a scanning issue.',
|
|
60
60
|
'diagnosticReport.continue': 'Continue',
|
|
61
61
|
'diagnosticReport.cancel': 'Cancel',
|
|
62
|
-
'diagnosticReport.
|
|
63
|
-
'diagnosticReport.
|
|
64
|
-
|
|
65
|
-
'diagnosticReport.
|
|
66
|
-
'diagnosticReport.uploadSuccess': 'Diagnostic report sent to support.',
|
|
67
|
-
'diagnosticReport.uploadFailed':
|
|
68
|
-
"Couldn't send the report. Please try again later.",
|
|
62
|
+
'diagnosticReport.sentTitle': 'Report sent',
|
|
63
|
+
'diagnosticReport.sentBody': 'Your diagnostic report was sent to support. Thank you.',
|
|
64
|
+
'diagnosticReport.failedTitle': "Couldn't send the report",
|
|
65
|
+
'diagnosticReport.failedBody': 'Please try again later.',
|
|
69
66
|
'livenessDetectionScreen.guideHeader': 'Face Verification',
|
|
70
67
|
'livenessDetectionScreen.guideText':
|
|
71
68
|
'Please ensure the following for best results:',
|
|
@@ -54,18 +54,15 @@ export default {
|
|
|
54
54
|
'resultScreen.demoVerbalConsentText': 'Metin',
|
|
55
55
|
'resultScreen.demoStartOver': 'Baştan Başla',
|
|
56
56
|
'diagnosticReport.button': 'Tarama sorununu bildir',
|
|
57
|
-
'diagnosticReport.noticeTitle': '
|
|
57
|
+
'diagnosticReport.noticeTitle': 'Tarama sorununu bildir',
|
|
58
58
|
'diagnosticReport.noticeBody':
|
|
59
|
-
'Bu işlem; tarama verilerinizi, belge görüntülerinizi ve teknik ayrıntıları
|
|
59
|
+
'Bu işlem; tarama verilerinizi, belge görüntülerinizi ve teknik ayrıntıları destek ekibine gönderir; böylece tarama sorununu çözebilirler.',
|
|
60
60
|
'diagnosticReport.continue': 'Devam et',
|
|
61
61
|
'diagnosticReport.cancel': 'İptal',
|
|
62
|
-
'diagnosticReport.
|
|
63
|
-
'diagnosticReport.
|
|
64
|
-
|
|
65
|
-
'diagnosticReport.
|
|
66
|
-
'diagnosticReport.uploadSuccess': 'Tanılama raporu destek ekibine gönderildi.',
|
|
67
|
-
'diagnosticReport.uploadFailed':
|
|
68
|
-
'Rapor gönderilemedi. Lütfen daha sonra tekrar deneyin.',
|
|
62
|
+
'diagnosticReport.sentTitle': 'Rapor gönderildi',
|
|
63
|
+
'diagnosticReport.sentBody': 'Tanılama raporunuz destek ekibine gönderildi. Teşekkürler.',
|
|
64
|
+
'diagnosticReport.failedTitle': 'Rapor gönderilemedi',
|
|
65
|
+
'diagnosticReport.failedBody': 'Lütfen daha sonra tekrar deneyin.',
|
|
69
66
|
'livenessDetectionScreen.guideHeader': 'Yüz Doğrulaması',
|
|
70
67
|
'livenessDetectionScreen.guideText':
|
|
71
68
|
'En iyi sonuç için lütfen şunları sağlayın:',
|
package/src/version.ts
CHANGED