@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.
- 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 +113 -49
- 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 +360 -104
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +3 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +25 -24
- 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 +8 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +20 -3
- 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 +179 -109
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
- package/src/hooks/useTemplateKYCFlow.tsx +1 -1
- package/src/modules/api/CardAuthentification.ts +450 -113
- package/src/modules/api/KYCService.ts +52 -39
- package/src/modules/camera/VisionCameraModule.ts +2 -2
- package/src/utils/cropByObb.ts +22 -3
- 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,21 @@ export class KYCService {
|
|
|
350
353
|
throw e;
|
|
351
354
|
}
|
|
352
355
|
}
|
|
356
|
+
|
|
353
357
|
// STEP 3 - MRZ TEXT EXTRACTION
|
|
354
|
-
async extractMrzText(
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
@@ -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
|
|
48
|
-
|
|
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
|
-
//
|
|
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
|
|
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;
|