@transfergratis/react-native-sdk 0.1.28 → 0.1.30

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 (39) hide show
  1. package/build/components/EnhancedCameraView.js +1 -1
  2. package/build/components/EnhancedCameraView.js.map +1 -1
  3. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  4. package/build/components/KYCElements/CountrySelectionTemplate.js +13 -42
  5. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  6. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  7. package/build/components/KYCElements/IDCardCapture.js +113 -49
  8. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  9. package/build/components/KYCElements/SelfieCaptureTemplate.js +2 -2
  10. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  11. package/build/hooks/useTemplateKYCFlow.js +1 -1
  12. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  13. package/build/modules/api/CardAuthentification.d.ts +15 -7
  14. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  15. package/build/modules/api/CardAuthentification.js +360 -104
  16. package/build/modules/api/CardAuthentification.js.map +1 -1
  17. package/build/modules/api/KYCService.d.ts +3 -1
  18. package/build/modules/api/KYCService.d.ts.map +1 -1
  19. package/build/modules/api/KYCService.js +25 -24
  20. package/build/modules/api/KYCService.js.map +1 -1
  21. package/build/modules/camera/VisionCameraModule.js +2 -2
  22. package/build/modules/camera/VisionCameraModule.js.map +1 -1
  23. package/build/utils/cropByObb.d.ts +8 -0
  24. package/build/utils/cropByObb.d.ts.map +1 -1
  25. package/build/utils/cropByObb.js +20 -3
  26. package/build/utils/cropByObb.js.map +1 -1
  27. package/build/utils/pathToBase64.js +1 -1
  28. package/build/utils/pathToBase64.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/components/EnhancedCameraView.tsx +1 -1
  31. package/src/components/KYCElements/CountrySelectionTemplate.tsx +24 -52
  32. package/src/components/KYCElements/IDCardCapture.tsx +179 -109
  33. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
  34. package/src/hooks/useTemplateKYCFlow.tsx +1 -1
  35. package/src/modules/api/CardAuthentification.ts +450 -113
  36. package/src/modules/api/KYCService.ts +52 -39
  37. package/src/modules/camera/VisionCameraModule.ts +2 -2
  38. package/src/utils/cropByObb.ts +22 -3
  39. package/src/utils/pathToBase64.ts +1 -1
@@ -216,7 +216,7 @@ export class KYCService {
216
216
 
217
217
  // STEP 1 - ID CARD VALIDATION
218
218
  async detectFaceOnId(idCardImageUri: string, token: string, docType: string, env: KycEnvironment = 'PRODUCTION'): Promise<{ result: boolean, detail: any[] }> {
219
- // SANDBOX mode: skip AI verification and return mock response
219
+ // SANDBOX mode
220
220
  if (env === 'SANDBOX') {
221
221
  console.log("SANDBOX mode: Skipping AI face detection on ID");
222
222
  logger.log("SANDBOX mode: Returning mock face detection response");
@@ -228,6 +228,7 @@ export class KYCService {
228
228
  }
229
229
 
230
230
  const formData = new FormData();
231
+ // Defaulting to photo since we don't pass postfix here currently, but making the name generic
231
232
  await appendFileToFormData(formData, 'file', idCardImageUri, 'id_card_photo.jpg', 'image/jpeg');
232
233
 
233
234
  logger.log('detectFaceOnId formData', JSON.stringify(formData, null, 2));
@@ -252,7 +253,7 @@ export class KYCService {
252
253
 
253
254
  //check templatetemplate_type
254
255
  async checkTemplateType(params: { fileUri: string; docType: string; docRegion: string; token: string; postfix: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
255
- // SANDBOX mode: skip AI verification and return mock response
256
+ // SANDBOX mode
256
257
  if (env === 'SANDBOX') {
257
258
  console.log("SANDBOX mode: Skipping AI template type check");
258
259
  logger.log("SANDBOX mode: Returning mock template type response");
@@ -265,7 +266,9 @@ export class KYCService {
265
266
 
266
267
  const { fileUri, docType, docRegion, token, postfix } = params;
267
268
  const formData = new FormData();
268
- await appendFileToFormData(formData, 'file', fileUri, 'id_card_front.jpg', 'image/jpeg');
269
+
270
+ // ✅ FIX: Dynamically assign the postfix to the filename
271
+ await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
269
272
 
270
273
  const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
271
274
  const url = `${this.mrzServiceURL}/get_template_version/?doc_type=${encodeURIComponent(docTypeShorted)}&doc_region=${encodeURIComponent(docRegion)}&postfix=${postfix}`;
@@ -285,11 +288,12 @@ export class KYCService {
285
288
 
286
289
  }
287
290
 
288
- async extractDocumentInformation(params: { fileUri: string; docType: string; docRegion: string; token: string }): Promise<any> {
289
- const { fileUri, docType, docRegion, token } = params;
291
+ async extractDocumentInformation(params: { fileUri: string; docType: string; docRegion: string; token: string; postfix?: string }): Promise<any> {
292
+ const { fileUri, docType, docRegion, token, postfix = 'front' } = params;
290
293
  const formData = new FormData();
291
- await appendFileToFormData(formData, 'file', fileUri, 'id_card_front.jpg', 'image/jpeg');
292
-
294
+
295
+ // ✅ FIX: Dynamic filename
296
+ await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
293
297
 
294
298
  const url = `${this.textExtractionServiceURL}/extract_doc_information/?doc_type=${encodeURIComponent(docType)}&doc_region=${encodeURIComponent(docRegion)}`;
295
299
  const attempt = async () => {
@@ -307,13 +311,12 @@ export class KYCService {
307
311
  } catch (e) {
308
312
  logger.error('Error extracting document information:', JSON.stringify(truncateFields(e)));
309
313
  throw e;
310
- // await new Promise(r => setTimeout(r, 1500));
311
- // return await attempt();
312
314
  }
313
315
  }
316
+
314
317
  // STEP 2 - barcode extraction
315
- async extractBarcode(params: { fileUri: string; token: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
316
- // SANDBOX mode: skip AI verification and return mock response
318
+ async extractBarcode(params: { fileUri: string; token: string; postfix?: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
319
+ // SANDBOX mode
317
320
  if (env === 'SANDBOX') {
318
321
  console.log("SANDBOX mode: Skipping AI barcode extraction");
319
322
  logger.log("SANDBOX mode: Returning mock barcode response");
@@ -323,10 +326,11 @@ export class KYCService {
323
326
  };
324
327
  }
325
328
 
326
- const { fileUri, token } = params;
329
+ const { fileUri, token, postfix = 'back' } = params;
327
330
  const formData = new FormData();
328
- await appendFileToFormData(formData, 'file', fileUri, 'id_card_back.jpg', 'image/jpeg');
329
-
331
+
332
+ // ✅ FIX: Dynamic filename
333
+ await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
330
334
 
331
335
  const url = `${this.barcodeServiceURL}/decode_barcode/`;
332
336
  const attempt = async () => {
@@ -336,7 +340,6 @@ export class KYCService {
336
340
  timeout: 60000,
337
341
  });
338
342
  logger.log('extractBarcode res', JSON.stringify(res.data, null, 2));
339
- //check if res.data has aleast one key
340
343
  if (Object.keys(res.data).length === 0) throw new Error('No data found');
341
344
  return res.data;
342
345
  } catch (e: any) {
@@ -350,9 +353,21 @@ export class KYCService {
350
353
  throw e;
351
354
  }
352
355
  }
356
+
353
357
  // STEP 3 - MRZ TEXT EXTRACTION
354
- async extractMrzText(params: { fileUri: string; docType: string; docRegion: string; postfix?: string; token: string; template_path: string; mrz_type: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
355
- // SANDBOX mode: skip AI verification and return mock response
358
+ async extractMrzText(
359
+ params: {
360
+ fileUri: string;
361
+ docType: string;
362
+ docRegion: string;
363
+ postfix?: string;
364
+ token: string;
365
+ template_path: string;
366
+ mrz_type?: string;
367
+ },
368
+ env: KycEnvironment = 'PRODUCTION'
369
+ ): Promise<any> {
370
+ // SANDBOX mode
356
371
  if (env === 'SANDBOX') {
357
372
  console.log("SANDBOX mode: Skipping AI MRZ extraction");
358
373
  logger.log("SANDBOX mode: Returning mock MRZ response");
@@ -372,29 +387,40 @@ export class KYCService {
372
387
 
373
388
  const { fileUri, docType, docRegion, postfix = 'back', token, template_path, mrz_type } = params;
374
389
  const formData = new FormData();
375
- await appendFileToFormData(formData, 'file', fileUri, 'id_card_back.jpg', 'image/jpeg');
390
+
391
+ // ✅ Dynamic filename to prevent passports from crashing (since their MRZ is on the front)
392
+ await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
376
393
 
377
- const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
394
+ const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType] || docType;
378
395
  logger.log("docTypeShorted", docTypeShorted, docRegion, postfix);
379
396
 
380
- const url = `${this.mrzServiceURL}/extract_mrz_text/?doc_type=${docTypeShorted}&doc_region=${docRegion}&postfix=${postfix}&template_path=${template_path}&mrz_type=${mrz_type}`;
397
+ let url = `${this.mrzServiceURL}/extract_mrz_text/?doc_type=${encodeURIComponent(docTypeShorted)}&doc_region=${encodeURIComponent(docRegion)}&postfix=${encodeURIComponent(postfix)}&template_path=${encodeURIComponent(template_path)}`;
398
+
399
+ if (mrz_type && mrz_type.trim() !== '') {
400
+ url += `&mrz_type=${encodeURIComponent(mrz_type)}`;
401
+ }
402
+
381
403
  logger.log("url", url);
382
404
 
383
405
  const attempt = async () => {
384
406
  try {
385
407
  const res = await axios.post<ExtractMrzTextResponse>(url, formData, {
386
- headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
387
- timeout: 60000,
408
+ headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}` },
409
+ // Note: Reduced timeout to 10000ms (10s) based on our earlier network fix to prevent infinite hanging!
410
+ timeout: 10000,
388
411
  });
412
+
389
413
  logger.log('extractMrzText res', JSON.stringify(res.data, null, 2));
390
- //check if res.data has aleast one key
414
+
391
415
  if (Object.keys(res.data).length === 0) throw new Error('No data found');
392
- if (res.data?.success === false) throw new Error(res.data.parsed_data.status);
416
+ if (res.data?.success === false) throw new Error(res.data.parsed_data?.status || 'Échec de l\'extraction MRZ');
417
+
393
418
  return res.data;
394
419
  } catch (e: any) {
395
420
  throw new Error(e?.message || 'Erreur de détection du MRZ');
396
421
  }
397
422
  };
423
+
398
424
  try {
399
425
  return await attempt();
400
426
  } catch (e) {
@@ -405,7 +431,7 @@ export class KYCService {
405
431
 
406
432
  // STEP 2 - SELFIE VALIDATION
407
433
  async recognizeFace(params: { idPhotoUri: string; selfiePhotoUri: string }, env: KycEnvironment = 'PRODUCTION'): Promise<{ is_match: boolean; similarity: number; id_bbox?: number[]; selfie_bbox?: number[] }> {
408
- // SANDBOX mode: skip AI verification and return mock response
434
+ // SANDBOX mode
409
435
  if (env === 'SANDBOX') {
410
436
  console.log("SANDBOX mode: Skipping AI face recognition");
411
437
  logger.log("SANDBOX mode: Returning mock face recognition response");
@@ -422,7 +448,6 @@ export class KYCService {
422
448
  await appendFileToFormData(formData, 'id_photo', idPhotoUri, 'id_card_photo.jpg', 'image/jpeg');
423
449
  await appendFileToFormData(formData, 'selfie_photo', selfiePhotoUri, 'selfie_final.jpg', 'image/jpeg');
424
450
 
425
-
426
451
  const res = await axios.post(`${this.faceServiceURL}/recognize_face/`, formData, { timeout: 45000 });
427
452
  if (res.data?.detail) throw new Error(res.data.detail);
428
453
  return res.data;
@@ -481,13 +506,8 @@ export class KYCService {
481
506
  async verificationSession(payload: VerificationSessionRequest): Promise<any> {
482
507
  console.log('apiKey in verificationSession', payload.apiKey);
483
508
  try {
484
-
485
-
486
- // /api/v1/verification/api/kyc/sessions/{session_id}/steps/{step}/
487
509
  const { session_id, step, data, templateId, action, apiKey } = payload;
488
-
489
510
  const token = apiKey ? undefined : await authentification();
490
- // const session_id = "kyc-8b4e069258d8";
491
511
  const payloadData = {
492
512
  action: action,
493
513
  data: {
@@ -501,7 +521,6 @@ export class KYCService {
501
521
  }
502
522
  const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
503
523
 
504
-
505
524
  const logPayload = truncateFields({ payloadData, session_id, step });
506
525
  logger.log('verificationSession payload',
507
526
  JSON.stringify(truncateFields({ logPayload, token: token ?? "-", path: url, apiKey }),
@@ -518,7 +537,6 @@ export class KYCService {
518
537
  logger.log('verificationSession res', JSON.stringify(truncateFields(res.data), null, 2));
519
538
  return res.data;
520
539
 
521
-
522
540
  } catch (error) {
523
541
  logger.error('Error validating component:', JSON.stringify(error, null, 2));
524
542
  throw new Error(errorMessage(error));
@@ -565,7 +583,6 @@ export class KYCService {
565
583
  return res.data;
566
584
  }
567
585
 
568
- // reultat de la verification
569
586
  async getVerificationResult(session_id: string): Promise<VerificationResult> {
570
587
  try {
571
588
  const token = await authentification();
@@ -581,11 +598,9 @@ export class KYCService {
581
598
  }
582
599
  }
583
600
 
584
-
585
601
  const kycService = new KYCService("", "");
586
602
  export default kycService;
587
603
 
588
-
589
604
  // Pour éviter d'afficher des champs trop longs, on tronque les valeurs longues dans le log
590
605
  export function truncateFields(obj: any, maxLength = 420): any {
591
606
  if (typeof obj !== 'object' || obj === null) return obj;
@@ -604,6 +619,7 @@ export function truncateFields(obj: any, maxLength = 420): any {
604
619
  }
605
620
  return truncated;
606
621
  }
622
+
607
623
  export const authentification = async () => {
608
624
  try {
609
625
  logger.log('authentification params', 'kyc_frontend', 'ZCW4mGaJXWR0UuI40lM1pHNQmYLw2xvX');
@@ -631,7 +647,6 @@ export const authentification = async () => {
631
647
  method: error.config?.method
632
648
  });
633
649
 
634
- // Extract backend error message if available
635
650
  const backendMessage = errorMessage(error);
636
651
 
637
652
  if (backendMessage) {
@@ -643,8 +658,6 @@ export const authentification = async () => {
643
658
  }
644
659
  }
645
660
 
646
-
647
-
648
661
  export const errorMessage = (error: any) => {
649
662
  return error.response?.data?.message ||
650
663
  error.response?.data?.detail ||
@@ -26,7 +26,7 @@ async function copyFileCompat(from: string, to: string): Promise<void> {
26
26
  // This is the recommended way to avoid deprecation warnings
27
27
  try {
28
28
  // @ts-ignore - legacy export might not be in types
29
- const LegacyFileSystem = require('expo-file-system/legacy');
29
+ const LegacyFileSystem = require('expo-file-system');
30
30
  if (LegacyFileSystem && LegacyFileSystem.copyAsync && typeof LegacyFileSystem.copyAsync === 'function') {
31
31
  await LegacyFileSystem.copyAsync({ from, to });
32
32
  return;
@@ -81,7 +81,7 @@ function getDocumentDirectory(): string {
81
81
  // This is the recommended way to avoid deprecation warnings
82
82
  try {
83
83
  // @ts-ignore
84
- const LegacyFileSystem = require('expo-file-system/legacy');
84
+ const LegacyFileSystem = require('expo-file-system');
85
85
  if (LegacyFileSystem && LegacyFileSystem.documentDirectory) {
86
86
  return LegacyFileSystem.documentDirectory;
87
87
  }
@@ -37,6 +37,14 @@ export function getObbConfidence(cardObb: any): number | null {
37
37
  * - false => card not fully in frame
38
38
  * - null => field not present (backward compatibility)
39
39
  */
40
+ /**
41
+ * Extracts the framing data from card_obb.
42
+ * STRICT CHECK: Returns true ONLY if card_in_frame is true AND cropped_sides is empty.
43
+ * Returns:
44
+ * - true => card fully in frame (no sides cropped)
45
+ * - false => card not fully in frame OR edges are cropped
46
+ * - null => field not present (backward compatibility)
47
+ */
40
48
  export function getCardInFrame(cardObb: any): boolean | null {
41
49
  if (!cardObb) return null;
42
50
 
@@ -44,11 +52,22 @@ export function getCardInFrame(cardObb: any): boolean | null {
44
52
  const first = Array.isArray(cardObb) ? cardObb[0] : cardObb;
45
53
 
46
54
  if (first && typeof first === 'object' && 'card_in_frame' in first) {
47
- const value = (first as any).card_in_frame;
48
- return typeof value === 'boolean' ? value : null;
55
+ const isCardInFrame = first.card_in_frame === true;
56
+ const hasCroppedSides = Array.isArray(first.cropped_sides) && first.cropped_sides.length > 0;
57
+
58
+ // If the API explicitly says it's not in frame, OR if it lists cropped edges, reject it.
59
+ if (!isCardInFrame || hasCroppedSides) {
60
+ if (hasCroppedSides) {
61
+ console.warn(`Card is cropped on sides: ${first.cropped_sides.join(', ')}`);
62
+ }
63
+ return false;
64
+ }
65
+
66
+ // If we made it here, card_in_frame is true AND cropped_sides is empty.
67
+ return true;
49
68
  }
50
69
 
51
- // Some APIs might put card_in_frame at the top level
70
+ // Backward compatibility check
52
71
  if (cardObb && typeof cardObb === 'object' && 'card_in_frame' in cardObb) {
53
72
  const value = (cardObb as any).card_in_frame;
54
73
  return typeof value === 'boolean' ? value : null;
@@ -13,7 +13,7 @@ export async function pathToBase64(uri: string): Promise<string> {
13
13
  // This is the recommended way to avoid deprecation warnings
14
14
  try {
15
15
  // @ts-ignore - legacy export might not be in types
16
- const LegacyFileSystem = require('expo-file-system/legacy');
16
+ const LegacyFileSystem = require('expo-file-system');
17
17
  if (LegacyFileSystem?.readAsStringAsync) {
18
18
  const base64 = await LegacyFileSystem.readAsStringAsync(uri, { encoding: LegacyFileSystem.EncodingType.Base64 });
19
19
  if (base64) return base64;