vesant-sdk 1.7.2-dev.617dd44 → 1.7.2-dev.9ef17db
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/README.md +4 -0
- package/dist/{client-D23KhWeh.d.mts → client-5q4whhGu.d.mts} +1 -1
- package/dist/{client-fy3trF2K.d.ts → client-BaUekQT8.d.ts} +32 -10
- package/dist/{client-B3DCAHlg.d.ts → client-DUHC-68_.d.ts} +1 -1
- package/dist/{client-BwXMQA2a.d.mts → client-XDEhh9VN.d.mts} +32 -10
- package/dist/compliance/index.d.mts +2 -2
- package/dist/compliance/index.d.ts +2 -2
- package/dist/compliance/index.js +39 -1
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +39 -1
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +2 -2
- package/dist/geolocation/index.d.ts +2 -2
- package/dist/geolocation/index.js +39 -1
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +39 -1
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +113 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -31
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +1 -1
- package/dist/kyc/core.d.ts +1 -1
- package/dist/kyc/core.js +74 -29
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +74 -30
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +334 -65
- package/dist/kyc/index.d.ts +334 -65
- package/dist/kyc/index.js +74 -29
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +74 -30
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +126 -48
- package/dist/react.d.ts +126 -48
- package/dist/react.js +257 -35
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +255 -36
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -423,14 +423,11 @@ function useCipherText(client) {
|
|
|
423
423
|
const [configLoading, setConfigLoading] = useState(!!client);
|
|
424
424
|
const [configError, setConfigError] = useState(null);
|
|
425
425
|
const configRef = useRef(void 0);
|
|
426
|
-
const configPromiseRef = useRef(null);
|
|
427
426
|
useEffect(() => {
|
|
428
427
|
if (!client) return;
|
|
429
428
|
let cancelled = false;
|
|
430
429
|
setConfigLoading(true);
|
|
431
|
-
|
|
432
|
-
configPromiseRef.current = promise;
|
|
433
|
-
promise.then((cfg) => {
|
|
430
|
+
client.getGPSConfig().then((cfg) => {
|
|
434
431
|
if (!cancelled) {
|
|
435
432
|
configRef.current = cfg;
|
|
436
433
|
setConfigLoading(false);
|
|
@@ -445,13 +442,21 @@ function useCipherText(client) {
|
|
|
445
442
|
cancelled = true;
|
|
446
443
|
};
|
|
447
444
|
}, [client]);
|
|
445
|
+
const refreshConfig = useCallback(async () => {
|
|
446
|
+
if (!client) return;
|
|
447
|
+
try {
|
|
448
|
+
configRef.current = await client.refreshGPSConfig();
|
|
449
|
+
setConfigError(null);
|
|
450
|
+
} catch (error) {
|
|
451
|
+
setConfigError(error instanceof Error ? error : new Error("Failed to refresh GPS config"));
|
|
452
|
+
}
|
|
453
|
+
}, [client]);
|
|
448
454
|
const generate = useCallback(
|
|
449
455
|
async (options) => {
|
|
450
456
|
try {
|
|
451
|
-
if (
|
|
457
|
+
if (client) {
|
|
452
458
|
try {
|
|
453
|
-
|
|
454
|
-
configRef.current = cfg;
|
|
459
|
+
configRef.current = await client.getGPSConfig();
|
|
455
460
|
} catch {
|
|
456
461
|
}
|
|
457
462
|
}
|
|
@@ -461,9 +466,9 @@ function useCipherText(client) {
|
|
|
461
466
|
return null;
|
|
462
467
|
}
|
|
463
468
|
},
|
|
464
|
-
[]
|
|
469
|
+
[client]
|
|
465
470
|
);
|
|
466
|
-
return { generate, configLoading, configError };
|
|
471
|
+
return { generate, refreshConfig, configLoading, configError };
|
|
467
472
|
}
|
|
468
473
|
function useLocationRequests(client, options = {}) {
|
|
469
474
|
const [requests, setRequests] = useState([]);
|
|
@@ -924,19 +929,19 @@ var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0id
|
|
|
924
929
|
var FACE_MESSAGES = {
|
|
925
930
|
idle: "Initializing camera...",
|
|
926
931
|
loading: "Loading face detection...",
|
|
927
|
-
scanning: "Position your face in the
|
|
932
|
+
scanning: "Position your face in the oval",
|
|
928
933
|
too_far: "Face too far. Please move closer to the camera",
|
|
929
934
|
too_close: "Too close. Please move back a little",
|
|
930
|
-
off_center: "Center your face in the
|
|
935
|
+
off_center: "Center your face in the oval",
|
|
931
936
|
ok: "Looking great! Tap the button to capture"
|
|
932
937
|
};
|
|
933
938
|
var FACE_COLORS = {
|
|
934
939
|
idle: "#9ca3af",
|
|
935
|
-
loading: "#
|
|
936
|
-
scanning: "#
|
|
940
|
+
loading: "#ef4444",
|
|
941
|
+
scanning: "#ef4444",
|
|
937
942
|
too_far: "#ef4444",
|
|
938
|
-
too_close: "#
|
|
939
|
-
off_center: "#
|
|
943
|
+
too_close: "#ef4444",
|
|
944
|
+
off_center: "#ef4444",
|
|
940
945
|
ok: "#10b981"
|
|
941
946
|
};
|
|
942
947
|
var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
|
|
@@ -1001,11 +1006,77 @@ function loadMediaPipeFaceDetection(assetBasePath) {
|
|
|
1001
1006
|
return loader;
|
|
1002
1007
|
}
|
|
1003
1008
|
|
|
1009
|
+
// src/kyc/hostedJourney.ts
|
|
1010
|
+
var REUSE_KYC_SESSION_EXPIRY_MS = 10 * 60 * 1e3;
|
|
1011
|
+
var JOURNEY_FRAME_HEIGHT = "clamp(500px, 64vh, 680px)";
|
|
1012
|
+
var JOURNEY_CONFIRM_TIMEOUT_MS = 45e3;
|
|
1013
|
+
function journeyOutcomeFromMessage(data) {
|
|
1014
|
+
const status = data?.verification_status;
|
|
1015
|
+
if (typeof status !== "string") return null;
|
|
1016
|
+
switch (status) {
|
|
1017
|
+
case "verification.accepted":
|
|
1018
|
+
return "accepted";
|
|
1019
|
+
case "verification.declined":
|
|
1020
|
+
return "declined";
|
|
1021
|
+
case "verification.cancelled":
|
|
1022
|
+
case "request.timeout":
|
|
1023
|
+
case "request.invalid":
|
|
1024
|
+
case "request.unauthorized":
|
|
1025
|
+
case "request.deleted":
|
|
1026
|
+
return "cancelled";
|
|
1027
|
+
default:
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function journeyPollIntervalMs(confirming) {
|
|
1032
|
+
return confirming ? 1e3 : 2500;
|
|
1033
|
+
}
|
|
1034
|
+
function pendingJourneyResult(reference) {
|
|
1035
|
+
return {
|
|
1036
|
+
event: "reuse_kyc",
|
|
1037
|
+
reference,
|
|
1038
|
+
resource_id: "",
|
|
1039
|
+
status: "pending",
|
|
1040
|
+
data: {
|
|
1041
|
+
retries_remaining: 0,
|
|
1042
|
+
retry_limit_exceeded: false,
|
|
1043
|
+
trigger_alert: false,
|
|
1044
|
+
enforce_logout: false,
|
|
1045
|
+
freeze_account: false,
|
|
1046
|
+
block: false,
|
|
1047
|
+
freeze_duration_minutes: 0
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function tryStartOperation(guard) {
|
|
1052
|
+
if (guard.current) return false;
|
|
1053
|
+
guard.current = true;
|
|
1054
|
+
return true;
|
|
1055
|
+
}
|
|
1056
|
+
function releaseOperation(guard) {
|
|
1057
|
+
guard.current = false;
|
|
1058
|
+
}
|
|
1059
|
+
function retryTarget(hasHostedJourney) {
|
|
1060
|
+
return hasHostedJourney ? "journey" : "capture";
|
|
1061
|
+
}
|
|
1062
|
+
var CAPTURE_MODAL_MAX_WIDTH = 448;
|
|
1063
|
+
function modalMaxWidth(hasHostedJourney) {
|
|
1064
|
+
return hasHostedJourney ? CAPTURE_MODAL_MAX_WIDTH : CAPTURE_MODAL_MAX_WIDTH;
|
|
1065
|
+
}
|
|
1066
|
+
var JOURNEY_CONTENT_PADDING = 12;
|
|
1067
|
+
function resultForModalClose(stageKind, result, unconfirmed) {
|
|
1068
|
+
const terminal = stageKind === "accepted" || stageKind === "declined" || stageKind === "max_attempts";
|
|
1069
|
+
if (terminal && result !== void 0) return result;
|
|
1070
|
+
const awaitingConfirmation = stageKind === "confirming" || stageKind === "unconfirmed";
|
|
1071
|
+
if (awaitingConfirmation && unconfirmed !== void 0) return unconfirmed;
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1004
1075
|
// src/kyc/FaceCaptureModal.tsx
|
|
1005
1076
|
var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
1006
|
-
var
|
|
1077
|
+
var ACCEPTED_AUTO_CLOSE_MS = 1200;
|
|
1007
1078
|
var LIVENESS_MESSAGES = [
|
|
1008
|
-
"Position your face inside the
|
|
1079
|
+
"Position your face inside the oval",
|
|
1009
1080
|
"Make sure your face is well lit",
|
|
1010
1081
|
"Remove glasses or hats if possible",
|
|
1011
1082
|
"Look straight at the camera, then tap Capture"
|
|
@@ -1014,6 +1085,12 @@ function headerSubtitle(stageKind) {
|
|
|
1014
1085
|
switch (stageKind) {
|
|
1015
1086
|
case "qr":
|
|
1016
1087
|
return "Use your phone to continue";
|
|
1088
|
+
case "journey":
|
|
1089
|
+
return "Follow the steps to verify your identity";
|
|
1090
|
+
case "confirming":
|
|
1091
|
+
return "Confirming your result";
|
|
1092
|
+
case "unconfirmed":
|
|
1093
|
+
return "Still confirming";
|
|
1017
1094
|
case "accepted":
|
|
1018
1095
|
return "All set";
|
|
1019
1096
|
case "declined":
|
|
@@ -1039,7 +1116,9 @@ function FaceCaptureModal({
|
|
|
1039
1116
|
}) {
|
|
1040
1117
|
const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
|
|
1041
1118
|
const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
|
|
1119
|
+
const journeyMode = Boolean(session.verification_url);
|
|
1042
1120
|
const initialStage = (() => {
|
|
1121
|
+
if (journeyMode) return { kind: "journey", url: session.verification_url ?? "" };
|
|
1043
1122
|
if (initialChoice === "this") return { kind: "capture" };
|
|
1044
1123
|
if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
|
|
1045
1124
|
return { kind: "choose" };
|
|
@@ -1057,19 +1136,20 @@ function FaceCaptureModal({
|
|
|
1057
1136
|
const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
|
|
1058
1137
|
const cancelledRef = useRef(false);
|
|
1059
1138
|
const submittingRef = useRef(false);
|
|
1139
|
+
const journeyRetryingRef = useRef(false);
|
|
1060
1140
|
useEffect(() => {
|
|
1061
1141
|
const timer = setTimeout(() => {
|
|
1062
1142
|
setStage(
|
|
1063
|
-
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
|
|
1143
|
+
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" || prev.kind === "journey" ? { kind: "error", message: "Session expired" } : prev
|
|
1064
1144
|
);
|
|
1065
|
-
},
|
|
1145
|
+
}, REUSE_KYC_SESSION_EXPIRY_MS);
|
|
1066
1146
|
return () => clearTimeout(timer);
|
|
1067
1147
|
}, [session.token]);
|
|
1068
1148
|
useEffect(() => {
|
|
1069
1149
|
if (stage.kind !== "qr") return;
|
|
1070
1150
|
let stopped = false;
|
|
1071
1151
|
const intervalMs = 2e3;
|
|
1072
|
-
const deadline = Date.now() +
|
|
1152
|
+
const deadline = Date.now() + REUSE_KYC_SESSION_EXPIRY_MS;
|
|
1073
1153
|
let mobileSeen = stage.mobileConnected;
|
|
1074
1154
|
const tick = async () => {
|
|
1075
1155
|
if (stopped || cancelledRef.current) return;
|
|
@@ -1110,6 +1190,82 @@ function FaceCaptureModal({
|
|
|
1110
1190
|
stopped = true;
|
|
1111
1191
|
};
|
|
1112
1192
|
}, [stage.kind, stage.kind === "qr" ? stage.mobileConnected : false]);
|
|
1193
|
+
const journeyUrl = stage.kind === "journey" || stage.kind === "confirming" ? stage.url : "";
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
if (stage.kind !== "journey" && stage.kind !== "confirming" || !journeyUrl) return;
|
|
1196
|
+
const intervalMs = journeyPollIntervalMs(stage.kind === "confirming");
|
|
1197
|
+
let stopped = false;
|
|
1198
|
+
const checkOnce = async () => {
|
|
1199
|
+
try {
|
|
1200
|
+
const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
|
|
1201
|
+
if (applyResult(result)) {
|
|
1202
|
+
stopped = true;
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
1205
|
+
} catch {
|
|
1206
|
+
}
|
|
1207
|
+
return false;
|
|
1208
|
+
};
|
|
1209
|
+
const tick = async () => {
|
|
1210
|
+
if (stopped || cancelledRef.current) return;
|
|
1211
|
+
await checkOnce();
|
|
1212
|
+
if (!stopped && !cancelledRef.current) {
|
|
1213
|
+
setTimeout(tick, intervalMs);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
void tick();
|
|
1217
|
+
let frameOrigin = "";
|
|
1218
|
+
try {
|
|
1219
|
+
frameOrigin = new URL(journeyUrl).origin;
|
|
1220
|
+
} catch {
|
|
1221
|
+
frameOrigin = "";
|
|
1222
|
+
}
|
|
1223
|
+
const onMessage = (e) => {
|
|
1224
|
+
if (!frameOrigin || e.origin !== frameOrigin) return;
|
|
1225
|
+
const outcome = journeyOutcomeFromMessage(e.data);
|
|
1226
|
+
if (!outcome) return;
|
|
1227
|
+
void checkOnce();
|
|
1228
|
+
setStage((prev) => {
|
|
1229
|
+
if (prev.kind !== "journey") return prev;
|
|
1230
|
+
if (outcome === "cancelled") {
|
|
1231
|
+
return { kind: "error", message: "The verification was closed before it finished. Please try again." };
|
|
1232
|
+
}
|
|
1233
|
+
return { kind: "confirming", outcome, url: prev.url };
|
|
1234
|
+
});
|
|
1235
|
+
};
|
|
1236
|
+
window.addEventListener("message", onMessage);
|
|
1237
|
+
return () => {
|
|
1238
|
+
stopped = true;
|
|
1239
|
+
window.removeEventListener("message", onMessage);
|
|
1240
|
+
};
|
|
1241
|
+
}, [stage.kind, journeyUrl]);
|
|
1242
|
+
useEffect(() => {
|
|
1243
|
+
if (stage.kind !== "confirming") return;
|
|
1244
|
+
const outcome = stage.outcome;
|
|
1245
|
+
const timer = setTimeout(() => {
|
|
1246
|
+
setStage((prev) => prev.kind === "confirming" ? { kind: "unconfirmed", outcome } : prev);
|
|
1247
|
+
}, JOURNEY_CONFIRM_TIMEOUT_MS);
|
|
1248
|
+
return () => clearTimeout(timer);
|
|
1249
|
+
}, [stage.kind, stage.kind === "confirming" ? stage.outcome : ""]);
|
|
1250
|
+
const handleJourneyRetry = async () => {
|
|
1251
|
+
if (!tryStartOperation(journeyRetryingRef)) return;
|
|
1252
|
+
setStage({ kind: "journey", url: "" });
|
|
1253
|
+
try {
|
|
1254
|
+
const refreshed = await client.retryEventBasedFaceVerificationJourney(session.token);
|
|
1255
|
+
if (refreshed.verification_url) {
|
|
1256
|
+
setStage({ kind: "journey", url: refreshed.verification_url });
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
setStage({ kind: "error", message: "Unable to start a new attempt. Please try again." });
|
|
1260
|
+
} catch (err) {
|
|
1261
|
+
setStage({
|
|
1262
|
+
kind: "error",
|
|
1263
|
+
message: err instanceof Error ? err.message : "Unable to start a new attempt. Please try again."
|
|
1264
|
+
});
|
|
1265
|
+
} finally {
|
|
1266
|
+
releaseOperation(journeyRetryingRef);
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1113
1269
|
const applyResult = (result) => {
|
|
1114
1270
|
if (result.status === "accepted") {
|
|
1115
1271
|
setStage({ kind: "accepted", result });
|
|
@@ -1237,12 +1393,23 @@ function FaceCaptureModal({
|
|
|
1237
1393
|
}, 3e3);
|
|
1238
1394
|
return () => clearInterval(interval);
|
|
1239
1395
|
}, [captureMode]);
|
|
1240
|
-
const close = (result) => {
|
|
1396
|
+
const close = useCallback((result) => {
|
|
1241
1397
|
cancelledRef.current = true;
|
|
1242
1398
|
if (result === null && onCancel) {
|
|
1243
1399
|
onCancel();
|
|
1244
1400
|
}
|
|
1245
1401
|
onComplete(result);
|
|
1402
|
+
}, [onCancel, onComplete]);
|
|
1403
|
+
useEffect(() => {
|
|
1404
|
+
if (stage.kind !== "accepted") return;
|
|
1405
|
+
const result = stage.result;
|
|
1406
|
+
const timer = setTimeout(() => close(result), ACCEPTED_AUTO_CLOSE_MS);
|
|
1407
|
+
return () => clearTimeout(timer);
|
|
1408
|
+
}, [close, stage]);
|
|
1409
|
+
const handleHeaderClose = () => {
|
|
1410
|
+
const result = "result" in stage ? stage.result : void 0;
|
|
1411
|
+
const unconfirmed = stage.kind === "confirming" || stage.kind === "unconfirmed" ? pendingJourneyResult(session.reference) : void 0;
|
|
1412
|
+
close(resultForModalClose(stage.kind, result, unconfirmed));
|
|
1246
1413
|
};
|
|
1247
1414
|
const renderChoose = () => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React.createElement("div", { style: circleStyle }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })))), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "How would you like to verify?"), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement(
|
|
1248
1415
|
"button",
|
|
@@ -1301,12 +1468,37 @@ function FaceCaptureModal({
|
|
|
1301
1468
|
};
|
|
1302
1469
|
const renderSubmitting = () => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Verifying your photo\u2026"), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
|
|
1303
1470
|
const renderAccepted = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: successCircleStyle }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Verified"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Your identity has been confirmed. You can continue."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Continue")));
|
|
1304
|
-
const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1471
|
+
const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, journeyMode ? /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => void handleJourneyRetry() }, /* @__PURE__ */ React.createElement("span", null, "Try Again")) : /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1305
1472
|
const renderMaxAttempts = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Maximum Attempts Reached"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "For your security, your account has been temporarily restricted", result.data.freeze_duration_minutes ? ` for ${result.data.freeze_duration_minutes} minutes` : "", ". Please contact support if you need immediate help."), result.data?.enforce_logout && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), result.data?.block && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "This action has been blocked for security reasons."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
|
|
1306
1473
|
const renderError = (message) => {
|
|
1307
1474
|
const sessionExpired = /session\s+expired/i.test(message);
|
|
1308
|
-
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React.createElement(
|
|
1475
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React.createElement(
|
|
1476
|
+
"button",
|
|
1477
|
+
{
|
|
1478
|
+
style: primaryButtonStyle,
|
|
1479
|
+
onClick: () => retryTarget(journeyMode) === "journey" ? void handleJourneyRetry() : setStage({ kind: "capture" })
|
|
1480
|
+
},
|
|
1481
|
+
"Try Again"
|
|
1482
|
+
), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
|
|
1309
1483
|
};
|
|
1484
|
+
const renderConfirming = () => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Confirming your result\u2026"), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
|
|
1485
|
+
const renderUnconfirmed = (outcome) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Still confirming"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, outcome === "accepted" ? "Your verification was submitted, but we haven't received the final result yet. You can close this window and we'll keep checking in the background." : "Your last attempt wasn't accepted, but we haven't received the details yet. You can try again or close this window."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, outcome === "declined" && /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => void handleJourneyRetry() }, "Try Again"), /* @__PURE__ */ React.createElement(
|
|
1486
|
+
"button",
|
|
1487
|
+
{
|
|
1488
|
+
style: outcome === "accepted" ? primaryButtonStyle : cancelButtonStyle,
|
|
1489
|
+
onClick: () => close(pendingJourneyResult(session.reference))
|
|
1490
|
+
},
|
|
1491
|
+
"Close"
|
|
1492
|
+
)));
|
|
1493
|
+
const renderJourney = (url) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, padding: JOURNEY_CONTENT_PADDING } }, url ? /* @__PURE__ */ React.createElement(
|
|
1494
|
+
"iframe",
|
|
1495
|
+
{
|
|
1496
|
+
src: url,
|
|
1497
|
+
title: "Identity verification",
|
|
1498
|
+
allow: "camera; microphone",
|
|
1499
|
+
style: journeyFrameStyle
|
|
1500
|
+
}
|
|
1501
|
+
) : /* @__PURE__ */ React.createElement("div", { style: { padding: 24, textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Preparing your next attempt\u2026")), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, textAlign: "center" } }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1310
1502
|
const body = (() => {
|
|
1311
1503
|
switch (stage.kind) {
|
|
1312
1504
|
case "choose":
|
|
@@ -1315,6 +1507,12 @@ function FaceCaptureModal({
|
|
|
1315
1507
|
return renderQRStage(stage.mobileConnected);
|
|
1316
1508
|
case "capture":
|
|
1317
1509
|
return renderCapture(stage.declinedReason);
|
|
1510
|
+
case "journey":
|
|
1511
|
+
return renderJourney(stage.url);
|
|
1512
|
+
case "confirming":
|
|
1513
|
+
return renderConfirming();
|
|
1514
|
+
case "unconfirmed":
|
|
1515
|
+
return renderUnconfirmed(stage.outcome);
|
|
1318
1516
|
case "submitting":
|
|
1319
1517
|
return renderSubmitting();
|
|
1320
1518
|
case "accepted":
|
|
@@ -1327,10 +1525,10 @@ function FaceCaptureModal({
|
|
|
1327
1525
|
return renderError(stage.message);
|
|
1328
1526
|
}
|
|
1329
1527
|
})();
|
|
1330
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: modalStyle }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1528
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: { ...modalStyle, maxWidth: modalMaxWidth(journeyMode) } }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1331
1529
|
"button",
|
|
1332
1530
|
{
|
|
1333
|
-
onClick:
|
|
1531
|
+
onClick: handleHeaderClose,
|
|
1334
1532
|
style: closeButtonStyle,
|
|
1335
1533
|
"aria-label": "Close"
|
|
1336
1534
|
},
|
|
@@ -1360,7 +1558,8 @@ var modalStyle = {
|
|
|
1360
1558
|
borderRadius: 16,
|
|
1361
1559
|
boxShadow: "0 25px 50px -12px rgba(0,0,0,.25)",
|
|
1362
1560
|
width: "100%",
|
|
1363
|
-
|
|
1561
|
+
maxHeight: "calc(100vh - 32px)",
|
|
1562
|
+
overflowY: "auto",
|
|
1364
1563
|
animation: "zoomIn .2s ease-out"
|
|
1365
1564
|
};
|
|
1366
1565
|
var headerStyle = {
|
|
@@ -1386,6 +1585,13 @@ var contentStyle = {
|
|
|
1386
1585
|
flexDirection: "column",
|
|
1387
1586
|
gap: 16
|
|
1388
1587
|
};
|
|
1588
|
+
var journeyFrameStyle = {
|
|
1589
|
+
width: "100%",
|
|
1590
|
+
height: JOURNEY_FRAME_HEIGHT,
|
|
1591
|
+
border: 0,
|
|
1592
|
+
borderRadius: 12,
|
|
1593
|
+
background: "#f9fafb"
|
|
1594
|
+
};
|
|
1389
1595
|
var visualGuideContainerStyle = { display: "flex", justifyContent: "center" };
|
|
1390
1596
|
var visualGuideStyle = { position: "relative" };
|
|
1391
1597
|
var circleStyle = {
|
|
@@ -1652,9 +1858,10 @@ function LiveCamera({ onCapture, onCancel, message, assetBasePath }) {
|
|
|
1652
1858
|
if (error) {
|
|
1653
1859
|
return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center", padding: 0 } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Camera unavailable"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, error), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Back")));
|
|
1654
1860
|
}
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
const
|
|
1861
|
+
const detectionAvailable = !faceDetectionFailed;
|
|
1862
|
+
const detectionActive = ready && detectionAvailable;
|
|
1863
|
+
const liveMessage = detectionAvailable ? FACE_MESSAGES[faceStatus] : message;
|
|
1864
|
+
const liveFaceColor = detectionAvailable ? FACE_COLORS[faceStatus] : void 0;
|
|
1658
1865
|
const captureDisabled = isCaptureDisabled(ready, detectionActive, faceStatus);
|
|
1659
1866
|
const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
|
|
1660
1867
|
const dynamicCircleStyle = {
|
|
@@ -1770,20 +1977,22 @@ var faceOverlayContainerStyle = {
|
|
|
1770
1977
|
};
|
|
1771
1978
|
var faceRingWrapStyle = {
|
|
1772
1979
|
position: "relative",
|
|
1773
|
-
|
|
1774
|
-
aspectRatio: "
|
|
1980
|
+
height: "64%",
|
|
1981
|
+
aspectRatio: "3 / 4"
|
|
1775
1982
|
};
|
|
1776
1983
|
var faceDashedRingStyle = {
|
|
1777
1984
|
position: "absolute",
|
|
1778
1985
|
inset: -6,
|
|
1779
|
-
borderRadius:
|
|
1780
|
-
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1781
|
-
animation: "spin 10s linear infinite"
|
|
1986
|
+
borderRadius: 9999,
|
|
1987
|
+
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1782
1988
|
};
|
|
1783
1989
|
var faceCircleStyle = {
|
|
1784
1990
|
position: "absolute",
|
|
1785
1991
|
inset: 0,
|
|
1786
|
-
|
|
1992
|
+
// Tailwind's `rounded-full` resolves to a very large fixed radius. On a
|
|
1993
|
+
// 3:4 box that creates the normal KYC capsule/head guide with straighter
|
|
1994
|
+
// sides; 50% would instead produce a mathematically perfect ellipse.
|
|
1995
|
+
borderRadius: 9999,
|
|
1787
1996
|
border: "4px solid #00bc7d",
|
|
1788
1997
|
boxShadow: "0 0 0 1000px rgba(0, 0, 0, 0.5)"
|
|
1789
1998
|
};
|
|
@@ -2126,6 +2335,7 @@ function isValidFileSize(file, maxSizeBytes = 10 * 1024 * 1024) {
|
|
|
2126
2335
|
}
|
|
2127
2336
|
function formatKycStatus(status) {
|
|
2128
2337
|
const statusMap = {
|
|
2338
|
+
awaiting_kyc: "Awaiting KYC",
|
|
2129
2339
|
pending: "Pending Review",
|
|
2130
2340
|
accepted: "Approved",
|
|
2131
2341
|
declined: "Rejected",
|
|
@@ -2136,6 +2346,8 @@ function formatKycStatus(status) {
|
|
|
2136
2346
|
}
|
|
2137
2347
|
function getStatusColor(status) {
|
|
2138
2348
|
const colorMap = {
|
|
2349
|
+
awaiting_kyc: "#94a3b8",
|
|
2350
|
+
// slate — not started
|
|
2139
2351
|
pending: "#f59e0b",
|
|
2140
2352
|
// amber
|
|
2141
2353
|
accepted: "#10b981",
|
|
@@ -2162,6 +2374,13 @@ function getRiskColor(risk) {
|
|
|
2162
2374
|
};
|
|
2163
2375
|
return colorMap[risk] || "#6b7280";
|
|
2164
2376
|
}
|
|
2377
|
+
function isMobileDevice(userAgent) {
|
|
2378
|
+
const ua = userAgent ?? (typeof navigator === "undefined" ? "" : navigator.userAgent);
|
|
2379
|
+
return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
|
|
2380
|
+
}
|
|
2381
|
+
function buildEventBasedFaceVerificationQrPayload(token) {
|
|
2382
|
+
return `vesant://reuse-kyc?token=${encodeURIComponent(token)}`;
|
|
2383
|
+
}
|
|
2165
2384
|
function useEventBasedFaceVerificationSubmission(client) {
|
|
2166
2385
|
const startSession = useCallback(
|
|
2167
2386
|
(request) => client.createEventBasedFaceVerificationSession(request),
|
|
@@ -2209,6 +2428,6 @@ function useEventBasedFaceVerificationSubmission(client) {
|
|
|
2209
2428
|
return { startSession, verifyFace, runFlow };
|
|
2210
2429
|
}
|
|
2211
2430
|
|
|
2212
|
-
export { createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useEventBasedFaceVerificationSubmission, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useTransactionVerification };
|
|
2431
|
+
export { FaceCaptureModal_default as FaceCaptureModal, buildEventBasedFaceVerificationQrPayload, createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isMobileDevice, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useEventBasedFaceVerificationSubmission, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useTransactionVerification };
|
|
2213
2432
|
//# sourceMappingURL=react.mjs.map
|
|
2214
2433
|
//# sourceMappingURL=react.mjs.map
|