astra-sdk-web 1.0.0 → 1.1.1

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.
Files changed (43) hide show
  1. package/dist/.htaccess +9 -0
  2. package/dist/astra-sdk.cjs.js +1719 -1
  3. package/dist/astra-sdk.cjs.js.map +1 -1
  4. package/dist/astra-sdk.css +934 -0
  5. package/dist/astra-sdk.css.map +1 -0
  6. package/dist/astra-sdk.d.cts +155 -0
  7. package/dist/astra-sdk.es.js +1706 -1
  8. package/dist/astra-sdk.es.js.map +1 -1
  9. package/dist/components.cjs.js +1473 -0
  10. package/dist/components.cjs.js.map +1 -0
  11. package/dist/components.css +934 -0
  12. package/dist/components.css.map +1 -0
  13. package/dist/components.d.cts +13 -0
  14. package/dist/components.d.ts +13 -0
  15. package/dist/components.es.js +1467 -0
  16. package/dist/components.es.js.map +1 -0
  17. package/dist/index.d.ts +155 -115
  18. package/package.json +24 -2
  19. package/src/App.tsx +12 -8
  20. package/src/components/KycFlow.tsx +41 -0
  21. package/src/components/index.ts +3 -0
  22. package/src/contexts/KycContext.tsx +66 -0
  23. package/src/features/documentUpload/hooks/useDocumentUpload.ts +226 -0
  24. package/src/features/documentUpload/index.ts +3 -0
  25. package/src/features/documentUpload/types.ts +16 -0
  26. package/src/features/faceScan/hooks/useCamera.ts +62 -0
  27. package/src/features/faceScan/hooks/useFaceScan.ts +249 -0
  28. package/src/features/faceScan/index.ts +4 -0
  29. package/src/features/faceScan/types.ts +29 -0
  30. package/src/index.css +13 -62
  31. package/src/pages/DocumentUploadModal.tsx +262 -0
  32. package/src/pages/FaceScanModal.tsx +207 -0
  33. package/src/pages/MobileRoute.tsx +42 -0
  34. package/src/pages/QRCodePage.tsx +125 -0
  35. package/src/sdk/index.ts +18 -30
  36. package/src/services/faceMeshService.ts +382 -0
  37. package/src/services/index.ts +5 -0
  38. package/src/services/kycApiService.ts +194 -0
  39. package/src/utils/deviceDetection.ts +28 -0
  40. package/dist/astra-sdk.umd.js +0 -2
  41. package/dist/astra-sdk.umd.js.map +0 -1
  42. package/dist/vite.svg +0 -1
  43. package/src/App.css +0 -42
@@ -0,0 +1,382 @@
1
+ import { FaceMesh, FACEMESH_TESSELATION, FACEMESH_FACE_OVAL, FACEMESH_LEFT_EYE, FACEMESH_RIGHT_EYE, FACEMESH_LIPS } from '@mediapipe/face_mesh';
2
+ import { drawConnectors, drawLandmarks as drawMPLandmarks } from '@mediapipe/drawing_utils';
3
+
4
+ export type LivenessStage = 'CENTER' | 'LEFT' | 'RIGHT' | 'SNAP' | 'DONE';
5
+
6
+ export interface FaceMeshServiceCallbacks {
7
+ onFaceDetected?: (faceOnCanvas: Array<{ x: number; y: number }>) => void;
8
+ onLivenessUpdate?: (stage: LivenessStage, instruction: string) => void;
9
+ onModelLoaded?: () => void;
10
+ onModelFailed?: (error: Error) => void;
11
+ onCaptureTrigger?: () => void;
12
+ }
13
+
14
+ export interface LivenessState {
15
+ centerHold: number;
16
+ leftHold: number;
17
+ rightHold: number;
18
+ snapTriggered: boolean;
19
+ lastResultsAt: number;
20
+ stage: LivenessStage;
21
+ livenessReady: boolean;
22
+ }
23
+
24
+ export class FaceMeshService {
25
+ private faceMesh: FaceMesh | null = null;
26
+ private videoRef: React.RefObject<HTMLVideoElement | null>;
27
+ private canvasRef: React.RefObject<HTMLCanvasElement | null>;
28
+ private callbacks: FaceMeshServiceCallbacks;
29
+ private cameraDriverRef: React.MutableRefObject<number | null>;
30
+ private livenessStateRef: React.MutableRefObject<LivenessState>;
31
+ private cancelled = false;
32
+
33
+ constructor(
34
+ videoRef: React.RefObject<HTMLVideoElement | null>,
35
+ canvasRef: React.RefObject<HTMLCanvasElement | null>,
36
+ cameraDriverRef: React.MutableRefObject<number | null>,
37
+ livenessStateRef: React.MutableRefObject<LivenessState>,
38
+ callbacks: FaceMeshServiceCallbacks
39
+ ) {
40
+ this.videoRef = videoRef;
41
+ this.canvasRef = canvasRef;
42
+ this.cameraDriverRef = cameraDriverRef;
43
+ this.livenessStateRef = livenessStateRef;
44
+ this.callbacks = callbacks;
45
+ }
46
+
47
+ private drawOverlays(ctx: CanvasRenderingContext2D, normalized: Array<{ x: number; y: number }>) {
48
+ drawConnectors(ctx, normalized as any, FACEMESH_TESSELATION, { color: "#60a5fa", lineWidth: 0.5 });
49
+ drawConnectors(ctx, normalized as any, FACEMESH_FACE_OVAL, { color: "#f59e0b", lineWidth: 2 });
50
+ drawConnectors(ctx, normalized as any, FACEMESH_LEFT_EYE, { color: "#10b981", lineWidth: 1.5 });
51
+ drawConnectors(ctx, normalized as any, FACEMESH_RIGHT_EYE, { color: "#ef4444", lineWidth: 1.5 });
52
+ drawConnectors(ctx, normalized as any, FACEMESH_LIPS, { color: "#a855f7", lineWidth: 1.5 });
53
+ drawMPLandmarks(ctx, normalized as any, { color: "#2563eb", lineWidth: 0, radius: 1.5 });
54
+ }
55
+
56
+ private processResults(results: any) {
57
+ const canvas = this.canvasRef.current;
58
+ if (!canvas) return;
59
+ const ctx = canvas.getContext("2d");
60
+ if (!ctx) return;
61
+
62
+ const dpr = Math.max(1, Math.min(3, window.devicePixelRatio || 1));
63
+ const displayW = (canvas.parentElement as HTMLElement)?.clientWidth || canvas.width;
64
+ const displayH = (canvas.parentElement as HTMLElement)?.clientHeight || canvas.height;
65
+ if (canvas.width !== Math.round(displayW * dpr) || canvas.height !== Math.round(displayH * dpr)) {
66
+ canvas.width = Math.round(displayW * dpr);
67
+ canvas.height = Math.round(displayH * dpr);
68
+ }
69
+ const w = canvas.width, h = canvas.height;
70
+
71
+ ctx.fillStyle = 'rgb(0, 0, 0)';
72
+ ctx.fillRect(0, 0, w, h);
73
+
74
+ const faces = results.multiFaceLandmarks as Array<Array<{ x: number; y: number }>> | undefined;
75
+ const face = faces && faces[0];
76
+
77
+ if (face) {
78
+ const vid = this.videoRef.current as HTMLVideoElement | null;
79
+ const vidW = Math.max(1, vid?.videoWidth || displayW);
80
+ const vidH = Math.max(1, vid?.videoHeight || displayH);
81
+ const scale = Math.max(w / vidW, h / vidH);
82
+ const offsetX = (w - vidW * scale) / 2;
83
+ const offsetY = (h - vidH * scale) / 2;
84
+
85
+ const faceOnCanvas = face.map(p => {
86
+ const mappedX = (p.x * vidW * scale + offsetX) / w;
87
+ return {
88
+ x: 1 - mappedX,
89
+ y: (p.y * vidH * scale + offsetY) / h,
90
+ };
91
+ });
92
+
93
+ const guideCX = w / 2;
94
+ const guideCY = h / 2;
95
+ const guideR = Math.min(w, h) * 0.45;
96
+
97
+ ctx.save();
98
+ ctx.beginPath();
99
+ ctx.arc(guideCX, guideCY, guideR, 0, Math.PI * 2);
100
+ ctx.clip();
101
+ ctx.drawImage(vid!, offsetX, offsetY, vidW * scale, vidH * scale);
102
+ ctx.restore();
103
+
104
+ this.drawOverlays(ctx, faceOnCanvas as any);
105
+
106
+ this.livenessStateRef.current.lastResultsAt = Date.now();
107
+
108
+ if (this.callbacks.onFaceDetected) {
109
+ this.callbacks.onFaceDetected(faceOnCanvas);
110
+ }
111
+
112
+ this.processLiveness(faceOnCanvas, w, h);
113
+ } else {
114
+ const vid = this.videoRef.current as HTMLVideoElement | null;
115
+ if (vid) {
116
+ const vidW = Math.max(1, vid?.videoWidth || displayW);
117
+ const vidH = Math.max(1, vid?.videoHeight || displayH);
118
+ const scale = Math.max(w / vidW, h / vidH);
119
+ const offsetX = (w - vidW * scale) / 2;
120
+ const offsetY = (h - vidH * scale) / 2;
121
+
122
+ const guideCX = w / 2;
123
+ const guideCY = h / 2;
124
+ const guideR = Math.min(w, h) * 0.45;
125
+
126
+ ctx.save();
127
+ ctx.beginPath();
128
+ ctx.arc(guideCX, guideCY, guideR, 0, Math.PI * 2);
129
+ ctx.clip();
130
+ ctx.drawImage(vid, offsetX, offsetY, vidW * scale, vidH * scale);
131
+ ctx.restore();
132
+ }
133
+
134
+ if (Date.now() - this.livenessStateRef.current.lastResultsAt > 2000) {
135
+ if (this.callbacks.onLivenessUpdate) {
136
+ this.callbacks.onLivenessUpdate(
137
+ this.livenessStateRef.current.stage,
138
+ "No face detected. Center your face in frame with good lighting."
139
+ );
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ private processLiveness(faceOnCanvas: Array<{ x: number; y: number }>, w: number, h: number) {
146
+ const eyeA = faceOnCanvas[33];
147
+ const eyeB = faceOnCanvas[263];
148
+ const flipX = (p: any) => ({ x: 1 - p.x, y: p.y });
149
+ const eA = flipX(eyeA);
150
+ const eB = flipX(eyeB);
151
+ const n1 = faceOnCanvas[1];
152
+ const n4 = faceOnCanvas[4];
153
+ const nT = flipX(n1 && n4 ? { x: (n1.x + n4.x) / 2, y: (n1.y + n4.y) / 2 } : (n1 || n4 || faceOnCanvas[197]));
154
+ const leftEyeOuter = eA.x < eB.x ? eA : eB;
155
+ const rightEyeOuter = eA.x < eB.x ? eB : eA;
156
+
157
+ if (leftEyeOuter && rightEyeOuter && nT) {
158
+ const faceWidth = Math.abs(rightEyeOuter.x - leftEyeOuter.x);
159
+ const midX = (leftEyeOuter.x + rightEyeOuter.x) / 2;
160
+ const yaw = (nT.x - midX) / Math.max(1e-6, faceWidth);
161
+ const absYaw = Math.abs(yaw);
162
+
163
+ const xs = faceOnCanvas.map(p => p.x), ys = faceOnCanvas.map(p => p.y);
164
+ const minX = Math.min(...xs) * w, maxX = Math.max(...xs) * w;
165
+ const minY = Math.min(...ys) * h, maxY = Math.max(...ys) * h;
166
+ const boxCX = (minX + maxX) / 2, boxCY = (minY + maxY) / 2;
167
+ const guideCX = w / 2;
168
+ const guideCY = h / 2;
169
+ const guideR = Math.min(w, h) * 0.45;
170
+ const dx = boxCX - guideCX;
171
+ const dy = boxCY - guideCY;
172
+ const insideGuide = (dx * dx + dy * dy) <= (guideR * guideR)
173
+ && (maxX - minX) <= guideR * 2 * 1.05 && (maxY - minY) <= guideR * 2 * 1.05;
174
+
175
+ if (!this.livenessStateRef.current.livenessReady) {
176
+ this.livenessStateRef.current.livenessReady = true;
177
+ }
178
+
179
+ const centerThreshold = 0.05;
180
+ const leftThreshold = 0.08;
181
+ const rightThreshold = 0.08;
182
+ const holdFramesCenter = 12;
183
+ const holdFramesTurn = 12;
184
+
185
+ const state = this.livenessStateRef.current;
186
+
187
+ if (state.stage === "CENTER") {
188
+ if (!insideGuide) {
189
+ if (this.callbacks.onLivenessUpdate) {
190
+ this.callbacks.onLivenessUpdate(state.stage, "Center your face inside the circle");
191
+ }
192
+ } else if (absYaw < centerThreshold) {
193
+ state.centerHold += 1;
194
+ if (state.centerHold >= holdFramesCenter) {
195
+ const newStage: LivenessStage = "LEFT";
196
+ state.stage = newStage;
197
+ state.centerHold = 0;
198
+ if (this.callbacks.onLivenessUpdate) {
199
+ this.callbacks.onLivenessUpdate(newStage, "Turn your face LEFT");
200
+ }
201
+ }
202
+ } else {
203
+ state.centerHold = 0;
204
+ if (this.callbacks.onLivenessUpdate) {
205
+ this.callbacks.onLivenessUpdate(state.stage, yaw > 0 ? "Move your face slightly LEFT" : "Move your face slightly RIGHT");
206
+ }
207
+ }
208
+ } else if (state.stage === "LEFT") {
209
+ if (faceWidth < 0.08) {
210
+ if (this.callbacks.onLivenessUpdate) {
211
+ this.callbacks.onLivenessUpdate(state.stage, "Move closer to the camera");
212
+ }
213
+ } else if (yaw < -leftThreshold) {
214
+ state.leftHold += 1;
215
+ if (state.leftHold >= holdFramesTurn) {
216
+ const newStage: LivenessStage = "RIGHT";
217
+ state.stage = newStage;
218
+ state.leftHold = 0;
219
+ if (this.callbacks.onLivenessUpdate) {
220
+ this.callbacks.onLivenessUpdate(newStage, "Great! Now turn your face RIGHT");
221
+ }
222
+ }
223
+ } else {
224
+ state.leftHold = 0;
225
+ if (this.callbacks.onLivenessUpdate) {
226
+ this.callbacks.onLivenessUpdate(state.stage, yaw > rightThreshold ? "You're facing right. Turn LEFT" : "Turn a bit more LEFT");
227
+ }
228
+ }
229
+ } else if (state.stage === "RIGHT") {
230
+ if (faceWidth < 0.08) {
231
+ if (this.callbacks.onLivenessUpdate) {
232
+ this.callbacks.onLivenessUpdate(state.stage, "Move closer to the camera");
233
+ }
234
+ } else if (yaw > rightThreshold) {
235
+ state.rightHold += 1;
236
+ if (state.rightHold >= holdFramesTurn) {
237
+ state.rightHold = 0;
238
+ if (!state.snapTriggered) {
239
+ state.snapTriggered = true;
240
+ const newStage: LivenessStage = "DONE";
241
+ state.stage = newStage;
242
+ if (this.callbacks.onLivenessUpdate) {
243
+ this.callbacks.onLivenessUpdate(newStage, "Capturing...");
244
+ }
245
+ if (this.callbacks.onCaptureTrigger) {
246
+ this.callbacks.onCaptureTrigger();
247
+ }
248
+ }
249
+ }
250
+ } else {
251
+ state.rightHold = 0;
252
+ if (this.callbacks.onLivenessUpdate) {
253
+ this.callbacks.onLivenessUpdate(state.stage, yaw < -leftThreshold ? "You're facing left. Turn RIGHT" : "Turn a bit more RIGHT");
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ private waitForVideoReady(): Promise<void> {
261
+ return new Promise((resolve, reject) => {
262
+ let attempts = 0;
263
+ const maxAttempts = 100;
264
+
265
+ const checkReady = () => {
266
+ if (this.cancelled) {
267
+ reject(new Error('Cancelled'));
268
+ return;
269
+ }
270
+
271
+ const video = this.videoRef.current;
272
+ if (
273
+ video &&
274
+ video.readyState >= 2 &&
275
+ video.videoWidth > 0 &&
276
+ video.videoHeight > 0 &&
277
+ !isNaN(video.videoWidth) &&
278
+ !isNaN(video.videoHeight)
279
+ ) {
280
+ resolve();
281
+ } else if (attempts >= maxAttempts) {
282
+ if (video) {
283
+ resolve();
284
+ } else {
285
+ reject(new Error('Video not ready'));
286
+ }
287
+ } else {
288
+ attempts++;
289
+ requestAnimationFrame(checkReady);
290
+ }
291
+ };
292
+ checkReady();
293
+ });
294
+ }
295
+
296
+ async initialize(): Promise<void> {
297
+ try {
298
+ const fm = new FaceMesh({
299
+ locateFile: (file: string) => `https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh@0.4.1633559619/${file}`
300
+ });
301
+ fm.setOptions({
302
+ selfieMode: true,
303
+ maxNumFaces: 1,
304
+ refineLandmarks: true,
305
+ minDetectionConfidence: 0.5,
306
+ minTrackingConfidence: 0.5
307
+ } as any);
308
+
309
+ this.faceMesh = fm;
310
+
311
+ if (this.cancelled) return;
312
+
313
+ if (this.callbacks.onModelLoaded) {
314
+ this.callbacks.onModelLoaded();
315
+ }
316
+
317
+ fm.onResults((results) => {
318
+ if (!this.cancelled) {
319
+ this.processResults(results);
320
+ }
321
+ });
322
+
323
+ if (this.videoRef.current) {
324
+ try {
325
+ await this.waitForVideoReady();
326
+ } catch (error) {
327
+ if (!this.cancelled) {
328
+ console.debug('Video ready check failed, continuing anyway:', error);
329
+ }
330
+ }
331
+
332
+ const tick = async () => {
333
+ if (this.cancelled) return;
334
+ if (!this.videoRef.current || !this.faceMesh) return;
335
+
336
+ const video = this.videoRef.current;
337
+ if (
338
+ video.readyState >= 2 &&
339
+ video.videoWidth > 0 &&
340
+ video.videoHeight > 0 &&
341
+ !isNaN(video.videoWidth) &&
342
+ !isNaN(video.videoHeight)
343
+ ) {
344
+ try {
345
+ await this.faceMesh.send({ image: video as HTMLVideoElement });
346
+ } catch (error) {
347
+ if (!this.cancelled) {
348
+ console.debug('MediaPipe send error (non-critical):', error);
349
+ }
350
+ }
351
+ }
352
+
353
+ if (!this.cancelled) {
354
+ this.cameraDriverRef.current = requestAnimationFrame(tick);
355
+ }
356
+ };
357
+
358
+ this.cameraDriverRef.current = requestAnimationFrame(tick);
359
+ }
360
+ } catch (e) {
361
+ if (!this.cancelled && this.callbacks.onModelFailed) {
362
+ this.callbacks.onModelFailed(e as Error);
363
+ }
364
+ throw e;
365
+ }
366
+ }
367
+
368
+ cleanup(): void {
369
+ this.cancelled = true;
370
+ if (this.cameraDriverRef.current) {
371
+ cancelAnimationFrame(this.cameraDriverRef.current);
372
+ this.cameraDriverRef.current = null;
373
+ }
374
+ if (this.faceMesh) {
375
+ try {
376
+ (this.faceMesh as any).close?.();
377
+ } catch {}
378
+ this.faceMesh = null;
379
+ }
380
+ }
381
+ }
382
+
@@ -0,0 +1,5 @@
1
+ export { FaceMeshService } from './faceMeshService';
2
+ export type { LivenessStage, FaceMeshServiceCallbacks, LivenessState } from './faceMeshService';
3
+ export { KycApiService } from './kycApiService';
4
+ export type { KycApiConfig, SessionStatusResponse, FaceScanResponse, DocumentUploadResponse } from './kycApiService';
5
+
@@ -0,0 +1,194 @@
1
+ /**
2
+ * KYC API Service
3
+ * Handles all KYC-related API calls (face scan, document upload, status check)
4
+ */
5
+
6
+ export interface KycApiConfig {
7
+ apiBaseUrl: string;
8
+ sessionId: string;
9
+ serverKey: string;
10
+ deviceType?: string;
11
+ }
12
+
13
+ export interface SessionStatusResponse {
14
+ status: string;
15
+ message: string;
16
+ data: {
17
+ session_id: string;
18
+ status: 'ACTIVE' | 'INACTIVE' | 'EXPIRED' | 'COMPLETED';
19
+ completed_steps: string[];
20
+ next_step: string;
21
+ };
22
+ }
23
+
24
+ export interface FaceScanResponse {
25
+ status: string;
26
+ message: string;
27
+ data?: unknown;
28
+ }
29
+
30
+ export interface DocumentUploadResponse {
31
+ status: string;
32
+ message: string;
33
+ data?: unknown;
34
+ }
35
+
36
+ export class KycApiService {
37
+ private config: KycApiConfig;
38
+
39
+ constructor(config: KycApiConfig) {
40
+ this.config = config;
41
+ }
42
+
43
+ /**
44
+ * Detect device type
45
+ */
46
+ private detectDeviceType(): string {
47
+ const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera;
48
+ if (/android/i.test(userAgent)) return 'android';
49
+ if (/iPad|iPhone|iPod/.test(userAgent) && !(window as any).MSStream) return 'ios';
50
+ if (/Mac|Windows|Linux/.test(userAgent)) return 'desktop';
51
+ return 'unknown';
52
+ }
53
+
54
+ /**
55
+ * Get session status
56
+ */
57
+ async getSessionStatus(): Promise<SessionStatusResponse> {
58
+ const deviceType = this.config.deviceType || this.detectDeviceType();
59
+
60
+ try {
61
+ const response = await fetch(
62
+ `${this.config.apiBaseUrl}/api/v2/dashboard/merchant/onsite/session/${this.config.sessionId}/status`,
63
+ {
64
+ method: 'GET',
65
+ headers: {
66
+ 'x-server-key': this.config.serverKey,
67
+ 'device-type': deviceType,
68
+ 'Content-Type': 'application/json',
69
+ },
70
+ credentials: 'include',
71
+ }
72
+ );
73
+
74
+ if (!response.ok) {
75
+ const errorData = await response.json().catch(() => ({}));
76
+ const message = errorData?.message || `Status fetch failed with status ${response.status}`;
77
+ throw new Error(message);
78
+ }
79
+
80
+ const data = await response.json();
81
+ return data;
82
+ } catch (error: any) {
83
+ const message = error?.message || 'Status fetch failed';
84
+ throw new Error(`Status fetch failed: ${message}`);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Upload face scan image
90
+ */
91
+ async uploadFaceScan(faceBlob: Blob | File): Promise<FaceScanResponse> {
92
+ // Check session status first
93
+ await this.checkSessionActive();
94
+
95
+ const deviceType = this.config.deviceType || this.detectDeviceType();
96
+ const formData = new FormData();
97
+ const faceFileName = (faceBlob as File)?.name || `face-${Date.now()}.jpg`;
98
+ formData.append('face_scan_img', faceBlob, faceFileName);
99
+
100
+ try {
101
+ const response = await fetch(
102
+ `${this.config.apiBaseUrl}/api/v2/dashboard/merchant/onsite/session/${this.config.sessionId}/face`,
103
+ {
104
+ method: 'POST',
105
+ headers: {
106
+ 'x-server-key': this.config.serverKey,
107
+ 'device-type': deviceType,
108
+ },
109
+ credentials: 'include',
110
+ body: formData,
111
+ }
112
+ );
113
+
114
+ if (!response.ok) {
115
+ const errorData = await response.json().catch(() => ({}));
116
+ const message = errorData?.message || `Face upload failed with status ${response.status}`;
117
+ throw new Error(message);
118
+ }
119
+
120
+ const data = await response.json();
121
+ return data;
122
+ } catch (error: any) {
123
+ const message = error?.message || 'Face upload failed';
124
+ throw new Error(`Face upload failed: ${message}`);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Upload document scan image
130
+ */
131
+ async uploadDocument(docBlob: Blob | File, docType: string): Promise<DocumentUploadResponse> {
132
+ // Check session status first
133
+ await this.checkSessionActive();
134
+
135
+ const deviceType = this.config.deviceType || this.detectDeviceType();
136
+ const formData = new FormData();
137
+ const docFileName = (docBlob as File)?.name || `document-${Date.now()}.jpg`;
138
+ formData.append('docs_scan_img', docBlob, docFileName);
139
+ formData.append('docType', docType);
140
+
141
+ try {
142
+ const response = await fetch(
143
+ `${this.config.apiBaseUrl}/api/v2/dashboard/merchant/onsite/session/${this.config.sessionId}/docs`,
144
+ {
145
+ method: 'POST',
146
+ headers: {
147
+ 'x-server-key': this.config.serverKey,
148
+ 'device-type': deviceType,
149
+ },
150
+ credentials: 'include',
151
+ body: formData,
152
+ }
153
+ );
154
+
155
+ if (!response.ok) {
156
+ const errorData = await response.json().catch(() => ({}));
157
+ const message = errorData?.message || `Document upload failed with status ${response.status}`;
158
+ throw new Error(message);
159
+ }
160
+
161
+ const data = await response.json();
162
+ return data;
163
+ } catch (error: any) {
164
+ const message = error?.message || 'Document upload failed';
165
+ throw new Error(`Document upload failed: ${message}`);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Check if session is active, throw error if not
171
+ */
172
+ async checkSessionActive(): Promise<void> {
173
+ const status = await this.getSessionStatus();
174
+
175
+ if (status.data.status !== 'ACTIVE') {
176
+ throw new Error('Session expired or inactive. Please start a new session.');
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Update configuration
182
+ */
183
+ updateConfig(config: Partial<KycApiConfig>): void {
184
+ this.config = { ...this.config, ...config };
185
+ }
186
+
187
+ /**
188
+ * Get current configuration
189
+ */
190
+ getConfig(): KycApiConfig {
191
+ return { ...this.config };
192
+ }
193
+ }
194
+
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Utility functions for device detection
3
+ */
4
+
5
+ export function isMobileDevice(): boolean {
6
+ if (typeof window === 'undefined') {
7
+ return false;
8
+ }
9
+
10
+ // Check user agent
11
+ const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera;
12
+
13
+ // Common mobile device patterns
14
+ const mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i;
15
+
16
+ // Check screen width (mobile devices typically have smaller screens)
17
+ const isSmallScreen = window.innerWidth <= 768;
18
+
19
+ // Check for touch support
20
+ const hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
21
+
22
+ return mobileRegex.test(userAgent) || (isSmallScreen && hasTouchScreen);
23
+ }
24
+
25
+ export function getDeviceType(): 'mobile' | 'desktop' {
26
+ return isMobileDevice() ? 'mobile' : 'desktop';
27
+ }
28
+
@@ -1,2 +0,0 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).AstraSDK={})}(this,function(t){"use strict";class e extends Error{status;code;details;constructor(t,s,r,i){super(t),this.name="AstraSDKError",this.status=s,this.code=r,this.details=i,Object.setPrototypeOf(this,e.prototype)}}class s{config;constructor(t){this.config=t}async get(t,e){return this.request(t,{...e,method:"GET"})}async post(t,e,s){return this.request(t,{...s,method:"POST",body:e})}async put(t,e,s){return this.request(t,{...s,method:"PUT",body:e})}async patch(t,e,s){return this.request(t,{...s,method:"PATCH",body:e})}async delete(t,e){return this.request(t,{...e,method:"DELETE"})}async request(t,s={}){const r=()=>async function(t,s={},r){const{method:i="GET",headers:n={},body:a,params:o,timeout:c=r.timeout}=s,u=new URL(t,r.baseURL);o&&Object.entries(o).forEach(([t,e])=>{u.searchParams.append(t,String(e))});const h={method:i,headers:new Headers({...r.headers,...n,Authorization:`Bearer ${r.apiKey}`}),signal:AbortSignal.timeout(c)};a&&"GET"!==i&&(h.body=JSON.stringify(a));try{const t=await fetch(u.toString(),h),s={};let r;t.headers.forEach((t,e)=>{s[e]=t});const i=t.headers.get("content-type");if(r=i?.includes("application/json")?await t.json():await t.text(),!t.ok){const s={message:`Request failed with status ${t.status}`,status:t.status,code:t.statusText,details:r};throw new e(s.message,s.status,s.code,s.details)}return{data:r,status:t.status,statusText:t.statusText,headers:s}}catch(d){if(d instanceof e)throw d;if(d instanceof Error){if("AbortError"===d.name)throw new e("Request timeout",408,"TIMEOUT");throw new e(d.message,void 0,"NETWORK_ERROR")}throw new e("Unknown error occurred",void 0,"UNKNOWN_ERROR")}}(t,s,{apiKey:this.config.apiKey,baseURL:this.config.baseURL,timeout:this.config.timeout,headers:this.config.headers});return this.config.retries>0?async function(t,s,r){let i;for(let a=0;a<=s;a++)try{return await t()}catch(n){if(i=n instanceof Error?n:new Error(String(n)),n instanceof e&&n.status&&n.status>=400&&n.status<500&&429!==n.status)throw n;a<s&&await new Promise(t=>setTimeout(t,r*(a+1)))}throw i}(r,this.config.retries,this.config.retryDelay):r()}updateConfig(t){this.config={...this.config,...t,headers:{...this.config.headers,...t.headers}}}getConfig(){return{...this.config}}}const r="https://api.astra.com",i=3e4,n={"Content-Type":"application/json"},a=3,o=1e3;class c{client;constructor(t){if(!t.apiKey)throw new e("API key is required",400,"MISSING_API_KEY");const c={apiKey:(u=t).apiKey,baseURL:u.baseURL??r,timeout:u.timeout??i,headers:{...n,...u.headers},retries:u.retries??a,retryDelay:u.retryDelay??o};var u;this.client=new s(c)}getClient(){return this.client}updateConfig(t){this.client.updateConfig(t)}async get(t,e){return this.client.get(t,e)}async post(t,e,s){return this.client.post(t,e,s)}async put(t,e,s){return this.client.put(t,e,s)}async patch(t,e,s){return this.client.patch(t,e,s)}async delete(t,e){return this.client.delete(t,e)}async request(t,e){return this.client.request(t,e)}}t.ApiClient=s,t.AstraSDK=c,t.AstraSDKError=e,t.default=c,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
2
- //# sourceMappingURL=astra-sdk.umd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"astra-sdk.umd.js","sources":["../src/sdk/types/index.ts","../src/sdk/client/index.ts","../src/sdk/utils/http.ts","../src/sdk/config/index.ts","../src/sdk/index.ts"],"sourcesContent":["\r\nexport interface AstraSDKConfig {\r\n apiKey: string;\r\n baseURL?: string;\r\n timeout?: number;\r\n headers?: Record<string, string>;\r\n retries?: number;\r\n retryDelay?: number;\r\n}\r\n\r\nexport interface RequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n headers?: Record<string, string>;\r\n body?: unknown;\r\n params?: Record<string, string | number | boolean>;\r\n timeout?: number;\r\n}\r\n\r\nexport interface ApiResponse<T = unknown> {\r\n data: T;\r\n status: number;\r\n statusText: string;\r\n headers: Record<string, string>;\r\n}\r\n\r\nexport interface ApiError {\r\n message: string;\r\n status?: number;\r\n code?: string;\r\n details?: unknown;\r\n}\r\n\r\nexport class AstraSDKError extends Error {\r\n status?: number;\r\n code?: string;\r\n details?: unknown;\r\n\r\n constructor(message: string, status?: number, code?: string, details?: unknown) {\r\n super(message);\r\n this.name = 'AstraSDKError';\r\n this.status = status;\r\n this.code = code;\r\n this.details = details;\r\n Object.setPrototypeOf(this, AstraSDKError.prototype);\r\n }\r\n}\r\n\r\n","/**\r\n * API Client for Astra SDK\r\n */\r\n\r\nimport type { RequestOptions, ApiResponse } from '../types';\r\nimport { makeRequest, retryRequest } from '../utils/http';\r\nimport type { AstraSDKConfig } from '../types';\r\n\r\nexport class ApiClient {\r\n private config: Required<AstraSDKConfig>;\r\n\r\n constructor(config: Required<AstraSDKConfig>) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n * Make a GET request\r\n */\r\n async get<T = unknown>(\r\n endpoint: string,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.request<T>(endpoint, { ...options, method: 'GET' });\r\n }\r\n\r\n /**\r\n * Make a POST request\r\n */\r\n async post<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.request<T>(endpoint, { ...options, method: 'POST', body });\r\n }\r\n\r\n /**\r\n * Make a PUT request\r\n */\r\n async put<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.request<T>(endpoint, { ...options, method: 'PUT', body });\r\n }\r\n\r\n /**\r\n * Make a PATCH request\r\n */\r\n async patch<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.request<T>(endpoint, { ...options, method: 'PATCH', body });\r\n }\r\n\r\n /**\r\n * Make a DELETE request\r\n */\r\n async delete<T = unknown>(\r\n endpoint: string,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.request<T>(endpoint, { ...options, method: 'DELETE' });\r\n }\r\n\r\n /**\r\n * Make a generic request\r\n */\r\n async request<T = unknown>(\r\n endpoint: string,\r\n options: RequestOptions = {}\r\n ): Promise<ApiResponse<T>> {\r\n const requestFn = () =>\r\n makeRequest<T>(endpoint, options, {\r\n apiKey: this.config.apiKey,\r\n baseURL: this.config.baseURL,\r\n timeout: this.config.timeout,\r\n headers: this.config.headers,\r\n });\r\n\r\n if (this.config.retries > 0) {\r\n return retryRequest(requestFn, this.config.retries, this.config.retryDelay);\r\n }\r\n\r\n return requestFn();\r\n }\r\n\r\n /**\r\n * Update configuration\r\n */\r\n updateConfig(config: Partial<AstraSDKConfig>): void {\r\n this.config = {\r\n ...this.config,\r\n ...config,\r\n headers: {\r\n ...this.config.headers,\r\n ...config.headers,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Get current configuration\r\n */\r\n getConfig(): Required<AstraSDKConfig> {\r\n return { ...this.config };\r\n }\r\n}\r\n\r\n","/**\r\n * HTTP utility functions\r\n */\r\n\r\nimport type { RequestOptions, ApiResponse, ApiError } from '../types';\r\nimport { AstraSDKError } from '../types';\r\n\r\nexport async function makeRequest<T = unknown>(\r\n url: string,\r\n options: RequestOptions = {},\r\n config: {\r\n apiKey: string;\r\n baseURL: string;\r\n timeout: number;\r\n headers: Record<string, string>;\r\n }\r\n): Promise<ApiResponse<T>> {\r\n const {\r\n method = 'GET',\r\n headers: customHeaders = {},\r\n body,\r\n params,\r\n timeout = config.timeout,\r\n } = options;\r\n\r\n // Build URL with query parameters\r\n const urlObj = new URL(url, config.baseURL);\r\n if (params) {\r\n Object.entries(params).forEach(([key, value]) => {\r\n urlObj.searchParams.append(key, String(value));\r\n });\r\n }\r\n\r\n // Prepare headers\r\n const headers = new Headers({\r\n ...config.headers,\r\n ...customHeaders,\r\n Authorization: `Bearer ${config.apiKey}`,\r\n });\r\n\r\n // Prepare request options\r\n const requestOptions: RequestInit = {\r\n method,\r\n headers,\r\n signal: AbortSignal.timeout(timeout),\r\n };\r\n\r\n if (body && method !== 'GET') {\r\n requestOptions.body = JSON.stringify(body);\r\n }\r\n\r\n try {\r\n const response = await fetch(urlObj.toString(), requestOptions);\r\n const responseHeaders: Record<string, string> = {};\r\n response.headers.forEach((value, key) => {\r\n responseHeaders[key] = value;\r\n });\r\n\r\n let data: T;\r\n const contentType = response.headers.get('content-type');\r\n if (contentType?.includes('application/json')) {\r\n data = await response.json();\r\n } else {\r\n data = (await response.text()) as unknown as T;\r\n }\r\n\r\n if (!response.ok) {\r\n const error: ApiError = {\r\n message: `Request failed with status ${response.status}`,\r\n status: response.status,\r\n code: response.statusText,\r\n details: data,\r\n };\r\n throw new AstraSDKError(\r\n error.message,\r\n error.status,\r\n error.code,\r\n error.details\r\n );\r\n }\r\n\r\n return {\r\n data,\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers: responseHeaders,\r\n };\r\n } catch (error) {\r\n if (error instanceof AstraSDKError) {\r\n throw error;\r\n }\r\n if (error instanceof Error) {\r\n if (error.name === 'AbortError') {\r\n throw new AstraSDKError('Request timeout', 408, 'TIMEOUT');\r\n }\r\n throw new AstraSDKError(error.message, undefined, 'NETWORK_ERROR');\r\n }\r\n throw new AstraSDKError('Unknown error occurred', undefined, 'UNKNOWN_ERROR');\r\n }\r\n}\r\n\r\nexport async function retryRequest<T>(\r\n fn: () => Promise<T>,\r\n retries: number,\r\n delay: number\r\n): Promise<T> {\r\n let lastError: Error;\r\n \r\n for (let i = 0; i <= retries; i++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error instanceof Error ? error : new Error(String(error));\r\n \r\n // Don't retry on client errors (4xx) except 429 (rate limit)\r\n if (error instanceof AstraSDKError && error.status) {\r\n if (error.status >= 400 && error.status < 500 && error.status !== 429) {\r\n throw error;\r\n }\r\n }\r\n \r\n if (i < retries) {\r\n await new Promise((resolve) => setTimeout(resolve, delay * (i + 1)));\r\n }\r\n }\r\n }\r\n \r\n throw lastError!;\r\n}\r\n\r\n","/**\r\n * Configuration management for Astra SDK\r\n */\r\n\r\nimport type { AstraSDKConfig } from '../types';\r\n\r\nexport const DEFAULT_CONFIG: Required<Omit<AstraSDKConfig, 'apiKey'>> = {\r\n baseURL: 'https://api.astra.com',\r\n timeout: 30000,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n retries: 3,\r\n retryDelay: 1000,\r\n};\r\n\r\nexport function mergeConfig(userConfig: AstraSDKConfig): Required<AstraSDKConfig> {\r\n return {\r\n apiKey: userConfig.apiKey,\r\n baseURL: userConfig.baseURL ?? DEFAULT_CONFIG.baseURL,\r\n timeout: userConfig.timeout ?? DEFAULT_CONFIG.timeout,\r\n headers: {\r\n ...DEFAULT_CONFIG.headers,\r\n ...userConfig.headers,\r\n },\r\n retries: userConfig.retries ?? DEFAULT_CONFIG.retries,\r\n retryDelay: userConfig.retryDelay ?? DEFAULT_CONFIG.retryDelay,\r\n };\r\n}\r\n\r\n","/**\r\n * Astra SDK - Main entry point\r\n */\r\n\r\nimport { ApiClient } from './client';\r\nimport { mergeConfig } from './config';\r\nimport type { AstraSDKConfig, ApiResponse, RequestOptions } from './types';\r\nimport { AstraSDKError } from './types';\r\n\r\nexport class AstraSDK {\r\n private client: ApiClient;\r\n\r\n constructor(config: AstraSDKConfig) {\r\n if (!config.apiKey) {\r\n throw new AstraSDKError('API key is required', 400, 'MISSING_API_KEY');\r\n }\r\n\r\n const mergedConfig = mergeConfig(config);\r\n this.client = new ApiClient(mergedConfig);\r\n }\r\n\r\n /**\r\n * Get the underlying API client\r\n */\r\n getClient(): ApiClient {\r\n return this.client;\r\n }\r\n\r\n /**\r\n * Update SDK configuration\r\n */\r\n updateConfig(config: Partial<AstraSDKConfig>): void {\r\n this.client.updateConfig(config);\r\n }\r\n\r\n /**\r\n * Make a GET request\r\n */\r\n async get<T = unknown>(\r\n endpoint: string,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.get<T>(endpoint, options);\r\n }\r\n\r\n /**\r\n * Make a POST request\r\n */\r\n async post<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.post<T>(endpoint, body, options);\r\n }\r\n\r\n /**\r\n * Make a PUT request\r\n */\r\n async put<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.put<T>(endpoint, body, options);\r\n }\r\n\r\n /**\r\n * Make a PATCH request\r\n */\r\n async patch<T = unknown>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.patch<T>(endpoint, body, options);\r\n }\r\n\r\n /**\r\n * Make a DELETE request\r\n */\r\n async delete<T = unknown>(\r\n endpoint: string,\r\n options?: Omit<RequestOptions, 'method' | 'body'>\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.delete<T>(endpoint, options);\r\n }\r\n\r\n /**\r\n * Make a generic request\r\n */\r\n async request<T = unknown>(\r\n endpoint: string,\r\n options?: RequestOptions\r\n ): Promise<ApiResponse<T>> {\r\n return this.client.request<T>(endpoint, options);\r\n }\r\n}\r\n\r\n// Export types\r\nexport type { AstraSDKConfig, ApiResponse, RequestOptions, ApiError } from './types';\r\nexport { AstraSDKError } from './types';\r\n\r\n// Export client for advanced usage\r\nexport { ApiClient } from './client';\r\n\r\n// Default export\r\nexport default AstraSDK;\r\n\r\n"],"names":["AstraSDKError","Error","status","code","details","constructor","message","super","this","name","Object","setPrototypeOf","prototype","ApiClient","config","get","endpoint","options","request","method","post","body","put","patch","requestFn","async","url","headers","customHeaders","params","timeout","urlObj","URL","baseURL","entries","forEach","key","value","searchParams","append","String","requestOptions","Headers","Authorization","apiKey","signal","AbortSignal","JSON","stringify","response","fetch","toString","responseHeaders","data","contentType","includes","json","text","ok","error","statusText","makeRequest","retries","fn","delay","lastError","i","Promise","resolve","setTimeout","retryRequest","retryDelay","updateConfig","getConfig","DEFAULT_CONFIG","AstraSDK","client","mergedConfig","userConfig","getClient","delete"],"mappings":"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"}
package/dist/vite.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>