@transfergratis/react-native-sdk 0.1.28 → 0.1.29

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 (39) hide show
  1. package/build/components/EnhancedCameraView.js +1 -1
  2. package/build/components/EnhancedCameraView.js.map +1 -1
  3. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  4. package/build/components/KYCElements/CountrySelectionTemplate.js +13 -42
  5. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  6. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  7. package/build/components/KYCElements/IDCardCapture.js +64 -31
  8. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  9. package/build/components/KYCElements/SelfieCaptureTemplate.js +2 -2
  10. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  11. package/build/hooks/useTemplateKYCFlow.js +1 -1
  12. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  13. package/build/modules/api/CardAuthentification.d.ts +15 -7
  14. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  15. package/build/modules/api/CardAuthentification.js +215 -50
  16. package/build/modules/api/CardAuthentification.js.map +1 -1
  17. package/build/modules/api/KYCService.d.ts +2 -0
  18. package/build/modules/api/KYCService.d.ts.map +1 -1
  19. package/build/modules/api/KYCService.js +16 -19
  20. package/build/modules/api/KYCService.js.map +1 -1
  21. package/build/modules/camera/VisionCameraModule.js +2 -2
  22. package/build/modules/camera/VisionCameraModule.js.map +1 -1
  23. package/build/utils/cropByObb.d.ts +8 -0
  24. package/build/utils/cropByObb.d.ts.map +1 -1
  25. package/build/utils/cropByObb.js +20 -3
  26. package/build/utils/cropByObb.js.map +1 -1
  27. package/build/utils/pathToBase64.js +1 -1
  28. package/build/utils/pathToBase64.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/components/EnhancedCameraView.tsx +1 -1
  31. package/src/components/KYCElements/CountrySelectionTemplate.tsx +24 -52
  32. package/src/components/KYCElements/IDCardCapture.tsx +115 -86
  33. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
  34. package/src/hooks/useTemplateKYCFlow.tsx +1 -1
  35. package/src/modules/api/CardAuthentification.ts +289 -64
  36. package/src/modules/api/KYCService.ts +25 -33
  37. package/src/modules/camera/VisionCameraModule.ts +2 -2
  38. package/src/utils/cropByObb.ts +22 -3
  39. package/src/utils/pathToBase64.ts +1 -1
@@ -43,7 +43,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
43
43
  const { t, locale } = useI18n();
44
44
  const [showCamera, setShowCamera] = useState(false);
45
45
  const [capturedImages, setCapturedImages] = useState<Record<string, IIDCardPayload>>(value || {});
46
- const [cropImageUri, setCropImageUri] = useState<string>('');
46
+ // const [cropImageUri, setCropImageUri] = useState<string>('');
47
47
  const [currentSide, setCurrentSide] = useState<string>('front');
48
48
  // Stocker les bbox par côté pour pouvoir restaurer les images croppées
49
49
  const [bboxBySide, setBboxBySide] = useState<Record<string, IBbox>>({});
@@ -97,6 +97,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
97
97
  return countrySelectionData;
98
98
  }, [countrySelectionData]);
99
99
 
100
+ // console.log();
100
101
 
101
102
  // Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
102
103
  useEffect(() => {
@@ -131,7 +132,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
131
132
  logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
132
133
  if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
133
134
  cropImageWithBBox(capturedImages[currentSide].dir, silentCaptureResult.bbox)?.then((uri) => {
134
- setCropImageUri(uri);
135
+ // setCropImageUri(uri);
135
136
  });
136
137
  }
137
138
  }, [capturedImages[currentSide]?.dir, silentCaptureResult?.bbox])
@@ -145,9 +146,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
145
146
 
146
147
  return {
147
148
  aspectRatio: 4 / 3,
148
- quality: 0.8,
149
+ quality: 0.7,
149
150
  flashMode: 'auto' as const,
150
151
  cameraType: 'back' as const,
152
+ autoFocus: 'on',
153
+ whiteBalance: 'auto',
151
154
  allowRetake: true,
152
155
  maxRetakes: 3,
153
156
  overlay: {
@@ -170,8 +173,19 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
170
173
  setShowCamera(true);
171
174
  actions.showCustomStepper(false);
172
175
  setCapturedImages({ ...capturedImages, [currentSide]: { dir: '', file: '', mrz: '' } });
173
- setSilentCaptureResult((prev) => ({ ...prev, path: '', success: false, isAnalyzing: false, error: '' }));
174
- setCropImageUri('');
176
+
177
+ setSilentCaptureResult((prev) => ({
178
+ ...prev,
179
+ path: '',
180
+ success: false,
181
+ isAnalyzing: false,
182
+ error: '',
183
+ templatePath: '',
184
+ bbox: undefined,
185
+ mrz: ''
186
+ }));
187
+
188
+ // setCropImageUri('');
175
189
  onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
176
190
  };
177
191
 
@@ -381,31 +395,32 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
381
395
  }
382
396
  }
383
397
 
384
- // Handle capture
385
398
  const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
386
- console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
387
-
388
399
  if (silentCaptureResult.path) {
389
- // Créer une image rognée avec tolérance de 10% pour l'envoi
390
400
  let imagePathForUpload = silentCaptureResult.path;
391
401
  if (silentCaptureResult.bbox) {
392
402
  try {
393
- logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
394
403
  imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
395
- logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
396
404
  } catch (error) {
397
- logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
398
- // En cas d'erreur, on utilise l'image originale
399
405
  imagePathForUpload = silentCaptureResult.path;
400
406
  }
401
407
  }
402
408
 
403
409
  const base64 = await pathToBase64(imagePathForUpload);
404
410
 
405
- logger.log("silentCaptureResult captured", JSON.stringify(truncateFields(silentCaptureResult), null, 2));
406
- // Utiliser l'image originale pour l'affichage (dir) mais l'image rognée avec tolérance pour l'envoi (file)
407
- const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "", templatePath: silentCaptureResult.templatePath } };
411
+ // ✅ FIX: Use 'imagePathForUpload' (the cropped image) for the local display directory!
412
+ const newImages = {
413
+ ...capturedImages,
414
+ [currentSide]: {
415
+ dir: imagePathForUpload, // <--- CHANGED THIS LINE
416
+ file: base64,
417
+ mrz: silentCaptureResult.mrz || "",
418
+ templatePath: silentCaptureResult.templatePath
419
+ }
420
+ };
421
+
408
422
  setCapturedImages(newImages);
423
+
409
424
  if (silentCaptureResult.country && silentCaptureResult.documentType) {
410
425
  onValueChange({
411
426
  ...newImages,
@@ -425,10 +440,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
425
440
  setShowCamera(false);
426
441
  };
427
442
 
428
-
429
-
430
-
431
-
432
443
  useEffect(() => {
433
444
  actions.showCustomStepper(!showCamera);
434
445
  }, [showCamera]);
@@ -531,7 +542,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
531
542
 
532
543
 
533
544
  if (showCamera) {
534
-
535
545
  return (
536
546
  <View style={styles.cameraContainer}>
537
547
  <EnhancedCameraView
@@ -548,54 +558,63 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
548
558
  silentCaptureResult={silentCaptureResult}
549
559
  captureStabilizationDelayMs={3000}
550
560
  enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'}
551
- overlayComponent={<IdCardOverlay
552
- xMin={cameraConfig.overlay.bbox.xMin}
553
- yMin={cameraConfig.overlay.bbox.yMin}
554
- xMax={cameraConfig.overlay.bbox.xMax}
555
- yMax={cameraConfig.overlay.bbox.yMax}
556
- instructions={cameraConfig.overlay.guideText}
557
- cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
558
- isSuccess={silentCaptureResult.success}
559
- language={state.currentLanguage}
560
- stepperProps={{
561
- back: () => {
562
- if (currentSide === 'back') {
563
- setCurrentSide('front');
564
- setShowCamera(false);
565
- // Si une image front existe, on la restaure pour l'afficher
566
- if (capturedImages['front']?.dir) {
567
- // Restaurer l'état de capture du front pour afficher l'image
568
- const frontImage = capturedImages['front'];
569
- const frontBbox = bboxBySide['front'];
570
- setSilentCaptureResult((prev) => ({
571
- path: frontImage.dir,
572
- success: true,
573
- isAnalyzing: false,
574
- error: '',
575
- mrz: frontImage.mrz || '',
576
- templatePath: frontImage.templatePath || '',
577
- country: silentCaptureResult.country,
578
- documentType: silentCaptureResult.documentType,
579
- bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
580
- }));
581
- // Le useEffect va automatiquement recalculer le cropImageUri si bbox existe
561
+ overlayComponent={
562
+ <IdCardOverlay
563
+ xMin={cameraConfig.overlay.bbox.xMin}
564
+ yMin={cameraConfig.overlay.bbox.yMin}
565
+ xMax={cameraConfig.overlay.bbox.xMax}
566
+ yMax={cameraConfig.overlay.bbox.yMax}
567
+ instructions={cameraConfig.overlay.guideText}
568
+ cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
569
+ isSuccess={silentCaptureResult.success}
570
+ language={state.currentLanguage}
571
+ stepperProps={{
572
+ back: () => {
573
+ if (currentSide === 'back') {
574
+ setCurrentSide('front');
575
+ setShowCamera(false);
576
+ // Si une image front existe, on la restaure pour l'afficher
577
+ if (capturedImages['front']?.dir) {
578
+ // Restaurer l'état de capture du front pour afficher l'image
579
+ const frontImage = capturedImages['front'];
580
+ const frontBbox = bboxBySide['front'];
581
+ setSilentCaptureResult((prev) => ({
582
+ path: frontImage.dir,
583
+ success: true,
584
+ isAnalyzing: false,
585
+ error: '',
586
+ mrz: frontImage.mrz || '',
587
+ templatePath: frontImage.templatePath || '',
588
+ country: silentCaptureResult.country,
589
+ documentType: silentCaptureResult.documentType,
590
+ bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
591
+ }));
592
+ } else {
593
+ // Pas d'image front, on réinitialise
594
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
595
+ }
582
596
  } else {
583
- // Pas d'image front, on réinitialise
584
- setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
597
+ // Retour au composant précédent (country_selection)
598
+ actions.previousComponent();
585
599
  }
586
- } else {
587
- // Retour au composant précédent (country_selection)
588
- actions.previousComponent();
589
- }
590
- },
591
- selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
592
- step: state.currentComponentIndex + 1,
593
- totalSteps: state.template.components.length,
594
- side: currentSide,
595
- }}
596
- />
600
+ },
601
+ selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
602
+ step: state.currentComponentIndex + 1,
603
+ totalSteps: state.template.components.length,
604
+ side: currentSide,
605
+ }}
606
+ />
597
607
  }
598
608
  />
609
+
610
+ {silentCaptureResult.error ? (
611
+ <View style={styles.floatingErrorBanner}>
612
+ <Text style={styles.floatingErrorText}>
613
+ {silentCaptureResult.error}
614
+ </Text>
615
+ </View>
616
+ ) : null}
617
+
599
618
  </View>
600
619
  );
601
620
  }
@@ -614,26 +633,25 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
614
633
  </Text>
615
634
 
616
635
  <View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
617
- <View
636
+ <View
618
637
  style={{
619
638
  width: '100%',
620
639
  height: 200,
621
640
  borderRadius: 12,
622
641
  padding: 1,
623
642
  overflow: 'hidden',
624
- // Shadow for iOS
625
643
  shadowColor: '#000',
626
644
  shadowOffset: { width: 0, height: 4 },
627
645
  shadowOpacity: 0.18,
628
646
  shadowRadius: 8,
629
- // Shadow for Android
630
647
  elevation: 8,
631
- backgroundColor: '#fff', // Needed for shadow to show on iOS
648
+ backgroundColor: '#fff',
632
649
  }}
633
650
  >
634
- {cropImageUri ? (
651
+ {/* Simplified logic: Always show the saved, pre-cropped capture! */}
652
+ {capturedImages[currentSide]?.dir ? (
635
653
  <Image
636
- source={{ uri: cropImageUri }}
654
+ source={{ uri: capturedImages[currentSide].dir }}
637
655
  style={{
638
656
  width: '100%',
639
657
  height: 200,
@@ -641,8 +659,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
641
659
  resizeMode: 'cover',
642
660
  }}
643
661
  />
644
- ) : null}
645
- {!cropImageUri && silentCaptureResult.path ? (
662
+ ) : silentCaptureResult.path ? (
646
663
  <Image
647
664
  source={{ uri: silentCaptureResult.path }}
648
665
  style={{
@@ -653,18 +670,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
653
670
  }}
654
671
  />
655
672
  ) : null}
656
- {!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (
657
- <Image
658
- source={{ uri: capturedImages[currentSide].dir }}
659
- style={{
660
- width: '100%',
661
- height: 200,
662
- borderRadius: 12,
663
- resizeMode: 'cover',
664
- }}
665
- />
666
- ) : null}
667
-
668
673
  </View>
669
674
  {/* Capture button si aucune image n'a été capturée */}
670
675
  {!capturedImages[currentSide]?.dir && (
@@ -701,7 +706,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
701
706
  setShowCamera(true);
702
707
  setCurrentSide('back');
703
708
  setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
704
- setCropImageUri('');
709
+ // setCropImageUri('');
705
710
  }
706
711
  }}
707
712
  variant="primary"
@@ -980,4 +985,28 @@ const styles = StyleSheet.create({
980
985
  color: '#666',
981
986
  fontWeight: '600',
982
987
  },
988
+ floatingErrorBanner: {
989
+ position: 'absolute',
990
+ top: 60, // Pushes it down slightly from the very top of the screen
991
+ left: '10%',
992
+ right: '10%',
993
+ backgroundColor: 'rgba(220, 38, 38, 0.95)', // Bright red
994
+ paddingVertical: 12,
995
+ paddingHorizontal: 16,
996
+ borderRadius: 8,
997
+ alignItems: 'center',
998
+ justifyContent: 'center',
999
+ shadowColor: '#000',
1000
+ shadowOffset: { width: 0, height: 4 },
1001
+ shadowOpacity: 0.3,
1002
+ shadowRadius: 5,
1003
+ elevation: 8,
1004
+ zIndex: 100,
1005
+ },
1006
+ floatingErrorText: {
1007
+ color: 'white',
1008
+ fontSize: 14,
1009
+ fontWeight: '700',
1010
+ textAlign: 'center',
1011
+ },
983
1012
  });
@@ -76,9 +76,9 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
76
76
  const getOrientationLabel = (orientation: OrientationType): string => {
77
77
  switch (orientation) {
78
78
  case 'center':
79
- return state.currentLanguage === "en" ? "Front Profil Selfie" : "Selfie de face";
79
+ return state.currentLanguage === "en" ? "Front Profile Selfie" : "Selfie de face";
80
80
  case 'left':
81
- return state.currentLanguage === "en" ? "Left Profil Selfie" : "Selfie profil gauche";
81
+ return state.currentLanguage === "en" ? "Left Profile Selfie" : "Selfie profil gauche";
82
82
  case 'right':
83
83
  return state.currentLanguage === "en" ? "Right Profile Selfie" : "Selfie profil droit";
84
84
  default:
@@ -822,7 +822,7 @@ export const useTemplateKYCFlow = (
822
822
  isProcessing: false,
823
823
  errors: {
824
824
  ...prev.errors,
825
- [currentComp.id]: state.currentLanguage === "en" ? "please complete this step before move on" : " 'Veuillez compléter cette étape avant de continuer'"
825
+ [currentComp.id]: state.currentLanguage === "en" ? "please complete this step before moving on" : " 'Veuillez compléter cette étape avant de continuer'"
826
826
  }
827
827
  }));
828
828
  return;