@sanctum-key/react-native-sdk 1.0.9 → 1.0.10

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 (24) 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 +259 -63
  4. package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
  5. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  6. package/build/src/components/KYCElements/IDCardCapture.js +222 -69
  7. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  8. package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
  9. package/build/src/components/KYCElements/PhoneVerificationTemplate.js +160 -21
  10. package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
  11. package/build/src/config/region_mapping.json +727 -0
  12. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  13. package/build/src/modules/api/CardAuthentification.js +3 -7
  14. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  15. package/build/src/modules/api/KYCService.d.ts +1 -2
  16. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  17. package/build/src/modules/api/KYCService.js +106 -59
  18. package/build/src/modules/api/KYCService.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/components/KYCElements/CountrySelection.tsx +300 -74
  21. package/src/components/KYCElements/IDCardCapture.tsx +310 -156
  22. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +201 -29
  23. package/src/modules/api/CardAuthentification.ts +5 -8
  24. package/src/modules/api/KYCService.ts +167 -105
@@ -1,6 +1,6 @@
1
1
  import axios from 'axios';
2
2
  import { GovernmentDocumentType, GovernmentDocumentTypeShorted, OrientationVideoResponse } from '../../types/KYC.types';
3
- import { CheckTemplateTypeResponse, ExtractMrzTextResponse } from '../../components/OverLay/type';
3
+ import { CheckTemplateTypeResponse } from '../../components/OverLay/type';
4
4
  import { SessionResponse, VerificationResult, VerificationSessionRequest } from './types';
5
5
  import { KycEnvironment } from '../../types/env.types';
6
6
  import { logger } from '../../utils/logger';
@@ -60,9 +60,9 @@ export class KYCService {
60
60
  private baseURL: string;
61
61
  private apiKey: string;
62
62
  // Additional service base URLs (fixed as per current infra)
63
- private faceServiceURL = 'https://kyc-engine.transfergratis.net:8000';
64
- private textExtractionServiceURL = 'https://kyc-engine.transfergratis.net:8006';
65
- private mrzServiceURL = 'https://kyc-engine.transfergratis.net:8002';
63
+ private faceServiceURL = 'https://face-infera.sanctumkey.com:8000';
64
+ private textExtractionServiceURL = 'https://text-infera.sanctumkey.com:8006';
65
+ private mrzServiceURL = 'https://mrz-infera.sanctumkey.com:8002';
66
66
  private barcodeServiceURL = 'https://kyc-engine.transfergratis.net:8000';
67
67
  private orientationServiceURL = 'http://18.188.180.154:8080';
68
68
 
@@ -239,8 +239,8 @@ export class KYCService {
239
239
  const formData = new FormData();
240
240
  await appendFileToFormData(formData, 'file', idCardImageUri, 'id_card_photo.jpg', 'image/jpeg');
241
241
 
242
- const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
243
-
242
+ const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType] || docType;
243
+
244
244
  // Log metadata, NOT the FormData object
245
245
  logger.log('detectFaceOnId Request:', { docTypeShorted, imageUri: idCardImageUri });
246
246
 
@@ -305,7 +305,7 @@ export class KYCService {
305
305
  logger.log('checkTemplateType res', JSON.stringify(res.data, null, 2));
306
306
  return res.data;
307
307
  } catch (e: any) {
308
- logger.error('Error checking template type:', JSON.stringify(e));
308
+ logger.error('Error checkingg template type:', JSON.stringify(e));
309
309
  throw e;
310
310
  }
311
311
 
@@ -379,78 +379,104 @@ export class KYCService {
379
379
 
380
380
  // STEP 3 - MRZ TEXT EXTRACTION
381
381
  async extractMrzText(
382
- params: {
383
- fileUri: string;
384
- docType: string;
385
- docRegion: string;
386
- postfix?: string;
387
- token: string;
388
- template_path: string;
389
- mrz_type?: string;
390
- },
382
+ params: {
383
+ fileUri: string;
384
+ docType: string;
385
+ docRegion: string;
386
+ postfix?: string;
387
+ token: string;
388
+ template_path: string;
389
+ mrz_type?: string;
390
+ },
391
391
  env: KycEnvironment = 'PRODUCTION'
392
- ): Promise<any> {
393
- // SANDBOX mode
394
- if (env === 'SANDBOX') {
395
- console.log("SANDBOX mode: Skipping AI MRZ extraction");
396
- logger.log("SANDBOX mode: Returning mock MRZ response");
397
- const { docType, docRegion, postfix = 'back', mrz_type } = params;
398
- return {
399
- success: true,
400
- parsed_data: {
401
- status: 'success',
402
- document_type: docType,
403
- mrz_type: mrz_type || 'TD1',
404
- doc_region: docRegion,
405
- postfix: postfix
392
+ ): Promise<any> {
393
+ // SANDBOX mode
394
+ if (env === 'SANDBOX') {
395
+ console.log("SANDBOX mode: Skipping AI MRZ extraction");
396
+ const { docType, docRegion, postfix = 'back', mrz_type } = params;
397
+ return {
398
+ success: true,
399
+ parsed_data: {
400
+ status: 'success',
401
+ document_type: docType,
402
+ mrz_type: mrz_type || 'TD1',
403
+ doc_region: docRegion,
404
+ postfix: postfix
405
+ },
406
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
407
+ };
408
+ }
409
+
410
+ const { fileUri, docType, docRegion, postfix = 'back', token, template_path, mrz_type } = params;
411
+
412
+ // 1. Build the FormData ONLY for the file
413
+ const formData = new FormData();
414
+ const filePayload = {
415
+ uri: fileUri,
416
+ type: 'image/jpeg',
417
+ name: `id_card_${postfix}.jpg`,
418
+ };
419
+ formData.append('file', filePayload as any);
420
+
421
+ const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType] || docType;
422
+ const safeMrzType = mrz_type && mrz_type.trim() !== '' ? mrz_type : 'TD1';
423
+
424
+ logger.log("docTypeShorted", docTypeShorted, docRegion, postfix);
425
+
426
+ // 🚨 THE FIX: Pass all required text parameters in the URL query string for FastAPI
427
+ const queryParams = new URLSearchParams({
428
+ doc_type: docTypeShorted,
429
+ doc_region: docRegion,
430
+ postfix: postfix,
431
+ template_path: template_path,
432
+ mrz_type: safeMrzType,
433
+ reset_cache: 'true'
434
+ }).toString();
435
+
436
+ const url = `${this.mrzServiceURL}/extract_mrz_text/?${queryParams}`;
437
+ logger.log("url", url);
438
+
439
+ try {
440
+ const response = await fetch(url, {
441
+ method: 'POST',
442
+ headers: {
443
+ 'Authorization': `Bearer ${token}`,
444
+ 'Accept': 'application/json'
406
445
  },
407
- card_obb: { x: 50, y: 50, width: 200, height: 200 }
408
- };
409
- }
446
+ body: formData, // Only the file goes in the body
447
+ });
410
448
 
411
- const { fileUri, docType, docRegion, postfix = 'back', token, template_path, mrz_type } = params;
412
- const formData = new FormData();
413
-
414
- // Dynamic filename to prevent passports from crashing (since their MRZ is on the front)
415
- await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
449
+ if (!response.ok) {
450
+ const errorText = await response.text();
451
+ logger.error('Backend MRZ Error Details:', errorText);
452
+ throw new Error(`Erreur serveur: ${response.status}`);
453
+ }
416
454
 
417
- const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType] || docType;
418
- logger.log("docTypeShorted", docTypeShorted, docRegion, postfix);
455
+ // const data = await response.json();
456
+ // logger.log('extractMrzText res', JSON.stringify(data, null, 2));
419
457
 
420
- let url = `${this.mrzServiceURL}/extract_mrz_text/?doc_type=${encodeURIComponent(docTypeShorted)}&doc_region=${encodeURIComponent(docRegion)}&postfix=${encodeURIComponent(postfix)}&template_path=${encodeURIComponent(template_path)}`;
421
-
422
- if (mrz_type && mrz_type.trim() !== '') {
423
- url += `&mrz_type=${encodeURIComponent(mrz_type)}`;
424
- }
425
-
426
- logger.log("url", url);
458
+ // if (Object.keys(data).length === 0) throw new Error('No data found');
459
+ // if (data?.success === false) throw new Error(data.parsed_data?.status || 'Échec de l\'extraction MRZ');
427
460
 
428
- const attempt = async () => {
429
- try {
430
- const res = await axios.post<ExtractMrzTextResponse>(url, formData, {
431
- headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` },
432
- // Note: Reduced timeout to 10000ms (10s) based on our earlier network fix to prevent infinite hanging!
433
- timeout: 10000,
434
- });
435
-
436
- logger.log('extractMrzText res', JSON.stringify(res.data, null, 2));
437
-
438
- if (Object.keys(res.data).length === 0) throw new Error('No data found');
439
- if (res.data?.success === false) throw new Error(res.data.parsed_data?.status || 'Échec de l\'extraction MRZ');
440
-
441
- return res.data;
442
- } catch (e: any) {
443
- throw new Error(e?.message || 'Erreur de détection du MRZ');
444
- }
445
- };
461
+ // return data;
462
+ const data = await response.json();
463
+ logger.log('extractMrzText res', JSON.stringify(data, null, 2));
446
464
 
447
- try {
448
- return await attempt();
449
- } catch (e) {
450
- logger.error('Error extracting MRZ text:', JSON.stringify(errorMessage(e), null, 2));
451
- throw e;
452
- }
453
- }
465
+ if (Object.keys(data).length === 0) throw new Error('No data found');
466
+
467
+ // 🚨 UPDATE THIS LINE to grab the actual status_message:
468
+ if (data?.success === false) {
469
+ const serverMessage = data.parsed_data?.status_message || data.parsed_data?.status || 'Échec de l\'extraction MRZ';
470
+ throw new Error(`Lecture MRZ refusée: ${serverMessage}`);
471
+ }
472
+
473
+ return data;
474
+
475
+ } catch (error: any) {
476
+ logger.error("MRZ Fetch Error: ", error?.message || error);
477
+ throw new Error(error?.message || 'Erreur de connexion lors de l\'extraction MRZ');
478
+ }
479
+ }
454
480
 
455
481
  // STEP 2 - SELFIE VALIDATION
456
482
  async recognizeFace(params: { idPhotoUri: string; selfiePhotoUri: string }, env: KycEnvironment = 'PRODUCTION'): Promise<{ is_match: boolean; similarity: number; id_bbox?: number[]; selfie_bbox?: number[] }> {
@@ -627,49 +653,85 @@ export class KYCService {
627
653
  auth?: { apiKey?: string; token?: string }
628
654
  ): Promise<unknown> {
629
655
  const url = `${KYCConfig.getBackendUrl()}/accounts/send-whatsapp-verification/`;
630
- const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
631
656
 
632
- const res = await axios.post(
633
- url,
634
- {
657
+ // 1. Formatting (Ensure consistency with verification step)
658
+ const formattedPhoneNumber = phoneNumber.replace(/^\+/, '');
659
+
660
+ // 2. Prepare Payload
661
+ const payload = {
635
662
  session_id: sessionId,
636
- phone_number: phoneNumber
637
- },
638
- {
639
- headers: {
640
- 'Content-Type': 'application/json',
641
- ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
642
- },
643
- }
644
- );
645
- return res.data;
663
+ phone_number: formattedPhoneNumber
664
+ };
665
+
666
+
667
+
668
+ const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
669
+ const headers = {
670
+ 'Content-Type': 'application/json',
671
+ ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
672
+ };
673
+
674
+
675
+
676
+ try {
677
+ const res = await axios.post(url, payload, { headers });
678
+
679
+ logger.log("✅ WhatsApp Send Success:", res.data);
680
+ return res.data;
681
+
682
+ } catch (error: any) {
683
+ if (error.response) {
684
+ logger.error("🛑 WhatsApp Send Failed (Server Response):", {
685
+ status: error.response.status,
686
+ data: error.response.data,
687
+ headers: error.response.headers
688
+ });
689
+ }
690
+ throw error;
691
+ }
646
692
  }
647
693
 
648
- /** Verify WhatsApp code with OTP. POST /api/v1/accounts/verify-whatsapp-verification/ */
649
694
  async verifyWhatsAppCode(
650
695
  sessionId: string,
651
696
  otp: string,
697
+ phoneNumber: string,
652
698
  auth?: { apiKey?: string; token?: string }
653
699
  ): Promise<unknown> {
654
- // Note: Verify this endpoint matches your exact Django/backend route
655
- const url = `${KYCConfig.getBackendUrl()}/accounts/verify-whatsapp-verification/`;
656
- const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
700
+ const url = `${KYCConfig.getBackendUrl()}/accounts/verify-whatsapp-code/`;
657
701
 
658
- const res = await axios.post(
659
- url,
660
- {
702
+ const formattedPhoneNumber = phoneNumber.replace(/^\+/, '');
703
+
704
+ const payload = {
661
705
  session_id: sessionId,
662
- otp: otp
663
- }, // Adjust this payload if your backend also expects 'phone_number' here
664
- {
665
- headers: {
666
- 'Content-Type': 'application/json',
667
- ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
668
- },
669
- }
670
- );
671
- return res.data;
672
- }
706
+ verification_code: otp,
707
+ phone_number: formattedPhoneNumber
708
+ };
709
+
710
+
711
+
712
+ const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
713
+ const headers = {
714
+ 'Content-Type': 'application/json',
715
+ ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
716
+ };
717
+
718
+
719
+ try {
720
+ const res = await axios.post(url, payload, { headers });
721
+
722
+ return res.data;
723
+
724
+ } catch (error: any) {
725
+ if (error.response) {
726
+ logger.error("🛑 WhatsApp Verification Failed (Server Response):", {
727
+ status: error.response.status,
728
+ data: error.response.data,
729
+ headers: error.response.headers
730
+ });
731
+ }
732
+ throw error;
733
+ }
734
+ }
673
735
  }
674
736
 
675
737
  const kycService = new KYCService("", "");
@@ -719,7 +781,7 @@ export const authentification = async (): Promise<string> => {
719
781
  const params = 'client_id=kyc_frontend&client_secret=QhgAmvKgmwODzsEp98dnA4PeUEMMaFHd&grant_type=client_credentials';
720
782
 
721
783
  const res = await axios.post(
722
- `https://keycloak.transfergratis.net/realms/kyc/protocol/openid-connect/token`,
784
+ `https://idms.sanctumkey.com/realms/kyc/protocol/openid-connect/token`,
723
785
  params,
724
786
  {
725
787
  headers: {