@sanctum-key/react-native-sdk 1.0.57 → 1.0.59
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 +12 -2
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +5 -1
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +34 -13
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts +2 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +2 -2
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +1 -0
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +56 -12
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/config/countriesData.d.ts.map +1 -1
- package/build/src/config/countriesData.js +249 -77
- package/build/src/config/countriesData.js.map +1 -1
- package/build/src/config/region_mapping.d.ts.map +1 -1
- package/build/src/config/region_mapping.js +45 -15
- package/build/src/config/region_mapping.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +10 -2
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/utils/cropByObb.d.ts +1 -1
- package/build/src/utils/cropByObb.d.ts.map +1 -1
- package/build/src/utils/cropByObb.js +112 -5
- package/build/src/utils/cropByObb.js.map +1 -1
- package/build/src/utils/logger.d.ts +1 -0
- package/build/src/utils/logger.d.ts.map +1 -1
- package/build/src/utils/logger.js +37 -73
- package/build/src/utils/logger.js.map +1 -1
- package/build/src/web/WebKYCEntry.d.ts.map +1 -1
- package/build/src/web/WebKYCEntry.js +0 -2
- package/build/src/web/WebKYCEntry.js.map +1 -1
- package/package.json +12 -2
- package/src/components/EnhancedCameraView.tsx +4 -0
- package/src/components/KYCElements/IDCardCapture.tsx +49 -21
- package/src/components/TemplateKYCExample.tsx +4 -1
- package/src/components/TemplateKYCFlowRefactored.tsx +66 -11
- package/src/config/countriesData.ts +250 -78
- package/src/config/region_mapping.ts +700 -670
- package/src/modules/api/CardAuthentification.ts +11 -3
- package/src/utils/cropByObb.ts +160 -6
- package/src/utils/logger.ts +42 -93
- package/src/web/WebKYCEntry.tsx +0 -2
|
@@ -284,6 +284,7 @@ export async function backVerification(
|
|
|
284
284
|
|
|
285
285
|
logger.log("Framing, Country, and Match verified successfully. Proceeding to Data Extraction.");
|
|
286
286
|
let extractionResult: any = {};
|
|
287
|
+
let mrzCardObb: any;
|
|
287
288
|
|
|
288
289
|
if (result.code === 'CA') {
|
|
289
290
|
try {
|
|
@@ -331,6 +332,9 @@ export async function backVerification(
|
|
|
331
332
|
throw new Error(mrz?.parsed_data?.status_message || 'Lecture MRZ échouée');
|
|
332
333
|
}
|
|
333
334
|
extractionResult.mrz = mrz;
|
|
335
|
+
if (mrz?.card_obb) {
|
|
336
|
+
mrzCardObb = mrz.card_obb;
|
|
337
|
+
}
|
|
334
338
|
} catch (mrzError: any) {
|
|
335
339
|
logger.log(`MRZ échoué: ${mrzError?.message}`);
|
|
336
340
|
throw new Error(`Lecture MRZ invalide: ${mrzError?.message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
|
|
@@ -339,9 +343,13 @@ export async function backVerification(
|
|
|
339
343
|
|
|
340
344
|
let bbox: IBbox | undefined;
|
|
341
345
|
let croppedBase64: string | undefined;
|
|
346
|
+
// For MRZ-backed documents, extract_mrz sees the final verified back image
|
|
347
|
+
// and provides the authoritative card bounds. Keep the template OBB as a
|
|
348
|
+
// fallback for barcode-only and non-MRZ document backs.
|
|
349
|
+
const finalCardObb = mrzCardObb || templateResponse.card_obb;
|
|
342
350
|
|
|
343
351
|
try {
|
|
344
|
-
const crop = await cropByObb(result?.path || '',
|
|
352
|
+
const crop = await cropByObb(result?.path || '', finalCardObb);
|
|
345
353
|
bbox = crop.bbox;
|
|
346
354
|
croppedBase64 = crop.base64;
|
|
347
355
|
} catch {
|
|
@@ -352,7 +360,7 @@ export async function backVerification(
|
|
|
352
360
|
...extractionResult,
|
|
353
361
|
bbox,
|
|
354
362
|
croppedBase64,
|
|
355
|
-
card_obb:
|
|
363
|
+
card_obb: finalCardObb
|
|
356
364
|
};
|
|
357
365
|
|
|
358
366
|
} catch (e: any) {
|
|
@@ -443,4 +451,4 @@ export async function checkTemplateType(
|
|
|
443
451
|
|
|
444
452
|
throw new Error(typeof e === 'string' ? e : (e?.message || 'Erreur de vérification du template'));
|
|
445
453
|
}
|
|
446
|
-
}
|
|
454
|
+
}
|
package/src/utils/cropByObb.ts
CHANGED
|
@@ -4,6 +4,22 @@ import * as ImageManipulator from "expo-image-manipulator";
|
|
|
4
4
|
|
|
5
5
|
type Point = [number, number];
|
|
6
6
|
|
|
7
|
+
type PixelBbox = {
|
|
8
|
+
minX: number;
|
|
9
|
+
minY: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type BboxOrientation = 'direct' | 'clockwise' | 'counter-clockwise' | 'upside-down';
|
|
15
|
+
|
|
16
|
+
type OrientedBboxCandidate = {
|
|
17
|
+
bbox: PixelBbox;
|
|
18
|
+
orientation: BboxOrientation;
|
|
19
|
+
inFrameRatio: number;
|
|
20
|
+
score: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
7
23
|
export const OBB_CONFIDENCE_THRESHOLD = 0.85;
|
|
8
24
|
|
|
9
25
|
|
|
@@ -84,6 +100,105 @@ function computeAabb(points: Point[]) {
|
|
|
84
100
|
return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
|
|
85
101
|
}
|
|
86
102
|
|
|
103
|
+
function getInFrameRatio(bbox: PixelBbox, imageWidth: number, imageHeight: number): number {
|
|
104
|
+
const left = Math.max(0, bbox.minX);
|
|
105
|
+
const top = Math.max(0, bbox.minY);
|
|
106
|
+
const right = Math.min(imageWidth, bbox.minX + bbox.width);
|
|
107
|
+
const bottom = Math.min(imageHeight, bbox.minY + bbox.height);
|
|
108
|
+
const intersectionArea = Math.max(0, right - left) * Math.max(0, bottom - top);
|
|
109
|
+
const bboxArea = bbox.width * bbox.height;
|
|
110
|
+
|
|
111
|
+
return bboxArea > 0 ? intersectionArea / bboxArea : 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The API can return OBB coordinates in the camera sensor's pixel space while
|
|
116
|
+
* ImageManipulator returns an EXIF-normalized image. On phones held sideways,
|
|
117
|
+
* those spaces are 90 degrees apart. Select the representation which best fits
|
|
118
|
+
* the normalized image instead of applying the server coordinates blindly.
|
|
119
|
+
*/
|
|
120
|
+
function resolveBboxOrientation(
|
|
121
|
+
bbox: PixelBbox,
|
|
122
|
+
imageWidth: number,
|
|
123
|
+
imageHeight: number,
|
|
124
|
+
preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
|
|
125
|
+
): OrientedBboxCandidate {
|
|
126
|
+
const candidates: Array<{ bbox: PixelBbox; orientation: BboxOrientation; priority: number }> = [
|
|
127
|
+
{
|
|
128
|
+
bbox,
|
|
129
|
+
orientation: 'direct',
|
|
130
|
+
priority: 0.75,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
// Source canvas is imageHeight x imageWidth and is rotated clockwise.
|
|
134
|
+
bbox: {
|
|
135
|
+
minX: imageWidth - bbox.minY - bbox.height,
|
|
136
|
+
minY: bbox.minX,
|
|
137
|
+
width: bbox.height,
|
|
138
|
+
height: bbox.width,
|
|
139
|
+
},
|
|
140
|
+
orientation: 'clockwise',
|
|
141
|
+
priority: 0,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
// Source canvas is imageHeight x imageWidth and is rotated counter-clockwise.
|
|
145
|
+
bbox: {
|
|
146
|
+
minX: bbox.minY,
|
|
147
|
+
minY: imageHeight - bbox.minX - bbox.width,
|
|
148
|
+
width: bbox.height,
|
|
149
|
+
height: bbox.width,
|
|
150
|
+
},
|
|
151
|
+
orientation: 'counter-clockwise',
|
|
152
|
+
priority: 0,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
bbox: {
|
|
156
|
+
minX: imageWidth - bbox.minX - bbox.width,
|
|
157
|
+
minY: imageHeight - bbox.minY - bbox.height,
|
|
158
|
+
width: bbox.width,
|
|
159
|
+
height: bbox.height,
|
|
160
|
+
},
|
|
161
|
+
orientation: 'upside-down',
|
|
162
|
+
priority: -0.25,
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const scoredCandidates = candidates.map((candidate) => {
|
|
167
|
+
const candidateBbox = candidate.bbox;
|
|
168
|
+
const inFrameRatio = getInFrameRatio(candidateBbox, imageWidth, imageHeight);
|
|
169
|
+
const centerX = candidateBbox.minX + candidateBbox.width / 2;
|
|
170
|
+
const centerY = candidateBbox.minY + candidateBbox.height / 2;
|
|
171
|
+
const normalizedCenterDistance = Math.hypot(
|
|
172
|
+
(centerX - imageWidth / 2) / Math.max(1, imageWidth / 2),
|
|
173
|
+
(centerY - imageHeight / 2) / Math.max(1, imageHeight / 2),
|
|
174
|
+
);
|
|
175
|
+
const centerScore = 1 - Math.min(1, normalizedCenterDistance);
|
|
176
|
+
|
|
177
|
+
const isLandscapeCard = candidateBbox.width >= candidateBbox.height;
|
|
178
|
+
const preferredOrientationScore = preferredCardOrientation === 'either'
|
|
179
|
+
? 0
|
|
180
|
+
: ((preferredCardOrientation === 'landscape') === isLandscapeCard ? 1 : 0);
|
|
181
|
+
const areaRatio = (candidateBbox.width * candidateBbox.height) / Math.max(1, imageWidth * imageHeight);
|
|
182
|
+
const plausibleAreaScore = Math.min(1, areaRatio / 0.08);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
bbox: candidateBbox,
|
|
186
|
+
orientation: candidate.orientation,
|
|
187
|
+
inFrameRatio,
|
|
188
|
+
score:
|
|
189
|
+
inFrameRatio * 100
|
|
190
|
+
+ centerScore * 3
|
|
191
|
+
+ preferredOrientationScore * 2
|
|
192
|
+
+ plausibleAreaScore
|
|
193
|
+
+ candidate.priority,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return scoredCandidates.reduce((best, candidate) => (
|
|
198
|
+
candidate.score > best.score ? candidate : best
|
|
199
|
+
));
|
|
200
|
+
}
|
|
201
|
+
|
|
87
202
|
async function cropWeb(uri: string, points: Point[]): Promise<string> {
|
|
88
203
|
return new Promise((resolve, reject) => {
|
|
89
204
|
const img = new Image();
|
|
@@ -159,7 +274,12 @@ async function normalizeOrientationAndGetDimensions(
|
|
|
159
274
|
}
|
|
160
275
|
|
|
161
276
|
|
|
162
|
-
export async function cropImageWithBBoxWithTolerance(
|
|
277
|
+
export async function cropImageWithBBoxWithTolerance(
|
|
278
|
+
uri: string,
|
|
279
|
+
bbox: any,
|
|
280
|
+
tolerance: number = 0.02,
|
|
281
|
+
preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
|
|
282
|
+
): Promise<string> {
|
|
163
283
|
return new Promise<string>((resolve) => {
|
|
164
284
|
let isResolved = false;
|
|
165
285
|
|
|
@@ -195,10 +315,44 @@ export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tol
|
|
|
195
315
|
pixelWidth = bbox.width * actualWidth;
|
|
196
316
|
pixelHeight = bbox.height * actualHeight;
|
|
197
317
|
} else {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
318
|
+
const resolvedBbox = resolveBboxOrientation(
|
|
319
|
+
{
|
|
320
|
+
minX: bbox.minX,
|
|
321
|
+
minY: bbox.minY,
|
|
322
|
+
width: bbox.width,
|
|
323
|
+
height: bbox.height,
|
|
324
|
+
},
|
|
325
|
+
actualWidth,
|
|
326
|
+
actualHeight,
|
|
327
|
+
preferredCardOrientation,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (resolvedBbox.inFrameRatio < 0.6) {
|
|
331
|
+
if (!isResolved) {
|
|
332
|
+
isResolved = true;
|
|
333
|
+
clearTimeout(timeout);
|
|
334
|
+
console.warn('OBB does not match the normalized image bounds; returning the full image.', {
|
|
335
|
+
bbox,
|
|
336
|
+
imageSize: { width: actualWidth, height: actualHeight },
|
|
337
|
+
bestOrientation: resolvedBbox.orientation,
|
|
338
|
+
inFrameRatio: resolvedBbox.inFrameRatio,
|
|
339
|
+
});
|
|
340
|
+
resolve(normalizedUri);
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (resolvedBbox.orientation !== 'direct') {
|
|
346
|
+
console.info('Adjusted OBB coordinates for the normalized camera orientation.', {
|
|
347
|
+
orientation: resolvedBbox.orientation,
|
|
348
|
+
imageSize: { width: actualWidth, height: actualHeight },
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
pixelMinX = resolvedBbox.bbox.minX;
|
|
353
|
+
pixelMinY = resolvedBbox.bbox.minY;
|
|
354
|
+
pixelWidth = resolvedBbox.bbox.width;
|
|
355
|
+
pixelHeight = resolvedBbox.bbox.height;
|
|
202
356
|
}
|
|
203
357
|
|
|
204
358
|
// STEP 3: Add tolerance and clamp to image bounds.
|
|
@@ -309,4 +463,4 @@ export async function cropToCenterScanArea(
|
|
|
309
463
|
}
|
|
310
464
|
})();
|
|
311
465
|
});
|
|
312
|
-
}
|
|
466
|
+
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Platform, AppState, AppStateStatus } from 'react-native';
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import PostHog from 'posthog-react-native';
|
|
3
|
+
|
|
4
|
+
const posthogClient = new PostHog('phc_qjCHjCgkdVniammXrrfLrmNPoPH5uAmkzoQMhrEhGaYR', {
|
|
5
|
+
host: 'https://eu.i.posthog.com',
|
|
6
|
+
disabled: false,
|
|
7
|
+
captureAppLifecycleEvents: false,
|
|
8
|
+
flushAt: 1,
|
|
9
|
+
flushInterval: 1000, // Backup: flush automatically every 1 second
|
|
10
|
+
});
|
|
4
11
|
|
|
5
12
|
const isProduction = (): boolean => {
|
|
6
13
|
if (typeof __DEV__ !== 'undefined') {
|
|
@@ -11,7 +18,6 @@ const isProduction = (): boolean => {
|
|
|
11
18
|
}
|
|
12
19
|
return false;
|
|
13
20
|
};
|
|
14
|
-
|
|
15
21
|
const IS_PROD = isProduction();
|
|
16
22
|
|
|
17
23
|
export const logger = {
|
|
@@ -23,32 +29,16 @@ export const logger = {
|
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
// ==========================================
|
|
26
|
-
// 2. SDK TELEMETRY LOGGER (
|
|
32
|
+
// 2. SDK TELEMETRY LOGGER (POSTHOG ONLY)
|
|
27
33
|
// ==========================================
|
|
28
|
-
|
|
29
|
-
type LogLevel = 'info' | 'warning' | 'error' | 'debug';
|
|
30
|
-
|
|
31
|
-
interface LogEntry {
|
|
32
|
-
level: LogLevel;
|
|
33
|
-
message: string;
|
|
34
|
-
timestamp: string;
|
|
35
|
-
session_id?: string;
|
|
36
|
-
metadata?: any;
|
|
37
|
-
}
|
|
34
|
+
type LogLevel = 'info' | 'warning' | 'error' | 'debug';
|
|
38
35
|
|
|
39
36
|
class SDKTelemetryLogger {
|
|
40
|
-
private queue: LogEntry[] = [];
|
|
41
|
-
private isFlushing = false;
|
|
42
|
-
private maxQueueSize = 3;
|
|
43
|
-
private flushIntervalMs = 3000;
|
|
44
37
|
private sessionId: string | undefined;
|
|
45
|
-
private apiKey: string | undefined;
|
|
46
|
-
private timerId?: ReturnType<typeof setInterval>;
|
|
47
38
|
private appStateSubscription: any;
|
|
48
39
|
|
|
49
40
|
constructor() {
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
// Flush PostHog when the app goes into the background
|
|
52
42
|
if (Platform.OS !== 'web') {
|
|
53
43
|
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
|
|
54
44
|
}
|
|
@@ -62,29 +52,33 @@ class SDKTelemetryLogger {
|
|
|
62
52
|
|
|
63
53
|
public setSessionId(id: string) {
|
|
64
54
|
this.sessionId = id;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!log.session_id) {
|
|
68
|
-
return { ...log, session_id: id };
|
|
69
|
-
}
|
|
70
|
-
return log;
|
|
55
|
+
posthogClient.identify(id, {
|
|
56
|
+
kyc_session_id: id,
|
|
71
57
|
});
|
|
72
|
-
|
|
73
|
-
this.flush();
|
|
74
58
|
}
|
|
75
59
|
|
|
76
60
|
public clearSessionId() {
|
|
77
61
|
this.sessionId = undefined;
|
|
62
|
+
posthogClient.reset(); // Clears identity
|
|
78
63
|
}
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
// Kept as a no-op just in case your SDK still calls this somewhere
|
|
66
|
+
public setApiKey(_key: string) {
|
|
67
|
+
// No longer needed for PostHog
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public track(eventName: string, properties?: Record<string, any>) {
|
|
71
|
+
posthogClient.capture(`[SanctumKey] ${eventName}`, {
|
|
72
|
+
session_id: this.sessionId || null,
|
|
73
|
+
os: Platform.OS,
|
|
74
|
+
osVersion: Platform.Version,
|
|
75
|
+
...properties,
|
|
76
|
+
});
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
private parseArgs(args: any[]): { message: string, metadata: any } {
|
|
85
80
|
let message = '';
|
|
86
81
|
let metadata: any = {};
|
|
87
|
-
|
|
88
82
|
args.forEach(arg => {
|
|
89
83
|
if (typeof arg === 'string') {
|
|
90
84
|
message += (message ? ' ' : '') + arg;
|
|
@@ -97,7 +91,6 @@ class SDKTelemetryLogger {
|
|
|
97
91
|
message += (message ? ' ' : '') + String(arg);
|
|
98
92
|
}
|
|
99
93
|
});
|
|
100
|
-
|
|
101
94
|
return { message: message || 'No message provided', metadata };
|
|
102
95
|
}
|
|
103
96
|
|
|
@@ -107,77 +100,33 @@ class SDKTelemetryLogger {
|
|
|
107
100
|
if (!IS_PROD) {
|
|
108
101
|
const devPrefix = `[SDK_TELEMETRY_${level.toUpperCase()}]`;
|
|
109
102
|
if (level === 'error') console.error(devPrefix, message, metadata);
|
|
110
|
-
else if (level === 'warning') console.warn(devPrefix, message, metadata);
|
|
103
|
+
else if (level === 'warning') console.warn(devPrefix, message, metadata);
|
|
111
104
|
else console.log(devPrefix, message, metadata);
|
|
112
105
|
}
|
|
113
106
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
session_id: this.sessionId,
|
|
119
|
-
metadata: {
|
|
120
|
-
...metadata,
|
|
121
|
-
os: Platform.OS,
|
|
122
|
-
osVersion: Platform.Version,
|
|
123
|
-
}
|
|
107
|
+
const payloadMetadata = {
|
|
108
|
+
...metadata,
|
|
109
|
+
os: Platform.OS,
|
|
110
|
+
osVersion: Platform.Version,
|
|
124
111
|
};
|
|
125
112
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
const levelLabel = level.toUpperCase();
|
|
114
|
+
posthogClient.capture(`[SanctumKey] ${message}`, {
|
|
115
|
+
session_id: this.sessionId || null,
|
|
116
|
+
log_level: levelLabel,
|
|
117
|
+
...payloadMetadata,
|
|
118
|
+
});
|
|
130
119
|
|
|
131
|
-
if (
|
|
120
|
+
if (level === 'error') {
|
|
132
121
|
this.flush();
|
|
133
122
|
}
|
|
134
123
|
}
|
|
135
124
|
|
|
136
125
|
public async flush() {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const batch = this.queue.map(log => ({
|
|
140
|
-
...log,
|
|
141
|
-
session_id: log.session_id || 'pre-session-startup'
|
|
142
|
-
}));
|
|
143
|
-
|
|
144
|
-
this.isFlushing = true;
|
|
145
|
-
this.queue = [];
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const url = `${KYCConfig.getBackendUrl()}/core/sdk/logs/`;
|
|
149
|
-
|
|
150
|
-
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
151
|
-
if (this.apiKey) {
|
|
152
|
-
headers['Authorization'] = `ApiKey ${this.apiKey}`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
await axios.post(url, { logs: batch }, {
|
|
156
|
-
headers,
|
|
157
|
-
timeout: 5000
|
|
158
|
-
});
|
|
159
|
-
} catch (error: any) {
|
|
160
|
-
// 🚨 FIX 2: Stop Infinite Loops
|
|
161
|
-
// If the backend actively rejects our payload (400-499 error code), do NOT requeue it.
|
|
162
|
-
const isClientError = error.response && error.response.status >= 400 && error.response.status < 500;
|
|
163
|
-
|
|
164
|
-
if (!isClientError) {
|
|
165
|
-
// Only retry if it's a network failure or a 5xx Server Error
|
|
166
|
-
if (this.queue.length < 50) {
|
|
167
|
-
this.queue = [...batch, ...this.queue].slice(0, 50);
|
|
168
|
-
}
|
|
169
|
-
} else {
|
|
170
|
-
if (!IS_PROD) {
|
|
171
|
-
console.error("[SDK_TELEMETRY] Backend rejected log payload. Dropping batch to prevent infinite loop.", error.response.data);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
} finally {
|
|
175
|
-
this.isFlushing = false;
|
|
176
|
-
}
|
|
126
|
+
await posthogClient.flush();
|
|
177
127
|
}
|
|
178
128
|
|
|
179
129
|
public destroy() {
|
|
180
|
-
if (this.timerId) clearInterval(this.timerId);
|
|
181
130
|
if (this.appStateSubscription) this.appStateSubscription.remove();
|
|
182
131
|
this.flush();
|
|
183
132
|
}
|
|
@@ -192,10 +141,10 @@ export const sdkLogger = {
|
|
|
192
141
|
destroy: () => telemetryInstance.destroy(),
|
|
193
142
|
setApiKey: (key: string) => telemetryInstance.setApiKey(key),
|
|
194
143
|
|
|
144
|
+
track: (eventName: string, properties?: Record<string, any>) => telemetryInstance.track(eventName, properties),
|
|
195
145
|
log: (...args: any[]) => telemetryInstance.processLog('info', ...args),
|
|
196
146
|
info: (...args: any[]) => telemetryInstance.processLog('info', ...args),
|
|
197
|
-
|
|
198
|
-
warn: (...args: any[]) => telemetryInstance.processLog('warning', ...args),
|
|
147
|
+
warn: (...args: any[]) => telemetryInstance.processLog('warning', ...args),
|
|
199
148
|
error: (...args: any[]) => telemetryInstance.processLog('error', ...args),
|
|
200
149
|
debug: (...args: any[]) => telemetryInstance.processLog('debug', ...args),
|
|
201
150
|
};
|
package/src/web/WebKYCEntry.tsx
CHANGED
|
@@ -42,7 +42,6 @@ interface VerificationSteps {
|
|
|
42
42
|
liveness_checked?: boolean;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// 🚨 MODIFIED: Accepts the webhook secret to generate the signature
|
|
46
45
|
const postWebhook = async (url: string, payload: unknown, secret?: string, timeoutMs: number = 5000) => {
|
|
47
46
|
const body = JSON.stringify(payload);
|
|
48
47
|
|
|
@@ -50,7 +49,6 @@ const postWebhook = async (url: string, payload: unknown, secret?: string, timeo
|
|
|
50
49
|
'Content-Type': 'application/json',
|
|
51
50
|
};
|
|
52
51
|
|
|
53
|
-
// 🚨 Generate HMAC SHA-256 Signature if secret is provided
|
|
54
52
|
if (secret) {
|
|
55
53
|
const signature = CryptoJS.HmacSHA256(body, secret).toString(CryptoJS.enc.Hex);
|
|
56
54
|
headers['x-sanctumkey-signature'] = signature;
|