@transfergratis/react-native-sdk 0.1.26 → 0.1.29

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 +76 -35
  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 +215 -44
  16. package/build/modules/api/CardAuthentification.js.map +1 -1
  17. package/build/modules/api/KYCService.d.ts +2 -0
  18. package/build/modules/api/KYCService.d.ts.map +1 -1
  19. package/build/modules/api/KYCService.js +16 -19
  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 +19 -1
  24. package/build/utils/cropByObb.d.ts.map +1 -1
  25. package/build/utils/cropByObb.js +49 -1
  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 +129 -90
  33. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
  34. package/src/hooks/useTemplateKYCFlow.tsx +1 -1
  35. package/src/modules/api/CardAuthentification.ts +290 -58
  36. package/src/modules/api/KYCService.ts +25 -33
  37. package/src/modules/camera/VisionCameraModule.ts +2 -2
  38. package/src/utils/cropByObb.ts +57 -1
  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,10 @@ export class KYCService {
350
353
  throw e;
351
354
  }
352
355
  }
356
+
353
357
  // STEP 3 - MRZ TEXT EXTRACTION
354
358
  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
359
+ // SANDBOX mode
356
360
  if (env === 'SANDBOX') {
357
361
  console.log("SANDBOX mode: Skipping AI MRZ extraction");
358
362
  logger.log("SANDBOX mode: Returning mock MRZ response");
@@ -372,7 +376,9 @@ export class KYCService {
372
376
 
373
377
  const { fileUri, docType, docRegion, postfix = 'back', token, template_path, mrz_type } = params;
374
378
  const formData = new FormData();
375
- await appendFileToFormData(formData, 'file', fileUri, 'id_card_back.jpg', 'image/jpeg');
379
+
380
+ // ✅ FIX: Dynamic filename to prevent passports from crashing (since their MRZ is on the front)
381
+ await appendFileToFormData(formData, 'file', fileUri, `id_card_${postfix}.jpg`, 'image/jpeg');
376
382
 
377
383
  const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
378
384
  logger.log("docTypeShorted", docTypeShorted, docRegion, postfix);
@@ -387,7 +393,6 @@ export class KYCService {
387
393
  timeout: 60000,
388
394
  });
389
395
  logger.log('extractMrzText res', JSON.stringify(res.data, null, 2));
390
- //check if res.data has aleast one key
391
396
  if (Object.keys(res.data).length === 0) throw new Error('No data found');
392
397
  if (res.data?.success === false) throw new Error(res.data.parsed_data.status);
393
398
  return res.data;
@@ -405,7 +410,7 @@ export class KYCService {
405
410
 
406
411
  // STEP 2 - SELFIE VALIDATION
407
412
  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
413
+ // SANDBOX mode
409
414
  if (env === 'SANDBOX') {
410
415
  console.log("SANDBOX mode: Skipping AI face recognition");
411
416
  logger.log("SANDBOX mode: Returning mock face recognition response");
@@ -422,7 +427,6 @@ export class KYCService {
422
427
  await appendFileToFormData(formData, 'id_photo', idPhotoUri, 'id_card_photo.jpg', 'image/jpeg');
423
428
  await appendFileToFormData(formData, 'selfie_photo', selfiePhotoUri, 'selfie_final.jpg', 'image/jpeg');
424
429
 
425
-
426
430
  const res = await axios.post(`${this.faceServiceURL}/recognize_face/`, formData, { timeout: 45000 });
427
431
  if (res.data?.detail) throw new Error(res.data.detail);
428
432
  return res.data;
@@ -481,13 +485,8 @@ export class KYCService {
481
485
  async verificationSession(payload: VerificationSessionRequest): Promise<any> {
482
486
  console.log('apiKey in verificationSession', payload.apiKey);
483
487
  try {
484
-
485
-
486
- // /api/v1/verification/api/kyc/sessions/{session_id}/steps/{step}/
487
488
  const { session_id, step, data, templateId, action, apiKey } = payload;
488
-
489
489
  const token = apiKey ? undefined : await authentification();
490
- // const session_id = "kyc-8b4e069258d8";
491
490
  const payloadData = {
492
491
  action: action,
493
492
  data: {
@@ -501,7 +500,6 @@ export class KYCService {
501
500
  }
502
501
  const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
503
502
 
504
-
505
503
  const logPayload = truncateFields({ payloadData, session_id, step });
506
504
  logger.log('verificationSession payload',
507
505
  JSON.stringify(truncateFields({ logPayload, token: token ?? "-", path: url, apiKey }),
@@ -518,7 +516,6 @@ export class KYCService {
518
516
  logger.log('verificationSession res', JSON.stringify(truncateFields(res.data), null, 2));
519
517
  return res.data;
520
518
 
521
-
522
519
  } catch (error) {
523
520
  logger.error('Error validating component:', JSON.stringify(error, null, 2));
524
521
  throw new Error(errorMessage(error));
@@ -565,7 +562,6 @@ export class KYCService {
565
562
  return res.data;
566
563
  }
567
564
 
568
- // reultat de la verification
569
565
  async getVerificationResult(session_id: string): Promise<VerificationResult> {
570
566
  try {
571
567
  const token = await authentification();
@@ -581,11 +577,9 @@ export class KYCService {
581
577
  }
582
578
  }
583
579
 
584
-
585
580
  const kycService = new KYCService("", "");
586
581
  export default kycService;
587
582
 
588
-
589
583
  // Pour éviter d'afficher des champs trop longs, on tronque les valeurs longues dans le log
590
584
  export function truncateFields(obj: any, maxLength = 420): any {
591
585
  if (typeof obj !== 'object' || obj === null) return obj;
@@ -604,6 +598,7 @@ export function truncateFields(obj: any, maxLength = 420): any {
604
598
  }
605
599
  return truncated;
606
600
  }
601
+
607
602
  export const authentification = async () => {
608
603
  try {
609
604
  logger.log('authentification params', 'kyc_frontend', 'ZCW4mGaJXWR0UuI40lM1pHNQmYLw2xvX');
@@ -631,7 +626,6 @@ export const authentification = async () => {
631
626
  method: error.config?.method
632
627
  });
633
628
 
634
- // Extract backend error message if available
635
629
  const backendMessage = errorMessage(error);
636
630
 
637
631
  if (backendMessage) {
@@ -643,8 +637,6 @@ export const authentification = async () => {
643
637
  }
644
638
  }
645
639
 
646
-
647
-
648
640
  export const errorMessage = (error: any) => {
649
641
  return error.response?.data?.message ||
650
642
  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
  }
@@ -9,17 +9,73 @@ type Point = [number, number];
9
9
  export const OBB_CONFIDENCE_THRESHOLD = 0.85;
10
10
 
11
11
  /**
12
- * card_obb format from API: [ [ [p1,p2,p3,p4], confidence ] ]
12
+ * card_obb format from API:
13
+ * - Legacy: [ [ [p1,p2,p3,p4], confidence ] ]
14
+ * - New: [ { obb: [p1,p2,p3,p4], confidence, card_in_frame?, cropped_sides? } ]
13
15
  * Returns confidence in [0,1] or null if not present.
14
16
  */
15
17
  export function getObbConfidence(cardObb: any): number | null {
16
18
  if (!cardObb || !Array.isArray(cardObb) || cardObb.length === 0) return null;
17
19
  const first = cardObb[0];
20
+
21
+ // New format: first is an object with a confidence field
22
+ if (first && typeof first === 'object' && 'confidence' in first) {
23
+ const score = (first as any).confidence;
24
+ return typeof score === 'number' ? score : null;
25
+ }
26
+
27
+ // Legacy format: [ [points], score ]
18
28
  if (!Array.isArray(first) || first.length < 2) return null;
19
29
  const score = first[1];
20
30
  return typeof score === 'number' ? score : null;
21
31
  }
22
32
 
33
+ /**
34
+ * Extracts the card_in_frame flag from card_obb when present.
35
+ * Returns:
36
+ * - true => card fully in frame
37
+ * - false => card not fully in frame
38
+ * - null => field not present (backward compatibility)
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
+ */
48
+ export function getCardInFrame(cardObb: any): boolean | null {
49
+ if (!cardObb) return null;
50
+
51
+ // card_obb can be an array of objects or a single object
52
+ const first = Array.isArray(cardObb) ? cardObb[0] : cardObb;
53
+
54
+ if (first && typeof first === 'object' && 'card_in_frame' in first) {
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;
68
+ }
69
+
70
+ // Backward compatibility check
71
+ if (cardObb && typeof cardObb === 'object' && 'card_in_frame' in cardObb) {
72
+ const value = (cardObb as any).card_in_frame;
73
+ return typeof value === 'boolean' ? value : null;
74
+ }
75
+
76
+ return null;
77
+ }
78
+
23
79
  // Compute axis-aligned bounding box from oriented quadrilateral
24
80
  function computeAabb(points: Point[]) {
25
81
  const xs = points.map(p => p[0]);
@@ -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;