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.
- package/dist/.htaccess +9 -0
- package/dist/astra-sdk.cjs.js +1719 -1
- package/dist/astra-sdk.cjs.js.map +1 -1
- package/dist/astra-sdk.css +934 -0
- package/dist/astra-sdk.css.map +1 -0
- package/dist/astra-sdk.d.cts +155 -0
- package/dist/astra-sdk.es.js +1706 -1
- package/dist/astra-sdk.es.js.map +1 -1
- package/dist/components.cjs.js +1473 -0
- package/dist/components.cjs.js.map +1 -0
- package/dist/components.css +934 -0
- package/dist/components.css.map +1 -0
- package/dist/components.d.cts +13 -0
- package/dist/components.d.ts +13 -0
- package/dist/components.es.js +1467 -0
- package/dist/components.es.js.map +1 -0
- package/dist/index.d.ts +155 -115
- package/package.json +24 -2
- package/src/App.tsx +12 -8
- package/src/components/KycFlow.tsx +41 -0
- package/src/components/index.ts +3 -0
- package/src/contexts/KycContext.tsx +66 -0
- package/src/features/documentUpload/hooks/useDocumentUpload.ts +226 -0
- package/src/features/documentUpload/index.ts +3 -0
- package/src/features/documentUpload/types.ts +16 -0
- package/src/features/faceScan/hooks/useCamera.ts +62 -0
- package/src/features/faceScan/hooks/useFaceScan.ts +249 -0
- package/src/features/faceScan/index.ts +4 -0
- package/src/features/faceScan/types.ts +29 -0
- package/src/index.css +13 -62
- package/src/pages/DocumentUploadModal.tsx +262 -0
- package/src/pages/FaceScanModal.tsx +207 -0
- package/src/pages/MobileRoute.tsx +42 -0
- package/src/pages/QRCodePage.tsx +125 -0
- package/src/sdk/index.ts +18 -30
- package/src/services/faceMeshService.ts +382 -0
- package/src/services/index.ts +5 -0
- package/src/services/kycApiService.ts +194 -0
- package/src/utils/deviceDetection.ts +28 -0
- package/dist/astra-sdk.umd.js +0 -2
- package/dist/astra-sdk.umd.js.map +0 -1
- package/dist/vite.svg +0 -1
- package/src/App.css +0 -42
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { useRef, useEffect, useState } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import DocumentUploadModal from './DocumentUploadModal';
|
|
4
|
+
import { useCamera } from '../features/faceScan/hooks/useCamera';
|
|
5
|
+
import { useFaceScan } from '../features/faceScan/hooks/useFaceScan';
|
|
6
|
+
import { useKycContext } from '../contexts/KycContext';
|
|
7
|
+
import '../index.css';
|
|
8
|
+
|
|
9
|
+
interface FaceScanModalProps {
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
onComplete?: (capturedImage: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function FaceScanModal({ onComplete }: FaceScanModalProps) {
|
|
15
|
+
const faceCanvasRef = useRef<HTMLCanvasElement>(null);
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
const { apiService } = useKycContext();
|
|
18
|
+
const [sessionError, setSessionError] = useState<string | null>(null);
|
|
19
|
+
|
|
20
|
+
const { videoRef, cameraReady, stopCamera } = useCamera();
|
|
21
|
+
const { state, setState, refs, handleFaceCapture } = useFaceScan(videoRef, faceCanvasRef, {
|
|
22
|
+
onFaceUpload: async (blob: Blob) => {
|
|
23
|
+
if (!apiService) {
|
|
24
|
+
throw new Error('API service not initialized');
|
|
25
|
+
}
|
|
26
|
+
await apiService.uploadFaceScan(blob);
|
|
27
|
+
},
|
|
28
|
+
onFaceCaptureComplete: (imageData: string) => {
|
|
29
|
+
if (onComplete) {
|
|
30
|
+
onComplete(imageData);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Check session status on mount
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const checkSession = async () => {
|
|
38
|
+
if (!apiService) return;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await apiService.checkSessionActive();
|
|
42
|
+
setSessionError(null);
|
|
43
|
+
} catch (error: any) {
|
|
44
|
+
const message = error.message || 'Session expired or inactive';
|
|
45
|
+
setSessionError(message);
|
|
46
|
+
// Redirect to QR page after showing error
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
navigate('/qr', { replace: true });
|
|
49
|
+
}, 2000);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
checkSession();
|
|
54
|
+
}, [apiService, navigate]);
|
|
55
|
+
|
|
56
|
+
// Sync camera ready state
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setState(prev => ({ ...prev, cameraReady }));
|
|
59
|
+
}, [cameraReady, setState]);
|
|
60
|
+
|
|
61
|
+
const handleRetry = () => {
|
|
62
|
+
stopCamera();
|
|
63
|
+
setState({
|
|
64
|
+
cameraReady: false,
|
|
65
|
+
livenessStage: 'CENTER',
|
|
66
|
+
livenessReady: false,
|
|
67
|
+
livenessFailed: false,
|
|
68
|
+
modelLoading: true,
|
|
69
|
+
modelLoaded: false,
|
|
70
|
+
livenessInstruction: 'Look straight at the camera',
|
|
71
|
+
loading: false,
|
|
72
|
+
allStepsCompleted: false,
|
|
73
|
+
capturedImage: null,
|
|
74
|
+
showDocumentUpload: false,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
refs.centerHold.current = 0;
|
|
78
|
+
refs.leftHold.current = 0;
|
|
79
|
+
refs.rightHold.current = 0;
|
|
80
|
+
refs.snapTriggered.current = false;
|
|
81
|
+
refs.lastResultsAt.current = 0;
|
|
82
|
+
refs.modelLoaded.current = false;
|
|
83
|
+
refs.livenessFailed.current = false;
|
|
84
|
+
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
window.location.reload();
|
|
87
|
+
}, 100);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (state.showDocumentUpload) {
|
|
91
|
+
return (
|
|
92
|
+
<DocumentUploadModal
|
|
93
|
+
onComplete={(_file, _docType) => {
|
|
94
|
+
if (onComplete && state.capturedImage) {
|
|
95
|
+
onComplete(state.capturedImage);
|
|
96
|
+
}
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Show session error if present
|
|
103
|
+
if (sessionError) {
|
|
104
|
+
return (
|
|
105
|
+
<div className="fixed inset-0 bg-black p-5 z-[1000] flex items-center justify-center font-sans overflow-y-auto custom__scrollbar">
|
|
106
|
+
<div className="max-w-[400px] w-full mx-auto bg-[#0b0f17] rounded-2xl p-6 shadow-xl">
|
|
107
|
+
<div className="text-center">
|
|
108
|
+
<h2 className="m-0 mb-4 text-[26px] font-bold text-red-500">
|
|
109
|
+
Session Expired
|
|
110
|
+
</h2>
|
|
111
|
+
<p className="text-[#e5e7eb] mb-4">{sessionError}</p>
|
|
112
|
+
<p className="text-[#9ca3af] text-sm">Redirecting to QR code page...</p>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className="fixed inset-0 bg-black p-5 z-[1000] flex items-center justify-center font-sans overflow-y-auto custom__scrollbar">
|
|
121
|
+
<div className="max-w-[400px] w-full mx-auto bg-[#0b0f17] rounded-2xl p-6 shadow-xl mt-48">
|
|
122
|
+
<div className="relative mb-4">
|
|
123
|
+
<h2 className="m-0 mb-4 text-[26px] font-bold text-white text-center">
|
|
124
|
+
Capture Face
|
|
125
|
+
</h2>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="grid gap-4">
|
|
129
|
+
{!state.modelLoading && state.modelLoaded && !state.livenessFailed && (
|
|
130
|
+
<div className="bg-[#0a2315] text-[#34d399] py-3.5 px-4 rounded-xl text-sm border border-[#155e3b] text-left">
|
|
131
|
+
Face detection model loaded.
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
{state.modelLoading && !state.livenessFailed && (
|
|
135
|
+
<div className="bg-[#1f2937] text-[#e5e7eb] py-3.5 px-4 rounded-xl text-sm border border-[#374151] text-left">
|
|
136
|
+
Loading face detection model...
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
<div className="relative w-full aspect-square rounded-full overflow-hidden bg-black">
|
|
141
|
+
<video
|
|
142
|
+
ref={videoRef}
|
|
143
|
+
playsInline
|
|
144
|
+
muted
|
|
145
|
+
className="w-full h-full block bg-black object-cover -scale-x-100 origin-center"
|
|
146
|
+
/>
|
|
147
|
+
<canvas
|
|
148
|
+
ref={faceCanvasRef}
|
|
149
|
+
className="absolute top-0 left-0 w-full h-full pointer-events-none z-[2]"
|
|
150
|
+
/>
|
|
151
|
+
<svg viewBox="0 0 100 100" preserveAspectRatio="none" className="absolute inset-0 pointer-events-none z-[3]">
|
|
152
|
+
<circle cx="50" cy="50" r="44" fill="none" stroke="#22c55e" strokeWidth="2" strokeDasharray="1 3" />
|
|
153
|
+
</svg>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
{!state.livenessFailed && (
|
|
157
|
+
<div className="bg-gradient-to-b from-[rgba(17,24,39,0.9)] to-[rgba(17,24,39,0.6)] text-[#e5e7eb] p-4 rounded-2xl text-base border border-[#30363d]">
|
|
158
|
+
<div className="font-bold mb-2.5 text-[22px] text-white">Liveness Check</div>
|
|
159
|
+
<div className="mb-2.5 text-base">{state.livenessInstruction}</div>
|
|
160
|
+
<div className="grid gap-2.5 text-lg">
|
|
161
|
+
<div className={state.livenessStage === "CENTER" || state.livenessStage === "LEFT" || state.livenessStage === "RIGHT" || state.livenessStage === "DONE" ? "opacity-100" : "opacity-40"}>
|
|
162
|
+
1. Look Straight
|
|
163
|
+
</div>
|
|
164
|
+
<div className={state.livenessStage === "RIGHT" || state.livenessStage === "DONE" ? "opacity-100" : "opacity-40"}>
|
|
165
|
+
2. Turn your face right
|
|
166
|
+
</div>
|
|
167
|
+
<div className={state.livenessStage === "DONE" ? "opacity-100" : "opacity-30"}>
|
|
168
|
+
3. Turn your face left
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
disabled={!state.cameraReady || state.loading || (!state.livenessFailed && state.livenessStage !== "DONE")}
|
|
177
|
+
onClick={handleFaceCapture}
|
|
178
|
+
className={`py-3.5 px-4 rounded-xl text-base font-bold border-none transition-colors ${
|
|
179
|
+
state.cameraReady && !state.loading && (state.livenessFailed || state.livenessStage === "DONE")
|
|
180
|
+
? "bg-[#22c55e] text-[#0b0f17] cursor-pointer hover:bg-[#16a34a]"
|
|
181
|
+
: "bg-[#374151] text-[#e5e7eb] cursor-not-allowed"
|
|
182
|
+
}`}
|
|
183
|
+
>
|
|
184
|
+
{state.loading
|
|
185
|
+
? "Capturing..."
|
|
186
|
+
: (state.livenessFailed || state.livenessStage === "DONE")
|
|
187
|
+
? "Capture & Continue"
|
|
188
|
+
: "Complete steps to continue"}
|
|
189
|
+
</button>
|
|
190
|
+
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
onClick={handleRetry}
|
|
194
|
+
disabled={state.loading}
|
|
195
|
+
className={`py-3 px-4 rounded-[10px] text-[15px] font-semibold border-none w-full transition-colors ${
|
|
196
|
+
state.loading ? "bg-[#374151] text-[#e5e7eb] cursor-not-allowed opacity-50" : "bg-[#374151] text-[#e5e7eb] cursor-pointer hover:bg-[#4b5563]"
|
|
197
|
+
}`}
|
|
198
|
+
>
|
|
199
|
+
Restart
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default FaceScanModal;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import { isMobileDevice } from '../utils/deviceDetection';
|
|
4
|
+
import FaceScanModal from './FaceScanModal';
|
|
5
|
+
import '../index.css';
|
|
6
|
+
|
|
7
|
+
interface MobileRouteProps {
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function MobileRoute({ onClose }: MobileRouteProps = {}) {
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!isMobileDevice()) {
|
|
16
|
+
navigate('/qr', { replace: true });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}, [navigate]);
|
|
20
|
+
|
|
21
|
+
const handleClose = () => {
|
|
22
|
+
if (onClose) {
|
|
23
|
+
onClose();
|
|
24
|
+
} else {
|
|
25
|
+
navigate(-1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const handleComplete = (capturedImage: string) => {
|
|
30
|
+
// Handle completion - send captured image to backend or parent
|
|
31
|
+
console.log('Face capture completed with image:', capturedImage.substring(0, 50) + '...');
|
|
32
|
+
// You can emit an event, call a callback, or navigate here
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (!isMobileDevice()) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return <FaceScanModal onClose={handleClose} onComplete={handleComplete} />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default MobileRoute;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { QRCodeSVG } from 'qrcode.react';
|
|
3
|
+
import { useSearchParams, useNavigate } from 'react-router-dom';
|
|
4
|
+
import '../index.css';
|
|
5
|
+
|
|
6
|
+
interface QRCodePageProps {
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function QRCodePage({ onClose }: QRCodePageProps = {}) {
|
|
11
|
+
const [searchParams] = useSearchParams();
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
const [qrUrl, setQrUrl] = useState<string>('');
|
|
14
|
+
const [copied, setCopied] = useState<boolean>(false);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const currentUrl = window.location.origin;
|
|
18
|
+
|
|
19
|
+
const params: Record<string, string> = {};
|
|
20
|
+
searchParams.forEach((value, key) => {
|
|
21
|
+
params[key] = value;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const mobileRoute = '/mobileroute';
|
|
25
|
+
const queryString = new URLSearchParams(params).toString();
|
|
26
|
+
const fullUrl = `${currentUrl}${mobileRoute}${queryString ? `?${queryString}` : ''}`;
|
|
27
|
+
|
|
28
|
+
setQrUrl(fullUrl);
|
|
29
|
+
}, [searchParams]);
|
|
30
|
+
|
|
31
|
+
const handleCopyUrl = async () => {
|
|
32
|
+
if (qrUrl) {
|
|
33
|
+
try {
|
|
34
|
+
await navigator.clipboard.writeText(qrUrl);
|
|
35
|
+
setCopied(true);
|
|
36
|
+
setTimeout(() => setCopied(false), 2000);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('Failed to copy:', err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleClose = () => {
|
|
44
|
+
if (onClose) {
|
|
45
|
+
onClose();
|
|
46
|
+
} else {
|
|
47
|
+
navigate(-1);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleRefresh = () => {
|
|
52
|
+
window.location.reload();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="fixed inset-0 flex items-center justify-center bg-black p-4 sm:p-6 md:p-8 z-[1000]">
|
|
57
|
+
<div className="relative bg-[rgba(20,20,20,0.95)] rounded-2xl p-5 sm:p-6 md:p-7 max-w-[450px] w-full h-[80vh] flex flex-col text-center shadow-[0_8px_32px_rgba(0,0,0,0.5)]">
|
|
58
|
+
<button
|
|
59
|
+
className="absolute top-5 right-5 bg-transparent border-none text-white cursor-pointer p-2 flex items-center justify-center rounded transition-colors hover:bg-white/10 active:bg-white/20"
|
|
60
|
+
onClick={handleClose}
|
|
61
|
+
aria-label="Close"
|
|
62
|
+
>
|
|
63
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
64
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
65
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
66
|
+
</svg>
|
|
67
|
+
</button>
|
|
68
|
+
|
|
69
|
+
<div className="flex flex-col items-center gap-3 sm:gap-4 flex-1 overflow-y-auto custom__scrollbar">
|
|
70
|
+
<h1 className="m-0 text-white text-xl sm:text-2xl font-semibold leading-tight">Continue on Mobile</h1>
|
|
71
|
+
<p className="m-0 text-white text-sm sm:text-base opacity-90 leading-relaxed">
|
|
72
|
+
Scan this QR on your phone to capture your face and document
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
{qrUrl && (
|
|
76
|
+
<div className="flex justify-center items-center p-3 sm:p-4 bg-white rounded-xl border-2 border-white">
|
|
77
|
+
<QRCodeSVG
|
|
78
|
+
value={qrUrl}
|
|
79
|
+
size={180}
|
|
80
|
+
level="H"
|
|
81
|
+
includeMargin={true}
|
|
82
|
+
bgColor="transparent"
|
|
83
|
+
fgColor="#000000"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
<div className="w-full text-left mt-auto">
|
|
89
|
+
<p className="m-0 mb-2 text-white text-xs sm:text-sm opacity-80">Or open:</p>
|
|
90
|
+
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 p-2 sm:p-3 bg-white/10 rounded-lg border border-white/20">
|
|
91
|
+
<code className="flex-1 text-white text-xs sm:text-sm break-all text-left m-0 font-mono">{qrUrl}</code>
|
|
92
|
+
<button
|
|
93
|
+
className="bg-transparent border-none text-white cursor-pointer p-1.5 flex items-center justify-center rounded transition-colors flex-shrink-0 hover:bg-white/10 active:bg-white/20 self-end sm:self-auto"
|
|
94
|
+
onClick={handleCopyUrl}
|
|
95
|
+
aria-label="Copy URL"
|
|
96
|
+
title={copied ? 'Copied!' : 'Copy URL'}
|
|
97
|
+
>
|
|
98
|
+
{copied ? (
|
|
99
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
100
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
101
|
+
</svg>
|
|
102
|
+
) : (
|
|
103
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
104
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
105
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
106
|
+
</svg>
|
|
107
|
+
)}
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<button
|
|
113
|
+
className="w-full py-3 sm:py-4 px-6 bg-gradient-to-r from-[#FF842D] to-[#FF2D55] border-none rounded-lg text-white text-sm sm:text-base font-semibold cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-[0_4px_12px_rgba(255,107,53,0.4)] active:translate-y-0"
|
|
114
|
+
onClick={handleRefresh}
|
|
115
|
+
>
|
|
116
|
+
I've completed on mobile - Refresh
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default QRCodePage;
|
|
125
|
+
|
package/src/sdk/index.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Astra SDK - Main entry point
|
|
3
|
-
*/
|
|
4
1
|
|
|
5
2
|
import { ApiClient } from './client';
|
|
6
3
|
import { mergeConfig } from './config';
|
|
@@ -19,23 +16,17 @@ export class AstraSDK {
|
|
|
19
16
|
this.client = new ApiClient(mergedConfig);
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
* Get the underlying API client
|
|
24
|
-
*/
|
|
19
|
+
|
|
25
20
|
getClient(): ApiClient {
|
|
26
21
|
return this.client;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
* Update SDK configuration
|
|
31
|
-
*/
|
|
24
|
+
|
|
32
25
|
updateConfig(config: Partial<AstraSDKConfig>): void {
|
|
33
26
|
this.client.updateConfig(config);
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
* Make a GET request
|
|
38
|
-
*/
|
|
29
|
+
|
|
39
30
|
async get<T = unknown>(
|
|
40
31
|
endpoint: string,
|
|
41
32
|
options?: Omit<RequestOptions, 'method' | 'body'>
|
|
@@ -43,9 +34,7 @@ export class AstraSDK {
|
|
|
43
34
|
return this.client.get<T>(endpoint, options);
|
|
44
35
|
}
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
* Make a POST request
|
|
48
|
-
*/
|
|
37
|
+
|
|
49
38
|
async post<T = unknown>(
|
|
50
39
|
endpoint: string,
|
|
51
40
|
body?: unknown,
|
|
@@ -54,9 +43,6 @@ export class AstraSDK {
|
|
|
54
43
|
return this.client.post<T>(endpoint, body, options);
|
|
55
44
|
}
|
|
56
45
|
|
|
57
|
-
/**
|
|
58
|
-
* Make a PUT request
|
|
59
|
-
*/
|
|
60
46
|
async put<T = unknown>(
|
|
61
47
|
endpoint: string,
|
|
62
48
|
body?: unknown,
|
|
@@ -65,9 +51,6 @@ export class AstraSDK {
|
|
|
65
51
|
return this.client.put<T>(endpoint, body, options);
|
|
66
52
|
}
|
|
67
53
|
|
|
68
|
-
/**
|
|
69
|
-
* Make a PATCH request
|
|
70
|
-
*/
|
|
71
54
|
async patch<T = unknown>(
|
|
72
55
|
endpoint: string,
|
|
73
56
|
body?: unknown,
|
|
@@ -76,9 +59,6 @@ export class AstraSDK {
|
|
|
76
59
|
return this.client.patch<T>(endpoint, body, options);
|
|
77
60
|
}
|
|
78
61
|
|
|
79
|
-
/**
|
|
80
|
-
* Make a DELETE request
|
|
81
|
-
*/
|
|
82
62
|
async delete<T = unknown>(
|
|
83
63
|
endpoint: string,
|
|
84
64
|
options?: Omit<RequestOptions, 'method' | 'body'>
|
|
@@ -86,9 +66,7 @@ export class AstraSDK {
|
|
|
86
66
|
return this.client.delete<T>(endpoint, options);
|
|
87
67
|
}
|
|
88
68
|
|
|
89
|
-
|
|
90
|
-
* Make a generic request
|
|
91
|
-
*/
|
|
69
|
+
|
|
92
70
|
async request<T = unknown>(
|
|
93
71
|
endpoint: string,
|
|
94
72
|
options?: RequestOptions
|
|
@@ -97,13 +75,23 @@ export class AstraSDK {
|
|
|
97
75
|
}
|
|
98
76
|
}
|
|
99
77
|
|
|
100
|
-
// Export types
|
|
101
78
|
export type { AstraSDKConfig, ApiResponse, RequestOptions, ApiError } from './types';
|
|
102
79
|
export { AstraSDKError } from './types';
|
|
103
80
|
|
|
104
|
-
// Export client for advanced usage
|
|
105
81
|
export { ApiClient } from './client';
|
|
106
82
|
|
|
107
|
-
//
|
|
83
|
+
// Export KYC components
|
|
84
|
+
export { KycFlow } from '../components/KycFlow';
|
|
85
|
+
export type { KycFlowProps } from '../components/KycFlow';
|
|
86
|
+
|
|
87
|
+
// Export KYC API service
|
|
88
|
+
export { KycApiService } from '../services/kycApiService';
|
|
89
|
+
export type {
|
|
90
|
+
KycApiConfig,
|
|
91
|
+
SessionStatusResponse,
|
|
92
|
+
FaceScanResponse,
|
|
93
|
+
DocumentUploadResponse
|
|
94
|
+
} from '../services/kycApiService';
|
|
95
|
+
|
|
108
96
|
export default AstraSDK;
|
|
109
97
|
|