@transfergratis/react-native-sdk 0.1.25 → 0.1.28
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/android/src/main/AndroidManifest.xml +12 -0
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +76 -21
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/EmailVerificationTemplate.js +48 -29
- package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +48 -11
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/WelcomeTemplate.js +2 -1
- package/build/components/KYCElements/WelcomeTemplate.js.map +1 -1
- package/build/components/OverLay/type.d.ts +2 -0
- 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 +8 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +2 -2
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +10 -2
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +13 -3
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/config/KYCConfig.js +1 -1
- package/build/config/KYCConfig.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +14 -2
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +175 -84
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/i18n/en/index.d.ts +2 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +3 -1
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +2 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +3 -1
- package/build/i18n/fr/index.js.map +1 -1
- package/build/i18n/types.d.ts +2 -0
- package/build/i18n/types.d.ts.map +1 -1
- package/build/i18n/types.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +28 -2
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +10 -0
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +24 -0
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/camera/VisionCameraModule.web.d.ts.map +1 -1
- package/build/modules/camera/VisionCameraModule.web.js +27 -8
- package/build/modules/camera/VisionCameraModule.web.js.map +1 -1
- package/build/types/KYC.types.d.ts +6 -2
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.d.ts +17 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +51 -1
- package/build/utils/cropByObb.js.map +1 -1
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +11 -5
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/index.d.ts +1 -0
- package/plugin/build/index.js +3 -1
- package/plugin/build/withRemovePermissions.d.ts +3 -0
- package/plugin/build/withRemovePermissions.js +67 -0
- package/plugin/src/index.ts +2 -1
- package/plugin/src/withRemovePermissions.js +85 -0
- package/plugin/src/withRemovePermissions.ts +83 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/plugin.js +6 -1
- package/src/components/EnhancedCameraView.web.tsx +76 -21
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +47 -33
- package/src/components/KYCElements/IDCardCapture.tsx +51 -10
- package/src/components/KYCElements/WelcomeTemplate.tsx +2 -1
- package/src/components/OverLay/type.ts +2 -0
- package/src/components/TemplateKYCExample.tsx +9 -5
- package/src/components/TemplateKYCFlowRefactored.tsx +24 -6
- package/src/config/KYCConfig.ts +1 -1
- package/src/hooks/useTemplateKYCFlow.tsx +189 -95
- package/src/i18n/en/index.ts +3 -1
- package/src/i18n/fr/index.ts +3 -1
- package/src/i18n/types.ts +2 -0
- package/src/modules/api/CardAuthentification.ts +30 -2
- package/src/modules/api/KYCService.ts +41 -0
- package/src/modules/camera/VisionCameraModule.web.ts +30 -12
- package/src/types/KYC.types.ts +7 -3
- package/src/utils/cropByObb.ts +57 -1
- package/src/web/WebKYCEntry.tsx +17 -6
|
@@ -524,6 +524,47 @@ export class KYCService {
|
|
|
524
524
|
throw new Error(errorMessage(error));
|
|
525
525
|
}
|
|
526
526
|
}
|
|
527
|
+
|
|
528
|
+
/** Send email verification code. POST /api/v1/accounts/email/send/ */
|
|
529
|
+
async sendEmailVerificationCode(
|
|
530
|
+
email: string,
|
|
531
|
+
auth?: { apiKey?: string; token?: string }
|
|
532
|
+
): Promise<unknown> {
|
|
533
|
+
const url = `${KYCConfig.getBackendUrl()}/accounts/email/send/`;
|
|
534
|
+
const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
|
|
535
|
+
const res = await axios.post(
|
|
536
|
+
url,
|
|
537
|
+
{ email },
|
|
538
|
+
{
|
|
539
|
+
headers: {
|
|
540
|
+
'Content-Type': 'application/json',
|
|
541
|
+
...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
return res.data;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Verify email with OTP. POST /api/v1/accounts/email/verify/ */
|
|
549
|
+
async verifyEmailCode(
|
|
550
|
+
otp: string,
|
|
551
|
+
auth?: { apiKey?: string; token?: string }
|
|
552
|
+
): Promise<unknown> {
|
|
553
|
+
const url = `${KYCConfig.getBackendUrl()}/accounts/email/verify/`;
|
|
554
|
+
const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
|
|
555
|
+
const res = await axios.post(
|
|
556
|
+
url,
|
|
557
|
+
{ otp },
|
|
558
|
+
{
|
|
559
|
+
headers: {
|
|
560
|
+
'Content-Type': 'application/json',
|
|
561
|
+
...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
|
|
562
|
+
},
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
return res.data;
|
|
566
|
+
}
|
|
567
|
+
|
|
527
568
|
// reultat de la verification
|
|
528
569
|
async getVerificationResult(session_id: string): Promise<VerificationResult> {
|
|
529
570
|
try {
|
|
@@ -151,17 +151,18 @@ export class VisionCameraModule {
|
|
|
151
151
|
return false;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
// Use min + ideal so Android Chrome doesn't cache low resolution (e.g. 640x480)
|
|
155
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
156
|
+
video: {
|
|
156
157
|
facingMode: 'user',
|
|
157
|
-
width: {
|
|
158
|
-
height: {
|
|
159
|
-
}
|
|
158
|
+
width: { min: 1280, ideal: 1920 },
|
|
159
|
+
height: { min: 720, ideal: 1080 },
|
|
160
|
+
},
|
|
160
161
|
});
|
|
161
|
-
|
|
162
|
+
|
|
162
163
|
// Libérer le stream de test
|
|
163
164
|
stream.getTracks().forEach(track => track.stop());
|
|
164
|
-
|
|
165
|
+
|
|
165
166
|
return true;
|
|
166
167
|
} catch (error) {
|
|
167
168
|
console.error('Error requesting camera permission:', error);
|
|
@@ -330,13 +331,30 @@ export class VisionCameraModule {
|
|
|
330
331
|
return;
|
|
331
332
|
}
|
|
332
333
|
|
|
333
|
-
|
|
334
|
+
const constraints: MediaStreamConstraints = {
|
|
334
335
|
video: {
|
|
335
336
|
facingMode: 'user',
|
|
336
|
-
width: {
|
|
337
|
-
height: {
|
|
337
|
+
width: { min: 1280, ideal: 1920 },
|
|
338
|
+
height: { min: 720, ideal: 1080 },
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
this.stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
const name = err instanceof Error ? err.name : '';
|
|
346
|
+
if (name === 'OverconstrainedError') {
|
|
347
|
+
this.stream = await navigator.mediaDevices.getUserMedia({
|
|
348
|
+
video: {
|
|
349
|
+
facingMode: 'user',
|
|
350
|
+
width: { ideal: 1920 },
|
|
351
|
+
height: { ideal: 1080 },
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
throw err;
|
|
338
356
|
}
|
|
339
|
-
}
|
|
357
|
+
}
|
|
340
358
|
|
|
341
359
|
if (this.videoElement) {
|
|
342
360
|
this.videoElement.srcObject = this.stream;
|
|
@@ -531,7 +549,7 @@ export class VisionCameraModule {
|
|
|
531
549
|
context.drawImage(this.videoElement, 0, 0);
|
|
532
550
|
|
|
533
551
|
// Convertir en base64
|
|
534
|
-
const imageDataUrl = this.canvasElement.toDataURL('image/jpeg', 0.
|
|
552
|
+
const imageDataUrl = this.canvasElement.toDataURL('image/jpeg', 0.95);
|
|
535
553
|
|
|
536
554
|
// Créer un nom de fichier unique
|
|
537
555
|
const timestamp = new Date().getTime();
|
package/src/types/KYC.types.ts
CHANGED
|
@@ -455,6 +455,8 @@ export interface SessionState {
|
|
|
455
455
|
isInitialized: boolean;
|
|
456
456
|
isProcessing: boolean;
|
|
457
457
|
error: string | null;
|
|
458
|
+
/** True once loadSessionData has run (for resume flow); allows UI to show loading until country/data restored */
|
|
459
|
+
sessionDataRestored?: boolean;
|
|
458
460
|
}
|
|
459
461
|
|
|
460
462
|
export type VerificationStatus = 'idle' | 'in_progress' | 'success' | 'failed';
|
|
@@ -469,11 +471,11 @@ export interface VerificationState {
|
|
|
469
471
|
// Actions pour le template KYC
|
|
470
472
|
export interface TemplateActions {
|
|
471
473
|
initializeTemplate: (template: KYCTemplate) => void;
|
|
472
|
-
nextComponent: () => void;
|
|
474
|
+
nextComponent: (overrideData?: any) => void;
|
|
473
475
|
previousComponent: () => void;
|
|
474
476
|
goToComponent: (componentId: number) => void;
|
|
475
477
|
updateComponentData: (componentId: number, data: any) => void;
|
|
476
|
-
validateComponent: (componentId: number) => boolean;
|
|
478
|
+
validateComponent: (componentId: number, dataOverride?: any) => boolean;
|
|
477
479
|
submitTemplate: () => Promise<void>;
|
|
478
480
|
resetTemplate: () => void;
|
|
479
481
|
setLanguage: (language: string) => void;
|
|
@@ -482,7 +484,7 @@ export interface TemplateActions {
|
|
|
482
484
|
submitVerification: () => Promise<void>;
|
|
483
485
|
}
|
|
484
486
|
|
|
485
|
-
// Hook pour le template KYC
|
|
487
|
+
// Hook pour le template KYC (return type of useTemplateKYCFlow)
|
|
486
488
|
export interface UseTemplateReturn {
|
|
487
489
|
state: TemplateState;
|
|
488
490
|
actions: TemplateActions;
|
|
@@ -494,6 +496,8 @@ export interface UseTemplateReturn {
|
|
|
494
496
|
getLocalizedText: (text: LocalizedText) => string;
|
|
495
497
|
initializeSession: () => Promise<void>;
|
|
496
498
|
env: 'PRODUCTION' | 'SANDBOX';
|
|
499
|
+
/** Optional API key for backend auth (e.g. email verification endpoints). */
|
|
500
|
+
apiKey?: string;
|
|
497
501
|
}
|
|
498
502
|
|
|
499
503
|
|
package/src/utils/cropByObb.ts
CHANGED
|
@@ -5,6 +5,58 @@ import { logger } from './logger';
|
|
|
5
5
|
|
|
6
6
|
type Point = [number, number];
|
|
7
7
|
|
|
8
|
+
/** OBB confidence below this = card not fully in frame; don't crop, give user feedback. */
|
|
9
|
+
export const OBB_CONFIDENCE_THRESHOLD = 0.85;
|
|
10
|
+
|
|
11
|
+
/**
|
|
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? } ]
|
|
15
|
+
* Returns confidence in [0,1] or null if not present.
|
|
16
|
+
*/
|
|
17
|
+
export function getObbConfidence(cardObb: any): number | null {
|
|
18
|
+
if (!cardObb || !Array.isArray(cardObb) || cardObb.length === 0) return null;
|
|
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 ]
|
|
28
|
+
if (!Array.isArray(first) || first.length < 2) return null;
|
|
29
|
+
const score = first[1];
|
|
30
|
+
return typeof score === 'number' ? score : null;
|
|
31
|
+
}
|
|
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
|
+
export function getCardInFrame(cardObb: any): boolean | null {
|
|
41
|
+
if (!cardObb) return null;
|
|
42
|
+
|
|
43
|
+
// card_obb can be an array of objects or a single object
|
|
44
|
+
const first = Array.isArray(cardObb) ? cardObb[0] : cardObb;
|
|
45
|
+
|
|
46
|
+
if (first && typeof first === 'object' && 'card_in_frame' in first) {
|
|
47
|
+
const value = (first as any).card_in_frame;
|
|
48
|
+
return typeof value === 'boolean' ? value : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Some APIs might put card_in_frame at the top level
|
|
52
|
+
if (cardObb && typeof cardObb === 'object' && 'card_in_frame' in cardObb) {
|
|
53
|
+
const value = (cardObb as any).card_in_frame;
|
|
54
|
+
return typeof value === 'boolean' ? value : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
8
60
|
// Compute axis-aligned bounding box from oriented quadrilateral
|
|
9
61
|
function computeAabb(points: Point[]) {
|
|
10
62
|
const xs = points.map(p => p[0]);
|
|
@@ -49,7 +101,11 @@ async function cropWeb(uri: string, points: Point[]): Promise<string> {
|
|
|
49
101
|
// Fallback: return original for native (no dependency added); caller can still use bbox to draw overlay
|
|
50
102
|
export async function cropByObb(uri: string, cardObb: any): Promise<{ base64?: string; bbox?: { minX: number; minY: number; width: number; height: number } }> {
|
|
51
103
|
try {
|
|
52
|
-
// card_obb format: [ [ [p1,p2,p3,p4], score ] ]
|
|
104
|
+
// card_obb format: [ [ [p1,p2,p3,p4], score ] ] — if confidence too low, don't crop (send full image)
|
|
105
|
+
const confidence = getObbConfidence(cardObb);
|
|
106
|
+
if (confidence !== null && confidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
53
109
|
const first = Array.isArray(cardObb) ? cardObb[0] : null;
|
|
54
110
|
const points = Array.isArray(first?.[0]) ? (first[0] as Point[]) : null;
|
|
55
111
|
if (!points || points.length !== 4) return {};
|
package/src/web/WebKYCEntry.tsx
CHANGED
|
@@ -25,7 +25,11 @@ interface URLParams {
|
|
|
25
25
|
env?: 'PRODUCTION' | 'SANDBOX'; // Environment mode
|
|
26
26
|
template_id?: string; // Template ID for dynamic template loading
|
|
27
27
|
server_env?: BackendEnvironment; // Backend environment mode
|
|
28
|
-
step?: string; //
|
|
28
|
+
step?: string; // Deprecated: use component_index.
|
|
29
|
+
component_index?: string; // Index in template.components (0-based) to resume at
|
|
30
|
+
country?: string; // Code pays pour restaurer la sélection (reprise multi-appareil)
|
|
31
|
+
document_type?: string; // Type de document (national_id, passport, etc.)
|
|
32
|
+
region?: string; // Région si applicable
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
interface VerificationSteps {
|
|
@@ -70,6 +74,10 @@ const WebKYCEntry: React.FC<WebKYCEntryProps> = ({
|
|
|
70
74
|
template_id: urlParams.get('template_id') || undefined,
|
|
71
75
|
server_env: validServerEnv,
|
|
72
76
|
step: urlParams.get('step') || undefined,
|
|
77
|
+
component_index: urlParams.get('component_index') || undefined,
|
|
78
|
+
country: urlParams.get('country') || undefined,
|
|
79
|
+
document_type: urlParams.get('document_type') || undefined,
|
|
80
|
+
region: urlParams.get('region') || undefined,
|
|
73
81
|
};
|
|
74
82
|
}, []);
|
|
75
83
|
|
|
@@ -294,12 +302,15 @@ const WebKYCEntry: React.FC<WebKYCEntryProps> = ({
|
|
|
294
302
|
API_KEY={urlParams.token}
|
|
295
303
|
templateId={urlParams.template_id}
|
|
296
304
|
env={urlParams.env || 'PRODUCTION'}
|
|
305
|
+
serverEnv={urlParams.server_env}
|
|
297
306
|
existingSessionId={urlParams.kyc_id}
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
307
|
+
initialComponentIndex={(() => {
|
|
308
|
+
const raw = urlParams.component_index ?? urlParams.step;
|
|
309
|
+
if (raw == null || raw === '') return undefined;
|
|
310
|
+
const index = parseInt(raw, 10);
|
|
311
|
+
return Number.isNaN(index) ? undefined : index;
|
|
312
|
+
})()}
|
|
313
|
+
initialCountryResume={urlParams.country && urlParams.document_type ? { code: urlParams.country, documentType: urlParams.document_type, region: urlParams.region || undefined } : undefined}
|
|
303
314
|
/>
|
|
304
315
|
</SafeAreaView>
|
|
305
316
|
);
|