@sanctum-key/react-native-sdk 1.0.51 → 1.0.53
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/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/xml_file_paths.xml.flat +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +1 -1
- package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +1 -1
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +41 -41
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +52 -52
- package/build/package.json +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +73 -66
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts +2 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +20 -18
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +1 -0
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +20 -20
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/config/KYCConfig.d.ts.map +1 -1
- package/build/src/config/KYCConfig.js +4 -2
- package/build/src/config/KYCConfig.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts +1 -0
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/hooks/useTemplateLoader.d.ts.map +1 -1
- package/build/src/hooks/useTemplateLoader.js +0 -2
- package/build/src/hooks/useTemplateLoader.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts +0 -23
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +46 -83
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +96 -38
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/modules/api/SelfieVerification.d.ts.map +1 -1
- package/build/src/modules/api/SelfieVerification.js +12 -19
- package/build/src/modules/api/SelfieVerification.js.map +1 -1
- package/build/src/modules/api/TemplateService.d.ts.map +1 -1
- package/build/src/modules/api/TemplateService.js +2 -1
- package/build/src/modules/api/TemplateService.js.map +1 -1
- package/build/src/modules/api/types.d.ts +34 -27
- package/build/src/modules/api/types.d.ts.map +1 -1
- package/build/src/modules/api/types.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/IDCardCapture.tsx +192 -191
- package/src/components/TemplateKYCExample.tsx +29 -20
- package/src/components/TemplateKYCFlowRefactored.tsx +33 -31
- package/src/config/KYCConfig.ts +7 -5
- package/src/hooks/useTemplateKYCFlow.tsx +1 -0
- package/src/hooks/useTemplateLoader.ts +0 -2
- package/src/modules/api/CardAuthentification.ts +409 -445
- package/src/modules/api/KYCService.ts +599 -449
- package/src/modules/api/SelfieVerification.ts +22 -29
- package/src/modules/api/TemplateService.ts +3 -2
- package/src/modules/api/types.ts +36 -29
|
@@ -504,6 +504,8 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
504
504
|
return;
|
|
505
505
|
if (isAnalyzingRef.current)
|
|
506
506
|
return;
|
|
507
|
+
// 🚨 SYNCHRONOUS LOCK: Block all subsequent camera frames immediately
|
|
508
|
+
isAnalyzingRef.current = true;
|
|
507
509
|
const side = currentSideRef.current;
|
|
508
510
|
const captured = capturedImagesRef.current;
|
|
509
511
|
const docType = selectedDocTypeRef.current;
|
|
@@ -516,17 +518,30 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
516
518
|
let templateResponse;
|
|
517
519
|
if (!docType) {
|
|
518
520
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Document type not selected' }));
|
|
521
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK
|
|
519
522
|
return;
|
|
520
523
|
}
|
|
521
524
|
const issuingSubdivision = docType.region !== 'root' ? docType.region : undefined;
|
|
522
525
|
const watchdog = setTimeout(() => {
|
|
523
526
|
if (isAnalyzingRef.current) {
|
|
524
|
-
|
|
527
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK inside watchdog
|
|
528
|
+
setSilentCaptureResult((prev) => ({
|
|
529
|
+
...prev,
|
|
530
|
+
isAnalyzing: false,
|
|
531
|
+
success: false,
|
|
532
|
+
error: state.currentLanguage === 'en' ? 'Taking longer than expected. Hold steady.' : 'Traitement long. Veuillez rester immobile.'
|
|
533
|
+
}));
|
|
525
534
|
}
|
|
526
535
|
}, 20000);
|
|
527
536
|
try {
|
|
528
537
|
if (!templatePath) {
|
|
529
|
-
const templateType = await checkTemplateType({
|
|
538
|
+
const templateType = await checkTemplateType({
|
|
539
|
+
path: safeCapturePath,
|
|
540
|
+
docType: docType.type,
|
|
541
|
+
docRegion: cData?.code || "",
|
|
542
|
+
postfix: side,
|
|
543
|
+
issuingSubdivision: issuingSubdivision
|
|
544
|
+
}, env);
|
|
530
545
|
templateResponse = templateType;
|
|
531
546
|
if (templateType.template_path)
|
|
532
547
|
templatePath = templateType.template_path;
|
|
@@ -535,6 +550,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
535
550
|
if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
536
551
|
clearTimeout(watchdog);
|
|
537
552
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: t('kyc.idCardCapture.cardNotFullyInFrame') }));
|
|
553
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK
|
|
538
554
|
return;
|
|
539
555
|
}
|
|
540
556
|
try {
|
|
@@ -551,13 +567,34 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
551
567
|
if (side === 'front') {
|
|
552
568
|
const matchedAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'front');
|
|
553
569
|
const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || 'TD1';
|
|
554
|
-
verificationRes = await frontVerification({
|
|
570
|
+
verificationRes = await frontVerification({
|
|
571
|
+
path: safeCapturePath,
|
|
572
|
+
regionMapping: { authMethod: matchedAuthMethod ? [matchedAuthMethod] : [], mrzTypes: mrzType ? [mrzType] : [] },
|
|
573
|
+
selectedDocumentType: GovernmentDocumentTypeShorted[docType.type] || '',
|
|
574
|
+
code: cData?.code || '',
|
|
575
|
+
currentSide: side,
|
|
576
|
+
templatePath,
|
|
577
|
+
mrzType,
|
|
578
|
+
issuingSubdivision: issuingSubdivision,
|
|
579
|
+
clientSideBarcodeData: silentCaptureResult.barcode
|
|
580
|
+
}, env);
|
|
555
581
|
}
|
|
556
582
|
else {
|
|
557
583
|
const matchedBackAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'back');
|
|
558
584
|
const backMrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
|
|
559
585
|
const frontTemplatePath = captured['front']?.templatePath || '';
|
|
560
|
-
verificationRes = await backVerification({
|
|
586
|
+
verificationRes = await backVerification({
|
|
587
|
+
path: safeCapturePath,
|
|
588
|
+
regionMapping: { authMethod: matchedBackAuthMethod ? [matchedBackAuthMethod] : [], mrzTypes: backMrzType ? [backMrzType] : [] },
|
|
589
|
+
selectedDocumentType: GovernmentDocumentTypeShorted[docType.type] || '',
|
|
590
|
+
code: cData?.code || '',
|
|
591
|
+
currentSide: side,
|
|
592
|
+
templatePath,
|
|
593
|
+
mrzType: backMrzType,
|
|
594
|
+
templateResponse,
|
|
595
|
+
issuingSubdivision: issuingSubdivision,
|
|
596
|
+
frontTemplatePath: frontTemplatePath
|
|
597
|
+
}, env);
|
|
561
598
|
}
|
|
562
599
|
const bbox = verificationRes?.bbox || templateBbox;
|
|
563
600
|
const verifiedMrz = verificationRes?.mrz ? JSON.stringify(verificationRes.mrz) : "";
|
|
@@ -574,113 +611,83 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
574
611
|
const token = await authentification();
|
|
575
612
|
parsedDictionary = await kycService.formatBarcodeData({ textInput: barcodeData, token }, env);
|
|
576
613
|
}
|
|
577
|
-
const verifiedResult = {
|
|
614
|
+
const verifiedResult = {
|
|
615
|
+
path: safeCapturePath, templatePath: templatePath, bbox: bbox, success: true,
|
|
616
|
+
mrz: JSON.stringify(parsedDictionary), barcode: barcodeData, isAnalyzing: false,
|
|
617
|
+
country: cData?.code, documentType: docType.type, region: docType.region !== 'root' ? docType.region : undefined
|
|
618
|
+
};
|
|
578
619
|
clearTimeout(watchdog);
|
|
579
620
|
setSilentCaptureResult(verifiedResult);
|
|
580
621
|
if (bbox)
|
|
581
622
|
setBboxBySide(prev => ({ ...prev, ['back']: bbox }));
|
|
623
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK on success
|
|
582
624
|
await autoCapture(safeCapturePath, verifiedResult);
|
|
583
625
|
return;
|
|
584
626
|
}
|
|
585
|
-
sdkLogger.info(`Verification passed. Waiting for barcode scan phase.`);
|
|
627
|
+
sdkLogger.info(`Verification passed. Waiting for barcode scan phase.`);
|
|
586
628
|
clearTimeout(watchdog);
|
|
587
629
|
setPendingCAImage({ path: safeCapturePath, templateResponse, templatePath, bbox });
|
|
588
630
|
setScanPhase('barcode');
|
|
589
631
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false }));
|
|
632
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK before phase shift
|
|
590
633
|
return;
|
|
591
634
|
}
|
|
592
|
-
const verifiedResult = {
|
|
635
|
+
const verifiedResult = {
|
|
636
|
+
path: safeCapturePath, templatePath, bbox, success: true, mrz: verifiedMrz, isAnalyzing: false,
|
|
637
|
+
country: cData?.code, documentType: docType.type, region: issuingSubdivision
|
|
638
|
+
};
|
|
593
639
|
clearTimeout(watchdog);
|
|
594
640
|
setSilentCaptureResult(verifiedResult);
|
|
595
641
|
if (bbox)
|
|
596
642
|
setBboxBySide(prev => ({ ...prev, [side]: bbox }));
|
|
643
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK on final success
|
|
597
644
|
await autoCapture(safeCapturePath, verifiedResult);
|
|
598
645
|
}
|
|
599
646
|
catch (error) {
|
|
600
647
|
clearTimeout(watchdog);
|
|
601
648
|
const rawMessage = (typeof error === 'string' ? error : (error?.message || '')).toLowerCase();
|
|
602
649
|
const statusCode = error?.response?.status || error?.status;
|
|
603
|
-
if (statusCode === 401 ||
|
|
604
|
-
|
|
605
|
-
rawMessage.includes('
|
|
606
|
-
rawMessage.includes('unauthorized') ||
|
|
607
|
-
rawMessage.includes('token') ||
|
|
608
|
-
rawMessage.includes('unauthenticated')) {
|
|
650
|
+
if (statusCode === 401 || statusCode === 403 ||
|
|
651
|
+
rawMessage.includes('401') || rawMessage.includes('unauthorized') ||
|
|
652
|
+
rawMessage.includes('token') || rawMessage.includes('unauthenticated')) {
|
|
609
653
|
sdkLogger.error(`Secure API Session Expired during AI extraction`);
|
|
610
654
|
setShowCamera(false);
|
|
611
655
|
setIsProcessingCapture(false);
|
|
612
656
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: '' }));
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
? 'Your secure session has expired. Please restart the process.'
|
|
616
|
-
: 'Votre session sécurisée a expiré. Veuillez recommencer.', [
|
|
617
|
-
{
|
|
618
|
-
text: state.currentLanguage === 'en' ? 'Restart' : 'Recommencer',
|
|
619
|
-
onPress: () => handleExitKYCFlow() // Redirects ONLY when they click the button
|
|
620
|
-
}
|
|
621
|
-
], { cancelable: false } // Forces them to click the button
|
|
622
|
-
);
|
|
657
|
+
Alert.alert(state.currentLanguage === 'en' ? 'Session Expired' : 'Session Expirée', state.currentLanguage === 'en' ? 'Your secure session has expired. Please restart the process.' : 'Votre session sécurisée a expiré. Veuillez recommencer.', [{ text: state.currentLanguage === 'en' ? 'Restart' : 'Recommencer', onPress: () => handleExitKYCFlow() }], { cancelable: false });
|
|
658
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK on session expiry
|
|
623
659
|
return;
|
|
624
660
|
}
|
|
625
661
|
let hintMessage = '';
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
rawMessage.includes('non entièrement visible') ||
|
|
629
|
-
rawMessage.includes('aucun visage détecté') ||
|
|
630
|
-
rawMessage.includes('stabiliser la caméra')) {
|
|
631
|
-
hintMessage = state.currentLanguage === 'en'
|
|
632
|
-
? 'Ensure the entire card and your face are clearly inside the frame.'
|
|
633
|
-
: 'Assurez-vous que toute la carte et le visage sont clairement dans le cadre.';
|
|
662
|
+
if (rawMessage.includes('card_not_fully_in_frame') || rawMessage.includes('non entièrement visible') || rawMessage.includes('aucun visage détecté') || rawMessage.includes('stabiliser la caméra')) {
|
|
663
|
+
hintMessage = state.currentLanguage === 'en' ? 'Ensure the entire card and your face are clearly inside the frame.' : 'Assurez-vous que toute la carte et le visage sont clairement dans le cadre.';
|
|
634
664
|
}
|
|
635
|
-
// 2. Document/Country Mismatch
|
|
636
|
-
// Catches: 'pays sélectionné', 'LPIPS'
|
|
637
665
|
else if (rawMessage.includes('pays sélectionné')) {
|
|
638
|
-
hintMessage = state.currentLanguage === 'en'
|
|
639
|
-
? 'This document does not match the selected country.'
|
|
640
|
-
: 'Ce document ne correspond pas au pays sélectionné.';
|
|
666
|
+
hintMessage = state.currentLanguage === 'en' ? 'This document does not match the selected country.' : 'Ce document ne correspond pas au pays sélectionné.';
|
|
641
667
|
}
|
|
642
|
-
// 3. Front/Back Document Mismatch
|
|
643
|
-
// Catches: 'ne correspond pas au recto', 'document différent'
|
|
644
668
|
else if (rawMessage.includes('ne correspond pas au recto') || rawMessage.includes('document différent')) {
|
|
645
|
-
hintMessage = state.currentLanguage === 'en'
|
|
646
|
-
? 'Please scan the back of the exact same ID.'
|
|
647
|
-
: 'Veuillez scanner le verso de la même pièce d\'identité.';
|
|
669
|
+
hintMessage = state.currentLanguage === 'en' ? 'Please scan the back of the exact same ID.' : 'Veuillez scanner le verso de la même pièce d\'identité.';
|
|
648
670
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
else if (rawMessage.includes('impossible de lire') ||
|
|
652
|
-
rawMessage.includes('impossible d\'extraire') ||
|
|
653
|
-
rawMessage.includes('mrz') ||
|
|
654
|
-
rawMessage.includes('extraction') ||
|
|
655
|
-
rawMessage.includes('illisible') ||
|
|
656
|
-
rawMessage.includes('invalide')) {
|
|
657
|
-
hintMessage = state.currentLanguage === 'en'
|
|
658
|
-
? 'Cannot read document text. Avoid glare, improve lighting, and hold steady.'
|
|
659
|
-
: 'Impossible de lire le texte. Évitez les reflets, améliorez l\'éclairage et restez immobile.';
|
|
671
|
+
else if (rawMessage.includes('impossible de lire') || rawMessage.includes('impossible d\'extraire') || rawMessage.includes('mrz') || rawMessage.includes('extraction') || rawMessage.includes('illisible') || rawMessage.includes('invalide')) {
|
|
672
|
+
hintMessage = state.currentLanguage === 'en' ? 'Cannot read document text. Avoid glare, improve lighting, and hold steady.' : 'Impossible de lire le texte. Évitez les reflets, améliorez l\'éclairage et restez immobile.';
|
|
660
673
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
else if (rawMessage.includes('cloudflare') ||
|
|
664
|
-
rawMessage.includes('tunnel') ||
|
|
665
|
-
rawMessage.includes('502') ||
|
|
666
|
-
rawMessage.includes('503')) {
|
|
667
|
-
hintMessage = state.currentLanguage === 'en'
|
|
668
|
-
? 'Server is temporarily offline. Please try again in a few minutes.'
|
|
669
|
-
: 'Le serveur est temporairement hors ligne. Veuillez réessayer dans quelques minutes.';
|
|
674
|
+
else if (rawMessage.includes('cloudflare') || rawMessage.includes('tunnel') || rawMessage.includes('502') || rawMessage.includes('503')) {
|
|
675
|
+
hintMessage = state.currentLanguage === 'en' ? 'Server is temporarily offline. Please try again in a few minutes.' : 'Le serveur est temporairement hors ligne. Veuillez réessayer dans quelques minutes.';
|
|
670
676
|
}
|
|
671
|
-
// 6. True Network Errors vs. Fallback Processing
|
|
672
677
|
else {
|
|
673
|
-
// Strictly detect Axios network drops without swallowing unknown custom errors
|
|
674
678
|
const isNetworkError = error?.isAxiosError || rawMessage === 'network error' || error?.code === 'econnaborted' || rawMessage.includes('network');
|
|
675
679
|
if (isNetworkError) {
|
|
676
|
-
hintMessage = state.currentLanguage === 'en'
|
|
677
|
-
? 'Poor connection. Please check your internet.'
|
|
678
|
-
: 'Connexion faible. Veuillez vérifier votre internet.';
|
|
680
|
+
hintMessage = state.currentLanguage === 'en' ? 'Poor connection. Please check your internet.' : 'Connexion faible. Veuillez vérifier votre internet.';
|
|
679
681
|
}
|
|
680
682
|
}
|
|
681
683
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: hintMessage }));
|
|
684
|
+
isAnalyzingRef.current = false; // 🚨 UNLOCK at end of catch block
|
|
682
685
|
}
|
|
683
686
|
}
|
|
687
|
+
else {
|
|
688
|
+
// 🚨 UNLOCK if the camera frame was invalid or empty
|
|
689
|
+
isAnalyzingRef.current = false;
|
|
690
|
+
}
|
|
684
691
|
};
|
|
685
692
|
useEffect(() => {
|
|
686
693
|
if (scanPhase === 'barcode' && pendingCAImage && silentCaptureResult.barcode) {
|