astra-sdk-web 1.1.9 → 1.1.10
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/astra-sdk.cjs.js +130 -8
- package/dist/astra-sdk.cjs.js.map +1 -1
- package/dist/astra-sdk.css +19 -0
- package/dist/astra-sdk.css.map +1 -1
- package/dist/astra-sdk.d.cts +4 -0
- package/dist/astra-sdk.es.js +130 -8
- package/dist/astra-sdk.es.js.map +1 -1
- package/dist/components.cjs.js +130 -8
- package/dist/components.cjs.js.map +1 -1
- package/dist/components.css +19 -0
- package/dist/components.css.map +1 -1
- package/dist/components.es.js +130 -8
- package/dist/components.es.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/package.json +1 -1
- package/src/pages/DocumentUploadModal.tsx +44 -2
- package/src/pages/FaceScanModal.tsx +99 -7
- package/src/services/kycApiService.ts +34 -0
package/dist/astra-sdk.cjs.js
CHANGED
|
@@ -342,6 +342,36 @@ var KycApiService = class {
|
|
|
342
342
|
throw new Error(`Document upload failed: ${message}`);
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Retry session - resets the session to allow face registration again
|
|
347
|
+
*/
|
|
348
|
+
async retrySession() {
|
|
349
|
+
const deviceType = this.config.deviceType || this.detectDeviceType();
|
|
350
|
+
try {
|
|
351
|
+
const response = await fetch(
|
|
352
|
+
`${this.config.apiBaseUrl}/api/v2/dashboard/merchant/onsite/session/${this.config.sessionId}/retry`,
|
|
353
|
+
{
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: {
|
|
356
|
+
"x-server-key": this.config.serverKey,
|
|
357
|
+
"device-type": deviceType,
|
|
358
|
+
"Content-Type": "application/json"
|
|
359
|
+
},
|
|
360
|
+
credentials: "include"
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
const errorData = await response.json().catch(() => ({}));
|
|
365
|
+
const message = errorData?.message || `Retry failed with status ${response.status}`;
|
|
366
|
+
throw new Error(message);
|
|
367
|
+
}
|
|
368
|
+
const data = await response.json();
|
|
369
|
+
return data;
|
|
370
|
+
} catch (error) {
|
|
371
|
+
const message = error?.message || "Retry failed";
|
|
372
|
+
throw new Error(`Retry failed: ${message}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
345
375
|
/**
|
|
346
376
|
* Check if session is active, throw error if not
|
|
347
377
|
*/
|
|
@@ -616,6 +646,7 @@ function DocumentUploadModal({ onComplete }) {
|
|
|
616
646
|
const navigate = reactRouterDom.useNavigate();
|
|
617
647
|
const { apiService } = useKycContext();
|
|
618
648
|
const [sessionError, setSessionError] = React.useState(null);
|
|
649
|
+
const [kycCompleted, setKycCompleted] = React.useState(false);
|
|
619
650
|
const {
|
|
620
651
|
state,
|
|
621
652
|
setState,
|
|
@@ -631,6 +662,15 @@ function DocumentUploadModal({ onComplete }) {
|
|
|
631
662
|
throw new Error("API service not initialized");
|
|
632
663
|
}
|
|
633
664
|
await apiService.uploadDocument(blob, docType);
|
|
665
|
+
try {
|
|
666
|
+
const statusResponse = await apiService.getSessionStatus();
|
|
667
|
+
const { completed_steps, status } = statusResponse.data;
|
|
668
|
+
if (status === "COMPLETED" || completed_steps.includes(COMPLETED_STEPS.COMPLETED)) {
|
|
669
|
+
setKycCompleted(true);
|
|
670
|
+
}
|
|
671
|
+
} catch (error) {
|
|
672
|
+
console.error("Error checking completion status:", error);
|
|
673
|
+
}
|
|
634
674
|
},
|
|
635
675
|
onUpload: (file, docType) => {
|
|
636
676
|
if (onComplete) {
|
|
@@ -649,11 +689,18 @@ function DocumentUploadModal({ onComplete }) {
|
|
|
649
689
|
try {
|
|
650
690
|
const statusResponse = await apiService.getSessionStatus();
|
|
651
691
|
const { completed_steps, next_step, status } = statusResponse.data;
|
|
692
|
+
if (status === "COMPLETED" || completed_steps.includes(COMPLETED_STEPS.COMPLETED)) {
|
|
693
|
+
setKycCompleted(true);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
652
696
|
if (status !== "ACTIVE") {
|
|
653
697
|
throw new Error("Session expired or inactive");
|
|
654
698
|
}
|
|
655
699
|
if (completed_steps.includes(COMPLETED_STEPS.DOCS)) {
|
|
656
|
-
|
|
700
|
+
if (completed_steps.includes(COMPLETED_STEPS.FACE) && completed_steps.includes(COMPLETED_STEPS.DOCS)) {
|
|
701
|
+
setKycCompleted(true);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
657
704
|
}
|
|
658
705
|
if (next_step === COMPLETED_STEPS.FACE && !completed_steps.includes(COMPLETED_STEPS.FACE)) {
|
|
659
706
|
console.warn("Face scan not completed, but in document upload modal");
|
|
@@ -669,6 +716,14 @@ function DocumentUploadModal({ onComplete }) {
|
|
|
669
716
|
};
|
|
670
717
|
checkSession();
|
|
671
718
|
}, [apiService, navigate]);
|
|
719
|
+
if (kycCompleted) {
|
|
720
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black p-5 z-[1000] flex items-center justify-center font-sans overflow-y-auto custom__scrollbar", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[400px] w-full mx-auto bg-[#0b0f17] rounded-2xl p-6 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 text-6xl", children: "\u2705" }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "m-0 mb-4 text-[26px] font-bold text-green-500", children: "KYC Completed" }),
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[#e5e7eb] mb-4 text-lg", children: "All steps have been completed successfully." }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[#9ca3af] text-sm mb-6", children: "Please return to your desktop to continue." })
|
|
725
|
+
] }) }) });
|
|
726
|
+
}
|
|
672
727
|
if (sessionError) {
|
|
673
728
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black p-4 z-[1000] flex items-center justify-center font-sans overflow-y-auto custom__scrollbar", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[400px] w-full mx-auto bg-[#0b0f17] rounded-2xl p-6 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
674
729
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "m-0 mb-4 text-[26px] font-bold text-red-500", children: "Session Expired" }),
|
|
@@ -1505,6 +1560,9 @@ function FaceScanModal({ onComplete }) {
|
|
|
1505
1560
|
const { apiService } = useKycContext();
|
|
1506
1561
|
const [sessionError, setSessionError] = React.useState(null);
|
|
1507
1562
|
const [toast, setToast] = React.useState(null);
|
|
1563
|
+
const [showRetryButton, setShowRetryButton] = React.useState(false);
|
|
1564
|
+
const [isRetrying, setIsRetrying] = React.useState(false);
|
|
1565
|
+
const [kycCompleted, setKycCompleted] = React.useState(false);
|
|
1508
1566
|
const { videoRef, cameraReady, stopCamera } = useCamera();
|
|
1509
1567
|
const { state, setState, refs, handleFaceCapture } = useFaceScan(videoRef, faceCanvasRef, {
|
|
1510
1568
|
onFaceUpload: async (blob) => {
|
|
@@ -1517,10 +1575,12 @@ function FaceScanModal({ onComplete }) {
|
|
|
1517
1575
|
const errorMessage = error?.message || "";
|
|
1518
1576
|
const errorData = error?.errorData || {};
|
|
1519
1577
|
if (errorMessage.includes("Face already registered") || errorMessage.includes("already registered") || errorData?.message?.includes("Face already registered") || error?.statusCode === 500 && errorMessage.includes("Face")) {
|
|
1578
|
+
setShowRetryButton(true);
|
|
1520
1579
|
setToast({
|
|
1521
|
-
message: "Face already registered",
|
|
1580
|
+
message: "Face already registered. Click Retry to register again.",
|
|
1522
1581
|
type: "warning"
|
|
1523
1582
|
});
|
|
1583
|
+
setState((prev) => ({ ...prev, loading: false }));
|
|
1524
1584
|
return;
|
|
1525
1585
|
}
|
|
1526
1586
|
throw error;
|
|
@@ -1541,6 +1601,10 @@ function FaceScanModal({ onComplete }) {
|
|
|
1541
1601
|
if (status !== "ACTIVE") {
|
|
1542
1602
|
throw new Error("Session expired or inactive");
|
|
1543
1603
|
}
|
|
1604
|
+
if (status === "COMPLETED" || completed_steps.includes(COMPLETED_STEPS.COMPLETED)) {
|
|
1605
|
+
setKycCompleted(true);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1544
1608
|
if (completed_steps.includes(COMPLETED_STEPS.FACE)) {
|
|
1545
1609
|
setState((prev) => ({ ...prev, showDocumentUpload: true }));
|
|
1546
1610
|
return;
|
|
@@ -1588,7 +1652,47 @@ function FaceScanModal({ onComplete }) {
|
|
|
1588
1652
|
}
|
|
1589
1653
|
}
|
|
1590
1654
|
}, [cameraReady, apiService]);
|
|
1591
|
-
const handleRetry = () => {
|
|
1655
|
+
const handleRetry = async () => {
|
|
1656
|
+
if (!apiService || isRetrying) return;
|
|
1657
|
+
setIsRetrying(true);
|
|
1658
|
+
setShowRetryButton(false);
|
|
1659
|
+
setToast(null);
|
|
1660
|
+
try {
|
|
1661
|
+
await apiService.retrySession();
|
|
1662
|
+
stopCamera();
|
|
1663
|
+
setState({
|
|
1664
|
+
cameraReady: false,
|
|
1665
|
+
livenessStage: "CENTER",
|
|
1666
|
+
livenessReady: false,
|
|
1667
|
+
livenessFailed: false,
|
|
1668
|
+
modelLoading: true,
|
|
1669
|
+
modelLoaded: false,
|
|
1670
|
+
livenessInstruction: "Look straight at the camera",
|
|
1671
|
+
loading: false,
|
|
1672
|
+
allStepsCompleted: false,
|
|
1673
|
+
capturedImage: null,
|
|
1674
|
+
showDocumentUpload: false
|
|
1675
|
+
});
|
|
1676
|
+
refs.centerHold.current = 0;
|
|
1677
|
+
refs.leftHold.current = 0;
|
|
1678
|
+
refs.rightHold.current = 0;
|
|
1679
|
+
refs.snapTriggered.current = false;
|
|
1680
|
+
refs.lastResultsAt.current = 0;
|
|
1681
|
+
refs.modelLoaded.current = false;
|
|
1682
|
+
refs.livenessFailed.current = false;
|
|
1683
|
+
setTimeout(() => {
|
|
1684
|
+
window.location.reload();
|
|
1685
|
+
}, 500);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
setIsRetrying(false);
|
|
1688
|
+
setShowRetryButton(true);
|
|
1689
|
+
setToast({
|
|
1690
|
+
message: error?.message || "Retry failed. Please try again.",
|
|
1691
|
+
type: "error"
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
const handleRestart = () => {
|
|
1592
1696
|
stopCamera();
|
|
1593
1697
|
setState({
|
|
1594
1698
|
cameraReady: false,
|
|
@@ -1614,6 +1718,14 @@ function FaceScanModal({ onComplete }) {
|
|
|
1614
1718
|
window.location.reload();
|
|
1615
1719
|
}, 100);
|
|
1616
1720
|
};
|
|
1721
|
+
if (kycCompleted) {
|
|
1722
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black p-5 z-[1000] flex items-center justify-center font-sans overflow-y-auto custom__scrollbar", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[400px] w-full mx-auto bg-[#0b0f17] rounded-2xl p-6 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
1723
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 text-6xl", children: "\u2705" }),
|
|
1724
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "m-0 mb-4 text-[26px] font-bold text-green-500", children: "KYC Completed" }),
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[#e5e7eb] mb-4 text-lg", children: "All steps have been completed successfully." }),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[#9ca3af] text-sm mb-6", children: "Please return to your desktop to continue." })
|
|
1727
|
+
] }) }) });
|
|
1728
|
+
}
|
|
1617
1729
|
if (state.showDocumentUpload) {
|
|
1618
1730
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1619
1731
|
DocumentUploadModal_default,
|
|
@@ -1676,13 +1788,23 @@ function FaceScanModal({ onComplete }) {
|
|
|
1676
1788
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: state.livenessStage === "DONE" ? "opacity-100" : "opacity-30", children: "3. Turn your face left" })
|
|
1677
1789
|
] })
|
|
1678
1790
|
] }),
|
|
1791
|
+
showRetryButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1792
|
+
"button",
|
|
1793
|
+
{
|
|
1794
|
+
type: "button",
|
|
1795
|
+
onClick: handleRetry,
|
|
1796
|
+
disabled: isRetrying || state.loading,
|
|
1797
|
+
className: "py-3.5 px-4 rounded-xl text-base font-bold border-none transition-colors bg-[#f59e0b] text-[#0b0f17] cursor-pointer hover:bg-[#d97706] disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1798
|
+
children: isRetrying ? "Retrying..." : "Retry Face Registration"
|
|
1799
|
+
}
|
|
1800
|
+
),
|
|
1679
1801
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1680
1802
|
"button",
|
|
1681
1803
|
{
|
|
1682
1804
|
type: "button",
|
|
1683
|
-
disabled: !state.cameraReady || state.loading || !state.livenessFailed && state.livenessStage !== "DONE",
|
|
1805
|
+
disabled: !state.cameraReady || state.loading || !state.livenessFailed && state.livenessStage !== "DONE" || showRetryButton,
|
|
1684
1806
|
onClick: handleFaceCapture,
|
|
1685
|
-
className: `py-3.5 px-4 rounded-xl text-base font-bold border-none transition-colors ${state.cameraReady && !state.loading && (state.livenessFailed || state.livenessStage === "DONE") ? "bg-[#22c55e] text-[#0b0f17] cursor-pointer hover:bg-[#16a34a]" : "bg-[#374151] text-[#e5e7eb] cursor-not-allowed"}`,
|
|
1807
|
+
className: `py-3.5 px-4 rounded-xl text-base font-bold border-none transition-colors ${state.cameraReady && !state.loading && (state.livenessFailed || state.livenessStage === "DONE") && !showRetryButton ? "bg-[#22c55e] text-[#0b0f17] cursor-pointer hover:bg-[#16a34a]" : "bg-[#374151] text-[#e5e7eb] cursor-not-allowed"}`,
|
|
1686
1808
|
children: state.loading ? "Capturing..." : state.livenessFailed || state.livenessStage === "DONE" ? "Capture & Continue" : "Complete steps to continue"
|
|
1687
1809
|
}
|
|
1688
1810
|
),
|
|
@@ -1690,9 +1812,9 @@ function FaceScanModal({ onComplete }) {
|
|
|
1690
1812
|
"button",
|
|
1691
1813
|
{
|
|
1692
1814
|
type: "button",
|
|
1693
|
-
onClick:
|
|
1694
|
-
disabled: state.loading,
|
|
1695
|
-
className: `py-3 px-4 rounded-[10px] text-[15px] font-semibold border-none w-full transition-colors ${state.loading ? "bg-[#374151] text-[#e5e7eb] cursor-not-allowed opacity-50" : "bg-[#374151] text-[#e5e7eb] cursor-pointer hover:bg-[#4b5563]"}`,
|
|
1815
|
+
onClick: handleRestart,
|
|
1816
|
+
disabled: state.loading || isRetrying,
|
|
1817
|
+
className: `py-3 px-4 rounded-[10px] text-[15px] font-semibold border-none w-full transition-colors ${state.loading || isRetrying ? "bg-[#374151] text-[#e5e7eb] cursor-not-allowed opacity-50" : "bg-[#374151] text-[#e5e7eb] cursor-pointer hover:bg-[#4b5563]"}`,
|
|
1696
1818
|
children: "Restart"
|
|
1697
1819
|
}
|
|
1698
1820
|
)
|