@transfergratis/react-native-sdk 0.1.26 → 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.
- package/build/components/EnhancedCameraView.js +1 -1
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +13 -42
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +76 -35
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +2 -2
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +1 -1
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +15 -7
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +215 -44
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +2 -0
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +16 -19
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/camera/VisionCameraModule.js +2 -2
- package/build/modules/camera/VisionCameraModule.js.map +1 -1
- package/build/utils/cropByObb.d.ts +19 -1
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +49 -1
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/pathToBase64.js +1 -1
- package/build/utils/pathToBase64.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +1 -1
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +24 -52
- package/src/components/KYCElements/IDCardCapture.tsx +129 -90
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
- package/src/hooks/useTemplateKYCFlow.tsx +1 -1
- package/src/modules/api/CardAuthentification.ts +290 -58
- package/src/modules/api/KYCService.ts +25 -33
- package/src/modules/camera/VisionCameraModule.ts +2 -2
- package/src/utils/cropByObb.ts +57 -1
- 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.
|
|
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
|
-
|
|
174
|
-
|
|
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
|
|
|
@@ -324,8 +338,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
324
338
|
}).catch((e: any) => {
|
|
325
339
|
console.log("error front verification", e);
|
|
326
340
|
logger.log("error front verification", truncateFields(e));
|
|
327
|
-
const isCardNotFullyInFrame =
|
|
328
|
-
|
|
341
|
+
const isCardNotFullyInFrame =
|
|
342
|
+
e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
|
|
343
|
+
e?.message?.includes('entirement') ||
|
|
344
|
+
e?.message?.includes('fully in frame');
|
|
345
|
+
const errorMessage = isCardNotFullyInFrame
|
|
346
|
+
? t('kyc.idCardCapture.cardNotFullyInFrame')
|
|
347
|
+
: (e?.message || 'Erreur de détection du MRZ');
|
|
329
348
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
|
|
330
349
|
});
|
|
331
350
|
} catch (error: any) {
|
|
@@ -362,8 +381,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
362
381
|
}
|
|
363
382
|
}).catch((e: any) => {
|
|
364
383
|
logger.log("error back verification", truncateFields(e));
|
|
365
|
-
const isCardNotFullyInFrame =
|
|
366
|
-
|
|
384
|
+
const isCardNotFullyInFrame =
|
|
385
|
+
e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
|
|
386
|
+
e?.message?.includes('entirement') ||
|
|
387
|
+
e?.message?.includes('fully in frame');
|
|
388
|
+
const errorMessage = isCardNotFullyInFrame
|
|
389
|
+
? t('kyc.idCardCapture.cardNotFullyInFrame')
|
|
390
|
+
: (e?.message || 'Erreur de détection du MRZ');
|
|
367
391
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
|
|
368
392
|
})
|
|
369
393
|
}
|
|
@@ -371,31 +395,32 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
371
395
|
}
|
|
372
396
|
}
|
|
373
397
|
|
|
374
|
-
// Handle capture
|
|
375
398
|
const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
|
|
376
|
-
console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
|
|
377
|
-
|
|
378
399
|
if (silentCaptureResult.path) {
|
|
379
|
-
// Créer une image rognée avec tolérance de 10% pour l'envoi
|
|
380
400
|
let imagePathForUpload = silentCaptureResult.path;
|
|
381
401
|
if (silentCaptureResult.bbox) {
|
|
382
402
|
try {
|
|
383
|
-
logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
|
|
384
403
|
imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
|
|
385
|
-
logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
|
|
386
404
|
} catch (error) {
|
|
387
|
-
logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
|
|
388
|
-
// En cas d'erreur, on utilise l'image originale
|
|
389
405
|
imagePathForUpload = silentCaptureResult.path;
|
|
390
406
|
}
|
|
391
407
|
}
|
|
392
408
|
|
|
393
409
|
const base64 = await pathToBase64(imagePathForUpload);
|
|
394
410
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
+
|
|
398
422
|
setCapturedImages(newImages);
|
|
423
|
+
|
|
399
424
|
if (silentCaptureResult.country && silentCaptureResult.documentType) {
|
|
400
425
|
onValueChange({
|
|
401
426
|
...newImages,
|
|
@@ -415,10 +440,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
415
440
|
setShowCamera(false);
|
|
416
441
|
};
|
|
417
442
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
443
|
useEffect(() => {
|
|
423
444
|
actions.showCustomStepper(!showCamera);
|
|
424
445
|
}, [showCamera]);
|
|
@@ -521,7 +542,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
521
542
|
|
|
522
543
|
|
|
523
544
|
if (showCamera) {
|
|
524
|
-
|
|
525
545
|
return (
|
|
526
546
|
<View style={styles.cameraContainer}>
|
|
527
547
|
<EnhancedCameraView
|
|
@@ -538,54 +558,63 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
538
558
|
silentCaptureResult={silentCaptureResult}
|
|
539
559
|
captureStabilizationDelayMs={3000}
|
|
540
560
|
enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'}
|
|
541
|
-
overlayComponent={
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
+
}
|
|
572
596
|
} else {
|
|
573
|
-
//
|
|
574
|
-
|
|
597
|
+
// Retour au composant précédent (country_selection)
|
|
598
|
+
actions.previousComponent();
|
|
575
599
|
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
totalSteps: state.template.components.length,
|
|
584
|
-
side: currentSide,
|
|
585
|
-
}}
|
|
586
|
-
/>
|
|
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
|
+
/>
|
|
587
607
|
}
|
|
588
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
|
+
|
|
589
618
|
</View>
|
|
590
619
|
);
|
|
591
620
|
}
|
|
@@ -604,26 +633,25 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
604
633
|
</Text>
|
|
605
634
|
|
|
606
635
|
<View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
|
|
607
|
-
|
|
636
|
+
<View
|
|
608
637
|
style={{
|
|
609
638
|
width: '100%',
|
|
610
639
|
height: 200,
|
|
611
640
|
borderRadius: 12,
|
|
612
641
|
padding: 1,
|
|
613
642
|
overflow: 'hidden',
|
|
614
|
-
// Shadow for iOS
|
|
615
643
|
shadowColor: '#000',
|
|
616
644
|
shadowOffset: { width: 0, height: 4 },
|
|
617
645
|
shadowOpacity: 0.18,
|
|
618
646
|
shadowRadius: 8,
|
|
619
|
-
// Shadow for Android
|
|
620
647
|
elevation: 8,
|
|
621
|
-
backgroundColor: '#fff',
|
|
648
|
+
backgroundColor: '#fff',
|
|
622
649
|
}}
|
|
623
650
|
>
|
|
624
|
-
{
|
|
651
|
+
{/* Simplified logic: Always show the saved, pre-cropped capture! */}
|
|
652
|
+
{capturedImages[currentSide]?.dir ? (
|
|
625
653
|
<Image
|
|
626
|
-
source={{ uri:
|
|
654
|
+
source={{ uri: capturedImages[currentSide].dir }}
|
|
627
655
|
style={{
|
|
628
656
|
width: '100%',
|
|
629
657
|
height: 200,
|
|
@@ -631,8 +659,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
631
659
|
resizeMode: 'cover',
|
|
632
660
|
}}
|
|
633
661
|
/>
|
|
634
|
-
) :
|
|
635
|
-
{!cropImageUri && silentCaptureResult.path ? (
|
|
662
|
+
) : silentCaptureResult.path ? (
|
|
636
663
|
<Image
|
|
637
664
|
source={{ uri: silentCaptureResult.path }}
|
|
638
665
|
style={{
|
|
@@ -643,18 +670,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
643
670
|
}}
|
|
644
671
|
/>
|
|
645
672
|
) : null}
|
|
646
|
-
{!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (
|
|
647
|
-
<Image
|
|
648
|
-
source={{ uri: capturedImages[currentSide].dir }}
|
|
649
|
-
style={{
|
|
650
|
-
width: '100%',
|
|
651
|
-
height: 200,
|
|
652
|
-
borderRadius: 12,
|
|
653
|
-
resizeMode: 'cover',
|
|
654
|
-
}}
|
|
655
|
-
/>
|
|
656
|
-
) : null}
|
|
657
|
-
|
|
658
673
|
</View>
|
|
659
674
|
{/* Capture button si aucune image n'a été capturée */}
|
|
660
675
|
{!capturedImages[currentSide]?.dir && (
|
|
@@ -691,7 +706,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
691
706
|
setShowCamera(true);
|
|
692
707
|
setCurrentSide('back');
|
|
693
708
|
setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
|
|
694
|
-
setCropImageUri('');
|
|
709
|
+
// setCropImageUri('');
|
|
695
710
|
}
|
|
696
711
|
}}
|
|
697
712
|
variant="primary"
|
|
@@ -970,4 +985,28 @@ const styles = StyleSheet.create({
|
|
|
970
985
|
color: '#666',
|
|
971
986
|
fontWeight: '600',
|
|
972
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
|
+
},
|
|
973
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
|
|
79
|
+
return state.currentLanguage === "en" ? "Front Profile Selfie" : "Selfie de face";
|
|
80
80
|
case 'left':
|
|
81
|
-
return state.currentLanguage === "en" ? "Left
|
|
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
|
|
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;
|