@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
@@ -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
  const ISO_TO_COUNTRY_NAME = {
22
23
  'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada',
23
24
  'FR': 'France', 'GH': 'Ghana', 'ZA': 'South Africa', 'GB': 'Britain',
@@ -73,6 +74,11 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
73
74
  const [generatedCode, setGeneratedCode] = useState(null);
74
75
  const [isGeneratingCode, setIsGeneratingCode] = useState(false);
75
76
  const { actions, state, env, apiKey } = useTemplateKYCFlowContext();
77
+ useEffect(() => {
78
+ if (state.session?.session_id) {
79
+ sdkLogger.info("User started ID document verification step", { side: currentSide });
80
+ }
81
+ }, [state.session?.session_id, currentSide]);
76
82
  const { width: screenWidth, height: screenHeight } = useWindowDimensions();
77
83
  const countrySelectionData = useMemo(() => {
78
84
  const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
@@ -192,7 +198,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
192
198
  }
193
199
  }, [value, currentSide]);
194
200
  const handleStartCamera = async () => {
195
- // Web browsers handle permissions natively when the media stream is requested
201
+ sdkLogger.info(`Requesting camera start for ${currentSide} document verification`, { os: Platform.OS }); // 🚨 LOG
196
202
  if (Platform.OS === 'web') {
197
203
  setShowCamera(true);
198
204
  refreshCamera();
@@ -200,45 +206,36 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
200
206
  return;
201
207
  }
202
208
  try {
203
- // 1. Get current status
204
209
  let status = await Camera.getCameraPermissionStatus();
205
- // 2. If it's anything other than granted, request it
206
210
  if (status !== 'granted') {
211
+ sdkLogger.info(`Camera permission not granted yet, requesting...`);
207
212
  status = await Camera.requestCameraPermission();
208
213
  }
209
- // 3. Check final status
210
214
  if (status === 'granted') {
211
- // Permission given, start camera
215
+ sdkLogger.info(`Camera permission granted. Mounting camera...`);
212
216
  setShowCamera(true);
213
217
  refreshCamera();
214
218
  actions.showCustomStepper(false);
215
219
  }
216
220
  else {
217
- // Permission permanently denied or restricted
218
- Alert.alert(state.currentLanguage === 'en' ? 'Camera Access Required' : 'Accès à la caméra requis', state.currentLanguage === 'en'
219
- ? 'We need camera access to scan your ID. Please tap "Settings" to enable it.'
220
- : '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.', [
221
- {
222
- text: state.currentLanguage === 'en' ? 'Cancel' : 'Annuler',
223
- style: 'cancel'
224
- },
225
- {
226
- text: state.currentLanguage === 'en' ? 'Settings' : 'Paramètres',
227
- onPress: () => Linking.openSettings()
228
- }
221
+ sdkLogger.warn(`Camera permission denied by user.`);
222
+ Alert.alert(state.currentLanguage === 'en' ? 'Camera Access Required' : 'Accès à la caméra requis', 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.', [
223
+ { text: state.currentLanguage === 'en' ? 'Cancel' : 'Annuler', style: 'cancel' },
224
+ { text: state.currentLanguage === 'en' ? 'Settings' : 'Paramètres', onPress: () => Linking.openSettings() }
229
225
  ]);
230
226
  }
231
227
  }
232
228
  catch (error) {
233
- console.warn('Failed to request camera permission', error);
234
- // Fallback
229
+ sdkLogger.error('Failed to request native camera permission', { error: error?.message }); // 🚨 LOG
235
230
  setShowCamera(true);
236
231
  refreshCamera();
237
232
  actions.showCustomStepper(false);
238
233
  }
239
234
  };
240
235
  const cameraConfig = useMemo(() => {
241
- let instructions = selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions);
236
+ let instructions = selectedDocumentType
237
+ ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
238
+ : getLocalizedText(component.instructions);
242
239
  if (scanPhase === 'barcode')
243
240
  instructions = '';
244
241
  const isLandscape = screenWidth > screenHeight;
@@ -256,13 +253,23 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
256
253
  yMax = yMin + heightPercent;
257
254
  }
258
255
  else {
256
+ // Standard Horizontal Frame (Width: 90%, Height: 32%)
259
257
  xMin = 5;
260
258
  xMax = 95;
261
259
  yMin = 34;
262
260
  yMax = 66;
263
261
  }
264
- const platformBbox = { xMin, yMin, xMax, yMax, borderColor: scanPhase === 'barcode' ? '#EAB308' : '#2DBD60', borderWidth: Platform.OS === 'web' ? 2 : 3, cornerRadius: 8 };
265
- return { cameraType: Platform.OS === 'web' ? cameraType : 'back', flashMode: 'auto', overlay: { guideText: instructions, bbox: platformBbox } };
262
+ const platformBbox = {
263
+ xMin, yMin, xMax, yMax,
264
+ borderColor: scanPhase === 'barcode' ? '#EAB308' : '#2DBD60',
265
+ borderWidth: Platform.OS === 'web' ? 2 : 3,
266
+ cornerRadius: 8
267
+ };
268
+ return {
269
+ cameraType: Platform.OS === 'web' ? cameraType : 'back',
270
+ flashMode: 'auto',
271
+ overlay: { guideText: instructions, bbox: platformBbox }
272
+ };
266
273
  }, [selectedDocumentType, locale, component.instructions, cameraType, screenWidth, screenHeight, scanPhase]);
267
274
  const retakePicture = (sideToRetake) => {
268
275
  setIsProcessingCapture(false);
@@ -341,9 +348,25 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
341
348
  try {
342
349
  let imagePathForUpload = capturePath;
343
350
  const overlayBbox = cameraConfig.overlay.bbox;
344
- const uiCropBbox = { minX: overlayBbox.xMin / 100, minY: overlayBbox.yMin / 100, width: (overlayBbox.xMax - overlayBbox.xMin) / 100, height: (overlayBbox.yMax - overlayBbox.yMin) / 100 };
351
+ // 1. Default: Crop based on the landscape UI box
352
+ let uiCropBbox = {
353
+ minX: overlayBbox.xMin / 100,
354
+ minY: overlayBbox.yMin / 100,
355
+ width: (overlayBbox.xMax - overlayBbox.xMin) / 100,
356
+ height: (overlayBbox.yMax - overlayBbox.yMin) / 100
357
+ };
345
358
  try {
346
- imagePathForUpload = await cropImageWithBBoxWithTolerance(safeCapturePath, uiCropBbox, 0.06);
359
+ const isSpecificPortrait = verified.templatePath && verified.templatePath.includes('cameroon_id_front_1_1_new');
360
+ if (isSpecificPortrait) {
361
+ uiCropBbox = {
362
+ minX: 0.15,
363
+ minY: 0.15,
364
+ width: 0.70,
365
+ height: 0.70
366
+ };
367
+ }
368
+ const cropTolerance = isSpecificPortrait ? 0.15 : 0.06;
369
+ imagePathForUpload = await cropImageWithBBoxWithTolerance(safeCapturePath, uiCropBbox, cropTolerance);
347
370
  }
348
371
  catch (e) {
349
372
  console.warn("Crop failed, falling back to original image", e);
@@ -351,12 +374,13 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
351
374
  }
352
375
  const safeCroppedPath = (Platform.OS === 'ios' && !imagePathForUpload.startsWith('file://')) ? `file://${imagePathForUpload}` : imagePathForUpload;
353
376
  const base64 = await pathToBase64(safeCroppedPath);
354
- const side = currentSideRef.current; // Grab live ref
377
+ const side = currentSideRef.current;
355
378
  const newImages = {
356
379
  ...capturedImagesRef.current,
357
380
  [side]: { dir: safeCroppedPath, file: base64, mrz: verified.mrz || "", templatePath: verified.templatePath },
358
381
  };
359
382
  setCapturedImages(newImages);
383
+ sdkLogger.info(`User successfully captured ${side} document`);
360
384
  if (verified.country && verified.documentType) {
361
385
  const docType = verified.documentType;
362
386
  const resolvedDocumentType = GovernmentDocumentTypeBackend[docType] || (Object.values(GovernmentDocumentTypeBackend).includes(docType) ? docType : null) || Object.entries(GovernmentDocumentTypeBackend).find(([, v]) => v === docType)?.[1] || docType;
@@ -373,6 +397,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
373
397
  }, 600);
374
398
  }
375
399
  catch (e) {
400
+ sdkLogger.error(`Auto-capture formatting/saving failed`, { error: e.message });
376
401
  const friendlyError = state.currentLanguage === 'en' ? 'Unable to process document. Please try again.' : 'Impossible de traiter le document. Veuillez réessayer.';
377
402
  showAlert('Error', friendlyError);
378
403
  setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false }));
@@ -519,6 +544,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
519
544
  catch { }
520
545
  }
521
546
  }
547
+ sdkLogger.info(`Template check passed for ${side}`, { templatePath });
522
548
  const extractedCountryKey = templatePath ? templatePath.split('/')[0] : (ISO_TO_COUNTRY_NAME[cData?.code || ''] || 'root');
523
549
  const regionMappings = getCurrentSideVerification(side, extractedCountryKey, cData);
524
550
  let verificationRes;
@@ -556,6 +582,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
556
582
  await autoCapture(safeCapturePath, verifiedResult);
557
583
  return;
558
584
  }
585
+ sdkLogger.info(`Verification passed. Waiting for barcode scan phase.`); // 🚨 LOG
559
586
  clearTimeout(watchdog);
560
587
  setPendingCAImage({ path: safeCapturePath, templateResponse, templatePath, bbox });
561
588
  setScanPhase('barcode');
@@ -579,6 +606,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
579
606
  rawMessage.includes('unauthorized') ||
580
607
  rawMessage.includes('token') ||
581
608
  rawMessage.includes('unauthenticated')) {
609
+ sdkLogger.error(`Secure API Session Expired during AI extraction`);
582
610
  setShowCamera(false);
583
611
  setIsProcessingCapture(false);
584
612
  setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: '' }));
@@ -674,6 +702,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
674
702
  else if (errString.includes('notfounderror') || errString.includes('no camera')) {
675
703
  friendlyMessage = state.currentLanguage === 'en' ? 'No camera detected.' : 'Aucune caméra détectée.';
676
704
  }
705
+ sdkLogger.error("Hardware Camera Error encountered", { ...event, friendlyMessage }); // 🚨 LOG
677
706
  showAlert(state.currentLanguage === 'en' ? 'Camera Error' : 'Erreur', friendlyMessage);
678
707
  setShowCamera(false);
679
708
  setIsProcessingCapture(false);
@@ -813,9 +842,13 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
813
842
  return;
814
843
  }
815
844
  if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
845
+ sdkLogger.info("User submitting document captures and advancing to next step", {
846
+ documentType: selectedDocumentType.type
847
+ });
816
848
  actions.nextComponent();
817
849
  }
818
850
  else {
851
+ sdkLogger.info("User finished front verification, proceeding to back");
819
852
  setShowCamera(true);
820
853
  refreshCamera();
821
854
  setCurrentSide('back');