@sanctum-key/react-native-sdk 1.0.49 → 1.0.51

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/build/package.json +1 -1
  2. package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
  3. package/build/src/components/KYCElements/CountrySelection.js +33 -108
  4. package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
  5. package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  6. package/build/src/components/KYCElements/CountrySelectionTemplate.js +50 -164
  7. package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  8. package/build/src/components/KYCElements/DocumentTypeSelection.d.ts.map +1 -1
  9. package/build/src/components/KYCElements/DocumentTypeSelection.js +32 -109
  10. package/build/src/components/KYCElements/DocumentTypeSelection.js.map +1 -1
  11. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  12. package/build/src/components/KYCElements/IDCardCapture.js +58 -25
  13. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  14. package/build/src/components/KYCElements/InitializationStep.d.ts.map +1 -1
  15. package/build/src/components/KYCElements/InitializationStep.js +13 -1
  16. package/build/src/components/KYCElements/InitializationStep.js.map +1 -1
  17. package/build/src/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  18. package/build/src/components/KYCElements/LocationCaptureTemplate.js +46 -157
  19. package/build/src/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  20. package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  21. package/build/src/components/KYCElements/SelfieCapture.js +28 -24
  22. package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
  23. package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
  24. package/build/src/components/KYCElements/VerificationProgressTemplate.js +0 -4
  25. package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
  26. package/build/src/components/SelfieCapture.d.ts.map +1 -1
  27. package/build/src/components/SelfieCapture.js +33 -90
  28. package/build/src/components/SelfieCapture.js.map +1 -1
  29. package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
  30. package/build/src/components/TemplateKYCExample.js +69 -120
  31. package/build/src/components/TemplateKYCExample.js.map +1 -1
  32. package/build/src/components/TemplateKYCFlowRefactored.d.ts +0 -2
  33. package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  34. package/build/src/components/TemplateKYCFlowRefactored.js +95 -210
  35. package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
  36. package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  37. package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
  38. package/build/src/types/KYC.types.d.ts.map +1 -1
  39. package/build/src/types/KYC.types.js.map +1 -1
  40. package/build/src/utils/logger.d.ts +12 -4
  41. package/build/src/utils/logger.d.ts.map +1 -1
  42. package/build/src/utils/logger.js +156 -28
  43. package/build/src/utils/logger.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/components/KYCElements/CountrySelection.tsx +36 -109
  46. package/src/components/KYCElements/CountrySelectionTemplate.tsx +67 -266
  47. package/src/components/KYCElements/DocumentTypeSelection.tsx +34 -110
  48. package/src/components/KYCElements/IDCardCapture.tsx +93 -39
  49. package/src/components/KYCElements/InitializationStep.tsx +17 -2
  50. package/src/components/KYCElements/LocationCaptureTemplate.tsx +53 -170
  51. package/src/components/KYCElements/SelfieCapture.tsx +50 -58
  52. package/src/components/KYCElements/VerificationProgressTemplate.tsx +1 -4
  53. package/src/components/SelfieCapture.tsx +44 -100
  54. package/src/components/TemplateKYCExample.tsx +94 -145
  55. package/src/components/TemplateKYCFlowRefactored.tsx +105 -239
  56. package/src/hooks/useTemplateKYCFlow.tsx +1 -0
  57. package/src/types/KYC.types.ts +0 -1
  58. 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
- value: 'passport',
15
- label: 'Passeport',
16
- icon: '📘',
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={() => onValueChange(docType.value)}
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
- flex: 1,
93
- padding: 16,
94
- },
95
- title: {
96
- fontSize: 24,
97
- fontWeight: 'bold',
98
- color: '#333',
99
- marginBottom: 8,
100
- textAlign: 'center',
101
- },
102
- description: {
103
- fontSize: 16,
104
- color: '#666',
105
- textAlign: 'center',
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
- // Web browsers handle permissions natively when the media stream is requested
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
- // Permission given, start camera
241
+ sdkLogger.info(`Camera permission granted. Mounting camera...`);
239
242
  setShowCamera(true);
240
243
  refreshCamera();
241
244
  actions.showCustomStepper(false);
242
245
  } else {
243
- // Permission permanently denied or restricted
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
- text: state.currentLanguage === 'en' ? 'Cancel' : 'Annuler',
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
- console.warn('Failed to request camera permission', error);
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 ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions as Record<string, LocalizedText>);
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
- xMin = 5; xMax = 95; yMin = 34; yMax = 66;
286
+ // Standard Horizontal Frame (Width: 90%, Height: 32%)
287
+ xMin = 5;
288
+ xMax = 95;
289
+ yMin = 34;
290
+ yMax = 66;
290
291
  }
291
- const platformBbox = { xMin, yMin, xMax, yMax, borderColor: scanPhase === 'barcode' ? '#EAB308' : '#2DBD60', borderWidth: Platform.OS === 'web' ? 2 : 3, cornerRadius: 8 };
292
- return { cameraType: Platform.OS === 'web' ? cameraType : 'back', flashMode: 'auto' as const, overlay: { guideText: instructions, bbox: platformBbox } };
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
- const overlayBbox = cameraConfig.overlay.bbox;
361
- const uiCropBbox = { minX: overlayBbox.xMin / 100, minY: overlayBbox.yMin / 100, width: (overlayBbox.xMax - overlayBbox.xMin) / 100, height: (overlayBbox.yMax - overlayBbox.yMin) / 100 };
362
-
363
- try {
364
- imagePathForUpload = await cropImageWithBBoxWithTolerance(safeCapturePath, uiCropBbox, 0.06);
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; // Grab live ref
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') { actions.nextComponent(); } else { setShowCamera(true); refreshCamera(); setCurrentSide('back'); setSilentCaptureResult({ success: false, isAnalyzing: false, path: '', error: '', templatePath: undefined }); setIsProcessingCapture(false); setProcessingImagePath(null); setScanPhase('image'); setPendingCAImage(null); }
890
- }} variant="primary" size="large" fullWidth />
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 title={t('common.retry')} onPress={initializeSession} />
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
  }