@sanctum-key/react-native-sdk 1.0.24 → 1.0.25

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 (73) hide show
  1. package/build/package.json +3 -1
  2. package/build/src/components/EnhancedCameraView.d.ts +7 -1
  3. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  4. package/build/src/components/EnhancedCameraView.js +88 -30
  5. package/build/src/components/EnhancedCameraView.js.map +1 -1
  6. package/build/src/components/EnhancedCameraView.web.d.ts +5 -1
  7. package/build/src/components/EnhancedCameraView.web.d.ts.map +1 -1
  8. package/build/src/components/EnhancedCameraView.web.js +80 -295
  9. package/build/src/components/EnhancedCameraView.web.js.map +1 -1
  10. package/build/src/components/KYCElements/CameraCapture.js +1 -1
  11. package/build/src/components/KYCElements/CameraCapture.js.map +1 -1
  12. package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
  13. package/build/src/components/KYCElements/CountrySelection.js +38 -218
  14. package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
  15. package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  16. package/build/src/components/KYCElements/CountrySelectionTemplate.js +5 -17
  17. package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  18. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  19. package/build/src/components/KYCElements/IDCardCapture.js +509 -95
  20. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  21. package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
  22. package/build/src/components/KYCElements/VerificationProgressTemplate.js +93 -51
  23. package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
  24. package/build/src/components/OverLay/type.d.ts +1 -0
  25. package/build/src/components/OverLay/type.d.ts.map +1 -1
  26. package/build/src/components/OverLay/type.js.map +1 -1
  27. package/build/src/components/WebBarcodeScanner.d.ts +9 -0
  28. package/build/src/components/WebBarcodeScanner.d.ts.map +1 -0
  29. package/build/src/components/WebBarcodeScanner.js +152 -0
  30. package/build/src/components/WebBarcodeScanner.js.map +1 -0
  31. package/build/src/config/KYCConfigs.js +2 -2
  32. package/build/src/config/KYCConfigs.js.map +1 -1
  33. package/build/src/config/region_mapping.json +0 -14
  34. package/build/src/modules/api/CardAuthentification.d.ts +2 -0
  35. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  36. package/build/src/modules/api/CardAuthentification.js +22 -39
  37. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  38. package/build/src/modules/api/KYCService.d.ts +7 -2
  39. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  40. package/build/src/modules/api/KYCService.js +163 -57
  41. package/build/src/modules/api/KYCService.js.map +1 -1
  42. package/build/src/types/KYC.types.d.ts +1 -0
  43. package/build/src/types/KYC.types.d.ts.map +1 -1
  44. package/build/src/types/KYC.types.js.map +1 -1
  45. package/build/src/utils/aamvaParser.d.ts +62 -0
  46. package/build/src/utils/aamvaParser.d.ts.map +1 -0
  47. package/build/src/utils/aamvaParser.js +91 -0
  48. package/build/src/utils/aamvaParser.js.map +1 -0
  49. package/build/src/utils/cropByObb.d.ts.map +1 -1
  50. package/build/src/utils/cropByObb.js +4 -3
  51. package/build/src/utils/cropByObb.js.map +1 -1
  52. package/build/src/web/WebKYCEntry.d.ts +1 -1
  53. package/build/src/web/WebKYCEntry.d.ts.map +1 -1
  54. package/build/src/web/WebKYCEntry.js +105 -239
  55. package/build/src/web/WebKYCEntry.js.map +1 -1
  56. package/package.json +3 -1
  57. package/src/components/EnhancedCameraView.tsx +116 -42
  58. package/src/components/EnhancedCameraView.web.tsx +98 -338
  59. package/src/components/KYCElements/CameraCapture.tsx +1 -1
  60. package/src/components/KYCElements/CountrySelection.tsx +60 -240
  61. package/src/components/KYCElements/CountrySelectionTemplate.tsx +7 -20
  62. package/src/components/KYCElements/IDCardCapture.tsx +672 -151
  63. package/src/components/KYCElements/VerificationProgressTemplate.tsx +112 -65
  64. package/src/components/OverLay/type.ts +1 -1
  65. package/src/components/WebBarcodeScanner.tsx +273 -0
  66. package/src/config/KYCConfigs.ts +2 -2
  67. package/src/config/region_mapping.json +0 -14
  68. package/src/modules/api/CardAuthentification.ts +41 -56
  69. package/src/modules/api/KYCService.ts +236 -82
  70. package/src/types/KYC.types.ts +1 -0
  71. package/src/utils/aamvaParser.ts +102 -0
  72. package/src/utils/cropByObb.ts +5 -3
  73. package/src/web/WebKYCEntry.tsx +125 -301
@@ -13,6 +13,19 @@ const appendFileToFormData = async (formData, key, uri, name = 'file.jpg', type
13
13
  formData.append(key, { uri, type, name });
14
14
  }
15
15
  };
16
+ const documentTypeMap = {
17
+ passport: "passport",
18
+ driversLicense: "driving_licence",
19
+ healthInsuranceCard: "health_insurance_card",
20
+ residencePermit: "permanent_residence_card",
21
+ identityCard: "identity_card",
22
+ nationalId: "identity_card", // We saw this in your earlier logs!
23
+ national_id: "identity_card",
24
+ drivers_licence: "driving_licence", // Plural 'drivers' to singular 'driving'
25
+ drivers_license: "driving_licence", // American spelling 'license'
26
+ driving_license: "driving_licence", // American spelling 'license'
27
+ permanent_residence: "permanent_residence_card", // Missing the word 'card'
28
+ };
16
29
  let cachedToken = null;
17
30
  let tokenExpiryTime = null;
18
31
  let activeAuthRequest = null;
@@ -23,13 +36,8 @@ export class KYCService {
23
36
  faceServiceURL = 'https://face-infera.sanctumkey.com';
24
37
  textExtractionServiceURL = 'https://text-infera.sanctumkey.com';
25
38
  mrzServiceURL = 'https://mrz-infera.sanctumkey.com';
26
- barcodeServiceURL = 'https://kyc-engine.SanctumKey.net';
39
+ barcodeServiceURL = 'https://face-infera.sanctumkey.com';
27
40
  orientationServiceURL = 'http://18.188.180.154:8080';
28
- // private faceServiceURL = 'https://kyc-engine.transfergratis.net:8000';
29
- // private textExtractionServiceURL = 'https://kyc-engine.transfergratis.net:8006';
30
- // private mrzServiceURL = 'https://kyc-engine.transfergratis.net:8002';
31
- // private barcodeServiceURL = 'https://kyc-engine.transfergratis.net:8000';
32
- // private orientationServiceURL = 'http://18.188.180.154:8080';
33
41
  constructor(baseURL, apiKey) {
34
42
  this.baseURL = baseURL;
35
43
  this.apiKey = apiKey;
@@ -199,14 +207,12 @@ export class KYCService {
199
207
  card_obb: { x: 50, y: 50, width: 200, height: 200 }
200
208
  };
201
209
  }
202
- // 🚨 Destructure issuingSubdivision
203
210
  const { fileUri, docType, docRegion, token, postfix, issuingSubdivision } = params;
204
211
  const formData = new FormData();
205
212
  await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
206
213
  const docTypeShorted = GovernmentDocumentTypeShorted[docType];
207
214
  // Build the base URL
208
215
  let url = `${this.mrzServiceURL}/get_template_version/?doc_type=${encodeURIComponent(docTypeShorted)}&doc_region=${encodeURIComponent(docRegion)}&postfix=${postfix}`;
209
- // 🚨 Append the subdivision strictly for Canada/regional docs
210
216
  if (issuingSubdivision) {
211
217
  url += `&issuing_subdivision=${encodeURIComponent(issuingSubdivision)}`;
212
218
  }
@@ -228,16 +234,14 @@ export class KYCService {
228
234
  const { fileUri, docType, docRegion, templatePath, issuingSubdivision, token, postfix = 'front' } = params;
229
235
  // 1. Prepare Form Data
230
236
  const formData = new FormData();
231
- // Prefixing with 'front' or 'back' as strictly required by the docs
232
237
  await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
233
- // 2. Build the URL with ALL required parameters
234
- let queryParams = `doc_type=${encodeURIComponent(docType)}&doc_region=${encodeURIComponent(docRegion)}&template_path=${encodeURIComponent(templatePath)}`;
238
+ let queryParams = `doc_type=${encodeURIComponent(docType)}&doc_region=${encodeURIComponent(docRegion)}&template_path=${encodeURIComponent(templatePath)}&postfix=${postfix}&cleaned=true`;
235
239
  if (issuingSubdivision) {
236
240
  queryParams += `&issuing_subdivision=${encodeURIComponent(issuingSubdivision)}`;
237
241
  }
242
+ logger.log(`URL PARAMS: ${queryParams}`);
238
243
  const url = `${this.textExtractionServiceURL}/extract_doc_information/?${queryParams}`;
239
244
  try {
240
- // 3. Start the Job
241
245
  const startRes = await axios.post(url, formData, {
242
246
  headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` },
243
247
  timeout: 60000,
@@ -259,12 +263,11 @@ export class KYCService {
259
263
  const statusData = statusRes.data;
260
264
  if (statusData.status === 'completed') {
261
265
  logger.log('Extraction completed successfully', JSON.stringify(truncateFields(statusData.result)));
262
- return statusData.result; // 👈 Returns the actual extracted data
266
+ return statusData.result;
263
267
  }
264
268
  if (statusData.status === 'failed') {
265
269
  throw new Error(statusData.error || "Document extraction failed during processing.");
266
270
  }
267
- // If 'processing', loop continues...
268
271
  }
269
272
  throw new Error("Document extraction timed out waiting for results.");
270
273
  }
@@ -274,7 +277,6 @@ export class KYCService {
274
277
  throw new Error(errorMsg);
275
278
  }
276
279
  }
277
- // STEP 2 - barcode extraction
278
280
  async extractBarcode(params, env = 'PRODUCTION') {
279
281
  // SANDBOX mode
280
282
  if (env === 'SANDBOX') {
@@ -285,35 +287,72 @@ export class KYCService {
285
287
  card_obb: { x: 50, y: 50, width: 200, height: 200 }
286
288
  };
287
289
  }
288
- const { fileUri, token, postfix = 'back' } = params;
290
+ const { fileUri, token, docType, postfix = 'back' } = params;
291
+ logger.log(docType);
289
292
  const formData = new FormData();
290
- // ✅ FIX: Dynamic filename
291
- await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
292
- const url = `${this.barcodeServiceURL}/decode_barcode/`;
293
+ await appendFileToFormData(formData, 'file', fileUri, `id_${postfix}_photo.jpg`, 'image/jpeg');
294
+ const url = `${this.barcodeServiceURL}/decode_barcode/?doc_type=${encodeURIComponent(docType)}`;
293
295
  const attempt = async () => {
294
296
  try {
295
297
  const res = await axios.post(url, formData, {
296
- headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
298
+ headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` },
297
299
  timeout: 60000,
298
300
  });
299
- logger.log('extractBarcode res', JSON.stringify(res.data, null, 2));
300
- if (Object.keys(res.data).length === 0)
301
- throw new Error('No data found');
301
+ logger.log('📦 Raw extractBarcode response:', JSON.stringify(res.data, null, 2));
302
302
  return res.data;
303
303
  }
304
304
  catch (e) {
305
- throw new Error(e?.message || 'Erreur de détection du barcode');
305
+ throw new Error(e?.message || 'Erreur de détection du code-barres');
306
306
  }
307
307
  };
308
308
  try {
309
309
  return await attempt();
310
310
  }
311
311
  catch (e) {
312
- logger.error('Error extracting Barcode text:', JSON.stringify(e));
312
+ logger.error('❌ Error extracting Barcode text:', JSON.stringify(e));
313
+ throw e;
314
+ }
315
+ }
316
+ async formatBarcodeData(params, env = 'PRODUCTION') {
317
+ // SANDBOX mode
318
+ if (env === 'SANDBOX') {
319
+ console.log("SANDBOX mode: Skipping AI barcode formatting");
320
+ logger.log("SANDBOX mode: Returning mock formatted barcode response");
321
+ return {
322
+ barcode_type: "SANDBOX_MOCK",
323
+ first_name: "JOHN",
324
+ last_name: "DOE",
325
+ card_number: "000000000000"
326
+ };
327
+ }
328
+ const { textInput, token } = params;
329
+ const formData = new URLSearchParams();
330
+ formData.append('text_input', textInput);
331
+ const url = `${this.barcodeServiceURL}/format_barcode_data/`;
332
+ const attempt = async () => {
333
+ try {
334
+ const res = await axios.post(url, formData.toString(), {
335
+ headers: {
336
+ 'Content-Type': 'application/x-www-form-urlencoded',
337
+ 'Authorization': `Bearer ${token}`
338
+ },
339
+ timeout: 60000,
340
+ });
341
+ logger.log('📦 Raw formatBarcodeData response:', JSON.stringify(res.data, null, 2));
342
+ return res.data;
343
+ }
344
+ catch (e) {
345
+ throw new Error(e?.message || 'Erreur de formatage du code-barres');
346
+ }
347
+ };
348
+ try {
349
+ return await attempt();
350
+ }
351
+ catch (e) {
352
+ logger.error('❌ Error formatting Barcode text:', JSON.stringify(e));
313
353
  throw e;
314
354
  }
315
355
  }
316
- // STEP 3 - MRZ TEXT EXTRACTION
317
356
  async extractMrzText(params, env = 'PRODUCTION') {
318
357
  // SANDBOX mode
319
358
  if (env === 'SANDBOX') {
@@ -349,8 +388,6 @@ export class KYCService {
349
388
  try {
350
389
  let response;
351
390
  if (Platform.OS === 'web') {
352
- // 🚨 THE FIX: Completely bypass React Native Web's networking polyfills.
353
- // We explicitly grab the browser's native APIs so the bundler cannot interfere.
354
391
  const nativeFetch = window.fetch;
355
392
  const NativeFormData = window.FormData;
356
393
  const formData = new NativeFormData();
@@ -474,44 +511,91 @@ export class KYCService {
474
511
  async verificationSession(payload) {
475
512
  try {
476
513
  const { session_id, step, data, templateId, action, apiKey } = payload;
477
- // 1. Safe Auth Resolution
478
514
  const token = apiKey ? undefined : await authentification();
479
515
  if (!apiKey && !token) {
480
516
  throw new Error("Authentication failed: No API key or Token available.");
481
517
  }
518
+ // 1. Original Shallow Clone (Preserves file paths perfectly)
519
+ const normalizedData = { ...data };
520
+ logger.log("🔍 [DocType Check] Raw incoming data.metadata.document_type:", normalizedData?.metadata?.document_type);
521
+ logger.log("🔍 [DocType Check] Raw incoming data.documentType:", normalizedData?.documentType);
522
+ // Enhanced fallback to guarantee we find the document type
523
+ const rawDocType = normalizedData?.metadata?.document_type ||
524
+ normalizedData?.documentType ||
525
+ normalizedData?.documents?.documentType;
526
+ if (rawDocType) {
527
+ const originalDocType = rawDocType;
528
+ const mappedDocType = documentTypeMap[originalDocType] ?? originalDocType;
529
+ normalizedData.metadata = {
530
+ ...normalizedData.metadata,
531
+ document_type: mappedDocType,
532
+ };
533
+ normalizedData.documentType = mappedDocType;
534
+ if (normalizedData.documents) {
535
+ normalizedData.documents = {
536
+ ...normalizedData.documents,
537
+ documentType: mappedDocType
538
+ };
539
+ const isSingleSided = ['passport', 'visa'].includes(mappedDocType.toLowerCase());
540
+ if (isSingleSided && !normalizedData.documents.back) {
541
+ normalizedData.documents.back = {};
542
+ }
543
+ if (normalizedData.documents.front?.mrz && typeof normalizedData.documents.front.mrz === 'string') {
544
+ try {
545
+ normalizedData.documents.front = {
546
+ ...normalizedData.documents.front,
547
+ mrz: JSON.parse(normalizedData.documents.front.mrz)
548
+ };
549
+ }
550
+ catch (e) {
551
+ logger.log("Warning: Could not parse front MRZ JSON");
552
+ }
553
+ }
554
+ if (normalizedData.documents.back?.mrz && typeof normalizedData.documents.back.mrz === 'string') {
555
+ try {
556
+ normalizedData.documents.back = {
557
+ ...normalizedData.documents.back,
558
+ mrz: JSON.parse(normalizedData.documents.back.mrz)
559
+ };
560
+ }
561
+ catch (e) {
562
+ logger.log("Warning: Could not parse back MRZ JSON");
563
+ }
564
+ }
565
+ }
566
+ }
482
567
  const payloadData = {
483
- action: action,
568
+ action,
484
569
  data: {
485
- ...data,
486
- step: step,
487
- templateId: templateId,
488
- ...(action === "location_permission" ? { permissionGranted: true } : {}),
570
+ ...normalizedData,
571
+ step,
572
+ templateId,
489
573
  },
490
- session_id: session_id,
574
+ session_id,
491
575
  timestamp: new Date().toISOString(),
492
576
  };
493
577
  const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
494
- // 3. Clean Logging
495
578
  const logPayload = truncateFields({ payloadData, session_id, step });
496
- logger.log('verificationSession payload', JSON.stringify({
579
+ logger.log("verificationSession payload", JSON.stringify({
497
580
  url,
498
- apiKey: apiKey ? '***HIDDEN***' : 'None', // Don't log the raw API key!
499
- token: token ? '***HIDDEN***' : 'None',
500
- payload: logPayload
581
+ apiKey: apiKey ? "***HIDDEN***" : "None",
582
+ token: token ? "***HIDDEN***" : "None",
583
+ payload: logPayload,
501
584
  }, null, 2));
502
- // 4. API Execution
503
- const res = await axios.post(url, payloadData, {
504
- headers: {
505
- 'Content-Type': 'application/json',
506
- ...(apiKey ? { 'Authorization': `ApiKey ${apiKey}` } : { 'Authorization': `Bearer ${token}` })
507
- }
508
- });
509
- logger.log('verificationSession res', JSON.stringify(truncateFields(res.data), null, 2));
585
+ const headers = {
586
+ "Content-Type": "application/json",
587
+ ...(apiKey ? { Authorization: `ApiKey ${apiKey}` } : {}),
588
+ ...(token && !apiKey ? { Authorization: `Bearer ${token}` } : {})
589
+ };
590
+ const res = await axios.post(url, payloadData, { headers });
591
+ logger.log("verificationSession res", JSON.stringify(truncateFields(res.data), null, 2));
510
592
  return res.data;
511
593
  }
512
594
  catch (error) {
513
- logger.error('Error validating component:', JSON.stringify(errorMessage(error), null, 2));
514
- throw new Error(errorMessage(error));
595
+ const parsedError = errorMessage(error);
596
+ const finalErrorMessage = parsedError || error?.message || "Unknown validation error";
597
+ logger.error("Error validating component:", JSON.stringify(finalErrorMessage, null, 2));
598
+ throw new Error(typeof finalErrorMessage === 'string' ? finalErrorMessage : JSON.stringify(finalErrorMessage));
515
599
  }
516
600
  }
517
601
  /** Send email verification code. POST /api/v1/accounts/email/send/ */
@@ -538,16 +622,40 @@ export class KYCService {
538
622
  });
539
623
  return res.data;
540
624
  }
541
- async getVerificationResult(session_id) {
625
+ async getVerificationResult(session_id, apiKey) {
542
626
  try {
543
- const token = await authentification();
627
+ const token = apiKey ? undefined : await authentification();
544
628
  const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/result/?session_id=${session_id}`;
545
- const res = await axios.get(url, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } });
546
- logger.log('getVerificationResult res', JSON.stringify(truncateFields(res.data), null, 2));
629
+ const controller = new AbortController();
630
+ const timeoutId = setTimeout(() => {
631
+ logger.log("⏳ 8 seconds passed. Force-killing hanging Axios request...");
632
+ controller.abort();
633
+ }, 8000);
634
+ const headers = {
635
+ 'Content-Type': 'application/json',
636
+ };
637
+ if (apiKey) {
638
+ headers["Authorization"] = `ApiKey ${apiKey}`;
639
+ }
640
+ else if (token) {
641
+ headers["Authorization"] = `Bearer ${token}`;
642
+ }
643
+ logger.log("3. Firing Axios GET to:", url);
644
+ const res = await axios.get(url, {
645
+ headers,
646
+ signal: controller.signal
647
+ });
648
+ // Clear the timeout if the request succeeds!
649
+ clearTimeout(timeoutId);
547
650
  return res.data;
548
651
  }
549
652
  catch (error) {
550
- logger.error('Error getting verification result:', JSON.stringify(errorMessage(error), null, 2));
653
+ // Catch the AbortController kill signal and format it as a timeout
654
+ if (axios.isCancel(error) || error.name === 'AbortError' || error.name === 'CanceledError') {
655
+ logger.error('Axios request was forcefully aborted due to timeout');
656
+ throw new Error('Timeout exceeded');
657
+ }
658
+ logger.error('Error getting verification result:', JSON.stringify(errorMessage(error) || error.message, null, 2));
551
659
  throw error;
552
660
  }
553
661
  }
@@ -617,7 +725,6 @@ export class KYCService {
617
725
  return { is_match: true, status_message: "SANDBOX: Valid Match" };
618
726
  }
619
727
  const { region, docType, frontTemplateName, backTemplateName, subRegion, token } = params;
620
- // 🚨 THE FIX: Format the payload as URL Query Parameters
621
728
  const queryParams = new URLSearchParams({
622
729
  region: region,
623
730
  doc_type: docType,
@@ -705,7 +812,6 @@ export const authentification = async () => {
705
812
  }
706
813
  catch (error) {
707
814
  cachedToken = null; // Clear bad cache
708
- // 🚨 FIX: Extract Keycloak's exact error message so it doesn't log as {}
709
815
  const safeErrorMessage = error?.response?.data?.error_description ||
710
816
  error?.response?.data?.error ||
711
817
  error?.message ||