@sanctum-key/react-native-sdk 1.0.49 → 1.0.50
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/build/package.json +1 -1
- package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.js +33 -108
- package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.js +50 -164
- package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.js +32 -109
- package/build/src/components/KYCElements/DocumentTypeSelection.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +58 -25
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.d.ts.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.js +13 -1
- package/build/src/components/KYCElements/InitializationStep.js.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.js +46 -157
- package/build/src/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.js +28 -24
- package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +0 -4
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/components/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/SelfieCapture.js +33 -90
- package/build/src/components/SelfieCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +69 -120
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +0 -2
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +95 -210
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/build/src/utils/logger.d.ts +12 -4
- package/build/src/utils/logger.d.ts.map +1 -1
- package/build/src/utils/logger.js +156 -28
- package/build/src/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/CountrySelection.tsx +36 -109
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +67 -266
- package/src/components/KYCElements/DocumentTypeSelection.tsx +34 -110
- package/src/components/KYCElements/IDCardCapture.tsx +93 -39
- package/src/components/KYCElements/InitializationStep.tsx +17 -2
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +53 -170
- package/src/components/KYCElements/SelfieCapture.tsx +50 -58
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +1 -4
- package/src/components/SelfieCapture.tsx +44 -100
- package/src/components/TemplateKYCExample.tsx +94 -145
- package/src/components/TemplateKYCFlowRefactored.tsx +105 -239
- package/src/hooks/useTemplateKYCFlow.tsx +1 -0
- package/src/types/KYC.types.ts +0 -1
- package/src/utils/logger.ts +182 -29
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from 'react-native';
|
|
3
3
|
import { KYCElement, KYCElementOption } from '../../types/KYC.types';
|
|
4
|
+
import { sdkLogger } from '../../utils/logger'; // 🚨 IMPORT SDK LOGGER
|
|
5
|
+
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow'; // 🚨 IMPORT CONTEXT
|
|
4
6
|
|
|
5
7
|
interface DocumentTypeSelectionProps {
|
|
6
8
|
element: KYCElement;
|
|
@@ -10,30 +12,10 @@ interface DocumentTypeSelectionProps {
|
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
const defaultDocumentTypes: KYCElementOption[] = [
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
description: 'Document de voyage officiel'
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
value: 'id_card',
|
|
21
|
-
label: 'Carte d\'identité',
|
|
22
|
-
icon: '🆔',
|
|
23
|
-
description: 'Carte d\'identité nationale'
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
value: 'drivers_license',
|
|
27
|
-
label: 'Permis de conduire',
|
|
28
|
-
icon: '🚗',
|
|
29
|
-
description: 'Permis de conduire valide'
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
value: 'other',
|
|
33
|
-
label: 'Autre document',
|
|
34
|
-
icon: '📄',
|
|
35
|
-
description: 'Autre document d\'identité officiel'
|
|
36
|
-
},
|
|
15
|
+
{ value: 'passport', label: 'Passeport', icon: '📘', description: 'Document de voyage officiel' },
|
|
16
|
+
{ value: 'id_card', label: 'Carte d\'identité', icon: '🆔', description: 'Carte d\'identité nationale' },
|
|
17
|
+
{ value: 'drivers_license', label: 'Permis de conduire', icon: '🚗', description: 'Permis de conduire valide' },
|
|
18
|
+
{ value: 'other', label: 'Autre document', icon: '📄', description: 'Autre document d\'identité officiel' },
|
|
37
19
|
];
|
|
38
20
|
|
|
39
21
|
export const DocumentTypeSelection: React.FC<DocumentTypeSelectionProps> = ({
|
|
@@ -43,12 +25,19 @@ export const DocumentTypeSelection: React.FC<DocumentTypeSelectionProps> = ({
|
|
|
43
25
|
error,
|
|
44
26
|
}) => {
|
|
45
27
|
const documentTypes = element.options || defaultDocumentTypes;
|
|
28
|
+
const { state } = useTemplateKYCFlowContext();
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (state.session?.session_id) {
|
|
32
|
+
sdkLogger.info("User started standalone document type selection step");
|
|
33
|
+
}
|
|
34
|
+
}, [state.session?.session_id]);
|
|
46
35
|
|
|
47
36
|
return (
|
|
48
37
|
<View style={styles.container}>
|
|
49
38
|
<Text style={styles.title}>{element.title}</Text>
|
|
50
39
|
<Text style={styles.description}>{element.description}</Text>
|
|
51
|
-
|
|
40
|
+
|
|
52
41
|
<ScrollView style={styles.optionsContainer} showsVerticalScrollIndicator={false}>
|
|
53
42
|
{documentTypes.map((docType) => (
|
|
54
43
|
<TouchableOpacity
|
|
@@ -57,7 +46,10 @@ export const DocumentTypeSelection: React.FC<DocumentTypeSelectionProps> = ({
|
|
|
57
46
|
styles.option,
|
|
58
47
|
value === docType.value && styles.selectedOption
|
|
59
48
|
]}
|
|
60
|
-
onPress={() =>
|
|
49
|
+
onPress={() => {
|
|
50
|
+
sdkLogger.info("User selected document type", { documentType: docType.value }); // 🚨 LOG
|
|
51
|
+
onValueChange(docType.value);
|
|
52
|
+
}}
|
|
61
53
|
>
|
|
62
54
|
<Text style={styles.optionIcon}>{docType.icon}</Text>
|
|
63
55
|
<View style={styles.optionContent}>
|
|
@@ -88,86 +80,18 @@ export const DocumentTypeSelection: React.FC<DocumentTypeSelectionProps> = ({
|
|
|
88
80
|
};
|
|
89
81
|
|
|
90
82
|
const styles = StyleSheet.create({
|
|
91
|
-
container: {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
marginBottom: 24,
|
|
107
|
-
lineHeight: 22,
|
|
108
|
-
},
|
|
109
|
-
optionsContainer: {
|
|
110
|
-
flex: 1,
|
|
111
|
-
},
|
|
112
|
-
option: {
|
|
113
|
-
flexDirection: 'row',
|
|
114
|
-
alignItems: 'center',
|
|
115
|
-
padding: 16,
|
|
116
|
-
backgroundColor: 'white',
|
|
117
|
-
borderRadius: 12,
|
|
118
|
-
marginBottom: 12,
|
|
119
|
-
borderWidth: 2,
|
|
120
|
-
borderColor: '#e5e5e5',
|
|
121
|
-
shadowColor: '#000',
|
|
122
|
-
shadowOffset: {
|
|
123
|
-
width: 0,
|
|
124
|
-
height: 2,
|
|
125
|
-
},
|
|
126
|
-
shadowOpacity: 0.1,
|
|
127
|
-
shadowRadius: 3.84,
|
|
128
|
-
elevation: 5,
|
|
129
|
-
},
|
|
130
|
-
selectedOption: {
|
|
131
|
-
borderColor: '#4CAF50',
|
|
132
|
-
backgroundColor: '#f0f9f0',
|
|
133
|
-
},
|
|
134
|
-
optionIcon: {
|
|
135
|
-
fontSize: 32,
|
|
136
|
-
marginRight: 16,
|
|
137
|
-
},
|
|
138
|
-
optionContent: {
|
|
139
|
-
flex: 1,
|
|
140
|
-
},
|
|
141
|
-
optionLabel: {
|
|
142
|
-
fontSize: 18,
|
|
143
|
-
fontWeight: '600',
|
|
144
|
-
color: '#333',
|
|
145
|
-
},
|
|
146
|
-
selectedOptionLabel: {
|
|
147
|
-
color: '#4CAF50',
|
|
148
|
-
},
|
|
149
|
-
optionDescription: {
|
|
150
|
-
fontSize: 14,
|
|
151
|
-
color: '#666',
|
|
152
|
-
marginTop: 4,
|
|
153
|
-
},
|
|
154
|
-
checkmark: {
|
|
155
|
-
width: 24,
|
|
156
|
-
height: 24,
|
|
157
|
-
borderRadius: 12,
|
|
158
|
-
backgroundColor: '#4CAF50',
|
|
159
|
-
justifyContent: 'center',
|
|
160
|
-
alignItems: 'center',
|
|
161
|
-
},
|
|
162
|
-
checkmarkText: {
|
|
163
|
-
color: 'white',
|
|
164
|
-
fontSize: 16,
|
|
165
|
-
fontWeight: 'bold',
|
|
166
|
-
},
|
|
167
|
-
errorText: {
|
|
168
|
-
color: '#dc2626',
|
|
169
|
-
fontSize: 14,
|
|
170
|
-
marginTop: 8,
|
|
171
|
-
textAlign: 'center',
|
|
172
|
-
},
|
|
173
|
-
});
|
|
83
|
+
container: { flex: 1, padding: 16, },
|
|
84
|
+
title: { fontSize: 24, fontWeight: 'bold', color: '#333', marginBottom: 8, textAlign: 'center', },
|
|
85
|
+
description: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 24, lineHeight: 22, },
|
|
86
|
+
optionsContainer: { flex: 1, },
|
|
87
|
+
option: { flexDirection: 'row', alignItems: 'center', padding: 16, backgroundColor: 'white', borderRadius: 12, marginBottom: 12, borderWidth: 2, borderColor: '#e5e5e5', shadowColor: '#000', shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 3.84, elevation: 5, },
|
|
88
|
+
selectedOption: { borderColor: '#4CAF50', backgroundColor: '#f0f9f0', },
|
|
89
|
+
optionIcon: { fontSize: 32, marginRight: 16, },
|
|
90
|
+
optionContent: { flex: 1, },
|
|
91
|
+
optionLabel: { fontSize: 18, fontWeight: '600', color: '#333', },
|
|
92
|
+
selectedOptionLabel: { color: '#4CAF50', },
|
|
93
|
+
optionDescription: { fontSize: 14, color: '#666', marginTop: 4, },
|
|
94
|
+
checkmark: { width: 24, height: 24, borderRadius: 12, backgroundColor: '#4CAF50', justifyContent: 'center', alignItems: 'center', },
|
|
95
|
+
checkmarkText: { color: 'white', fontSize: 16, fontWeight: 'bold', },
|
|
96
|
+
errorText: { color: '#dc2626', fontSize: 14, marginTop: 8, textAlign: 'center', },
|
|
97
|
+
});
|
|
@@ -18,6 +18,7 @@ import { cropImageWithBBoxWithTolerance } from '../../utils/cropByObb';
|
|
|
18
18
|
import kycService, { authentification, clearAuthToken } from '../../modules/api/KYCService';
|
|
19
19
|
import { Linking } from 'react-native';
|
|
20
20
|
import { Camera } from 'react-native-vision-camera';
|
|
21
|
+
import { sdkLogger } from '../../utils/logger';
|
|
21
22
|
|
|
22
23
|
const ISO_TO_COUNTRY_NAME: Record<string, string> = {
|
|
23
24
|
'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada',
|
|
@@ -95,8 +96,15 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
95
96
|
const scannedBarcodeRef = useRef<string | null>(null);
|
|
96
97
|
const [generatedCode, setGeneratedCode] = useState<string | null>(null);
|
|
97
98
|
const [isGeneratingCode, setIsGeneratingCode] = useState(false);
|
|
98
|
-
|
|
99
99
|
const { actions, state, env, apiKey } = useTemplateKYCFlowContext();
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (state.session?.session_id) {
|
|
104
|
+
sdkLogger.info("User started ID document verification step", { side: currentSide });
|
|
105
|
+
}
|
|
106
|
+
}, [state.session?.session_id, currentSide]);
|
|
107
|
+
|
|
100
108
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
101
109
|
|
|
102
110
|
|
|
@@ -216,51 +224,37 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
216
224
|
}, [value, currentSide]);
|
|
217
225
|
|
|
218
226
|
const handleStartCamera = async () => {
|
|
219
|
-
|
|
227
|
+
sdkLogger.info(`Requesting camera start for ${currentSide} document verification`, { os: Platform.OS }); // 🚨 LOG
|
|
220
228
|
if (Platform.OS === 'web') {
|
|
221
229
|
setShowCamera(true);
|
|
222
230
|
refreshCamera();
|
|
223
231
|
actions.showCustomStepper(false);
|
|
224
232
|
return;
|
|
225
233
|
}
|
|
226
|
-
|
|
227
234
|
try {
|
|
228
|
-
// 1. Get current status
|
|
229
235
|
let status = await Camera.getCameraPermissionStatus();
|
|
230
|
-
|
|
231
|
-
// 2. If it's anything other than granted, request it
|
|
232
236
|
if (status !== 'granted') {
|
|
237
|
+
sdkLogger.info(`Camera permission not granted yet, requesting...`);
|
|
233
238
|
status = await Camera.requestCameraPermission();
|
|
234
239
|
}
|
|
235
|
-
|
|
236
|
-
// 3. Check final status
|
|
237
240
|
if (status === 'granted') {
|
|
238
|
-
|
|
241
|
+
sdkLogger.info(`Camera permission granted. Mounting camera...`);
|
|
239
242
|
setShowCamera(true);
|
|
240
243
|
refreshCamera();
|
|
241
244
|
actions.showCustomStepper(false);
|
|
242
245
|
} else {
|
|
243
|
-
|
|
246
|
+
sdkLogger.warn(`Camera permission denied by user.`);
|
|
244
247
|
Alert.alert(
|
|
245
248
|
state.currentLanguage === 'en' ? 'Camera Access Required' : 'Accès à la caméra requis',
|
|
246
|
-
state.currentLanguage === 'en'
|
|
247
|
-
? 'We need camera access to scan your ID. Please tap "Settings" to enable it.'
|
|
248
|
-
: 'Nous avons besoin de l\'accès à la caméra pour scanner votre pièce d\'identité. Veuillez appuyer sur "Paramètres" pour l\'activer.',
|
|
249
|
+
state.currentLanguage === 'en' ? 'We need camera access to scan your ID. Please tap "Settings" to enable it.' : 'Nous avons besoin de l\'accès à la caméra pour scanner votre pièce d\'identité. Veuillez appuyer sur "Paramètres" pour l\'activer.',
|
|
249
250
|
[
|
|
250
|
-
{
|
|
251
|
-
|
|
252
|
-
style: 'cancel'
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
text: state.currentLanguage === 'en' ? 'Settings' : 'Paramètres',
|
|
256
|
-
onPress: () => Linking.openSettings()
|
|
257
|
-
}
|
|
251
|
+
{ text: state.currentLanguage === 'en' ? 'Cancel' : 'Annuler', style: 'cancel' },
|
|
252
|
+
{ text: state.currentLanguage === 'en' ? 'Settings' : 'Paramètres', onPress: () => Linking.openSettings() }
|
|
258
253
|
]
|
|
259
254
|
);
|
|
260
255
|
}
|
|
261
|
-
} catch (error) {
|
|
262
|
-
|
|
263
|
-
// Fallback
|
|
256
|
+
} catch (error: any) {
|
|
257
|
+
sdkLogger.error('Failed to request native camera permission', { error: error?.message }); // 🚨 LOG
|
|
264
258
|
setShowCamera(true);
|
|
265
259
|
refreshCamera();
|
|
266
260
|
actions.showCustomStepper(false);
|
|
@@ -268,7 +262,10 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
268
262
|
};
|
|
269
263
|
|
|
270
264
|
const cameraConfig = useMemo(() => {
|
|
271
|
-
let instructions = selectedDocumentType
|
|
265
|
+
let instructions = selectedDocumentType
|
|
266
|
+
? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
|
|
267
|
+
: getLocalizedText(component.instructions as Record<string, LocalizedText>);
|
|
268
|
+
|
|
272
269
|
if (scanPhase === 'barcode') instructions = '';
|
|
273
270
|
|
|
274
271
|
const isLandscape = screenWidth > screenHeight;
|
|
@@ -276,22 +273,38 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
276
273
|
|
|
277
274
|
if (Platform.OS === 'web') {
|
|
278
275
|
const boxWidthPercent = isLandscape ? 0.40 : 0.85;
|
|
279
|
-
xMin = isLandscape ? 30 : 7.5;
|
|
276
|
+
xMin = isLandscape ? 30 : 7.5;
|
|
280
277
|
xMax = xMin + (boxWidthPercent * 100);
|
|
281
278
|
const containerWidth = Math.min(screenWidth * 0.95, 1000);
|
|
282
279
|
const containerHeight = isLandscape ? screenHeight * 0.80 : screenHeight * 0.90;
|
|
283
280
|
const boxPixelWidth = containerWidth * boxWidthPercent;
|
|
284
281
|
const boxPixelHeight = boxPixelWidth / 1.59;
|
|
285
282
|
const heightPercent = (boxPixelHeight / containerHeight) * 100;
|
|
286
|
-
yMin = (100 - heightPercent) / 2.5;
|
|
283
|
+
yMin = (100 - heightPercent) / 2.5;
|
|
287
284
|
yMax = yMin + heightPercent;
|
|
288
285
|
} else {
|
|
289
|
-
|
|
286
|
+
// Standard Horizontal Frame (Width: 90%, Height: 32%)
|
|
287
|
+
xMin = 5;
|
|
288
|
+
xMax = 95;
|
|
289
|
+
yMin = 34;
|
|
290
|
+
yMax = 66;
|
|
290
291
|
}
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
|
|
293
|
+
const platformBbox = {
|
|
294
|
+
xMin, yMin, xMax, yMax,
|
|
295
|
+
borderColor: scanPhase === 'barcode' ? '#EAB308' : '#2DBD60',
|
|
296
|
+
borderWidth: Platform.OS === 'web' ? 2 : 3,
|
|
297
|
+
cornerRadius: 8
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
cameraType: Platform.OS === 'web' ? cameraType : 'back',
|
|
302
|
+
flashMode: 'auto' as const,
|
|
303
|
+
overlay: { guideText: instructions, bbox: platformBbox }
|
|
304
|
+
};
|
|
293
305
|
}, [selectedDocumentType, locale, component.instructions, cameraType, screenWidth, screenHeight, scanPhase]);
|
|
294
306
|
|
|
307
|
+
|
|
295
308
|
const retakePicture = (sideToRetake: 'front' | 'back') => {
|
|
296
309
|
setIsProcessingCapture(false); setProcessingImagePath(null); scannedBarcodeRef.current = null;
|
|
297
310
|
setScanPhase('image'); setPendingCAImage(null);
|
|
@@ -357,11 +370,31 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
357
370
|
|
|
358
371
|
try {
|
|
359
372
|
let imagePathForUpload = capturePath;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
const overlayBbox = cameraConfig.overlay.bbox;
|
|
374
|
+
|
|
375
|
+
// 1. Default: Crop based on the landscape UI box
|
|
376
|
+
let uiCropBbox = {
|
|
377
|
+
minX: overlayBbox.xMin / 100,
|
|
378
|
+
minY: overlayBbox.yMin / 100,
|
|
379
|
+
width: (overlayBbox.xMax - overlayBbox.xMin) / 100,
|
|
380
|
+
height: (overlayBbox.yMax - overlayBbox.yMin) / 100
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
const isSpecificPortrait = verified.templatePath && verified.templatePath.includes('cameroon_id_front_1_1_new');
|
|
385
|
+
|
|
386
|
+
if (isSpecificPortrait) {
|
|
387
|
+
uiCropBbox = {
|
|
388
|
+
minX: 0.15,
|
|
389
|
+
minY: 0.15,
|
|
390
|
+
width: 0.70,
|
|
391
|
+
height: 0.70
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const cropTolerance = isSpecificPortrait ? 0.15 : 0.06;
|
|
396
|
+
|
|
397
|
+
imagePathForUpload = await cropImageWithBBoxWithTolerance(safeCapturePath, uiCropBbox, cropTolerance);
|
|
365
398
|
} catch (e) {
|
|
366
399
|
console.warn("Crop failed, falling back to original image", e);
|
|
367
400
|
imagePathForUpload = safeCapturePath;
|
|
@@ -369,7 +402,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
369
402
|
|
|
370
403
|
const safeCroppedPath = (Platform.OS === 'ios' && !imagePathForUpload.startsWith('file://')) ? `file://${imagePathForUpload}` : imagePathForUpload;
|
|
371
404
|
const base64 = await pathToBase64(safeCroppedPath);
|
|
372
|
-
const side = currentSideRef.current;
|
|
405
|
+
const side = currentSideRef.current;
|
|
373
406
|
|
|
374
407
|
const newImages = {
|
|
375
408
|
...capturedImagesRef.current,
|
|
@@ -377,6 +410,9 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
377
410
|
};
|
|
378
411
|
|
|
379
412
|
setCapturedImages(newImages);
|
|
413
|
+
|
|
414
|
+
sdkLogger.info(`User successfully captured ${side} document`);
|
|
415
|
+
|
|
380
416
|
if (verified.country && verified.documentType) {
|
|
381
417
|
const docType = verified.documentType as string;
|
|
382
418
|
const resolvedDocumentType = GovernmentDocumentTypeBackend[docType] || (Object.values(GovernmentDocumentTypeBackend).includes(docType as GovernmentDocumentType) ? docType : null) || Object.entries(GovernmentDocumentTypeBackend).find(([, v]) => v === docType)?.[1] || docType;
|
|
@@ -391,6 +427,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
391
427
|
}, 600);
|
|
392
428
|
|
|
393
429
|
} catch (e: any) {
|
|
430
|
+
sdkLogger.error(`Auto-capture formatting/saving failed`, { error: e.message });
|
|
394
431
|
const friendlyError = state.currentLanguage === 'en' ? 'Unable to process document. Please try again.' : 'Impossible de traiter le document. Veuillez réessayer.';
|
|
395
432
|
showAlert('Error', friendlyError);
|
|
396
433
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false }));
|
|
@@ -536,6 +573,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
536
573
|
}
|
|
537
574
|
}
|
|
538
575
|
|
|
576
|
+
sdkLogger.info(`Template check passed for ${side}`, { templatePath });
|
|
539
577
|
const extractedCountryKey = templatePath ? templatePath.split('/')[0] : (ISO_TO_COUNTRY_NAME[cData?.code || ''] || 'root');
|
|
540
578
|
const regionMappings = getCurrentSideVerification(side, extractedCountryKey, cData);
|
|
541
579
|
let verificationRes: any;
|
|
@@ -574,7 +612,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
574
612
|
await autoCapture(safeCapturePath!, verifiedResult);
|
|
575
613
|
return;
|
|
576
614
|
}
|
|
577
|
-
|
|
615
|
+
sdkLogger.info(`Verification passed. Waiting for barcode scan phase.`); // 🚨 LOG
|
|
578
616
|
clearTimeout(watchdog);
|
|
579
617
|
setPendingCAImage({ path: safeCapturePath!, templateResponse, templatePath, bbox });
|
|
580
618
|
setScanPhase('barcode');
|
|
@@ -602,6 +640,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
602
640
|
rawMessage.includes('token') ||
|
|
603
641
|
rawMessage.includes('unauthenticated')
|
|
604
642
|
) {
|
|
643
|
+
sdkLogger.error(`Secure API Session Expired during AI extraction`);
|
|
605
644
|
setShowCamera(false);
|
|
606
645
|
setIsProcessingCapture(false);
|
|
607
646
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: '' }));
|
|
@@ -713,6 +752,8 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
713
752
|
if (errString.includes('notreadableerror') || errString.includes('trackstarterror') || errString.includes('in use') || errString.includes('concurrent') || errString.includes('camera unavailable')) { friendlyMessage = state.currentLanguage === 'en' ? 'Camera is in use by another app.' : 'Caméra utilisée par une autre application.'; }
|
|
714
753
|
else if (errString.includes('notallowederror') || errString.includes('permission')) { friendlyMessage = state.currentLanguage === 'en' ? 'Allow camera access in settings.' : 'Autorisez l\'accès à la caméra.'; }
|
|
715
754
|
else if (errString.includes('notfounderror') || errString.includes('no camera')) { friendlyMessage = state.currentLanguage === 'en' ? 'No camera detected.' : 'Aucune caméra détectée.'; }
|
|
755
|
+
|
|
756
|
+
sdkLogger.error("Hardware Camera Error encountered", { ...event, friendlyMessage }); // 🚨 LOG
|
|
716
757
|
showAlert(state.currentLanguage === 'en' ? 'Camera Error' : 'Erreur', friendlyMessage);
|
|
717
758
|
setShowCamera(false); setIsProcessingCapture(false);
|
|
718
759
|
};
|
|
@@ -886,8 +927,21 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
886
927
|
<Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => retakePicture(currentSide)} variant="outline" size="medium" fullWidth />
|
|
887
928
|
<Button title={t('common.next')} onPress={() => {
|
|
888
929
|
if (!selectedDocumentType) { showAlert('Error', 'Document type not selected'); return; }
|
|
889
|
-
if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
|
|
890
|
-
|
|
930
|
+
if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
|
|
931
|
+
sdkLogger.info("User submitting document captures and advancing to next step", {
|
|
932
|
+
documentType: selectedDocumentType.type
|
|
933
|
+
});
|
|
934
|
+
actions.nextComponent();
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
sdkLogger.info("User finished front verification, proceeding to back");
|
|
938
|
+
setShowCamera(true);
|
|
939
|
+
refreshCamera();
|
|
940
|
+
setCurrentSide('back');
|
|
941
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false, path: '', error: '', templatePath: undefined }); setIsProcessingCapture(false); setProcessingImagePath(null); setScanPhase('image'); setPendingCAImage(null);
|
|
942
|
+
}
|
|
943
|
+
}}
|
|
944
|
+
variant="primary" size="large" fullWidth />
|
|
891
945
|
</>
|
|
892
946
|
)}
|
|
893
947
|
</View>
|
|
@@ -3,21 +3,36 @@ import { SessionState } from "../../types/KYC.types"
|
|
|
3
3
|
import { useEffect } from "react"
|
|
4
4
|
import { useTemplateKYCFlowContext } from "../../hooks/useTemplateKYCFlow"
|
|
5
5
|
import { useI18n } from "../../hooks/useI18n"
|
|
6
|
+
import { sdkLogger } from "../../utils/logger" // 🚨 IMPORT SDK LOGGER
|
|
6
7
|
|
|
7
8
|
export const InitializationStep = ({ session }: { session: SessionState }) => {
|
|
8
9
|
const { t } = useI18n();
|
|
9
10
|
const { initializeSession } = useTemplateKYCFlowContext()
|
|
11
|
+
|
|
10
12
|
useEffect(() => {
|
|
13
|
+
sdkLogger.info("Started KYC session initialization");
|
|
11
14
|
initializeSession()
|
|
12
15
|
}, [])
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (session.error) {
|
|
19
|
+
sdkLogger.error("Session initialization failed", {
|
|
20
|
+
error: session.error
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}, [session.error]);
|
|
15
24
|
|
|
16
25
|
if (session.error) {
|
|
17
26
|
return (
|
|
18
27
|
<View style={styles.container}>
|
|
19
28
|
<Text style={styles.errorText}>{t('errors.serverError')}</Text>
|
|
20
|
-
<Button
|
|
29
|
+
<Button
|
|
30
|
+
title={t('common.retry')}
|
|
31
|
+
onPress={() => {
|
|
32
|
+
sdkLogger.info("User requested session initialization retry"); // 🚨 LOG
|
|
33
|
+
initializeSession();
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
21
36
|
</View>
|
|
22
37
|
)
|
|
23
38
|
}
|