@transfergratis/react-native-sdk 0.1.4 → 0.1.5
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/api/axios.d.ts +30 -0
- package/build/api/axios.d.ts.map +1 -0
- package/build/api/axios.js +92 -0
- package/build/api/axios.js.map +1 -0
- package/build/components/EnhancedCameraView.d.ts +1 -41
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +75 -34
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts +1 -41
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +28 -4
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +2 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +71 -114
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +7 -3
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts +7 -2
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +253 -104
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/InitializationStep.d.ts +5 -0
- package/build/components/KYCElements/InitializationStep.d.ts.map +1 -0
- package/build/components/KYCElements/InitializationStep.js +41 -0
- package/build/components/KYCElements/InitializationStep.js.map +1 -0
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +15 -13
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/ReviewSubmitTemplate.d.ts +12 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.js +171 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.js.map +1 -0
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +6 -2
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +105 -35
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/VerificationProgressTemplate.d.ts +12 -0
- package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/VerificationProgressTemplate.js +93 -0
- package/build/components/KYCElements/VerificationProgressTemplate.js.map +1 -0
- package/build/components/OverLay/IdCard.d.ts +1 -1
- package/build/components/OverLay/IdCard.d.ts.map +1 -1
- package/build/components/OverLay/IdCard.js +10 -6
- package/build/components/OverLay/IdCard.js.map +1 -1
- package/build/components/OverLay/SelfieOverlay.d.ts +1 -1
- package/build/components/OverLay/SelfieOverlay.d.ts.map +1 -1
- package/build/components/OverLay/SelfieOverlay.js +5 -4
- package/build/components/OverLay/SelfieOverlay.js.map +1 -1
- package/build/components/OverLay/type.d.ts +71 -1
- package/build/components/OverLay/type.d.ts.map +1 -1
- package/build/components/OverLay/type.js.map +1 -1
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +72 -197
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +63 -39
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/OrientationVideoExample.d.ts.map +1 -1
- package/build/components/example/OrientationVideoExample.js +1 -5
- package/build/components/example/OrientationVideoExample.js.map +1 -1
- package/build/config/countriesData.d.ts +3 -0
- package/build/config/countriesData.d.ts.map +1 -0
- package/build/config/countriesData.js +79 -0
- package/build/config/countriesData.js.map +1 -0
- package/build/config/region_mapping.d.ts +3 -0
- package/build/config/region_mapping.d.ts.map +1 -0
- package/build/config/region_mapping.js +687 -0
- package/build/config/region_mapping.js.map +1 -0
- package/build/hooks/useI18n.d.ts +11 -0
- package/build/hooks/useI18n.d.ts.map +1 -0
- package/build/hooks/useI18n.js +37 -0
- package/build/hooks/useI18n.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +1 -2
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +2 -1
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useRealtimeVerifier.d.ts +28 -0
- package/build/hooks/useRealtimeVerifier.d.ts.map +1 -0
- package/build/hooks/useRealtimeVerifier.js +91 -0
- package/build/hooks/useRealtimeVerifier.js.map +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +337 -38
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/i18n/en/index.d.ts +168 -0
- package/build/i18n/en/index.d.ts.map +1 -0
- package/build/i18n/en/index.js +195 -0
- package/build/i18n/en/index.js.map +1 -0
- package/build/i18n/fr/index.d.ts +168 -0
- package/build/i18n/fr/index.d.ts.map +1 -0
- package/build/i18n/fr/index.js +194 -0
- package/build/i18n/fr/index.js.map +1 -0
- package/build/i18n/index.d.ts +10 -0
- package/build/i18n/index.d.ts.map +1 -0
- package/build/i18n/index.js +56 -0
- package/build/i18n/index.js.map +1 -0
- package/build/i18n/types.d.ts +153 -0
- package/build/i18n/types.d.ts.map +1 -0
- package/build/i18n/types.js +3 -0
- package/build/i18n/types.js.map +1 -0
- package/build/i18n/usage-example.d.ts +4 -0
- package/build/i18n/usage-example.d.ts.map +1 -0
- package/build/i18n/usage-example.js +189 -0
- package/build/i18n/usage-example.js.map +1 -0
- package/build/modules/api/CardAuthentification.d.ts +22 -0
- package/build/modules/api/CardAuthentification.d.ts.map +1 -0
- package/build/modules/api/CardAuthentification.js +107 -0
- package/build/modules/api/CardAuthentification.js.map +1 -0
- package/build/modules/api/KYCService.d.ts +57 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +348 -27
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -0
- package/build/modules/api/SelfieVerification.d.ts.map +1 -0
- package/build/modules/api/SelfieVerification.js +9 -0
- package/build/modules/api/SelfieVerification.js.map +1 -0
- package/build/modules/api/backendApi.d.ts +2 -0
- package/build/modules/api/backendApi.d.ts.map +1 -0
- package/build/modules/api/backendApi.js +6 -0
- package/build/modules/api/backendApi.js.map +1 -0
- package/build/modules/api/types.d.ts +20 -0
- package/build/modules/api/types.d.ts.map +1 -0
- package/build/modules/api/types.js +2 -0
- package/build/modules/api/types.js.map +1 -0
- package/build/types/KYC.types.d.ts +59 -7
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +9 -1
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.d.ts +11 -0
- package/build/utils/cropByObb.d.ts.map +1 -0
- package/build/utils/cropByObb.js +78 -0
- package/build/utils/cropByObb.js.map +1 -0
- package/build/utils/get-document-type-info.d.ts +13 -0
- package/build/utils/get-document-type-info.d.ts.map +1 -0
- package/build/utils/get-document-type-info.js +59 -0
- package/build/utils/get-document-type-info.js.map +1 -0
- package/build/utils/pathToBase64.d.ts +3 -0
- package/build/utils/pathToBase64.d.ts.map +1 -0
- package/build/utils/pathToBase64.js +47 -0
- package/build/utils/pathToBase64.js.map +1 -0
- package/build/utils/remove-duplicate.d.ts +2 -0
- package/build/utils/remove-duplicate.d.ts.map +1 -0
- package/build/utils/remove-duplicate.js +4 -0
- package/build/utils/remove-duplicate.js.map +1 -0
- package/package.json +3 -1
- package/src/api/axios.ts +144 -0
- package/src/components/EnhancedCameraView.tsx +96 -78
- package/src/components/EnhancedCameraView.web.tsx +41 -40
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +104 -136
- package/src/components/KYCElements/FileUploadTemplate.tsx +14 -8
- package/src/components/KYCElements/IDCardCapture.tsx +311 -115
- package/src/components/KYCElements/InitializationStep.tsx +53 -0
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +17 -15
- package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
- package/src/components/KYCElements/ReviewSubmitTemplate.tsx +201 -0
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +140 -53
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +123 -0
- package/src/components/OverLay/IdCard.tsx +17 -9
- package/src/components/OverLay/SelfieOverlay.tsx +6 -5
- package/src/components/OverLay/type.ts +64 -2
- package/src/components/TemplateKYCExample.tsx +76 -197
- package/src/components/TemplateKYCFlowRefactored.tsx +74 -46
- package/src/components/example/OrientationVideoExample.tsx +3 -7
- package/src/config/countriesData.ts +84 -0
- package/src/config/region_mapping.ts +688 -0
- package/src/hooks/useI18n.ts +53 -0
- package/src/hooks/useOrientationVideo.ts +2 -2
- package/src/hooks/useRealtimeVerifier.ts +128 -0
- package/src/hooks/useTemplateKYCFlow.tsx +375 -53
- package/src/i18n/README.md +288 -0
- package/src/i18n/en/index.ts +206 -0
- package/src/i18n/fr/index.ts +205 -0
- package/src/i18n/index.ts +65 -0
- package/src/i18n/types.ts +172 -0
- package/src/i18n/usage-example.tsx +202 -0
- package/src/modules/api/CardAuthentification.ts +114 -0
- package/src/modules/api/KYCService.ts +403 -30
- package/src/modules/api/SelfieVerification.ts +11 -0
- package/src/modules/api/backendApi.ts +8 -0
- package/src/modules/api/types.ts +24 -0
- package/src/types/KYC.types.ts +83 -14
- package/src/utils/cropByObb.ts +99 -0
- package/src/utils/get-document-type-info.ts +62 -0
- package/src/utils/pathToBase64.ts +47 -0
- package/src/utils/remove-duplicate.ts +3 -0
- package/src/types/nativewind.d.ts +0 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import kycService, { authentification, errorMessage } from "./KYCService";
|
|
2
|
+
import { cropByObb } from "../../utils/cropByObb";
|
|
3
|
+
import { IBbox } from "../../types/KYC.types";
|
|
4
|
+
|
|
5
|
+
export async function frontVerification(result: { path?: string, regionMapping: string[], selectedDocumentType: string, code: string, currentSide: string, }) {
|
|
6
|
+
try {
|
|
7
|
+
|
|
8
|
+
console.log("Front verification", JSON.stringify({ result }, null, 2));
|
|
9
|
+
const token = await authentification();
|
|
10
|
+
const detected = await kycService.detectFaceOnId(result?.path || '', token, result?.selectedDocumentType || '')
|
|
11
|
+
|
|
12
|
+
if (!detected.result) {
|
|
13
|
+
throw new Error('Aucun visage détecté sur la carte. Veuillez reprendre.');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Optional: crop image using card_obb for better MRZ/barcode extraction
|
|
17
|
+
let croppedBase64: string | undefined;
|
|
18
|
+
let bbox: IBbox | undefined;
|
|
19
|
+
let mrz: any | undefined;
|
|
20
|
+
try {
|
|
21
|
+
const crop = await cropByObb(result?.path || '', (detected as any).card_obb);
|
|
22
|
+
croppedBase64 = crop.base64;
|
|
23
|
+
bbox = crop.bbox;
|
|
24
|
+
} catch { }
|
|
25
|
+
|
|
26
|
+
if (result.regionMapping.length > 0 && result.regionMapping.includes('MRZ')) {
|
|
27
|
+
mrz = await kycService.extractMrzText({ fileUri: result.path || '', docType: result?.selectedDocumentType || '', docRegion: result?.code || "", postfix: result?.currentSide, token: token })
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { ...detected, croppedBase64, bbox, ...(mrz ? { mrz } : {}) };
|
|
32
|
+
} catch (e: any) {
|
|
33
|
+
console.error('Error front verification:', JSON.stringify(errorMessage(e), null, 2));
|
|
34
|
+
throw new Error(e?.message || 'Erreur de détection du visage');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function backVerification(result: { path?: string, regionMapping: string[], selectedDocumentType: string, code: string, currentSide: string, }) {
|
|
39
|
+
try {
|
|
40
|
+
if (!result.path) throw new Error('No path provided');
|
|
41
|
+
console.log("result.regionMapping", result.regionMapping, result.currentSide, result.code);
|
|
42
|
+
const token = await authentification();
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Fonction helper pour essayer MRZ puis barcode en fallback
|
|
47
|
+
const tryMrzWithBarcodeFallback = async () => {
|
|
48
|
+
try {
|
|
49
|
+
console.log("Tentative d'extraction MRZ");
|
|
50
|
+
const mrz = await kycService.extractMrzText({
|
|
51
|
+
fileUri: result.path!,
|
|
52
|
+
docType: result?.selectedDocumentType || '',
|
|
53
|
+
docRegion: result?.code || '',
|
|
54
|
+
postfix: 'back',
|
|
55
|
+
token: token
|
|
56
|
+
});
|
|
57
|
+
return mrz;
|
|
58
|
+
} catch (mrzError: any) {
|
|
59
|
+
console.log("MRZ échoué, tentative d'extraction barcode");
|
|
60
|
+
try {
|
|
61
|
+
const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
|
|
62
|
+
return barcode;
|
|
63
|
+
} catch (barcodeError: any) {
|
|
64
|
+
throw new Error(`MRZ et barcode ont échoué. MRZ: ${mrzError?.message}, Barcode: ${barcodeError?.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if (result.regionMapping.length > 2) {
|
|
72
|
+
return await tryMrzWithBarcodeFallback();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (result.regionMapping.length > 0 && result.regionMapping.includes('MRZ')) {
|
|
76
|
+
try {
|
|
77
|
+
const mrz = await kycService.extractMrzText({
|
|
78
|
+
fileUri: result.path!,
|
|
79
|
+
docType: result?.selectedDocumentType || '',
|
|
80
|
+
docRegion: result?.code || '',
|
|
81
|
+
postfix: 'back',
|
|
82
|
+
token: token
|
|
83
|
+
});
|
|
84
|
+
let bbox: IBbox | undefined;
|
|
85
|
+
try {
|
|
86
|
+
const crop = await cropByObb(result?.path || '', (mrz as any).card_obb);
|
|
87
|
+
bbox = crop.bbox;
|
|
88
|
+
} catch { }
|
|
89
|
+
return { ...mrz, bbox };
|
|
90
|
+
} catch (mrzError: any) {
|
|
91
|
+
throw new Error(`MRZ et barcode ont échoué. MRZ: ${mrzError?.message}, Barcode: ${mrzError?.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (result.regionMapping.length > 0 && result.regionMapping.includes('2D_barcode')) {
|
|
96
|
+
try {
|
|
97
|
+
console.log("Tentative d'extraction barcode");
|
|
98
|
+
const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
|
|
99
|
+
let bbox: IBbox | undefined;
|
|
100
|
+
try {
|
|
101
|
+
const crop = await cropByObb(result?.path || '', (barcode as any).card_obb);
|
|
102
|
+
bbox = crop.bbox;
|
|
103
|
+
} catch { }
|
|
104
|
+
return { ...barcode, bbox };
|
|
105
|
+
} catch (barcodeError: any) {
|
|
106
|
+
throw new Error(`Barcode et MRZ ont échoué. Barcode: ${barcodeError?.message}, MRZ: ${barcodeError?.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
} catch (e: any) {
|
|
111
|
+
throw new Error(e?.message || 'Erreur de détection du MRZ ou barcode');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { OrientationVideoResponse } from '../../types/KYC.types';
|
|
2
|
+
import { GovernmentDocumentType, GovernmentDocumentTypeShorted, OrientationVideoResponse } from '../../types/KYC.types';
|
|
3
|
+
import { ExtractMrzTextResponse } from '../../components/OverLay/type';
|
|
4
|
+
import { SessionResponse, VerificationSessionRequest } from './types';
|
|
3
5
|
|
|
4
6
|
export interface KYCRequest {
|
|
5
7
|
userId: string;
|
|
@@ -26,9 +28,26 @@ export interface FaceDetectionResponse {
|
|
|
26
28
|
message: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
export interface SelfieVideoResponse {
|
|
32
|
+
orientation_direction: "center" | "left" | "right",
|
|
33
|
+
turn_score: number;
|
|
34
|
+
bbox: number[];
|
|
35
|
+
capture: boolean;
|
|
36
|
+
instruction: string;
|
|
37
|
+
error: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
29
41
|
export class KYCService {
|
|
30
42
|
private baseURL: string;
|
|
31
43
|
private apiKey: string;
|
|
44
|
+
// Additional service base URLs (fixed as per current infra)
|
|
45
|
+
private faceServiceURL = 'http://kyc-ecs-alb-278715158.us-east-2.elb.amazonaws.com:8000';
|
|
46
|
+
private textExtractionServiceURL = 'http://kyc-ecs-alb-278715158.us-east-2.elb.amazonaws.com:8006';
|
|
47
|
+
private mrzServiceURL = 'http://kyc-ecs-alb-278715158.us-east-2.elb.amazonaws.com:8002';
|
|
48
|
+
private barcodeServiceURL = 'http://kyc-ecs-alb-278715158.us-east-2.elb.amazonaws.com:8000';
|
|
49
|
+
private orientationServiceURL = 'http://18.188.180.154:8080';
|
|
50
|
+
private backendServiceURL = 'http://16.170.133.50/api/v1';
|
|
32
51
|
|
|
33
52
|
constructor(baseURL: string, apiKey: string) {
|
|
34
53
|
this.baseURL = baseURL;
|
|
@@ -97,40 +116,48 @@ export class KYCService {
|
|
|
97
116
|
}
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
async
|
|
119
|
+
async processSelfieOrientationPicture(videoFile: string, token: string): Promise<SelfieVideoResponse[]> {
|
|
101
120
|
try {
|
|
102
|
-
// Create FormData for multipart/form-data request
|
|
103
121
|
const formData = new FormData();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const response = await fetch(videoFile);
|
|
117
|
-
fileData = await response.blob();
|
|
122
|
+
const rnFile: any = {
|
|
123
|
+
uri: videoFile,
|
|
124
|
+
type: 'image/jpeg',
|
|
125
|
+
name: 'selfie_picture.jpg',
|
|
126
|
+
};
|
|
127
|
+
formData.append('file', rnFile);
|
|
128
|
+
const response = await axios.post<SelfieVideoResponse[]>(
|
|
129
|
+
`${this.faceServiceURL}/detect_face_orientation/`,
|
|
130
|
+
formData,
|
|
131
|
+
{
|
|
132
|
+
timeout: 20000,
|
|
133
|
+
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
|
|
118
134
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
);
|
|
136
|
+
return response.data;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error processing selfie orientation:', error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
123
142
|
|
|
124
|
-
|
|
143
|
+
async processOrientationVideo(videoFile: string): Promise<OrientationVideoResponse> {
|
|
144
|
+
try {
|
|
145
|
+
// Create FormData for multipart/form-data request
|
|
146
|
+
const formData = new FormData();
|
|
147
|
+
// In React Native, prefer the { uri, type, name } pattern rather than Blob
|
|
148
|
+
const rnFile: any = {
|
|
149
|
+
uri: videoFile,
|
|
150
|
+
type: 'video/mp4',
|
|
151
|
+
name: 'orientation_video.mp4',
|
|
152
|
+
};
|
|
153
|
+
formData.append('file', rnFile);
|
|
125
154
|
|
|
126
155
|
const response = await axios.post(
|
|
127
|
-
`${this.
|
|
156
|
+
`${this.orientationServiceURL}/process_orientation_video_stream/`,
|
|
128
157
|
formData,
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
'Content-Type': 'multipart/form-data',
|
|
133
|
-
}
|
|
158
|
+
{
|
|
159
|
+
// Let axios set the proper multipart boundary header automatically
|
|
160
|
+
timeout: 90000,
|
|
134
161
|
}
|
|
135
162
|
);
|
|
136
163
|
|
|
@@ -141,7 +168,7 @@ export class KYCService {
|
|
|
141
168
|
};
|
|
142
169
|
} catch (error: any) {
|
|
143
170
|
console.error('Error processing orientation video:', JSON.stringify(error, null, 2));
|
|
144
|
-
|
|
171
|
+
|
|
145
172
|
// Handle specific error cases
|
|
146
173
|
if (error.response?.status === 503) {
|
|
147
174
|
return {
|
|
@@ -166,6 +193,352 @@ export class KYCService {
|
|
|
166
193
|
};
|
|
167
194
|
}
|
|
168
195
|
}
|
|
196
|
+
|
|
197
|
+
// STEP 1 - ID CARD VALIDATION
|
|
198
|
+
async detectFaceOnId(idCardImageUri: string, token: string, docType: string): Promise<{ result: boolean, detail: any[] }> {
|
|
199
|
+
const formData = new FormData();
|
|
200
|
+
const rnFile: any = { uri: idCardImageUri, type: 'image/jpeg', name: 'id_card_photo.jpg' };
|
|
201
|
+
formData.append('file', rnFile);
|
|
202
|
+
|
|
203
|
+
const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const res = await axios.post(`${this.faceServiceURL}/detect_face/?doc_type=${docTypeShorted}`, formData,
|
|
207
|
+
{
|
|
208
|
+
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
|
|
209
|
+
timeout: 20000
|
|
210
|
+
});
|
|
211
|
+
console.log('detectFaceOnId res', JSON.stringify(res.data, null, 2));
|
|
212
|
+
|
|
213
|
+
if (res.data?.result) return res.data;
|
|
214
|
+
throw new Error(res.data?.detail || 'detect_face failed');
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('Error detecting face on id:', JSON.stringify(error));
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async extractDocumentInformation(params: { fileUri: string; docType: string; docRegion: string; token: string }): Promise<any> {
|
|
222
|
+
const { fileUri, docType, docRegion, token } = params;
|
|
223
|
+
const formData = new FormData();
|
|
224
|
+
const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_front.jpg' };
|
|
225
|
+
formData.append('file', rnFile);
|
|
226
|
+
|
|
227
|
+
const url = `${this.textExtractionServiceURL}/extract_doc_information/?doc_type=${encodeURIComponent(docType)}&doc_region=${encodeURIComponent(docRegion)}`;
|
|
228
|
+
const attempt = async () => {
|
|
229
|
+
const res = await axios.post(url, formData, {
|
|
230
|
+
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
|
|
231
|
+
timeout: 60000,
|
|
232
|
+
});
|
|
233
|
+
console.log('extractDocumentInformation res', JSON.stringify(res));
|
|
234
|
+
|
|
235
|
+
if (res.data?.detail) throw new Error(res.data.detail);
|
|
236
|
+
return res.data;
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
return await attempt();
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error('Error extracting document information:', JSON.stringify(e));
|
|
242
|
+
throw e;
|
|
243
|
+
// await new Promise(r => setTimeout(r, 1500));
|
|
244
|
+
// return await attempt();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// STEP 2 - barcode extraction
|
|
248
|
+
async extractBarcode(params: { fileUri: string; token: string }): Promise<any> {
|
|
249
|
+
const { fileUri, token } = params;
|
|
250
|
+
const formData = new FormData();
|
|
251
|
+
const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_back.jpg' };
|
|
252
|
+
formData.append('file', rnFile);
|
|
253
|
+
|
|
254
|
+
const url = `${this.barcodeServiceURL}/decode_barcode/`;
|
|
255
|
+
const attempt = async () => {
|
|
256
|
+
try {
|
|
257
|
+
const res = await axios.post(url, formData, {
|
|
258
|
+
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
|
|
259
|
+
timeout: 60000,
|
|
260
|
+
});
|
|
261
|
+
console.log('extractBarcode res', JSON.stringify(res.data));
|
|
262
|
+
//check if res.data has aleast one key
|
|
263
|
+
if (Object.keys(res.data).length === 0) throw new Error('No data found');
|
|
264
|
+
return res.data;
|
|
265
|
+
} catch (e: any) {
|
|
266
|
+
throw new Error(e?.message || 'Erreur de détection du barcode');
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
try {
|
|
270
|
+
return await attempt();
|
|
271
|
+
} catch (e) {
|
|
272
|
+
console.error('Error extracting Barcode text:', JSON.stringify(e));
|
|
273
|
+
throw e;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// STEP 3 - MRZ TEXT EXTRACTION
|
|
277
|
+
async extractMrzText(params: { fileUri: string; docType: string; docRegion: string; postfix?: string; token: string }): Promise<any> {
|
|
278
|
+
const { fileUri, docType, docRegion, postfix = 'back', token } = params;
|
|
279
|
+
const formData = new FormData();
|
|
280
|
+
const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_back.jpg' };
|
|
281
|
+
formData.append('file', rnFile);
|
|
282
|
+
const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
|
|
283
|
+
console.log("docTypeShorted", docTypeShorted, docRegion, postfix);
|
|
284
|
+
|
|
285
|
+
const url = `${this.mrzServiceURL}/extract_mrz_text/?doc_type=${docTypeShorted}&doc_region=${docRegion}&postfix=${postfix}`;
|
|
286
|
+
console.log("url", url);
|
|
287
|
+
|
|
288
|
+
const attempt = async () => {
|
|
289
|
+
try {
|
|
290
|
+
const res = await axios.post<ExtractMrzTextResponse>(url, formData, {
|
|
291
|
+
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${token}`, },
|
|
292
|
+
timeout: 60000,
|
|
293
|
+
});
|
|
294
|
+
console.log('extractMrzText res', JSON.stringify(res.data));
|
|
295
|
+
//check if res.data has aleast one key
|
|
296
|
+
if (Object.keys(res.data).length === 0) throw new Error('No data found');
|
|
297
|
+
if (res.data?.success === false) throw new Error(res.data.parsed_data.status);
|
|
298
|
+
return res.data;
|
|
299
|
+
} catch (e: any) {
|
|
300
|
+
throw new Error(e?.message || 'Erreur de détection du MRZ');
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
try {
|
|
304
|
+
return await attempt();
|
|
305
|
+
} catch (e) {
|
|
306
|
+
console.error('Error extracting MRZ text:', JSON.stringify(errorMessage(e), null, 2));
|
|
307
|
+
throw e;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// STEP 2 - SELFIE VALIDATION
|
|
312
|
+
async recognizeFace(params: { idPhotoUri: string; selfiePhotoUri: string }): Promise<{ is_match: boolean; similarity: number; id_bbox?: number[]; selfie_bbox?: number[] }> {
|
|
313
|
+
const { idPhotoUri, selfiePhotoUri } = params;
|
|
314
|
+
const formData = new FormData();
|
|
315
|
+
const idFile: any = { uri: idPhotoUri, type: 'image/jpeg', name: 'id_card_photo.jpg' };
|
|
316
|
+
const selfieFile: any = { uri: selfiePhotoUri, type: 'image/jpeg', name: 'selfie_final.jpg' };
|
|
317
|
+
formData.append('id_photo', idFile);
|
|
318
|
+
formData.append('selfie_photo', selfieFile);
|
|
319
|
+
|
|
320
|
+
const res = await axios.post(`${this.faceServiceURL}/recognize_face/`, formData, { timeout: 45000 });
|
|
321
|
+
if (res.data?.detail) throw new Error(res.data.detail);
|
|
322
|
+
return res.data;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// HEALTH CHECKS
|
|
326
|
+
async healthFaceDetection(): Promise<any> {
|
|
327
|
+
const res = await axios.get(`${this.faceServiceURL}/health`);
|
|
328
|
+
return res.data;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async healthTextExtraction(): Promise<any> {
|
|
332
|
+
const res = await axios.get(`${this.textExtractionServiceURL}/health`);
|
|
333
|
+
return res.data;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async healthMrzService(): Promise<any> {
|
|
337
|
+
const res = await axios.get(`${this.mrzServiceURL}/health`);
|
|
338
|
+
return res.data;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async healthOrientationService(): Promise<any> {
|
|
342
|
+
const res = await axios.get(`${this.orientationServiceURL}/health`);
|
|
343
|
+
return res.data;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async newSession(token: string): Promise<SessionResponse> {
|
|
347
|
+
try {
|
|
348
|
+
const data = {
|
|
349
|
+
"status": "PENDING",
|
|
350
|
+
"metadata": {},
|
|
351
|
+
}
|
|
352
|
+
const res = await axios.post<SessionResponse>(`${this.backendServiceURL}/verification/sessions/`,
|
|
353
|
+
data, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } });
|
|
354
|
+
return res.data;
|
|
355
|
+
} catch (error: any) {
|
|
356
|
+
console.error('Error creating session:', JSON.stringify(errorMessage(error), null, 2));
|
|
357
|
+
|
|
358
|
+
// Extract backend error message if available
|
|
359
|
+
const backendMessage = errorMessage(error);
|
|
360
|
+
|
|
361
|
+
if (backendMessage) {
|
|
362
|
+
console.error('Backend error message:', JSON.stringify(backendMessage, null, 2));
|
|
363
|
+
throw new Error(`Backend error: ${JSON.stringify(backendMessage)}`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async verificationSession(payload: VerificationSessionRequest): Promise<any> {
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
|
|
374
|
+
const token = await authentification();
|
|
375
|
+
|
|
376
|
+
// /api/v1/verification/api/kyc/sessions/{session_id}/steps/{step}/
|
|
377
|
+
const { session_id, step, data, templateId, action } = payload;
|
|
378
|
+
// const session_id = "kyc-8b4e069258d8";
|
|
379
|
+
const payloadData = {
|
|
380
|
+
action: action,
|
|
381
|
+
data: {
|
|
382
|
+
step,
|
|
383
|
+
templateId,
|
|
384
|
+
...(action === "location_permission" ? { permissionGranted: true, } : {}),
|
|
385
|
+
...data,
|
|
386
|
+
},
|
|
387
|
+
...({ session_id: session_id }),
|
|
388
|
+
timestamp: new Date().toISOString()
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
const logPayload = truncateFields({ payloadData, session_id, step });
|
|
394
|
+
console.log('verificationSession payload',
|
|
395
|
+
JSON.stringify({ logPayload, token, path: `${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/` },
|
|
396
|
+
null, 2));
|
|
397
|
+
|
|
398
|
+
// return Promise.resolve({ success: true, message: 'Verification session successful' });
|
|
399
|
+
// if (action === "document_upload") {
|
|
400
|
+
// // Upload un document à la fois pour éviter les timeouts
|
|
401
|
+
// const documents = data.documents;
|
|
402
|
+
// const sides = Object.keys(documents);
|
|
403
|
+
// const results = [];
|
|
404
|
+
|
|
405
|
+
// for (const side of sides) {
|
|
406
|
+
// console.log(`📤 Uploading ${side} document...`);
|
|
407
|
+
// const startTime = Date.now();
|
|
408
|
+
|
|
409
|
+
// const formData = new FormData();
|
|
410
|
+
// formData.append('action', 'document_upload');
|
|
411
|
+
// formData.append('session_id', session_id);
|
|
412
|
+
// formData.append('timestamp', new Date().toISOString());
|
|
413
|
+
// formData.append('side', side);
|
|
414
|
+
|
|
415
|
+
// const fileUri = documents[side].dir;
|
|
416
|
+
// const rnFile: any = {
|
|
417
|
+
// uri: fileUri,
|
|
418
|
+
// type: 'image/jpeg',
|
|
419
|
+
// name: `id_card_${side}.jpg`
|
|
420
|
+
// };
|
|
421
|
+
// formData.append('front_image', rnFile);
|
|
422
|
+
|
|
423
|
+
// // Ajouter MRZ si disponible
|
|
424
|
+
// if (documents[side].mrz) {
|
|
425
|
+
// formData.append('mrz', documents[side].mrz);
|
|
426
|
+
// }
|
|
427
|
+
|
|
428
|
+
// try {
|
|
429
|
+
// const res = await axios.post<SessionResponse>(
|
|
430
|
+
// `${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`,
|
|
431
|
+
// formData,
|
|
432
|
+
// {
|
|
433
|
+
// headers: {
|
|
434
|
+
// 'Authorization': `Bearer ${token}`
|
|
435
|
+
// // Pas de Content-Type, laissez axios le gérer
|
|
436
|
+
// },
|
|
437
|
+
// timeout: 30000, // 30 secondes max
|
|
438
|
+
// maxContentLength: 50 * 1024 * 1024, // 50MB max
|
|
439
|
+
// maxBodyLength: 50 * 1024 * 1024
|
|
440
|
+
// }
|
|
441
|
+
// );
|
|
442
|
+
|
|
443
|
+
// const uploadTime = Date.now() - startTime;
|
|
444
|
+
// console.log(`✅ ${side} uploaded in ${uploadTime}ms:`, res.data);
|
|
445
|
+
// results.push({ side, result: res.data, uploadTime });
|
|
446
|
+
|
|
447
|
+
// } catch (error) {
|
|
448
|
+
// console.error(`❌ ${side} upload failed:`, error);
|
|
449
|
+
// throw new Error(`Upload failed for ${side}: ${error}`);
|
|
450
|
+
// }
|
|
451
|
+
// }
|
|
452
|
+
|
|
453
|
+
// return {
|
|
454
|
+
// success: true,
|
|
455
|
+
// message: 'All documents uploaded successfully',
|
|
456
|
+
// results
|
|
457
|
+
// };
|
|
458
|
+
// }
|
|
459
|
+
|
|
460
|
+
const res = await axios.post<SessionResponse>(`${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`,
|
|
461
|
+
payloadData,
|
|
462
|
+
{
|
|
463
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }
|
|
464
|
+
});
|
|
465
|
+
console.log('verificationSession res', JSON.stringify(res.data, null, 2));
|
|
466
|
+
return res.data;
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.error('Error validating component:', JSON.stringify(errorMessage(error), null, 2));
|
|
471
|
+
throw new Error(errorMessage(error));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
const kycService = new KYCService("", "");
|
|
478
|
+
export default kycService;
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
// Pour éviter d'afficher des champs trop longs, on tronque les valeurs longues dans le log
|
|
482
|
+
export function truncateFields(obj: any, maxLength = 420): any {
|
|
483
|
+
if (typeof obj !== 'object' || obj === null) return obj;
|
|
484
|
+
if (Array.isArray(obj)) return obj.map(item => truncateFields(item, maxLength));
|
|
485
|
+
const truncated: any = {};
|
|
486
|
+
for (const key in obj) {
|
|
487
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
488
|
+
const value = obj[key];
|
|
489
|
+
if (typeof value === 'string' && value.length > maxLength) {
|
|
490
|
+
truncated[key] = value.slice(0, maxLength) + '... [truncated]';
|
|
491
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
492
|
+
truncated[key] = truncateFields(value, maxLength);
|
|
493
|
+
} else {
|
|
494
|
+
truncated[key] = value;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return truncated;
|
|
498
|
+
}
|
|
499
|
+
export const authentification = async () => {
|
|
500
|
+
try {
|
|
501
|
+
const params = new URLSearchParams();
|
|
502
|
+
params.append('client_id', 'kyc_frontend');
|
|
503
|
+
params.append('client_secret', 'ZCW4mGaJXWR0UuI40lM1pHNQmYLw2xvX');
|
|
504
|
+
params.append('grant_type', 'client_credentials');
|
|
505
|
+
const res = await axios.post(`http://16.170.133.50:7080/realms/kyc/protocol/openid-connect/token`,
|
|
506
|
+
params,
|
|
507
|
+
{
|
|
508
|
+
headers: {
|
|
509
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
510
|
+
},
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
console.log('authentification res', JSON.stringify(res.data));
|
|
514
|
+
return res.data.access_token;
|
|
515
|
+
} catch (error: any) {
|
|
516
|
+
console.error('Error authentifying:', {
|
|
517
|
+
message: error.message,
|
|
518
|
+
status: error.response?.status,
|
|
519
|
+
statusText: error.response?.statusText,
|
|
520
|
+
data: error.response?.data,
|
|
521
|
+
url: error.config?.url,
|
|
522
|
+
method: error.config?.method
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Extract backend error message if available
|
|
526
|
+
const backendMessage = errorMessage(error);
|
|
527
|
+
|
|
528
|
+
if (backendMessage) {
|
|
529
|
+
console.error('Backend authentication error:', JSON.stringify(backendMessage, null, 2));
|
|
530
|
+
throw new Error(`Authentication failed: ${JSON.stringify(backendMessage)}`);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
169
535
|
}
|
|
170
536
|
|
|
171
|
-
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
export const errorMessage = (error: any) => {
|
|
540
|
+
return error.response?.data?.message ||
|
|
541
|
+
error.response?.data?.detail ||
|
|
542
|
+
error.response?.data?.error ||
|
|
543
|
+
error.response?.data;
|
|
544
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import kycService, { authentification } from "./KYCService";
|
|
2
|
+
|
|
3
|
+
const selfieVerification = async (selfieImage: string) => {
|
|
4
|
+
const token = await authentification();
|
|
5
|
+
|
|
6
|
+
const response = await kycService.processSelfieOrientationPicture(selfieImage, token);
|
|
7
|
+
console.log("selfieVerification response", JSON.stringify(response, null, 2));
|
|
8
|
+
return response;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default selfieVerification;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface SessionResponse {
|
|
2
|
+
id: number;
|
|
3
|
+
session_id: string;
|
|
4
|
+
status: string;
|
|
5
|
+
created_at: string;
|
|
6
|
+
updated_at: string;
|
|
7
|
+
metadata: string;
|
|
8
|
+
user: number;
|
|
9
|
+
module: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export interface VerificationSessionRequest {
|
|
14
|
+
step: number;
|
|
15
|
+
data:VerificationSessionData;
|
|
16
|
+
templateId: string | null;
|
|
17
|
+
session_id: string;
|
|
18
|
+
token: string;
|
|
19
|
+
action:string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// dynamic type
|
|
24
|
+
export type VerificationSessionData = Record<string, any>;
|