@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.
- package/build/components/EnhancedCameraView.js +1 -1
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +13 -42
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +76 -35
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +2 -2
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +1 -1
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +15 -7
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +215 -44
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +2 -0
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +16 -19
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/camera/VisionCameraModule.js +2 -2
- package/build/modules/camera/VisionCameraModule.js.map +1 -1
- package/build/utils/cropByObb.d.ts +19 -1
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +49 -1
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/pathToBase64.js +1 -1
- package/build/utils/pathToBase64.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +1 -1
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +24 -52
- package/src/components/KYCElements/IDCardCapture.tsx +129 -90
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
- package/src/hooks/useTemplateKYCFlow.tsx +1 -1
- package/src/modules/api/CardAuthentification.ts +290 -58
- package/src/modules/api/KYCService.ts +25 -33
- package/src/modules/camera/VisionCameraModule.ts +2 -2
- package/src/utils/cropByObb.ts +57 -1
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
84
|
+
const LegacyFileSystem = require('expo-file-system');
|
|
85
85
|
if (LegacyFileSystem && LegacyFileSystem.documentDirectory) {
|
|
86
86
|
return LegacyFileSystem.documentDirectory;
|
|
87
87
|
}
|
package/src/utils/cropByObb.ts
CHANGED
|
@@ -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:
|
|
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
|
|
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;
|