@sanctum-key/react-native-sdk 1.0.47 → 1.0.48
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/package.json +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +84 -23
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +88 -41
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js +15 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +37 -59
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +7 -2
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/types/KYC.types.d.ts +1 -0
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/IDCardCapture.tsx +98 -24
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +91 -44
- package/src/hooks/useTemplateKYCFlow.tsx +19 -1
- package/src/modules/api/CardAuthentification.ts +205 -238
- package/src/modules/api/KYCService.ts +9 -2
- package/src/types/KYC.types.ts +1 -0
|
@@ -23,173 +23,133 @@ interface ApiVerificationResponse {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export async function frontVerification(
|
|
26
|
-
result: {
|
|
27
|
-
path?: string,
|
|
28
|
-
regionMapping: { authMethod: string[], mrzTypes: string[] },
|
|
29
|
-
selectedDocumentType: string,
|
|
30
|
-
code: string,
|
|
31
|
-
currentSide: string,
|
|
32
|
-
templatePath?: string,
|
|
33
|
-
mrzType?: string,
|
|
34
|
-
issuingSubdivision?: string,
|
|
35
|
-
clientSideBarcodeData?: string // 🚨 ADDED: Accept the SDK barcode data
|
|
36
|
-
},
|
|
26
|
+
result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string, issuingSubdivision?: string, clientSideBarcodeData?: string },
|
|
37
27
|
env: KycEnvironment = 'PRODUCTION'
|
|
38
|
-
) {
|
|
28
|
+
) {
|
|
39
29
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!detected.result) {
|
|
82
|
-
throw new Error('Aucun visage détecté sur la carte. Veuillez reprendre.');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const cardData = detected.card_obb && Array.isArray(detected.card_obb) && detected.card_obb.length > 0
|
|
86
|
-
? detected.card_obb[0]
|
|
87
|
-
: null;
|
|
88
|
-
|
|
89
|
-
let points: number[][] | null = null;
|
|
90
|
-
console.log(points);
|
|
91
|
-
let isCardInFrame = true;
|
|
92
|
-
let hasCroppedSides = false;
|
|
93
|
-
|
|
94
|
-
if (cardData) {
|
|
95
|
-
points = cardData.obb;
|
|
96
|
-
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
97
|
-
isCardInFrame = cardData.card_in_frame === true;
|
|
98
|
-
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!isCardInFrame || hasCroppedSides) {
|
|
103
|
-
logger.log(`Front Framing failed. Cropped sides detected by backend.`);
|
|
104
|
-
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const obbConfidence = getObbConfidence(detected.card_obb);
|
|
108
|
-
if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
109
|
-
throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
|
|
30
|
+
console.log("Front verification START", JSON.stringify({ result }, null, 2));
|
|
31
|
+
logger.log("Front verification", JSON.stringify({ result }, null, 2));
|
|
32
|
+
|
|
33
|
+
const authMethods = Array.isArray(result.regionMapping?.authMethod) ? result.regionMapping.authMethod.map(m => String(m).toUpperCase()) : [];
|
|
34
|
+
const hasMrz = authMethods.some(m => m.includes('MRZ'));
|
|
35
|
+
const hasBarcode = authMethods.some(m => m.includes('BARCODE') || m.includes('2D'));
|
|
36
|
+
|
|
37
|
+
if (env === 'SANDBOX') {
|
|
38
|
+
console.log("SANDBOX mode: Skipping AI verification for front document");
|
|
39
|
+
const mockBbox: IBbox = { minX: 400, minY: 800, width: 2200, height: 1400 };
|
|
40
|
+
const mockResponse = {
|
|
41
|
+
result: true, detail: [{ confidence: 0.95 }],
|
|
42
|
+
card_obb: [{ obb: [[400, 800], [2600, 800], [2600, 2200], [400, 2200]], confidence: 0.95, card_in_frame: true, cropped_sides: [] }],
|
|
43
|
+
bbox: mockBbox,
|
|
44
|
+
...(hasMrz ? { mrz: { success: true, parsed_data: { status: 'SUCCESS', document_type: result.selectedDocumentType, mrz_type: result.mrzType || 'TD1' } } } : {}),
|
|
45
|
+
...(hasBarcode ? { barcode: { barcode_data: 'SANDBOX_MOCK_BARCODE' } } : {})
|
|
46
|
+
};
|
|
47
|
+
return mockResponse;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const token = await authentification();
|
|
51
|
+
const detected = await kycService.detectFaceOnId(
|
|
52
|
+
result?.path || '', token, result?.selectedDocumentType || '', env
|
|
53
|
+
) as ApiVerificationResponse;
|
|
54
|
+
|
|
55
|
+
if (!detected.result) {
|
|
56
|
+
throw new Error('Aucun visage détecté sur la carte. Veuillez reprendre.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cardData = detected.card_obb && Array.isArray(detected.card_obb) && detected.card_obb.length > 0 ? detected.card_obb[0] : null;
|
|
60
|
+
let points: number[][] | null = null;
|
|
61
|
+
console.log(points)
|
|
62
|
+
let isCardInFrame = true;
|
|
63
|
+
let hasCroppedSides = false;
|
|
64
|
+
|
|
65
|
+
if (cardData) {
|
|
66
|
+
points = cardData.obb;
|
|
67
|
+
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
68
|
+
isCardInFrame = cardData.card_in_frame === true;
|
|
69
|
+
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
110
70
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!isCardInFrame || hasCroppedSides) {
|
|
74
|
+
logger.log(`Front Framing failed. Cropped sides detected by backend.`);
|
|
75
|
+
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const obbConfidence = getObbConfidence(detected.card_obb);
|
|
79
|
+
if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
80
|
+
throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let croppedBase64: string | undefined;
|
|
84
|
+
let bbox: IBbox | undefined;
|
|
85
|
+
let mrz: any | undefined;
|
|
86
|
+
let barcode: any | undefined;
|
|
87
|
+
let documentData: any | undefined;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const crop = await cropByObb(result?.path || '', detected.card_obb);
|
|
91
|
+
croppedBase64 = crop.base64;
|
|
92
|
+
bbox = crop.bbox;
|
|
93
|
+
} catch { }
|
|
94
|
+
|
|
95
|
+
if (result.code === 'CA') {
|
|
118
96
|
try {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
fileUri: result.path || '',
|
|
129
|
-
docType: result.selectedDocumentType,
|
|
130
|
-
docRegion: result.code,
|
|
131
|
-
templatePath: result.templatePath || '',
|
|
132
|
-
issuingSubdivision: result.issuingSubdivision,
|
|
133
|
-
token: token,
|
|
134
|
-
postfix: result.currentSide || 'front'
|
|
135
|
-
});
|
|
136
|
-
if (documentData?.no_data || (documentData && documentData.data && Object.keys(documentData.data).length === 0)) {
|
|
137
|
-
logger.log("Front OCR Extraction Failed: Backend returned empty data dict.");
|
|
138
|
-
if (!hasBarcode && !hasMrz) {
|
|
139
|
-
throw new Error("Impossible de lire les informations du document. L'image est peut-être floue ou mal éclairée.");
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch (err: any) {
|
|
143
|
-
const errorMsg = typeof err === 'object' ? JSON.stringify(err) : err;
|
|
144
|
-
logger.log("Front OCR Extraction Error:", errorMsg);
|
|
145
|
-
throw new Error(err?.message || "Impossible d'extraire les données. Veuillez reprendre la photo.");
|
|
97
|
+
logger.log("Tentative d'extraction OCR (Front) - Canada Only");
|
|
98
|
+
documentData = await kycService.extractDocumentInformation({
|
|
99
|
+
fileUri: result.path || '', docType: result.selectedDocumentType, docRegion: result.code, templatePath: result.templatePath || '', issuingSubdivision: result.issuingSubdivision, token: token, postfix: result.currentSide || 'front'
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (documentData?.no_data || (documentData && documentData.data && Object.keys(documentData.data).length === 0)) {
|
|
103
|
+
logger.log("Front OCR Extraction Failed: Backend returned empty data dict.");
|
|
104
|
+
if (!hasBarcode && !hasMrz) {
|
|
105
|
+
throw new Error("Impossible de lire les informations du document. L'image est peut-être floue ou mal éclairée.");
|
|
146
106
|
}
|
|
107
|
+
}
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
// 🚨 FIX: Allow 401/403 errors to bubble up directly!
|
|
110
|
+
if (err?.response?.status === 401 || err?.response?.status === 403 || err?.status === 401) throw err;
|
|
111
|
+
|
|
112
|
+
const errorMsg = typeof err === 'object' ? JSON.stringify(err) : err;
|
|
113
|
+
logger.log("Front OCR Extraction Error:", errorMsg);
|
|
114
|
+
throw new Error(err?.message || "Impossible d'extraire les données. Veuillez reprendre la photo.");
|
|
147
115
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
logger.log("Front MRZ Extraction Failed:", mrz?.parsed_data?.status_message);
|
|
169
|
-
throw new Error(`MRZ illisible: ${mrz?.parsed_data?.status_message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
|
|
170
|
-
}
|
|
171
|
-
} catch (err: any) {
|
|
172
|
-
logger.log("MRZ Extraction Error:", err);
|
|
173
|
-
throw new Error(err?.message || "Erreur lors de l'extraction MRZ. Veuillez reprendre la photo.");
|
|
174
|
-
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (hasMrz) {
|
|
119
|
+
try {
|
|
120
|
+
logger.log("Tentative d'extraction MRZ (Front)");
|
|
121
|
+
const mrzResponse = await kycService.extractMrzText(
|
|
122
|
+
{ fileUri: result.path || '', docType: result?.selectedDocumentType || '', docRegion: result?.code || "", postfix: result?.currentSide || 'front', token: token, template_path: result?.templatePath || '', mrz_type: result?.mrzType || 'TD1' }, env
|
|
123
|
+
);
|
|
124
|
+
mrz = typeof mrzResponse === 'string' ? JSON.parse(mrzResponse) : mrzResponse;
|
|
125
|
+
|
|
126
|
+
if (mrz?.parsed_data?.status === 'FAILURE' || mrz?.success === false) {
|
|
127
|
+
logger.log("Front MRZ Extraction Failed:", mrz?.parsed_data?.status_message);
|
|
128
|
+
throw new Error(`MRZ illisible: ${mrz?.parsed_data?.status_message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
|
|
129
|
+
}
|
|
130
|
+
} catch (err: any) {
|
|
131
|
+
// 🚨 FIX: Allow 401/403 errors to bubble up directly!
|
|
132
|
+
if (err?.response?.status === 401 || err?.response?.status === 403 || err?.status === 401) throw err;
|
|
133
|
+
|
|
134
|
+
logger.log("MRZ Extraction Error:", err);
|
|
135
|
+
throw new Error(err?.message || "Erreur lors de l'extraction MRZ. Veuillez reprendre la photo.");
|
|
175
136
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
...detected,
|
|
181
|
-
croppedBase64,
|
|
182
|
-
bbox,
|
|
183
|
-
...(mrz ? { mrz } : {}),
|
|
184
|
-
...(barcode ? { barcode } : {}),
|
|
185
|
-
...(documentData ? { documentData } : {})
|
|
186
|
-
};
|
|
187
|
-
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { ...detected, croppedBase64, bbox, ...(mrz ? { mrz } : {}), ...(barcode ? { barcode } : {}), ...(documentData ? { documentData } : {}) };
|
|
140
|
+
|
|
188
141
|
} catch (e: any) {
|
|
189
|
-
|
|
190
|
-
|
|
142
|
+
logger.log('Error front verification:', e?.message);
|
|
143
|
+
|
|
144
|
+
// 🚨 FINAL FIX: If the root catch block receives an API error with a status code,
|
|
145
|
+
// we must throw the RAW object so `handleSilentCapture` can read `e.response.status`.
|
|
146
|
+
if (e?.response?.status || e?.status || e?.isAxiosError) {
|
|
147
|
+
throw e;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
throw new Error(e?.message || 'Erreur de détection du visage');
|
|
191
151
|
}
|
|
192
|
-
}
|
|
152
|
+
}
|
|
193
153
|
|
|
194
154
|
export async function backVerification(
|
|
195
155
|
result: {
|
|
@@ -428,88 +388,95 @@ export async function backVerification(
|
|
|
428
388
|
}
|
|
429
389
|
|
|
430
390
|
export async function checkTemplateType(
|
|
431
|
-
result: {
|
|
432
|
-
path?: string,
|
|
433
|
-
docType: string,
|
|
434
|
-
docRegion: string,
|
|
435
|
-
postfix: string,
|
|
436
|
-
issuingSubdivision?: string
|
|
437
|
-
},
|
|
391
|
+
result: { path?: string, docType: string, docRegion: string, postfix: string, issuingSubdivision?: string },
|
|
438
392
|
env: KycEnvironment = 'PRODUCTION'
|
|
439
|
-
) {
|
|
393
|
+
) {
|
|
440
394
|
try {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
let hasCroppedSides = true;
|
|
480
|
-
|
|
481
|
-
if (cardData.obb) {
|
|
482
|
-
points = cardData.obb;
|
|
483
|
-
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
484
|
-
isCardInFrame = cardData.card_in_frame === true;
|
|
485
|
-
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
486
|
-
}
|
|
487
|
-
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
488
|
-
points = cardData[0];
|
|
489
|
-
isCardInFrame = true;
|
|
490
|
-
hasCroppedSides = false;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (!isCardInFrame || hasCroppedSides) {
|
|
494
|
-
logger.log(`Template Framing failed by backend check.`);
|
|
495
|
-
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const LPIPS_THRESHOLD = 0.75;
|
|
499
|
-
if (templateType.lpips_score !== undefined && templateType.lpips_score > LPIPS_THRESHOLD) {
|
|
500
|
-
logger.log(`Country Mismatch! LPIPS: ${templateType.lpips_score}`);
|
|
501
|
-
throw new Error(`Le document présenté ne correspond pas au pays sélectionné (${result.docRegion}).`);
|
|
395
|
+
if (env === 'SANDBOX') {
|
|
396
|
+
return {
|
|
397
|
+
template_path: `templates/${result.docType}_${result.docRegion}_${result.postfix}.jpg`,
|
|
398
|
+
card_obb: [{ obb: [[400, 800], [2600, 800], [2600, 2200], [400, 2200]], confidence: 0.95, card_in_frame: true, cropped_sides: [] }]
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const token = await authentification();
|
|
403
|
+
const isCanada = result.docRegion?.toUpperCase() === 'CA';
|
|
404
|
+
|
|
405
|
+
const templateType = await kycService.checkTemplateType({
|
|
406
|
+
fileUri: result.path || '',
|
|
407
|
+
docType: result?.docType as GovernmentDocumentType,
|
|
408
|
+
docRegion: result?.docRegion || "",
|
|
409
|
+
postfix: result?.postfix,
|
|
410
|
+
token: token,
|
|
411
|
+
issuingSubdivision: isCanada ? result.issuingSubdivision : undefined
|
|
412
|
+
}, env);
|
|
413
|
+
|
|
414
|
+
if (templateType.success === false || templateType.Error) {
|
|
415
|
+
logger.log("Backend returned an error:", templateType.Error);
|
|
416
|
+
throw new Error('Impossible de lire le document. Veuillez vous rapprocher et stabiliser la caméra.');
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const cardData = templateType.card_obb && Array.isArray(templateType.card_obb) && templateType.card_obb.length > 0 ? templateType.card_obb[0] : null;
|
|
420
|
+
|
|
421
|
+
if (!cardData) throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
422
|
+
|
|
423
|
+
let points: number[][] | null = null;
|
|
424
|
+
console.log(points);
|
|
425
|
+
let isCardInFrame = false;
|
|
426
|
+
let hasCroppedSides = true;
|
|
427
|
+
|
|
428
|
+
if (cardData.obb) {
|
|
429
|
+
points = cardData.obb;
|
|
430
|
+
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
431
|
+
isCardInFrame = cardData.card_in_frame === true;
|
|
432
|
+
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
502
433
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
434
|
+
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
435
|
+
points = cardData[0];
|
|
436
|
+
isCardInFrame = true;
|
|
437
|
+
hasCroppedSides = false;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!isCardInFrame || hasCroppedSides) {
|
|
441
|
+
logger.log(`Template Framing failed by backend check.`);
|
|
442
|
+
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const LPIPS_THRESHOLD = 0.75;
|
|
446
|
+
if (templateType.lpips_score !== undefined && templateType.lpips_score > LPIPS_THRESHOLD) {
|
|
447
|
+
logger.log(`Country Mismatch! LPIPS: ${templateType.lpips_score}`);
|
|
448
|
+
throw new Error(`Le document présenté ne correspond pas au pays sélectionné (${result.docRegion}).`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
logger.log("templateType result", JSON.stringify(truncateFields(templateType), null, 2));
|
|
452
|
+
|
|
453
|
+
return templateType;
|
|
507
454
|
} catch (e: any) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
455
|
+
logger.log('Error checking template type:', JSON.stringify(errorMessage(e), null, 2));
|
|
456
|
+
|
|
457
|
+
// Safely extract the raw message to check for string-based errors like "Unauthorized"
|
|
458
|
+
const rawMessage = (typeof e === 'string' ? e : (e?.message || '')).toLowerCase();
|
|
459
|
+
|
|
460
|
+
// 🚨 CRITICAL FIX: Ensure 401/403 and string-based "Unauthorized" errors bubble up exactly as they are!
|
|
461
|
+
if (
|
|
462
|
+
e?.response?.status === 401 ||
|
|
463
|
+
e?.response?.status === 403 ||
|
|
464
|
+
e?.status === 401 ||
|
|
465
|
+
rawMessage.includes('unauthorized') ||
|
|
466
|
+
rawMessage.includes('401')
|
|
467
|
+
) {
|
|
468
|
+
throw e;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (e?.isAxiosError) {
|
|
472
|
+
throw e;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (e?.message === 'CARD_NOT_FULLY_IN_FRAME' || e?.message?.includes('ne correspond pas')) {
|
|
476
|
+
throw e;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Safely fall back without swallowing string-based errors
|
|
480
|
+
throw new Error(typeof e === 'string' ? e : (e?.message || 'Erreur de vérification du template'));
|
|
514
481
|
}
|
|
515
|
-
}
|
|
482
|
+
}
|
|
@@ -474,14 +474,17 @@ export const authentification = async (): Promise<string> => {
|
|
|
474
474
|
|
|
475
475
|
cachedToken = token;
|
|
476
476
|
tokenExpiryTime = Date.now() + ((res.data.expires_in || 300) * 1000);
|
|
477
|
+
activeAuthRequest = null; // 🚨 FIX: Clear the promise once resolved
|
|
477
478
|
|
|
478
|
-
return cachedToken as string;
|
|
479
|
+
return cachedToken as string;
|
|
479
480
|
})();
|
|
480
481
|
|
|
481
482
|
return await activeAuthRequest;
|
|
482
483
|
} catch (error: any) {
|
|
483
484
|
cachedToken = null;
|
|
485
|
+
tokenExpiryTime = null;
|
|
484
486
|
activeAuthRequest = null;
|
|
487
|
+
|
|
485
488
|
const safeErrorMessage = error?.response?.data?.error_description || error?.message || 'Unknown error';
|
|
486
489
|
logger.error('Error authentifying:', safeErrorMessage);
|
|
487
490
|
throw new Error(`Échec de l'authentification: ${safeErrorMessage}`);
|
|
@@ -489,9 +492,13 @@ export const authentification = async (): Promise<string> => {
|
|
|
489
492
|
}
|
|
490
493
|
|
|
491
494
|
export const clearAuthToken = () => {
|
|
492
|
-
|
|
495
|
+
logger.log("Wiping AI services auth token cache...");
|
|
496
|
+
cachedToken = null;
|
|
497
|
+
tokenExpiryTime = null;
|
|
498
|
+
activeAuthRequest = null;
|
|
493
499
|
};
|
|
494
500
|
|
|
501
|
+
|
|
495
502
|
export const errorMessage = (error: any) => {
|
|
496
503
|
return error.response?.data?.message || error.response?.data?.detail || error.response?.data?.error || error.response?.data || error.message;
|
|
497
504
|
}
|
package/src/types/KYC.types.ts
CHANGED
|
@@ -478,6 +478,7 @@ export interface TemplateActions {
|
|
|
478
478
|
validateComponent: (componentId: number, dataOverride?: any) => boolean;
|
|
479
479
|
submitTemplate: () => Promise<void>;
|
|
480
480
|
resetTemplate: () => void;
|
|
481
|
+
restartSession: () => void;
|
|
481
482
|
setLanguage: (language: string) => void;
|
|
482
483
|
showCustomStepper: (show: boolean) => void;
|
|
483
484
|
setVerificationState: (state: VerificationState) => void;
|