@sanctum-key/react-native-sdk 1.0.48 → 1.0.50
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/package.json +1 -1
- package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.js +33 -108
- package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.js +50 -164
- package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.js +32 -109
- package/build/src/components/KYCElements/DocumentTypeSelection.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +58 -25
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.d.ts.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.js +13 -1
- package/build/src/components/KYCElements/InitializationStep.js.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.js +46 -157
- package/build/src/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.js +28 -24
- package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +31 -40
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/components/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/SelfieCapture.js +33 -90
- package/build/src/components/SelfieCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +69 -120
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +0 -2
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +95 -210
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/build/src/utils/logger.d.ts +12 -4
- package/build/src/utils/logger.d.ts.map +1 -1
- package/build/src/utils/logger.js +156 -28
- package/build/src/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/CountrySelection.tsx +36 -109
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +67 -266
- package/src/components/KYCElements/DocumentTypeSelection.tsx +34 -110
- package/src/components/KYCElements/IDCardCapture.tsx +93 -39
- package/src/components/KYCElements/InitializationStep.tsx +17 -2
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +53 -170
- package/src/components/KYCElements/SelfieCapture.tsx +50 -58
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +38 -45
- package/src/components/SelfieCapture.tsx +44 -100
- package/src/components/TemplateKYCExample.tsx +94 -145
- package/src/components/TemplateKYCFlowRefactored.tsx +105 -239
- package/src/hooks/useTemplateKYCFlow.tsx +1 -0
- package/src/types/KYC.types.ts +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
218
|
-
Alert.alert(state.currentLanguage === 'en' ? 'Camera Access Required' : 'Accès à la caméra requis', state.currentLanguage === 'en'
|
|
219
|
-
|
|
220
|
-
:
|
|
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
|
-
|
|
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
|
|
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 = {
|
|
265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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');
|