smartcomply-web-sdk 1.0.45 → 1.0.47
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/esm/flow/SmartComplyFlow.d.ts +22 -5
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +638 -504
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/flow/theme.d.ts +7 -1
- package/dist/esm/flow/theme.d.ts.map +1 -1
- package/dist/esm/flow/theme.js +27 -2
- package/dist/esm/flow/theme.js.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.d.ts +1 -1
- package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.js +5 -5
- package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts +1 -0
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +51 -33
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts +22 -5
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +638 -504
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/flow/theme.d.ts +7 -1
- package/dist/flow/theme.d.ts.map +1 -1
- package/dist/flow/theme.js +27 -2
- package/dist/flow/theme.js.map +1 -1
- package/dist/modules/liveness/LivenessUI.d.ts +1 -1
- package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/modules/liveness/LivenessUI.js +5 -5
- package/dist/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts +1 -0
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +51 -33
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +572 -428
- package/dist/smartcomply.browser.js.map +3 -3
- package/package.json +1 -1
|
@@ -4842,7 +4842,7 @@ var SmartComplySDK = (() => {
|
|
|
4842
4842
|
}
|
|
4843
4843
|
if (this.timerEl) this.timerEl.textContent = "";
|
|
4844
4844
|
}
|
|
4845
|
-
showResult(status,
|
|
4845
|
+
showResult(status, _verificationId) {
|
|
4846
4846
|
return new Promise((resolve) => {
|
|
4847
4847
|
if (!this.overlayEl) {
|
|
4848
4848
|
resolve();
|
|
@@ -4859,11 +4859,10 @@ var SmartComplySDK = (() => {
|
|
|
4859
4859
|
border:3px solid ${ok ? "#22c55e" : "#ef4444"};">
|
|
4860
4860
|
${ok ? "\u2713" : "\u2715"}
|
|
4861
4861
|
</div>
|
|
4862
|
-
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">${ok ? "
|
|
4863
|
-
<div style="font-size:12px;opacity:0.6;line-height:1.6;">${ok ? "
|
|
4864
|
-
${verificationId ? `<div style="margin-top:12px;padding:6px 12px;border-radius:8px;background:rgba(255,255,255,0.07);display:inline-block;font-size:11px;color:rgba(255,255,255,0.4);">Ref: #${verificationId}</div>` : ""}
|
|
4862
|
+
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">${ok ? "Verification submitted" : "Submission failed"}</div>
|
|
4863
|
+
<div style="font-size:12px;opacity:0.6;line-height:1.6;">${ok ? "Hold on while we wrap up\u2026" : "Something went wrong. Please try again."}</div>
|
|
4865
4864
|
</div>`;
|
|
4866
|
-
setTimeout(resolve,
|
|
4865
|
+
setTimeout(resolve, 1800);
|
|
4867
4866
|
});
|
|
4868
4867
|
}
|
|
4869
4868
|
showTimeout(onRetry) {
|
|
@@ -5034,8 +5033,9 @@ var SmartComplySDK = (() => {
|
|
|
5034
5033
|
*/
|
|
5035
5034
|
async create(params) {
|
|
5036
5035
|
const formData = new FormData();
|
|
5037
|
-
const
|
|
5038
|
-
|
|
5036
|
+
const rawIdentifier = params.identifier || `ID-${Date.now()}`;
|
|
5037
|
+
const safeIdentifier = rawIdentifier.length > 20 ? rawIdentifier.slice(0, 20) : rawIdentifier;
|
|
5038
|
+
formData.append("identifier", safeIdentifier);
|
|
5039
5039
|
formData.append("identifier_type", params.identifier_type);
|
|
5040
5040
|
formData.append("country", params.country);
|
|
5041
5041
|
formData.append(
|
|
@@ -5153,16 +5153,20 @@ var SmartComplySDK = (() => {
|
|
|
5153
5153
|
};
|
|
5154
5154
|
try {
|
|
5155
5155
|
let stream;
|
|
5156
|
-
|
|
5157
|
-
stream =
|
|
5158
|
-
}
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
}
|
|
5162
|
-
|
|
5163
|
-
|
|
5156
|
+
if (params.prewarmedStream && params.prewarmedStream.active) {
|
|
5157
|
+
stream = params.prewarmedStream;
|
|
5158
|
+
} else {
|
|
5159
|
+
try {
|
|
5160
|
+
stream = await camera.open();
|
|
5161
|
+
} catch (camErr) {
|
|
5162
|
+
if (camErr.name === "NotAllowedError" || camErr.name === "PermissionDeniedError") {
|
|
5163
|
+
throw new Error("Camera access denied. Please allow camera permission in your browser settings and try again.");
|
|
5164
|
+
}
|
|
5165
|
+
if (camErr.name === "NotFoundError" || camErr.name === "DevicesNotFoundError") {
|
|
5166
|
+
throw new Error("No camera found. Please connect a camera and try again.");
|
|
5167
|
+
}
|
|
5168
|
+
throw new Error(`Camera error: ${camErr.message || "Unable to access camera"}`);
|
|
5164
5169
|
}
|
|
5165
|
-
throw new Error(`Camera error: ${camErr.message || "Unable to access camera"}`);
|
|
5166
5170
|
}
|
|
5167
5171
|
let capturedDocBlob = params.id_file;
|
|
5168
5172
|
if (!capturedDocBlob && params.verification_type === "document_verification") {
|
|
@@ -5222,11 +5226,6 @@ var SmartComplySDK = (() => {
|
|
|
5222
5226
|
});
|
|
5223
5227
|
ui2.updateInstruction("Hold still...");
|
|
5224
5228
|
await new Promise((r2) => setTimeout(r2, 500));
|
|
5225
|
-
const autoshotBlob = await this.captureFrame(videoForDetection);
|
|
5226
|
-
tempVideo.pause();
|
|
5227
|
-
if (!autoshotBlob || autoshotBlob.size === 0) {
|
|
5228
|
-
throw new Error("Failed to capture autoshot selfie. Please ensure camera is working.");
|
|
5229
|
-
}
|
|
5230
5229
|
const challengeActions = actions || [...DEFAULT_CHALLENGE_ACTIONS];
|
|
5231
5230
|
let entry;
|
|
5232
5231
|
if (existingEntryId) {
|
|
@@ -5238,7 +5237,8 @@ var SmartComplySDK = (() => {
|
|
|
5238
5237
|
identifier_type: params.identifier_type,
|
|
5239
5238
|
country: params.country,
|
|
5240
5239
|
challenge_actions: challengeActions,
|
|
5241
|
-
|
|
5240
|
+
// W9: No autoshot at create time — real one captured post-challenge
|
|
5241
|
+
autoshot_file: void 0,
|
|
5242
5242
|
document: params.document || params.document_front || params.id_file || capturedDocBlob,
|
|
5243
5243
|
document_back: params.document_back,
|
|
5244
5244
|
identity_check: params.identity_check,
|
|
@@ -5248,6 +5248,9 @@ var SmartComplySDK = (() => {
|
|
|
5248
5248
|
fingerprint_id: deviceMeta.fingerprint_id
|
|
5249
5249
|
});
|
|
5250
5250
|
}
|
|
5251
|
+
tempVideo.pause();
|
|
5252
|
+
tempVideo.srcObject = null;
|
|
5253
|
+
tempVideo.remove();
|
|
5251
5254
|
const uiChallenge = {
|
|
5252
5255
|
actions: entry.challenge_actions,
|
|
5253
5256
|
time_limit_seconds: 45,
|
|
@@ -5280,8 +5283,11 @@ var SmartComplySDK = (() => {
|
|
|
5280
5283
|
const getLabel = (action) => uiChallenge.instructions?.[action] || this.describeAction(action);
|
|
5281
5284
|
const detectLoop = () => {
|
|
5282
5285
|
const now = performance.now();
|
|
5283
|
-
|
|
5284
|
-
|
|
5286
|
+
const remaining = pauseUntil - now;
|
|
5287
|
+
if (remaining > 0) {
|
|
5288
|
+
setTimeout(() => {
|
|
5289
|
+
animFrameId = requestAnimationFrame(detectLoop);
|
|
5290
|
+
}, remaining);
|
|
5285
5291
|
return;
|
|
5286
5292
|
}
|
|
5287
5293
|
if (pendingNextAction) {
|
|
@@ -5348,6 +5354,10 @@ var SmartComplySDK = (() => {
|
|
|
5348
5354
|
}
|
|
5349
5355
|
ui2.updateInstruction("Almost done \u2014 hold still...");
|
|
5350
5356
|
await new Promise((r2) => setTimeout(r2, 800));
|
|
5357
|
+
const autoshotBlob = await this.captureFrame(videoEl);
|
|
5358
|
+
if (!autoshotBlob || autoshotBlob.size === 0) {
|
|
5359
|
+
throw new Error("Failed to capture selfie. Please ensure your face is visible and try again.");
|
|
5360
|
+
}
|
|
5351
5361
|
const snapshotBlob = await this.captureFrame(videoEl);
|
|
5352
5362
|
let videoBlob;
|
|
5353
5363
|
let durationSeconds;
|
|
@@ -5645,8 +5655,25 @@ var SmartComplySDK = (() => {
|
|
|
5645
5655
|
isDark: false
|
|
5646
5656
|
}
|
|
5647
5657
|
};
|
|
5648
|
-
function getTheme(name) {
|
|
5649
|
-
|
|
5658
|
+
function getTheme(name, hex) {
|
|
5659
|
+
const base = { ...THEMES[name] || THEMES.default };
|
|
5660
|
+
if (hex && /^#[0-9A-Fa-f]{6}$/.test(hex.trim())) {
|
|
5661
|
+
const h2 = hex.trim();
|
|
5662
|
+
const hover = _darkenHex(h2, 15);
|
|
5663
|
+
base.primary = h2;
|
|
5664
|
+
base.primaryHover = hover;
|
|
5665
|
+
base.primaryGlow = `${h2}40`;
|
|
5666
|
+
base.borderFocus = h2;
|
|
5667
|
+
base.shimmer = `${h2}14`;
|
|
5668
|
+
}
|
|
5669
|
+
return base;
|
|
5670
|
+
}
|
|
5671
|
+
function _darkenHex(hex, amount) {
|
|
5672
|
+
const n2 = parseInt(hex.slice(1), 16);
|
|
5673
|
+
const r2 = Math.max(0, (n2 >> 16) - amount);
|
|
5674
|
+
const g2 = Math.max(0, (n2 >> 8 & 255) - amount);
|
|
5675
|
+
const b2 = Math.max(0, (n2 & 255) - amount);
|
|
5676
|
+
return `#${(r2 << 16 | g2 << 8 | b2).toString(16).padStart(6, "0")}`;
|
|
5650
5677
|
}
|
|
5651
5678
|
|
|
5652
5679
|
// src/utils/ImageQuality.ts
|
|
@@ -6582,13 +6609,13 @@ var SmartComplySDK = (() => {
|
|
|
6582
6609
|
|
|
6583
6610
|
// src/flow/SmartComplyFlow.ts
|
|
6584
6611
|
var COUNTRY_LABELS = {
|
|
6585
|
-
nigeria: "\
|
|
6586
|
-
global: "\
|
|
6587
|
-
ghana: "\
|
|
6588
|
-
kenya: "\
|
|
6589
|
-
south_africa: "\
|
|
6590
|
-
united_states: "\
|
|
6591
|
-
united_kingdom: "\
|
|
6612
|
+
nigeria: "\xF0\u0178\u2021\xB3\xF0\u0178\u2021\xAC Nigeria",
|
|
6613
|
+
global: "\xF0\u0178\u0152\x8D Other Countries",
|
|
6614
|
+
ghana: "\xF0\u0178\u2021\xAC\xF0\u0178\u2021\xAD Ghana",
|
|
6615
|
+
kenya: "\xF0\u0178\u2021\xB0\xF0\u0178\u2021\xAA Kenya",
|
|
6616
|
+
south_africa: "\xF0\u0178\u2021\xBF\xF0\u0178\u2021\xA6 South Africa",
|
|
6617
|
+
united_states: "\xF0\u0178\u2021\xBA\xF0\u0178\u2021\xB8 United States",
|
|
6618
|
+
united_kingdom: "\xF0\u0178\u2021\xAC\xF0\u0178\u2021\xA7 United Kingdom"
|
|
6592
6619
|
};
|
|
6593
6620
|
function isUploadFieldType(type) {
|
|
6594
6621
|
return type === "upload" || type === "image" || type === "file";
|
|
@@ -6602,37 +6629,37 @@ var SmartComplySDK = (() => {
|
|
|
6602
6629
|
return t2.includes("voter") || t2.includes("driver") || t2.includes("license") || t2.includes("national id") || t2.includes("national id card");
|
|
6603
6630
|
};
|
|
6604
6631
|
if (u2.includes("BVN") || u2.includes("BANK VERIFICATION")) {
|
|
6605
|
-
return { icon: "\
|
|
6632
|
+
return { icon: "\xF0\u0178\x8F\xA6", desc: "Enter your 11-digit Bank Verification Number (BVN)", requiresBack: false };
|
|
6606
6633
|
}
|
|
6607
6634
|
if (u2.includes("NIN") || u2.includes("NATIONAL IDENTITY NUMBER")) {
|
|
6608
|
-
return { icon: "\
|
|
6635
|
+
return { icon: "\xF0\u0178\u2020\u201D", desc: "Enter your 11-digit National Identification Number (NIN)", requiresBack: false };
|
|
6609
6636
|
}
|
|
6610
6637
|
if (u2.includes("GHANA")) {
|
|
6611
|
-
if (isDocumentFlow) return { icon: "\
|
|
6612
|
-
return { icon: "\
|
|
6638
|
+
if (isDocumentFlow) return { icon: "\xF0\u0178\xAA\xAA", desc: "Capture or upload your Ghana Card", requiresBack: true };
|
|
6639
|
+
return { icon: "\xF0\u0178\xAA\xAA", desc: "Enter your Ghana Card number", requiresBack: false };
|
|
6613
6640
|
}
|
|
6614
6641
|
if (u2.includes("SSNIT"))
|
|
6615
|
-
return { icon: "\
|
|
6642
|
+
return { icon: "\xF0\u0178\x8F\u203A\xEF\xB8\x8F", desc: "Enter your SSNIT number", requiresBack: false };
|
|
6616
6643
|
if (u2.includes("KENYA") || u2.includes("NATIONAL") && u2.includes("KENYA"))
|
|
6617
|
-
return { icon: "\
|
|
6644
|
+
return { icon: "\xF0\u0178\u2020\u201D", desc: "Enter your Kenya National ID number", requiresBack: false };
|
|
6618
6645
|
if (u2.includes("TIN") || u2.includes("TAX IDENTIFICATION"))
|
|
6619
|
-
return { icon: "\
|
|
6646
|
+
return { icon: "\xF0\u0178\xA7\xBE", desc: "Enter your Tax Identification Number (TIN)", requiresBack: false };
|
|
6620
6647
|
if (u2.includes("CAC") || u2.includes("CORPORATE AFFAIRS") || u2.includes("COMPANY"))
|
|
6621
|
-
return { icon: "\
|
|
6648
|
+
return { icon: "\xF0\u0178\x8F\xA2", desc: advanced ? "Enter RC number, company name & type" : "Enter your company registration number", requiresBack: false };
|
|
6622
6649
|
if (u2.includes("VOTER") || u2.includes("PVC")) {
|
|
6623
6650
|
const requiresBack = requiresBackSide(typeName);
|
|
6624
6651
|
return {
|
|
6625
|
-
icon: "\
|
|
6652
|
+
icon: "\xF0\u0178\u2014\xB3\xEF\xB8\x8F",
|
|
6626
6653
|
desc: isDocumentFlow ? `Capture or upload your voter's card${requiresBack ? " (front & back)" : ""}` : "Enter your voter ID number",
|
|
6627
6654
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6628
6655
|
};
|
|
6629
6656
|
}
|
|
6630
6657
|
if (u2.includes("PASSPORT"))
|
|
6631
|
-
return { icon: "\
|
|
6658
|
+
return { icon: "\xF0\u0178\u201C\u02DC", desc: isDocumentFlow ? "Capture or upload your passport" : "Enter your passport number", requiresBack: false };
|
|
6632
6659
|
if (u2.includes("DRIVER") || u2.includes("LICENSE") || u2.includes("LICENCE")) {
|
|
6633
6660
|
const requiresBack = requiresBackSide(typeName);
|
|
6634
6661
|
return {
|
|
6635
|
-
icon: "\
|
|
6662
|
+
icon: "\xF0\u0178\u0161\u2014",
|
|
6636
6663
|
desc: isDocumentFlow ? `Capture or upload your driver's license${requiresBack ? " (front & back)" : ""}` : "Enter your driver's license number",
|
|
6637
6664
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6638
6665
|
};
|
|
@@ -6640,15 +6667,15 @@ var SmartComplySDK = (() => {
|
|
|
6640
6667
|
if (u2.includes("NATIONAL ID") || u2.includes("NATIONAL") && u2.includes("CARD")) {
|
|
6641
6668
|
const requiresBack = requiresBackSide(typeName);
|
|
6642
6669
|
return {
|
|
6643
|
-
icon: "\
|
|
6670
|
+
icon: "\xF0\u0178\xAA\xAA",
|
|
6644
6671
|
desc: isDocumentFlow ? `Capture or upload your ID card${requiresBack ? " (front & back)" : ""}` : "Enter your National ID number",
|
|
6645
6672
|
requiresBack: isDocumentFlow ? requiresBack : false
|
|
6646
6673
|
};
|
|
6647
6674
|
}
|
|
6648
6675
|
if (u2.includes("LIVENESS") || u2.includes("FACE COMPARISON") || u2.includes("FACE MATCH"))
|
|
6649
|
-
return { icon: "\
|
|
6676
|
+
return { icon: "\xF0\u0178\xA4\xB3", desc: "We'll capture a short selfie video to verify you", requiresBack: false };
|
|
6650
6677
|
return {
|
|
6651
|
-
icon: isDocumentFlow ? "\
|
|
6678
|
+
icon: isDocumentFlow ? "\xF0\u0178\u201C\u201E" : "\xF0\u0178\u2020\u201D",
|
|
6652
6679
|
desc: isDocumentFlow ? "Capture or upload your document" : "Enter your ID number",
|
|
6653
6680
|
requiresBack: false
|
|
6654
6681
|
};
|
|
@@ -6693,6 +6720,40 @@ var SmartComplySDK = (() => {
|
|
|
6693
6720
|
if (raw.trim()[0] === raw.trim()[0].toLowerCase() && /^[a-z]/.test(raw.trim())) return null;
|
|
6694
6721
|
return raw.trim();
|
|
6695
6722
|
}
|
|
6723
|
+
function _parseOcrDate(raw) {
|
|
6724
|
+
if (!raw) return null;
|
|
6725
|
+
const s2 = raw.trim();
|
|
6726
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(s2)) return new Date(s2.slice(0, 10));
|
|
6727
|
+
const dmy = s2.match(/^(\d{2})[\/\-](\d{2})[\/\-](\d{4})/);
|
|
6728
|
+
if (dmy) return /* @__PURE__ */ new Date(`${dmy[3]}-${dmy[2]}-${dmy[1]}`);
|
|
6729
|
+
const mdy = s2.match(/^(\d{2})\/(\d{2})\/(\d{4})/);
|
|
6730
|
+
if (mdy) {
|
|
6731
|
+
if (parseInt(mdy[1], 10) > 12) return /* @__PURE__ */ new Date(`${mdy[3]}-${mdy[2]}-${mdy[1]}`);
|
|
6732
|
+
return /* @__PURE__ */ new Date(`${mdy[3]}-${mdy[1]}-${mdy[2]}`);
|
|
6733
|
+
}
|
|
6734
|
+
const dMonY = s2.match(/^(\d{1,2})\s+([A-Za-z]{3,})\s+(\d{4})/);
|
|
6735
|
+
if (dMonY) return /* @__PURE__ */ new Date(`${dMonY[2]} ${dMonY[1]} ${dMonY[3]}`);
|
|
6736
|
+
const d2 = new Date(s2);
|
|
6737
|
+
return isNaN(d2.getTime()) ? null : d2;
|
|
6738
|
+
}
|
|
6739
|
+
async function _compressImage(blob, maxWidth = 1600, quality = 0.85) {
|
|
6740
|
+
try {
|
|
6741
|
+
const bmp = await createImageBitmap(blob);
|
|
6742
|
+
const scale = Math.min(1, maxWidth / bmp.width);
|
|
6743
|
+
const w2 = Math.round(bmp.width * scale);
|
|
6744
|
+
const h2 = Math.round(bmp.height * scale);
|
|
6745
|
+
const canvas = document.createElement("canvas");
|
|
6746
|
+
canvas.width = w2;
|
|
6747
|
+
canvas.height = h2;
|
|
6748
|
+
canvas.getContext("2d").drawImage(bmp, 0, 0, w2, h2);
|
|
6749
|
+
bmp.close();
|
|
6750
|
+
return await new Promise(
|
|
6751
|
+
(res, rej) => canvas.toBlob((b2) => b2 ? res(b2) : rej(new Error("Compression failed")), "image/jpeg", quality)
|
|
6752
|
+
);
|
|
6753
|
+
} catch {
|
|
6754
|
+
return blob;
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6696
6757
|
function _formatDocType(code) {
|
|
6697
6758
|
const map = {
|
|
6698
6759
|
passport: "Passport",
|
|
@@ -6720,7 +6781,7 @@ var SmartComplySDK = (() => {
|
|
|
6720
6781
|
style.setAttribute("data-sc-animations", "true");
|
|
6721
6782
|
document.head.appendChild(style);
|
|
6722
6783
|
}
|
|
6723
|
-
var
|
|
6784
|
+
var _SmartComplyFlow = class _SmartComplyFlow {
|
|
6724
6785
|
constructor(options) {
|
|
6725
6786
|
this.theme = getTheme("default");
|
|
6726
6787
|
// DOM
|
|
@@ -6751,13 +6812,31 @@ var SmartComplySDK = (() => {
|
|
|
6751
6812
|
this.livenessEntryId = null;
|
|
6752
6813
|
this.isDestroyed = false;
|
|
6753
6814
|
this.headerBackBtn = null;
|
|
6815
|
+
// Retry safety — max 3 confirmation rejections or liveness failures per session
|
|
6816
|
+
this._confirmRetries = 0;
|
|
6817
|
+
this._livenessFailures = 0;
|
|
6818
|
+
// IVS result stored for confirm_identity screen
|
|
6819
|
+
this._ivsName = "";
|
|
6820
|
+
this._ivsGender = "";
|
|
6821
|
+
// OCR result stored for document_confirm screen
|
|
6822
|
+
this._ocrName = "";
|
|
6823
|
+
this._ocrDocNumber = "";
|
|
6824
|
+
this._ocrExpiry = "";
|
|
6825
|
+
this._ocrFaceImageUrl = "";
|
|
6826
|
+
this._ocrIsExpired = false;
|
|
6827
|
+
this._ocrExpirySoon = false;
|
|
6828
|
+
// Camera pre-warm stream (created during confirmation screen, used in liveness)
|
|
6829
|
+
this._prewarmedStream = null;
|
|
6830
|
+
// OCR gate poll timer — stored so it can be cancelled on navigation or close
|
|
6831
|
+
this._ocrPollTimer = null;
|
|
6832
|
+
// UI timeout from _startStatusPolling — stored so close() can cancel it
|
|
6833
|
+
this._uiTimeoutId = null;
|
|
6754
6834
|
// Session expiry
|
|
6755
6835
|
this.sessionExpiryTimer = null;
|
|
6756
6836
|
// Status polling (result screen)
|
|
6757
6837
|
this._pollStatusTimer = null;
|
|
6758
6838
|
// Components
|
|
6759
6839
|
this.docCapture = null;
|
|
6760
|
-
this._livenessSubmitResult = null;
|
|
6761
6840
|
this.options = options;
|
|
6762
6841
|
this.sdk = new SmartComply({
|
|
6763
6842
|
apiKey: options.apiKey,
|
|
@@ -6791,6 +6870,10 @@ var SmartComplySDK = (() => {
|
|
|
6791
6870
|
close() {
|
|
6792
6871
|
this.isDestroyed = true;
|
|
6793
6872
|
this.docCapture?.destroy();
|
|
6873
|
+
if (this._prewarmedStream) {
|
|
6874
|
+
this._prewarmedStream.getTracks().forEach((t2) => t2.stop());
|
|
6875
|
+
this._prewarmedStream = null;
|
|
6876
|
+
}
|
|
6794
6877
|
if (this.sessionExpiryTimer) {
|
|
6795
6878
|
clearTimeout(this.sessionExpiryTimer);
|
|
6796
6879
|
this.sessionExpiryTimer = null;
|
|
@@ -6799,6 +6882,14 @@ var SmartComplySDK = (() => {
|
|
|
6799
6882
|
clearTimeout(this._pollStatusTimer);
|
|
6800
6883
|
this._pollStatusTimer = null;
|
|
6801
6884
|
}
|
|
6885
|
+
if (this._ocrPollTimer) {
|
|
6886
|
+
clearTimeout(this._ocrPollTimer);
|
|
6887
|
+
this._ocrPollTimer = null;
|
|
6888
|
+
}
|
|
6889
|
+
if (this._uiTimeoutId) {
|
|
6890
|
+
clearTimeout(this._uiTimeoutId);
|
|
6891
|
+
this._uiTimeoutId = null;
|
|
6892
|
+
}
|
|
6802
6893
|
if (this.overlay) {
|
|
6803
6894
|
this.overlay.style.opacity = "0";
|
|
6804
6895
|
setTimeout(() => {
|
|
@@ -6813,9 +6904,9 @@ var SmartComplySDK = (() => {
|
|
|
6813
6904
|
}
|
|
6814
6905
|
this.options.onClose?.();
|
|
6815
6906
|
}
|
|
6816
|
-
//
|
|
6907
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6817
6908
|
// Initialization
|
|
6818
|
-
//
|
|
6909
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6819
6910
|
mount() {
|
|
6820
6911
|
this.injectStyles();
|
|
6821
6912
|
this.overlay = document.createElement("div");
|
|
@@ -6888,7 +6979,7 @@ var SmartComplySDK = (() => {
|
|
|
6888
6979
|
}
|
|
6889
6980
|
this.sdkConfig = await this.sdk.initializeConfig();
|
|
6890
6981
|
if (this.sdkConfig.theme) {
|
|
6891
|
-
this.theme = getTheme(this.sdkConfig.theme);
|
|
6982
|
+
this.theme = getTheme(this.sdkConfig.theme, this.sdkConfig.theme_hex || null);
|
|
6892
6983
|
this.applyTheme();
|
|
6893
6984
|
}
|
|
6894
6985
|
if (this.brandLabel && this.sdkConfig.brand_name) {
|
|
@@ -6900,12 +6991,16 @@ var SmartComplySDK = (() => {
|
|
|
6900
6991
|
this.showErrorStep(err.message || "Failed to initialize. Please try again.");
|
|
6901
6992
|
}
|
|
6902
6993
|
}
|
|
6903
|
-
//
|
|
6994
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6904
6995
|
// Step rendering
|
|
6905
|
-
//
|
|
6996
|
+
// ─────────────────────────────────────────────────────────────────
|
|
6906
6997
|
showStep(step) {
|
|
6907
6998
|
this.currentStep = step;
|
|
6908
6999
|
if (!this.contentArea) return;
|
|
7000
|
+
if (this._ocrPollTimer) {
|
|
7001
|
+
clearTimeout(this._ocrPollTimer);
|
|
7002
|
+
this._ocrPollTimer = null;
|
|
7003
|
+
}
|
|
6909
7004
|
this.updateProgress();
|
|
6910
7005
|
const oldContent = this.contentArea.firstChild;
|
|
6911
7006
|
if (oldContent) {
|
|
@@ -6938,17 +7033,27 @@ var SmartComplySDK = (() => {
|
|
|
6938
7033
|
case "id_input":
|
|
6939
7034
|
this.renderIdInput(wrapper);
|
|
6940
7035
|
break;
|
|
7036
|
+
case "confirm_identity":
|
|
7037
|
+
this.renderConfirmIdentity(wrapper);
|
|
7038
|
+
break;
|
|
6941
7039
|
case "document_capture":
|
|
6942
7040
|
this.renderDocumentCapture(wrapper);
|
|
6943
7041
|
break;
|
|
7042
|
+
case "ocr_gate":
|
|
7043
|
+
this.renderOcrGate(wrapper);
|
|
7044
|
+
break;
|
|
7045
|
+
case "document_confirm":
|
|
7046
|
+
this.renderDocumentConfirm(wrapper);
|
|
7047
|
+
break;
|
|
6944
7048
|
case "processing":
|
|
6945
|
-
this.renderProcessing(wrapper, this.livenessEntryId || void 0);
|
|
6946
7049
|
break;
|
|
6947
7050
|
case "liveness":
|
|
6948
7051
|
this.renderLiveness(wrapper);
|
|
6949
7052
|
break;
|
|
7053
|
+
case "done":
|
|
7054
|
+
this.renderDone(wrapper);
|
|
7055
|
+
break;
|
|
6950
7056
|
case "result":
|
|
6951
|
-
this.renderResult(wrapper);
|
|
6952
7057
|
break;
|
|
6953
7058
|
case "error":
|
|
6954
7059
|
break;
|
|
@@ -6959,7 +7064,7 @@ var SmartComplySDK = (() => {
|
|
|
6959
7064
|
wrapper.style.transform = "translateX(0)";
|
|
6960
7065
|
});
|
|
6961
7066
|
}
|
|
6962
|
-
//
|
|
7067
|
+
// ── Loading ─────────────────────────────────────────────────────
|
|
6963
7068
|
renderLoading(container) {
|
|
6964
7069
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
|
|
6965
7070
|
const spinnerWrap = document.createElement("div");
|
|
@@ -6990,7 +7095,7 @@ var SmartComplySDK = (() => {
|
|
|
6990
7095
|
sub.textContent = "Connecting to the verification service...";
|
|
6991
7096
|
container.appendChild(sub);
|
|
6992
7097
|
}
|
|
6993
|
-
//
|
|
7098
|
+
// ── Welcome ────────────────────────────────────────────────────
|
|
6994
7099
|
renderWelcome(container) {
|
|
6995
7100
|
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;";
|
|
6996
7101
|
const isDocFlow = this.isDocumentFlow();
|
|
@@ -7004,7 +7109,7 @@ var SmartComplySDK = (() => {
|
|
|
7004
7109
|
background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
|
|
7005
7110
|
border:1.5px solid ${this.theme.primary}33;
|
|
7006
7111
|
`;
|
|
7007
|
-
iconWrap.textContent = isDocFlow ? "\
|
|
7112
|
+
iconWrap.textContent = isDocFlow ? "\xF0\u0178\xAA\xAA" : "\xF0\u0178\u203A\xA1\xEF\xB8\x8F";
|
|
7008
7113
|
topRow.appendChild(iconWrap);
|
|
7009
7114
|
const badge = document.createElement("div");
|
|
7010
7115
|
badge.style.cssText = `
|
|
@@ -7015,7 +7120,7 @@ var SmartComplySDK = (() => {
|
|
|
7015
7120
|
color:${this.theme.primary};
|
|
7016
7121
|
border:1px solid ${this.theme.primary}30;
|
|
7017
7122
|
`;
|
|
7018
|
-
badge.textContent = isDocFlow ? "\
|
|
7123
|
+
badge.textContent = isDocFlow ? "\xF0\u0178\u201C\u201E Document Verification" : "\xF0\u0178\u201D\x8D Data Verification";
|
|
7019
7124
|
topRow.appendChild(badge);
|
|
7020
7125
|
container.appendChild(topRow);
|
|
7021
7126
|
const titleWrap = document.createElement("div");
|
|
@@ -7025,24 +7130,25 @@ var SmartComplySDK = (() => {
|
|
|
7025
7130
|
color:${this.theme.text};font-size:20px;font-weight:700;
|
|
7026
7131
|
margin:0;line-height:1.2;
|
|
7027
7132
|
`;
|
|
7028
|
-
|
|
7133
|
+
const brandName = this.sdkConfig?.brand_name;
|
|
7134
|
+
title.textContent = brandName ? `Verify your identity for ${brandName}` : "Identity Verification";
|
|
7029
7135
|
titleWrap.appendChild(title);
|
|
7030
7136
|
const desc = document.createElement("p");
|
|
7031
7137
|
desc.style.cssText = `
|
|
7032
7138
|
color:${this.theme.textSecondary};font-size:13px;line-height:1.5;
|
|
7033
7139
|
margin:0;max-width:300px;
|
|
7034
7140
|
`;
|
|
7035
|
-
desc.textContent = isDocFlow ? "
|
|
7141
|
+
desc.textContent = isDocFlow ? "You'll need your document and camera access. This takes about 2 minutes." : "You'll need your ID number and camera access. This takes about 2 minutes.";
|
|
7036
7142
|
titleWrap.appendChild(desc);
|
|
7037
7143
|
container.appendChild(titleWrap);
|
|
7038
7144
|
const steps = isDocFlow ? [
|
|
7039
|
-
{ icon: "\
|
|
7040
|
-
{ icon: "\
|
|
7041
|
-
{ icon: "\
|
|
7145
|
+
{ icon: "\xF0\u0178\xAA\xAA", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
|
|
7146
|
+
{ icon: "\xF0\u0178\u201C\xB7", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
|
|
7147
|
+
{ icon: "\xF0\u0178\xA4\xB3", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
7042
7148
|
] : [
|
|
7043
|
-
{ icon: "\
|
|
7044
|
-
{ icon: "\
|
|
7045
|
-
{ icon: "\
|
|
7149
|
+
{ icon: "\xF0\u0178\u2020\u201D", text: "Choose your ID type", sub: "NIN, BVN, Voter's Card and more" },
|
|
7150
|
+
{ icon: "\xF0\u0178\u201C\x9D", text: "Enter your ID details", sub: "Exactly as shown on your ID" },
|
|
7151
|
+
{ icon: "\xF0\u0178\xA4\xB3", text: "Face check", sub: "Quick selfie to confirm it's you" }
|
|
7046
7152
|
];
|
|
7047
7153
|
const stepsWrap = document.createElement("div");
|
|
7048
7154
|
stepsWrap.style.cssText = `
|
|
@@ -7082,7 +7188,7 @@ var SmartComplySDK = (() => {
|
|
|
7082
7188
|
stepsWrap.appendChild(row);
|
|
7083
7189
|
});
|
|
7084
7190
|
container.appendChild(stepsWrap);
|
|
7085
|
-
const btn = this.createPrimaryButton("Get Started \
|
|
7191
|
+
const btn = this.createPrimaryButton("Get Started \xE2\u2020\u2019");
|
|
7086
7192
|
btn.style.cssText += "margin-top:2px;";
|
|
7087
7193
|
btn.addEventListener("click", () => this.showStep("country"));
|
|
7088
7194
|
container.appendChild(btn);
|
|
@@ -7091,10 +7197,10 @@ var SmartComplySDK = (() => {
|
|
|
7091
7197
|
display:flex;align-items:center;gap:6px;
|
|
7092
7198
|
color:${this.theme.textMuted};font-size:10.5px;
|
|
7093
7199
|
`;
|
|
7094
|
-
trust.textContent = "\
|
|
7200
|
+
trust.textContent = "\xF0\u0178\u201D\u2019 End-to-end encrypted";
|
|
7095
7201
|
container.appendChild(trust);
|
|
7096
7202
|
}
|
|
7097
|
-
//
|
|
7203
|
+
// ── Country Select ──────────────────────────────────────────────
|
|
7098
7204
|
renderCountrySelect(container) {
|
|
7099
7205
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7100
7206
|
const channels = this.sdkConfig?.channels || {};
|
|
@@ -7122,7 +7228,7 @@ var SmartComplySDK = (() => {
|
|
|
7122
7228
|
container.appendChild(card);
|
|
7123
7229
|
}
|
|
7124
7230
|
}
|
|
7125
|
-
//
|
|
7231
|
+
// ── ID Type Select ──────────────────────────────────────────────
|
|
7126
7232
|
renderIdTypeSelect(container) {
|
|
7127
7233
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7128
7234
|
const globalDocFlow = this.isDocumentFlow();
|
|
@@ -7144,7 +7250,7 @@ var SmartComplySDK = (() => {
|
|
|
7144
7250
|
const requiresBack = requiresBackFromBackend ?? info.requiresBack;
|
|
7145
7251
|
let subtitle2 = info.desc;
|
|
7146
7252
|
if (requiresBack && isThisDocFlow) {
|
|
7147
|
-
subtitle2 += " \
|
|
7253
|
+
subtitle2 += " \xE2\u0161\xA0\xEF\xB8\x8F Both sides required";
|
|
7148
7254
|
}
|
|
7149
7255
|
const displayName = typeName.replace(/\s*with\s+face\s*/gi, " ").replace(/\s*\(NIN\)\s*/gi, " ").replace(/\s*\(BVN\)\s*/gi, " ").replace(/\s{2,}/g, " ").trim();
|
|
7150
7256
|
const card = this.createOptionCard(
|
|
@@ -7177,7 +7283,7 @@ var SmartComplySDK = (() => {
|
|
|
7177
7283
|
container.appendChild(card);
|
|
7178
7284
|
}
|
|
7179
7285
|
}
|
|
7180
|
-
//
|
|
7286
|
+
// ── ID Input (data verification — dynamic fields) ─────────────
|
|
7181
7287
|
renderIdInput(container) {
|
|
7182
7288
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7183
7289
|
const dataFields = this.selectedChannelFields.filter((f2) => f2.type !== "upload");
|
|
@@ -7207,13 +7313,13 @@ var SmartComplySDK = (() => {
|
|
|
7207
7313
|
{ match: "BVN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "BVN must be exactly 11 digits" } },
|
|
7208
7314
|
{ match: "NIN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
|
|
7209
7315
|
{ match: "NATIONAL IDENTITY", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
|
|
7210
|
-
{ match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6\
|
|
7211
|
-
{ match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3\
|
|
7212
|
-
{ match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6\
|
|
7213
|
-
{ match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8\
|
|
7214
|
-
{ match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7\
|
|
7215
|
-
{ match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8\
|
|
7216
|
-
{ match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10\
|
|
7316
|
+
{ match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6\xE2\u20AC\u201C20 alphanumeric characters" } },
|
|
7317
|
+
{ match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3\xE2\u20AC\u201C30 alphanumeric characters" } },
|
|
7318
|
+
{ match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6\xE2\u20AC\u201C30 alphanumeric characters" } },
|
|
7319
|
+
{ match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8\xE2\u20AC\u201C20 alphanumeric characters" } },
|
|
7320
|
+
{ match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7\xE2\u20AC\u201C15 alphanumeric characters" } },
|
|
7321
|
+
{ match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8\xE2\u20AC\u201C20 characters" } },
|
|
7322
|
+
{ match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10\xE2\u20AC\u201C12 characters" } }
|
|
7217
7323
|
];
|
|
7218
7324
|
function getFieldRule(label) {
|
|
7219
7325
|
const upper = label.toUpperCase();
|
|
@@ -7411,7 +7517,11 @@ var SmartComplySDK = (() => {
|
|
|
7411
7517
|
const fieldValues = Object.values(this.collectedFields);
|
|
7412
7518
|
this.idNumber = fieldValues[0] || "";
|
|
7413
7519
|
this.identityCheckId = this.verificationResult.data?.identity_check_id || null;
|
|
7414
|
-
this.
|
|
7520
|
+
const d2 = this.verificationResult.data || {};
|
|
7521
|
+
const nd = d2.normalized_data || d2.data || d2;
|
|
7522
|
+
this._ivsName = [nd.first_name || nd.firstname || "", nd.last_name || nd.lastname || nd.surname || ""].map((s2) => String(s2 || "").trim()).filter(Boolean).join(" ");
|
|
7523
|
+
this._ivsGender = nd.gender || "";
|
|
7524
|
+
this.showStep("confirm_identity");
|
|
7415
7525
|
} else {
|
|
7416
7526
|
errorEl.textContent = this.verificationResult.message || "We could not verify your details. Please check and try again.";
|
|
7417
7527
|
errorEl.style.display = "block";
|
|
@@ -7445,7 +7555,7 @@ var SmartComplySDK = (() => {
|
|
|
7445
7555
|
setTimeout(() => firstInput.focus(), 350);
|
|
7446
7556
|
}
|
|
7447
7557
|
}
|
|
7448
|
-
//
|
|
7558
|
+
// ── Document Capture ────────────────────────────────────────────
|
|
7449
7559
|
renderDocumentCapture(container) {
|
|
7450
7560
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7451
7561
|
const rawName = this.selectedIdTypeName || this.selectedIdType || "your document";
|
|
@@ -7481,123 +7591,366 @@ var SmartComplySDK = (() => {
|
|
|
7481
7591
|
this.showErrorStep(err.message || "Document capture failed. Please try again.");
|
|
7482
7592
|
});
|
|
7483
7593
|
}
|
|
7484
|
-
//
|
|
7485
|
-
|
|
7486
|
-
container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:
|
|
7594
|
+
// ── W3: Identity Confirmation (data verification) ───────────────
|
|
7595
|
+
renderConfirmIdentity(container) {
|
|
7596
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:8px 0;";
|
|
7597
|
+
if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
|
|
7598
|
+
this._renderRetryLimitReached(container);
|
|
7599
|
+
return;
|
|
7600
|
+
}
|
|
7601
|
+
this._prewarmCamera();
|
|
7602
|
+
const iconWrap = document.createElement("div");
|
|
7603
|
+
iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7604
|
+
iconWrap.textContent = "\xF0\u0178\u203A\xA1\xEF\xB8\x8F";
|
|
7605
|
+
container.appendChild(iconWrap);
|
|
7606
|
+
const title = document.createElement("h2");
|
|
7607
|
+
title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
7608
|
+
title.textContent = this._ivsName ? `Is this you?` : "Identity Confirmed";
|
|
7609
|
+
container.appendChild(title);
|
|
7610
|
+
if (this._ivsName) {
|
|
7611
|
+
const card = document.createElement("div");
|
|
7612
|
+
card.style.cssText = `width:100%;padding:16px;border-radius:14px;background:${this.theme.cardBg};border:1.5px solid ${this.theme.border};text-align:left;display:flex;flex-direction:column;gap:10px;`;
|
|
7613
|
+
const rows = [
|
|
7614
|
+
{ label: "Name", value: this._ivsName }
|
|
7615
|
+
];
|
|
7616
|
+
if (this._ivsGender) {
|
|
7617
|
+
const gLabel = this._ivsGender.toLowerCase();
|
|
7618
|
+
rows.push({ label: "Gender", value: gLabel === "m" || gLabel === "male" || gLabel === "1" ? "Male" : gLabel === "f" || gLabel === "female" || gLabel === "2" ? "Female" : this._ivsGender });
|
|
7619
|
+
}
|
|
7620
|
+
rows.push({ label: "ID Type", value: _formatDocType(this.selectedIdType) || this.selectedIdTypeName || this.selectedIdType });
|
|
7621
|
+
rows.forEach(({ label, value }) => {
|
|
7622
|
+
const row = document.createElement("div");
|
|
7623
|
+
row.style.cssText = "display:flex;justify-content:space-between;align-items:center;";
|
|
7624
|
+
const lEl = document.createElement("span");
|
|
7625
|
+
lEl.style.cssText = `color:${this.theme.textMuted};font-size:12px;font-weight:500;`;
|
|
7626
|
+
lEl.textContent = label;
|
|
7627
|
+
const vEl = document.createElement("span");
|
|
7628
|
+
vEl.style.cssText = `color:${this.theme.text};font-size:13px;font-weight:600;max-width:60%;text-align:right;`;
|
|
7629
|
+
vEl.textContent = value;
|
|
7630
|
+
row.appendChild(lEl);
|
|
7631
|
+
row.appendChild(vEl);
|
|
7632
|
+
card.appendChild(row);
|
|
7633
|
+
});
|
|
7634
|
+
container.appendChild(card);
|
|
7635
|
+
} else {
|
|
7636
|
+
const sub = document.createElement("p");
|
|
7637
|
+
sub.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.6;`;
|
|
7638
|
+
sub.textContent = "Your details have been confirmed. Please continue to the face check.";
|
|
7639
|
+
container.appendChild(sub);
|
|
7640
|
+
}
|
|
7641
|
+
const yesBtn = this.createPrimaryButton("Yes, that's me \xE2\u20AC\u201D continue");
|
|
7642
|
+
yesBtn.addEventListener("click", () => {
|
|
7643
|
+
this._confirmRetries = 0;
|
|
7644
|
+
this.showStep("liveness");
|
|
7645
|
+
});
|
|
7646
|
+
container.appendChild(yesBtn);
|
|
7647
|
+
const noBtn = this.createSecondaryButton("No, use a different ID");
|
|
7648
|
+
noBtn.addEventListener("click", () => {
|
|
7649
|
+
this._confirmRetries++;
|
|
7650
|
+
this._ivsName = "";
|
|
7651
|
+
this._ivsGender = "";
|
|
7652
|
+
this.identityCheckId = null;
|
|
7653
|
+
this.verificationResult = null;
|
|
7654
|
+
this.showStep("id_input");
|
|
7655
|
+
});
|
|
7656
|
+
container.appendChild(noBtn);
|
|
7657
|
+
}
|
|
7658
|
+
// ── W4: Document Confirmation (document verification) ────────────
|
|
7659
|
+
renderDocumentConfirm(container) {
|
|
7660
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:8px 0;";
|
|
7661
|
+
if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
|
|
7662
|
+
this._renderRetryLimitReached(container);
|
|
7663
|
+
return;
|
|
7664
|
+
}
|
|
7665
|
+
this._prewarmCamera();
|
|
7666
|
+
const iconWrap = document.createElement("div");
|
|
7667
|
+
iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7668
|
+
iconWrap.textContent = "\xF0\u0178\u201C\u201E";
|
|
7669
|
+
container.appendChild(iconWrap);
|
|
7670
|
+
const title = document.createElement("h2");
|
|
7671
|
+
title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
7672
|
+
title.textContent = "Are these your details?";
|
|
7673
|
+
container.appendChild(title);
|
|
7674
|
+
const sub = document.createElement("p");
|
|
7675
|
+
sub.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.5;`;
|
|
7676
|
+
sub.textContent = "We read the following from your document. Please confirm they are correct before continuing.";
|
|
7677
|
+
container.appendChild(sub);
|
|
7678
|
+
if (this._ocrFaceImageUrl) {
|
|
7679
|
+
const faceWrap = document.createElement("div");
|
|
7680
|
+
faceWrap.style.cssText = `width:80px;height:80px;border-radius:50%;overflow:hidden;border:3px solid ${this.theme.primary};flex-shrink:0;margin:0 auto;`;
|
|
7681
|
+
const faceImg = document.createElement("img");
|
|
7682
|
+
faceImg.src = this._ocrFaceImageUrl;
|
|
7683
|
+
faceImg.alt = "Photo from your document";
|
|
7684
|
+
faceImg.style.cssText = "width:100%;height:100%;object-fit:cover;";
|
|
7685
|
+
faceWrap.appendChild(faceImg);
|
|
7686
|
+
container.appendChild(faceWrap);
|
|
7687
|
+
}
|
|
7688
|
+
if (this._ocrExpirySoon && !this._ocrIsExpired) {
|
|
7689
|
+
const warn = document.createElement("div");
|
|
7690
|
+
warn.style.cssText = `width:100%;padding:10px 14px;border-radius:10px;background:${this.theme.warning}18;border:1px solid ${this.theme.warning}40;color:${this.theme.warning};font-size:12px;text-align:left;line-height:1.5;`;
|
|
7691
|
+
warn.textContent = "\xE2\u0161\xA0\xEF\xB8\x8F Your document expires soon. Some services may not accept it, but you can still continue.";
|
|
7692
|
+
container.appendChild(warn);
|
|
7693
|
+
}
|
|
7694
|
+
const card = document.createElement("div");
|
|
7695
|
+
card.style.cssText = `width:100%;padding:14px 16px;border-radius:14px;background:${this.theme.cardBg};border:1.5px solid ${this.theme.border};text-align:left;display:flex;flex-direction:column;gap:10px;`;
|
|
7696
|
+
const docRows = [];
|
|
7697
|
+
if (this._ocrName) docRows.push({ label: "Name", value: this._ocrName });
|
|
7698
|
+
if (this._ocrDocNumber) docRows.push({ label: "Document No", value: this._ocrDocNumber });
|
|
7699
|
+
if (this._ocrExpiry) docRows.push({ label: "Expires", value: this._ocrExpiry });
|
|
7700
|
+
docRows.push({ label: "Document Type", value: _formatDocType(this.selectedIdType) || this.selectedIdTypeName });
|
|
7701
|
+
if (docRows.length === 0) {
|
|
7702
|
+
const fallback = document.createElement("div");
|
|
7703
|
+
fallback.style.cssText = `color:${this.theme.textSecondary};font-size:12px;`;
|
|
7704
|
+
fallback.textContent = "Document scanned successfully.";
|
|
7705
|
+
card.appendChild(fallback);
|
|
7706
|
+
} else {
|
|
7707
|
+
docRows.forEach(({ label, value }) => {
|
|
7708
|
+
if (!value) return;
|
|
7709
|
+
const row = document.createElement("div");
|
|
7710
|
+
row.style.cssText = "display:flex;justify-content:space-between;align-items:center;";
|
|
7711
|
+
const lEl = document.createElement("span");
|
|
7712
|
+
lEl.style.cssText = `color:${this.theme.textMuted};font-size:12px;font-weight:500;`;
|
|
7713
|
+
lEl.textContent = label;
|
|
7714
|
+
const vEl = document.createElement("span");
|
|
7715
|
+
vEl.style.cssText = `color:${this.theme.text};font-size:13px;font-weight:600;max-width:60%;text-align:right;`;
|
|
7716
|
+
vEl.textContent = value;
|
|
7717
|
+
row.appendChild(lEl);
|
|
7718
|
+
row.appendChild(vEl);
|
|
7719
|
+
card.appendChild(row);
|
|
7720
|
+
});
|
|
7721
|
+
}
|
|
7722
|
+
container.appendChild(card);
|
|
7723
|
+
const yesBtn = this.createPrimaryButton("Yes, continue");
|
|
7724
|
+
yesBtn.addEventListener("click", () => {
|
|
7725
|
+
this._confirmRetries = 0;
|
|
7726
|
+
this.showStep("liveness");
|
|
7727
|
+
});
|
|
7728
|
+
container.appendChild(yesBtn);
|
|
7729
|
+
const noBtn = this.createSecondaryButton("No, retake document");
|
|
7730
|
+
noBtn.addEventListener("click", () => {
|
|
7731
|
+
this._confirmRetries++;
|
|
7732
|
+
this._ocrName = "";
|
|
7733
|
+
this._ocrDocNumber = "";
|
|
7734
|
+
this._ocrExpiry = "";
|
|
7735
|
+
this._ocrFaceImageUrl = "";
|
|
7736
|
+
this._ocrIsExpired = false;
|
|
7737
|
+
this._ocrExpirySoon = false;
|
|
7738
|
+
this.livenessEntryId = null;
|
|
7739
|
+
this.documentBlob = null;
|
|
7740
|
+
this.documentBackBlob = null;
|
|
7741
|
+
this.showStep("document_capture");
|
|
7742
|
+
});
|
|
7743
|
+
container.appendChild(noBtn);
|
|
7744
|
+
}
|
|
7745
|
+
// ── W6+W7+W8: OCR Gate (document verification) ───────────────────
|
|
7746
|
+
renderOcrGate(container) {
|
|
7747
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
|
|
7487
7748
|
const spinnerWrap = document.createElement("div");
|
|
7488
|
-
spinnerWrap.style.cssText = "position:relative;width:
|
|
7749
|
+
spinnerWrap.style.cssText = "position:relative;width:52px;height:52px;flex-shrink:0;";
|
|
7489
7750
|
const ring = document.createElement("div");
|
|
7490
|
-
ring.id = "sc-proc-ring";
|
|
7491
7751
|
ring.style.cssText = `position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.9s linear infinite;`;
|
|
7492
|
-
const
|
|
7493
|
-
|
|
7494
|
-
|
|
7752
|
+
const centre = document.createElement("div");
|
|
7753
|
+
centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
|
|
7754
|
+
centre.textContent = "\xF0\u0178\u201C\u201E";
|
|
7495
7755
|
spinnerWrap.appendChild(ring);
|
|
7496
|
-
spinnerWrap.appendChild(
|
|
7756
|
+
spinnerWrap.appendChild(centre);
|
|
7497
7757
|
container.appendChild(spinnerWrap);
|
|
7498
|
-
const
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
{ id: "result", label: "Finalising result", icon: "\u2713" }
|
|
7514
|
-
];
|
|
7515
|
-
const grid = document.createElement("div");
|
|
7516
|
-
grid.style.cssText = "width:100%;display:grid;grid-template-columns:1fr 1fr;gap:8px;";
|
|
7517
|
-
stages.forEach((s2) => {
|
|
7518
|
-
const el = document.createElement("div");
|
|
7519
|
-
el.id = `sc-stage-${s2.id}`;
|
|
7520
|
-
el.style.cssText = `display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:10px;background:${this.theme.cardBg};border:1px solid ${this.theme.border};transition:all 0.3s ease;`;
|
|
7521
|
-
el.innerHTML = `
|
|
7522
|
-
<div style="width:26px;height:26px;border-radius:7px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:12px;background:${this.theme.shimmer};">${s2.icon}</div>
|
|
7523
|
-
<div style="flex:1;color:${this.theme.textSecondary};font-size:11px;font-weight:500;">${s2.label}</div>
|
|
7524
|
-
<div id="sc-stage-status-${s2.id}" style="width:14px;height:14px;border-radius:50%;border:2px solid ${this.theme.border};flex-shrink:0;"></div>
|
|
7525
|
-
`;
|
|
7526
|
-
grid.appendChild(el);
|
|
7527
|
-
});
|
|
7528
|
-
container.appendChild(grid);
|
|
7529
|
-
if (entryId) {
|
|
7530
|
-
this._pollProcessingStages(entryId, statusLine, grid, stages);
|
|
7531
|
-
}
|
|
7532
|
-
}
|
|
7533
|
-
_pollProcessingStages(entryId, statusLine, _grid, stages) {
|
|
7534
|
-
const stageTimings = [0, 4e3, 1e4, 18e3];
|
|
7535
|
-
const startTime = Date.now();
|
|
7536
|
-
const setStageActive = (stageId) => {
|
|
7537
|
-
const el = document.getElementById(`sc-stage-${stageId}`);
|
|
7538
|
-
const statusEl = document.getElementById(`sc-stage-status-${stageId}`);
|
|
7539
|
-
if (!el || !statusEl) return;
|
|
7540
|
-
el.style.borderColor = this.theme.primary;
|
|
7541
|
-
el.style.background = `${this.theme.primary}10`;
|
|
7542
|
-
statusEl.innerHTML = `<div style="position:absolute;inset:1px;border-radius:50%;border:2px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>`;
|
|
7543
|
-
statusEl.style.position = "relative";
|
|
7544
|
-
};
|
|
7545
|
-
const setStageComplete = (stageId) => {
|
|
7546
|
-
const el = document.getElementById(`sc-stage-${stageId}`);
|
|
7547
|
-
const statusEl = document.getElementById(`sc-stage-status-${stageId}`);
|
|
7548
|
-
if (!el || !statusEl) return;
|
|
7549
|
-
el.style.borderColor = this.theme.success || "#22c55e";
|
|
7550
|
-
el.style.background = `${this.theme.success || "#22c55e"}10`;
|
|
7551
|
-
statusEl.innerHTML = `<div style="position:absolute;inset:2px;border-radius:50%;background:${this.theme.success || "#22c55e"};display:flex;align-items:center;justify-content:center;color:#fff;font-size:8px;font-weight:bold;">\u2713</div>`;
|
|
7552
|
-
statusEl.style.position = "relative";
|
|
7553
|
-
};
|
|
7554
|
-
stages.forEach((s2, i2) => {
|
|
7555
|
-
setTimeout(() => {
|
|
7556
|
-
if (this.isDestroyed) return;
|
|
7557
|
-
setStageActive(s2.id);
|
|
7558
|
-
if (i2 > 0) setStageComplete(stages[i2 - 1].id);
|
|
7559
|
-
statusLine.textContent = s2.id === "ocr" ? "Scanning document\u2026" : s2.id === "expiry" ? "Checking expiry date\u2026" : s2.id === "face" ? "Running face match\u2026" : "Finalising result\u2026";
|
|
7560
|
-
}, stageTimings[i2]);
|
|
7561
|
-
});
|
|
7562
|
-
const MAX_POLLS = 30;
|
|
7758
|
+
const statusEl = document.createElement("div");
|
|
7759
|
+
statusEl.setAttribute("aria-live", "polite");
|
|
7760
|
+
statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
|
|
7761
|
+
statusEl.textContent = "Reading your document\xE2\u20AC\xA6";
|
|
7762
|
+
container.appendChild(statusEl);
|
|
7763
|
+
const subEl = document.createElement("div");
|
|
7764
|
+
subEl.style.cssText = `color:${this.theme.textSecondary};font-size:12px;max-width:260px;line-height:1.5;`;
|
|
7765
|
+
subEl.textContent = "This usually takes 5\xE2\u20AC\u201C15 seconds. Please wait.";
|
|
7766
|
+
container.appendChild(subEl);
|
|
7767
|
+
if (!this.livenessEntryId) {
|
|
7768
|
+
this.showErrorStep("Document upload failed. Please try again.");
|
|
7769
|
+
return;
|
|
7770
|
+
}
|
|
7771
|
+
const entryId = this.livenessEntryId;
|
|
7772
|
+
const MAX_POLLS = 10;
|
|
7563
7773
|
let polls = 0;
|
|
7564
7774
|
const poll = async () => {
|
|
7565
7775
|
if (this.isDestroyed) return;
|
|
7566
7776
|
polls++;
|
|
7567
7777
|
try {
|
|
7568
7778
|
const result = await this.sdk.liveness.pollResult(entryId);
|
|
7569
|
-
if (result.
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7779
|
+
if (result.is_expired === true) {
|
|
7780
|
+
container.innerHTML = "";
|
|
7781
|
+
this._renderExpiredDocument(container);
|
|
7782
|
+
return;
|
|
7783
|
+
}
|
|
7784
|
+
if (result.ocr_quality_failed === true) {
|
|
7785
|
+
container.innerHTML = "";
|
|
7786
|
+
this._renderOcrQualityFail(container, result.ocr_rejection_reason);
|
|
7787
|
+
return;
|
|
7788
|
+
}
|
|
7789
|
+
const ocrDone = result.ocr_status === "done" || result.ocr_status === "ocr_complete" || result.ocr_name || result.ocr_document_number || result.ocr_expiry_date;
|
|
7790
|
+
if (ocrDone) {
|
|
7791
|
+
this._ocrName = result.ocr_name || "";
|
|
7792
|
+
this._ocrDocNumber = result.ocr_document_number || "";
|
|
7793
|
+
this._ocrExpiry = result.ocr_expiry_date || "";
|
|
7794
|
+
this._ocrFaceImageUrl = result.ocr_face_image_url || "";
|
|
7795
|
+
this._ocrIsExpired = result.is_expired === true;
|
|
7796
|
+
if (this._ocrExpiry) {
|
|
7797
|
+
const expDate = _parseOcrDate(this._ocrExpiry);
|
|
7798
|
+
if (expDate) {
|
|
7799
|
+
const sixMonths = /* @__PURE__ */ new Date();
|
|
7800
|
+
sixMonths.setMonth(sixMonths.getMonth() + 6);
|
|
7801
|
+
this._ocrExpirySoon = expDate <= sixMonths && expDate > /* @__PURE__ */ new Date();
|
|
7576
7802
|
}
|
|
7577
|
-
}
|
|
7803
|
+
}
|
|
7804
|
+
this.showStep("document_confirm");
|
|
7578
7805
|
return;
|
|
7579
7806
|
}
|
|
7807
|
+
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
7808
|
+
this._ocrPollTimer = setTimeout(poll, 2e3);
|
|
7809
|
+
} else if (!this.isDestroyed) {
|
|
7810
|
+
statusEl.textContent = "Taking longer than usual\xE2\u20AC\xA6";
|
|
7811
|
+
subEl.textContent = "We'll continue \xE2\u20AC\u201D you can confirm your details on the next screen.";
|
|
7812
|
+
this._ocrPollTimer = setTimeout(() => {
|
|
7813
|
+
if (!this.isDestroyed) this.showStep("document_confirm");
|
|
7814
|
+
}, 2e3);
|
|
7815
|
+
}
|
|
7580
7816
|
} catch {
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
statusLine.textContent = "Still processing \u2014 check back soon";
|
|
7817
|
+
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
7818
|
+
this._ocrPollTimer = setTimeout(poll, 3e3);
|
|
7819
|
+
} else if (!this.isDestroyed) {
|
|
7820
|
+
this.showErrorStep("We could not read your document. Please check your connection and try again.");
|
|
7821
|
+
}
|
|
7587
7822
|
}
|
|
7588
7823
|
};
|
|
7589
|
-
this.
|
|
7824
|
+
this._ocrPollTimer = setTimeout(poll, 3e3);
|
|
7825
|
+
}
|
|
7826
|
+
_renderExpiredDocument(container) {
|
|
7827
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
7828
|
+
const icon = document.createElement("div");
|
|
7829
|
+
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7830
|
+
icon.textContent = "\xF0\u0178\u201C\u2026";
|
|
7831
|
+
container.appendChild(icon);
|
|
7832
|
+
const t2 = document.createElement("h2");
|
|
7833
|
+
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
7834
|
+
t2.textContent = "Document Expired";
|
|
7835
|
+
container.appendChild(t2);
|
|
7836
|
+
const d2 = document.createElement("p");
|
|
7837
|
+
d2.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.6;`;
|
|
7838
|
+
d2.textContent = "The document you submitted has expired. Please use a valid, current ID.";
|
|
7839
|
+
container.appendChild(d2);
|
|
7840
|
+
const retryBtn = this.createPrimaryButton("Try a different document");
|
|
7841
|
+
retryBtn.addEventListener("click", () => {
|
|
7842
|
+
this.livenessEntryId = null;
|
|
7843
|
+
this.documentBlob = null;
|
|
7844
|
+
this.documentBackBlob = null;
|
|
7845
|
+
this.showStep("document_capture");
|
|
7846
|
+
});
|
|
7847
|
+
container.appendChild(retryBtn);
|
|
7848
|
+
const endBtn = this.createSecondaryButton("End session");
|
|
7849
|
+
endBtn.addEventListener("click", () => {
|
|
7850
|
+
this.options.onError?.(new Error("Document expired"));
|
|
7851
|
+
this.close();
|
|
7852
|
+
});
|
|
7853
|
+
container.appendChild(endBtn);
|
|
7854
|
+
}
|
|
7855
|
+
_renderOcrQualityFail(container, reason) {
|
|
7856
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
|
|
7857
|
+
const icon = document.createElement("div");
|
|
7858
|
+
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7859
|
+
icon.textContent = "\xF0\u0178\u201D\x8D";
|
|
7860
|
+
container.appendChild(icon);
|
|
7861
|
+
const t2 = document.createElement("h2");
|
|
7862
|
+
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
7863
|
+
t2.textContent = "Document Photo Unclear";
|
|
7864
|
+
container.appendChild(t2);
|
|
7865
|
+
const msg = reason === "image_too_blurry" ? "Your document photo is too blurry. Please retake it in good lighting with a steady hand." : reason === "image_too_dark" ? "Your document photo is too dark. Please retake it in better lighting." : reason === "image_too_bright" ? "Your document photo has too much glare. Please reduce reflections and retake." : reason === "document_not_detected" ? "We could not detect a document in the photo. Please ensure the full ID is visible." : "We could not read your document clearly. Please retake the photo in good lighting with no glare.";
|
|
7866
|
+
const d2 = document.createElement("p");
|
|
7867
|
+
d2.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.6;`;
|
|
7868
|
+
d2.textContent = msg;
|
|
7869
|
+
container.appendChild(d2);
|
|
7870
|
+
const retryBtn = this.createPrimaryButton("Retake photo");
|
|
7871
|
+
retryBtn.addEventListener("click", () => {
|
|
7872
|
+
this.livenessEntryId = null;
|
|
7873
|
+
this.documentBlob = null;
|
|
7874
|
+
this.documentBackBlob = null;
|
|
7875
|
+
this.showStep("document_capture");
|
|
7876
|
+
});
|
|
7877
|
+
container.appendChild(retryBtn);
|
|
7878
|
+
}
|
|
7879
|
+
// ── W11: You're all done (end-user final screen) ─────────────────
|
|
7880
|
+
renderDone(container) {
|
|
7881
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:24px 0;";
|
|
7882
|
+
container.setAttribute("role", "status");
|
|
7883
|
+
this.options.onComplete?.({
|
|
7884
|
+
entryId: this.livenessEntryId || 0,
|
|
7885
|
+
sessionId: this.sdk.sessionToken,
|
|
7886
|
+
status: "processing",
|
|
7887
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7888
|
+
verificationResult: this.verificationResult || void 0
|
|
7889
|
+
});
|
|
7890
|
+
const iconWrap = document.createElement("div");
|
|
7891
|
+
iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;font-size:36px;font-weight:700;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
|
|
7892
|
+
iconWrap.textContent = "\xE2\u0153\u201C";
|
|
7893
|
+
container.appendChild(iconWrap);
|
|
7894
|
+
const title = document.createElement("h2");
|
|
7895
|
+
title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
|
|
7896
|
+
title.textContent = "You're all done!";
|
|
7897
|
+
container.appendChild(title);
|
|
7898
|
+
const brandName = this.sdkConfig?.brand_name || "the service";
|
|
7899
|
+
const desc = document.createElement("p");
|
|
7900
|
+
desc.style.cssText = `color:${this.theme.textSecondary};font-size:14px;line-height:1.6;margin:0;max-width:300px;`;
|
|
7901
|
+
desc.textContent = `Your verification is complete. ${brandName} will review and be in touch shortly.`;
|
|
7902
|
+
container.appendChild(desc);
|
|
7903
|
+
const info = document.createElement("div");
|
|
7904
|
+
info.style.cssText = `color:${this.theme.textMuted};font-size:11px;display:flex;align-items:center;gap:4px;`;
|
|
7905
|
+
info.textContent = "\xF0\u0178\u201D\u2019 Your data is encrypted and secure";
|
|
7906
|
+
container.appendChild(info);
|
|
7907
|
+
const doneBtn = this.createPrimaryButton("Close");
|
|
7908
|
+
doneBtn.addEventListener("click", () => {
|
|
7909
|
+
this.close();
|
|
7910
|
+
});
|
|
7911
|
+
container.appendChild(doneBtn);
|
|
7912
|
+
}
|
|
7913
|
+
// ── W14: Retry limit reached ─────────────────────────────────────
|
|
7914
|
+
_renderRetryLimitReached(container) {
|
|
7915
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:24px 0;";
|
|
7916
|
+
const icon = document.createElement("div");
|
|
7917
|
+
icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
|
|
7918
|
+
icon.textContent = "\xE2\u0161\xA0\xEF\xB8\x8F";
|
|
7919
|
+
container.appendChild(icon);
|
|
7920
|
+
const t2 = document.createElement("h2");
|
|
7921
|
+
t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
|
|
7922
|
+
t2.textContent = "Unable to Complete Verification";
|
|
7923
|
+
container.appendChild(t2);
|
|
7924
|
+
const d2 = document.createElement("p");
|
|
7925
|
+
d2.style.cssText = `color:${this.theme.textSecondary};font-size:13px;margin:0;max-width:280px;line-height:1.6;`;
|
|
7926
|
+
d2.textContent = "We were unable to complete your verification at this time. Please contact support or try again later.";
|
|
7927
|
+
container.appendChild(d2);
|
|
7928
|
+
const closeBtn = this.createPrimaryButton("Close");
|
|
7929
|
+
closeBtn.addEventListener("click", () => {
|
|
7930
|
+
this.options.onError?.(new Error("Retry limit reached"));
|
|
7931
|
+
this.close();
|
|
7932
|
+
});
|
|
7933
|
+
container.appendChild(closeBtn);
|
|
7934
|
+
}
|
|
7935
|
+
// ── W13: Camera pre-warm ─────────────────────────────────────────
|
|
7936
|
+
_prewarmCamera() {
|
|
7937
|
+
if (this._prewarmedStream || this.isDestroyed) return;
|
|
7938
|
+
navigator.mediaDevices?.getUserMedia?.({ video: { facingMode: "user" } }).then((stream) => {
|
|
7939
|
+
if (this.isDestroyed) {
|
|
7940
|
+
stream.getTracks().forEach((t2) => t2.stop());
|
|
7941
|
+
return;
|
|
7942
|
+
}
|
|
7943
|
+
this._prewarmedStream = stream;
|
|
7944
|
+
}).catch(() => {
|
|
7945
|
+
});
|
|
7590
7946
|
}
|
|
7591
7947
|
/**
|
|
7592
7948
|
* Process document capture result with front/back support
|
|
7593
7949
|
*/
|
|
7594
7950
|
async processDocumentCaptureResult(result, captureContainer, container) {
|
|
7595
|
-
this.documentBlob = result.front;
|
|
7596
|
-
this.documentBackBlob = result.back
|
|
7597
|
-
await this.processDocumentBlob(
|
|
7598
|
-
if (result.back) {
|
|
7599
|
-
console.log("Back image captured, size:", result.back.size);
|
|
7600
|
-
}
|
|
7951
|
+
this.documentBlob = await _compressImage(result.front, 1600, 0.85);
|
|
7952
|
+
this.documentBackBlob = result.back ? await _compressImage(result.back, 1600, 0.85) : null;
|
|
7953
|
+
await this.processDocumentBlob(this.documentBlob, captureContainer, container);
|
|
7601
7954
|
}
|
|
7602
7955
|
/**
|
|
7603
7956
|
* Process document blob - common method for both CameraView and DocumentCapture
|
|
@@ -7618,7 +7971,7 @@ var SmartComplySDK = (() => {
|
|
|
7618
7971
|
<div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
|
|
7619
7972
|
<div style="position:relative;width:52px;height:52px;">
|
|
7620
7973
|
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
|
|
7621
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">\
|
|
7974
|
+
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">\xF0\u0178\u201D\x8D</div>
|
|
7622
7975
|
</div>
|
|
7623
7976
|
<div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
|
|
7624
7977
|
<div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>
|
|
@@ -7665,7 +8018,7 @@ var SmartComplySDK = (() => {
|
|
|
7665
8018
|
}
|
|
7666
8019
|
}
|
|
7667
8020
|
this.livenessEntryId = createResult.id;
|
|
7668
|
-
this.showStep("
|
|
8021
|
+
this.showStep("ocr_gate");
|
|
7669
8022
|
} catch (err) {
|
|
7670
8023
|
console.error("Document processing failed:", err);
|
|
7671
8024
|
if (retryCount < maxRetries) {
|
|
@@ -7676,7 +8029,7 @@ var SmartComplySDK = (() => {
|
|
|
7676
8029
|
<div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
|
|
7677
8030
|
<div style="position:relative;width:52px;height:52px;">
|
|
7678
8031
|
<div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
|
|
7679
|
-
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">\
|
|
8032
|
+
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">\xF0\u0178\u201D\u201E</div>
|
|
7680
8033
|
</div>
|
|
7681
8034
|
<div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
|
|
7682
8035
|
<div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
|
|
@@ -7717,7 +8070,7 @@ var SmartComplySDK = (() => {
|
|
|
7717
8070
|
wrap.setAttribute("role", "alert");
|
|
7718
8071
|
const icon = document.createElement("div");
|
|
7719
8072
|
icon.style.cssText = `width:56px;height:56px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:26px;`;
|
|
7720
|
-
icon.textContent = "\
|
|
8073
|
+
icon.textContent = "\xF0\u0178\u201D\x8D";
|
|
7721
8074
|
wrap.appendChild(icon);
|
|
7722
8075
|
const titleEl = document.createElement("div");
|
|
7723
8076
|
titleEl.style.cssText = `color:${this.theme.text};font-size:14px;font-weight:700;`;
|
|
@@ -7741,7 +8094,7 @@ var SmartComplySDK = (() => {
|
|
|
7741
8094
|
this.showErrorStep(userMessage);
|
|
7742
8095
|
}
|
|
7743
8096
|
}
|
|
7744
|
-
//
|
|
8097
|
+
// ── Liveness ────────────────────────────────────────────────────
|
|
7745
8098
|
renderLiveness(container) {
|
|
7746
8099
|
container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
|
|
7747
8100
|
const title = this.createStepTitle("Face verification");
|
|
@@ -7753,7 +8106,7 @@ var SmartComplySDK = (() => {
|
|
|
7753
8106
|
const livenessContainer = document.createElement("div");
|
|
7754
8107
|
livenessContainer.style.cssText = "margin:8px 0;width:100%;";
|
|
7755
8108
|
container.appendChild(livenessContainer);
|
|
7756
|
-
const identifier = this.idNumber || Object.values(this.collectedFields)[0] || this.selectedIdType || `DOC-${Date.now()}`;
|
|
8109
|
+
const identifier = this.identityCheckId ? "" : this.idNumber || Object.values(this.collectedFields)[0] || this.selectedIdType || `DOC-${Date.now()}`;
|
|
7757
8110
|
const COUNTRY_CODES = {
|
|
7758
8111
|
nigeria: "NG",
|
|
7759
8112
|
ghana: "GH",
|
|
@@ -7773,247 +8126,28 @@ var SmartComplySDK = (() => {
|
|
|
7773
8126
|
document: this.documentBlob || void 0,
|
|
7774
8127
|
document_back: this.documentBackBlob || void 0,
|
|
7775
8128
|
identity_check: this.identityCheckId || void 0,
|
|
7776
|
-
|
|
7777
|
-
|
|
8129
|
+
verification_type: this.identityCheckId ? "data_verification" : this.documentBlob ? "document_verification" : "liveness_only",
|
|
8130
|
+
// B4: pass pre-warmed stream so camera.open() is skipped
|
|
8131
|
+
prewarmedStream: this._prewarmedStream || void 0
|
|
7778
8132
|
},
|
|
7779
8133
|
["BLINK", "TURN_HEAD", "OPEN_MOUTH"],
|
|
7780
8134
|
this.livenessEntryId || void 0
|
|
7781
8135
|
).then((result) => {
|
|
7782
|
-
|
|
7783
|
-
|
|
8136
|
+
if (result.status === "failed") {
|
|
8137
|
+
this._livenessFailures++;
|
|
8138
|
+
}
|
|
8139
|
+
if (result.id) {
|
|
7784
8140
|
this.livenessEntryId = result.id;
|
|
7785
|
-
this.showStep("processing");
|
|
7786
|
-
} else {
|
|
7787
|
-
this.showResult(result);
|
|
7788
8141
|
}
|
|
8142
|
+
this.showStep("done");
|
|
7789
8143
|
}).catch((err) => {
|
|
8144
|
+
this._livenessFailures++;
|
|
7790
8145
|
this.showErrorStep(err.message || "Liveness verification failed. Please try again.");
|
|
7791
8146
|
});
|
|
7792
8147
|
}
|
|
7793
|
-
// ── Result ──────────────────────────────────────────────────────
|
|
7794
|
-
showResult(submitResult) {
|
|
7795
|
-
this.currentStep = "result";
|
|
7796
|
-
this.updateProgress();
|
|
7797
|
-
if (!this.contentArea) return;
|
|
7798
|
-
this.contentArea.innerHTML = "";
|
|
7799
|
-
const wrapper = document.createElement("div");
|
|
7800
|
-
wrapper.setAttribute("role", "region");
|
|
7801
|
-
wrapper.setAttribute("aria-label", "Verification result");
|
|
7802
|
-
wrapper.style.cssText = `
|
|
7803
|
-
display:flex;flex-direction:column;align-items:center;
|
|
7804
|
-
text-align:center;gap:16px;padding:20px 0;
|
|
7805
|
-
opacity:0;transform:scale(0.95);transition:all 0.4s ease;
|
|
7806
|
-
`;
|
|
7807
|
-
const iconWrap = document.createElement("div");
|
|
7808
|
-
iconWrap.style.cssText = `
|
|
7809
|
-
width:80px;height:80px;border-radius:50%;
|
|
7810
|
-
display:flex;align-items:center;justify-content:center;
|
|
7811
|
-
font-size:36px;
|
|
7812
|
-
background:${this.theme.cardBg};
|
|
7813
|
-
border:3px solid ${this.theme.border};
|
|
7814
|
-
transition:all 0.4s ease;
|
|
7815
|
-
`;
|
|
7816
|
-
iconWrap.innerHTML = `<div style="width:32px;height:32px;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.9s linear infinite;"></div>`;
|
|
7817
|
-
wrapper.appendChild(iconWrap);
|
|
7818
|
-
const title = document.createElement("h2");
|
|
7819
|
-
title.setAttribute("aria-live", "polite");
|
|
7820
|
-
title.style.cssText = `color:${this.theme.text};font-size:20px;font-weight:700;margin:0;`;
|
|
7821
|
-
title.textContent = "Checking your identity\u2026";
|
|
7822
|
-
wrapper.appendChild(title);
|
|
7823
|
-
const desc = document.createElement("p");
|
|
7824
|
-
desc.setAttribute("aria-live", "polite");
|
|
7825
|
-
desc.style.cssText = `color:${this.theme.textSecondary};font-size:14px;line-height:1.6;margin:0;max-width:300px;`;
|
|
7826
|
-
desc.textContent = "This usually takes a few seconds. Please wait.";
|
|
7827
|
-
wrapper.appendChild(desc);
|
|
7828
|
-
const resultCard = document.createElement("div");
|
|
7829
|
-
resultCard.style.cssText = `
|
|
7830
|
-
width:100%;padding:16px;border-radius:12px;
|
|
7831
|
-
background:${this.theme.cardBg};border:1px solid ${this.theme.border};
|
|
7832
|
-
text-align:left;display:flex;flex-direction:column;gap:10px;
|
|
7833
|
-
transition:border-color 0.3s ease;
|
|
7834
|
-
`;
|
|
7835
|
-
const statusBadge = document.createElement("span");
|
|
7836
|
-
statusBadge.setAttribute("role", "status");
|
|
7837
|
-
statusBadge.setAttribute("aria-live", "polite");
|
|
7838
|
-
statusBadge.setAttribute("aria-atomic", "true");
|
|
7839
|
-
statusBadge.style.cssText = `font-size:12px;font-weight:600;color:${this.theme.warning};`;
|
|
7840
|
-
statusBadge.textContent = "Processing\u2026";
|
|
7841
|
-
const statusRow = document.createElement("div");
|
|
7842
|
-
statusRow.style.cssText = "display:flex;justify-content:space-between;align-items:center;";
|
|
7843
|
-
statusRow.innerHTML = `<span style="color:${this.theme.textMuted};font-size:12px;font-weight:500;">Status</span>`;
|
|
7844
|
-
statusRow.appendChild(statusBadge);
|
|
7845
|
-
resultCard.appendChild(statusRow);
|
|
7846
|
-
const nameRow = document.createElement("div");
|
|
7847
|
-
nameRow.style.cssText = "display:none;justify-content:space-between;align-items:center;";
|
|
7848
|
-
nameRow.innerHTML = `<span style="color:${this.theme.textMuted};font-size:12px;font-weight:500;">Name</span><span id="sc-result-name" style="color:${this.theme.text};font-size:12px;font-weight:600;text-align:right;max-width:60%;"></span>`;
|
|
7849
|
-
resultCard.appendChild(nameRow);
|
|
7850
|
-
const docTypeRow = document.createElement("div");
|
|
7851
|
-
docTypeRow.style.cssText = "display:none;justify-content:space-between;align-items:center;";
|
|
7852
|
-
docTypeRow.innerHTML = `<span style="color:${this.theme.textMuted};font-size:12px;font-weight:500;">ID Type</span><span id="sc-result-doctype" style="color:${this.theme.text};font-size:12px;font-weight:600;"></span>`;
|
|
7853
|
-
resultCard.appendChild(docTypeRow);
|
|
7854
|
-
wrapper.appendChild(resultCard);
|
|
7855
|
-
const failureMsg = document.createElement("div");
|
|
7856
|
-
failureMsg.setAttribute("role", "alert");
|
|
7857
|
-
failureMsg.style.cssText = `
|
|
7858
|
-
display:none;width:100%;padding:12px 14px;border-radius:10px;
|
|
7859
|
-
background:${this.theme.errorBg};border:1px solid ${this.theme.error};
|
|
7860
|
-
color:${this.theme.error};font-size:13px;text-align:left;line-height:1.6;
|
|
7861
|
-
`;
|
|
7862
|
-
wrapper.appendChild(failureMsg);
|
|
7863
|
-
const btnRow = document.createElement("div");
|
|
7864
|
-
btnRow.style.cssText = "display:flex;flex-direction:column;gap:8px;width:100%;";
|
|
7865
|
-
const doneBtn = this.createPrimaryButton("Done");
|
|
7866
|
-
doneBtn.setAttribute("aria-label", "Close verification and continue");
|
|
7867
|
-
let finalStatus = submitResult.status;
|
|
7868
|
-
doneBtn.addEventListener("click", () => {
|
|
7869
|
-
if (this._pollStatusTimer) {
|
|
7870
|
-
clearTimeout(this._pollStatusTimer);
|
|
7871
|
-
this._pollStatusTimer = null;
|
|
7872
|
-
}
|
|
7873
|
-
this.options.onComplete?.({
|
|
7874
|
-
entryId: submitResult.id,
|
|
7875
|
-
sessionId: this.sdk.sessionToken,
|
|
7876
|
-
status: finalStatus,
|
|
7877
|
-
submittedAt: submitResult.submitted_at,
|
|
7878
|
-
verificationResult: this.verificationResult || void 0
|
|
7879
|
-
});
|
|
7880
|
-
this.close();
|
|
7881
|
-
});
|
|
7882
|
-
btnRow.appendChild(doneBtn);
|
|
7883
|
-
const retryBtn = this.createSecondaryButton("Try Again");
|
|
7884
|
-
retryBtn.setAttribute("aria-label", "Start verification again");
|
|
7885
|
-
retryBtn.style.display = "none";
|
|
7886
|
-
retryBtn.addEventListener("click", () => {
|
|
7887
|
-
if (this._pollStatusTimer) {
|
|
7888
|
-
clearTimeout(this._pollStatusTimer);
|
|
7889
|
-
this._pollStatusTimer = null;
|
|
7890
|
-
}
|
|
7891
|
-
this.initialize();
|
|
7892
|
-
});
|
|
7893
|
-
btnRow.appendChild(retryBtn);
|
|
7894
|
-
wrapper.appendChild(btnRow);
|
|
7895
|
-
this.contentArea.appendChild(wrapper);
|
|
7896
|
-
requestAnimationFrame(() => {
|
|
7897
|
-
wrapper.style.opacity = "1";
|
|
7898
|
-
wrapper.style.transform = "scale(1)";
|
|
7899
|
-
});
|
|
7900
|
-
this._startStatusPolling(
|
|
7901
|
-
submitResult.id,
|
|
7902
|
-
iconWrap,
|
|
7903
|
-
statusBadge,
|
|
7904
|
-
title,
|
|
7905
|
-
desc,
|
|
7906
|
-
resultCard,
|
|
7907
|
-
nameRow,
|
|
7908
|
-
docTypeRow,
|
|
7909
|
-
failureMsg,
|
|
7910
|
-
retryBtn,
|
|
7911
|
-
(resolved) => {
|
|
7912
|
-
finalStatus = resolved;
|
|
7913
|
-
}
|
|
7914
|
-
);
|
|
7915
|
-
}
|
|
7916
|
-
_startStatusPolling(entryId, iconWrap, statusBadge, title, desc, resultCard, nameRow, docTypeRow, failureMsg, retryBtn, onResolved) {
|
|
7917
|
-
const MAX_POLLS = 25;
|
|
7918
|
-
let polls = 0;
|
|
7919
|
-
let uiTimeoutId = setTimeout(() => {
|
|
7920
|
-
if (!this.isDestroyed) applyTimeout();
|
|
7921
|
-
}, 3e4);
|
|
7922
|
-
const clearUiTimeout = () => {
|
|
7923
|
-
if (uiTimeoutId) {
|
|
7924
|
-
clearTimeout(uiTimeoutId);
|
|
7925
|
-
uiTimeoutId = null;
|
|
7926
|
-
}
|
|
7927
|
-
};
|
|
7928
|
-
const applyTimeout = () => {
|
|
7929
|
-
if (this._pollStatusTimer) {
|
|
7930
|
-
clearTimeout(this._pollStatusTimer);
|
|
7931
|
-
this._pollStatusTimer = null;
|
|
7932
|
-
}
|
|
7933
|
-
iconWrap.innerHTML = "\u23F3";
|
|
7934
|
-
iconWrap.style.border = `3px solid ${this.theme.warning}`;
|
|
7935
|
-
statusBadge.style.color = this.theme.textMuted;
|
|
7936
|
-
statusBadge.textContent = "Still processing\u2026";
|
|
7937
|
-
title.textContent = "Still verifying";
|
|
7938
|
-
desc.textContent = "This is taking a little longer than usual. You can safely close this \u2014 your result will be available in your account.";
|
|
7939
|
-
};
|
|
7940
|
-
const applyPassed = (result) => {
|
|
7941
|
-
clearUiTimeout();
|
|
7942
|
-
iconWrap.innerHTML = "\u2713";
|
|
7943
|
-
iconWrap.style.cssText += `background:${this.theme.successBg};border:3px solid ${this.theme.success};color:${this.theme.success};font-size:36px;font-weight:700;`;
|
|
7944
|
-
resultCard.style.borderColor = this.theme.success;
|
|
7945
|
-
statusBadge.style.color = this.theme.success;
|
|
7946
|
-
statusBadge.textContent = "Verified \u2713";
|
|
7947
|
-
title.textContent = "Identity Verified";
|
|
7948
|
-
desc.textContent = "Your identity has been confirmed successfully.";
|
|
7949
|
-
const profile = result?.customer_profile || result?.data || {};
|
|
7950
|
-
const docResult = result?.document || {};
|
|
7951
|
-
const ocr = docResult?.ocr_result || {};
|
|
7952
|
-
const firstName = (profile?.first_name || ocr?.first_name || docResult?.first_name || "").trim();
|
|
7953
|
-
const lastName = (profile?.last_name || profile?.surname || ocr?.last_name || ocr?.surname || docResult?.last_name || "").trim();
|
|
7954
|
-
const fullName = [firstName, lastName].filter(Boolean).join(" ");
|
|
7955
|
-
const nameEl = document.getElementById("sc-result-name");
|
|
7956
|
-
if (fullName && nameEl) {
|
|
7957
|
-
nameEl.textContent = fullName;
|
|
7958
|
-
nameRow.style.display = "flex";
|
|
7959
|
-
}
|
|
7960
|
-
const rawDocType = docResult?.document_type || this.selectedIdType || result?.identifier_type || "";
|
|
7961
|
-
const docTypeLabel = _formatDocType(rawDocType);
|
|
7962
|
-
const docTypeEl = document.getElementById("sc-result-doctype");
|
|
7963
|
-
if (docTypeLabel && docTypeEl) {
|
|
7964
|
-
docTypeEl.textContent = docTypeLabel;
|
|
7965
|
-
docTypeRow.style.display = "flex";
|
|
7966
|
-
}
|
|
7967
|
-
};
|
|
7968
|
-
const applyFailed = (reason) => {
|
|
7969
|
-
clearUiTimeout();
|
|
7970
|
-
iconWrap.innerHTML = "\u2715";
|
|
7971
|
-
iconWrap.style.cssText += `background:${this.theme.errorBg};border:3px solid ${this.theme.error};color:${this.theme.error};font-size:32px;`;
|
|
7972
|
-
resultCard.style.borderColor = this.theme.error;
|
|
7973
|
-
statusBadge.style.color = this.theme.error;
|
|
7974
|
-
statusBadge.textContent = "Not verified";
|
|
7975
|
-
title.textContent = "Verification Unsuccessful";
|
|
7976
|
-
desc.textContent = "We were unable to confirm your identity this time.";
|
|
7977
|
-
const safeReason = _sanitizeUserMessage(reason);
|
|
7978
|
-
if (safeReason) {
|
|
7979
|
-
failureMsg.textContent = safeReason;
|
|
7980
|
-
failureMsg.style.display = "block";
|
|
7981
|
-
}
|
|
7982
|
-
retryBtn.style.display = "block";
|
|
7983
|
-
};
|
|
7984
|
-
const poll = async () => {
|
|
7985
|
-
if (this.isDestroyed) return;
|
|
7986
|
-
polls++;
|
|
7987
|
-
try {
|
|
7988
|
-
const result = await this.sdk.liveness.pollResult(entryId);
|
|
7989
|
-
if (result.status === "passed") {
|
|
7990
|
-
onResolved("passed");
|
|
7991
|
-
applyPassed(result);
|
|
7992
|
-
return;
|
|
7993
|
-
}
|
|
7994
|
-
if (result.status === "failed") {
|
|
7995
|
-
onResolved("failed");
|
|
7996
|
-
applyFailed(result.failure_reason || null);
|
|
7997
|
-
return;
|
|
7998
|
-
}
|
|
7999
|
-
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
8000
|
-
this._pollStatusTimer = setTimeout(poll, 3e3);
|
|
8001
|
-
} else if (!this.isDestroyed) {
|
|
8002
|
-
applyTimeout();
|
|
8003
|
-
}
|
|
8004
|
-
} catch {
|
|
8005
|
-
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
8006
|
-
this._pollStatusTimer = setTimeout(poll, 5e3);
|
|
8007
|
-
} else if (!this.isDestroyed) {
|
|
8008
|
-
applyTimeout();
|
|
8009
|
-
}
|
|
8010
|
-
}
|
|
8011
|
-
};
|
|
8012
|
-
this._pollStatusTimer = setTimeout(poll, 4e3);
|
|
8013
|
-
}
|
|
8014
8148
|
renderResult(_container) {
|
|
8015
8149
|
}
|
|
8016
|
-
//
|
|
8150
|
+
// ── Error ───────────────────────────────────────────────────────
|
|
8017
8151
|
showErrorStep(message) {
|
|
8018
8152
|
this.currentStep = "error";
|
|
8019
8153
|
if (!this.contentArea) return;
|
|
@@ -8030,7 +8164,7 @@ var SmartComplySDK = (() => {
|
|
|
8030
8164
|
const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
|
|
8031
8165
|
const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
|
|
8032
8166
|
const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
|
|
8033
|
-
const errorIcon = isCameraErr ? "\
|
|
8167
|
+
const errorIcon = isCameraErr ? "\xF0\u0178\u201C\xB7" : isNetworkErr ? "\xF0\u0178\u201C\xA1" : isAuthErr ? "\xE2\x8F\xB1" : isDocQuality ? "\xF0\u0178\u201D\x8D" : isExpiredDoc ? "\xF0\u0178\u201C\u2026" : isFaceMatch ? "\xF0\u0178\xA4\xB3" : "\xE2\u0153\u2022";
|
|
8034
8168
|
const errorTitle = isCameraErr ? "Camera Access Required" : isNetworkErr ? "Connection Problem" : isAuthErr ? "Session Expired" : isDocQuality ? "Document Photo Unclear" : isExpiredDoc ? "Document Expired" : isFaceMatch ? "Face Match Failed" : "Verification Unsuccessful";
|
|
8035
8169
|
const userMessage = isCameraErr ? "Please allow camera access in your browser settings and try again." : isNetworkErr ? "Check your internet connection and try again." : isAuthErr ? "Your session has expired. Please start again." : isDocQuality ? _sanitizeUserMessage(message) || "Please retake your document photo in good lighting with no glare." : isExpiredDoc ? "The document you submitted has expired. Please use a valid, current ID." : isFaceMatch ? "Your selfie did not match the photo on your ID. Please ensure you are using your own valid document and face the camera directly." : _sanitizeUserMessage(message) || "We were unable to complete your verification. Please try again.";
|
|
8036
8170
|
const iconWrap = document.createElement("div");
|
|
@@ -8066,9 +8200,9 @@ var SmartComplySDK = (() => {
|
|
|
8066
8200
|
wrapper.appendChild(closeBtn);
|
|
8067
8201
|
this.contentArea.appendChild(wrapper);
|
|
8068
8202
|
}
|
|
8069
|
-
//
|
|
8203
|
+
// ─────────────────────────────────────────────────────────────────
|
|
8070
8204
|
// UI Helpers
|
|
8071
|
-
//
|
|
8205
|
+
// ─────────────────────────────────────────────────────────────────
|
|
8072
8206
|
createHeader() {
|
|
8073
8207
|
const header = document.createElement("div");
|
|
8074
8208
|
header.setAttribute("role", "banner");
|
|
@@ -8119,7 +8253,7 @@ var SmartComplySDK = (() => {
|
|
|
8119
8253
|
`;
|
|
8120
8254
|
header.appendChild(this.stepLabel);
|
|
8121
8255
|
const closeBtn = document.createElement("button");
|
|
8122
|
-
closeBtn.innerHTML = "\
|
|
8256
|
+
closeBtn.innerHTML = "\xE2\u0153\u2022";
|
|
8123
8257
|
closeBtn.setAttribute("aria-label", "Close identity verification");
|
|
8124
8258
|
closeBtn.style.cssText = `
|
|
8125
8259
|
width:32px;height:32px;border-radius:8px;flex-shrink:0;
|
|
@@ -8200,7 +8334,7 @@ var SmartComplySDK = (() => {
|
|
|
8200
8334
|
}
|
|
8201
8335
|
const arrow = document.createElement("span");
|
|
8202
8336
|
arrow.style.cssText = `color:${this.theme.textMuted};font-size:18px;flex-shrink:0;`;
|
|
8203
|
-
arrow.textContent = "\
|
|
8337
|
+
arrow.textContent = "\xE2\u2020\u2019";
|
|
8204
8338
|
card.appendChild(textWrap);
|
|
8205
8339
|
card.appendChild(arrow);
|
|
8206
8340
|
return card;
|
|
@@ -8259,33 +8393,41 @@ var SmartComplySDK = (() => {
|
|
|
8259
8393
|
const isDocFlow = this.isDocumentFlow();
|
|
8260
8394
|
const countries = Object.keys(this.sdkConfig?.channels || {});
|
|
8261
8395
|
const singleCountry = countries.length <= 1;
|
|
8262
|
-
const
|
|
8263
|
-
const
|
|
8264
|
-
const
|
|
8265
|
-
const
|
|
8396
|
+
const stage1Steps = isDocFlow ? ["country", "id_type", "document_capture", "ocr_gate", "document_confirm"].filter((s2) => !(s2 === "country" && singleCountry)) : ["country", "id_type", "id_input", "confirm_identity"].filter((s2) => !(s2 === "country" && singleCountry));
|
|
8397
|
+
const stage2Steps = ["liveness"];
|
|
8398
|
+
const allActionSteps = [...stage1Steps, ...stage2Steps];
|
|
8399
|
+
const totalSteps = allActionSteps.length;
|
|
8400
|
+
const actionIndex = allActionSteps.indexOf(this.currentStep);
|
|
8401
|
+
const pct = this.currentStep === "done" || this.currentStep === "result" ? 100 : actionIndex >= 0 ? (actionIndex + 1) / totalSteps * 100 : 0;
|
|
8266
8402
|
if (this.progressBar) {
|
|
8267
8403
|
this.progressBar.style.width = `${pct}%`;
|
|
8268
8404
|
}
|
|
8269
8405
|
if (this.stepLabel) {
|
|
8270
8406
|
if (this.currentStep === "loading" || this.currentStep === "welcome") {
|
|
8271
8407
|
this.stepLabel.textContent = "";
|
|
8272
|
-
} else if (this.currentStep === "result") {
|
|
8273
|
-
this.stepLabel.textContent = "Complete \
|
|
8274
|
-
} else if (
|
|
8275
|
-
this.stepLabel.textContent =
|
|
8408
|
+
} else if (this.currentStep === "done" || this.currentStep === "result") {
|
|
8409
|
+
this.stepLabel.textContent = "Complete \xE2\u0153\u201C";
|
|
8410
|
+
} else if (stage2Steps.includes(this.currentStep)) {
|
|
8411
|
+
this.stepLabel.textContent = "Stage 2 of 2: Face Check";
|
|
8412
|
+
} else if (stage1Steps.includes(this.currentStep)) {
|
|
8413
|
+
this.stepLabel.textContent = isDocFlow ? "Stage 1 of 2: Scan Document" : "Stage 1 of 2: Verify Identity";
|
|
8276
8414
|
} else {
|
|
8277
8415
|
this.stepLabel.textContent = "";
|
|
8278
8416
|
}
|
|
8279
8417
|
}
|
|
8280
8418
|
if (this.headerBackBtn) {
|
|
8281
|
-
const countries2 = Object.keys(this.sdkConfig?.channels || {});
|
|
8282
8419
|
const backTargets = {
|
|
8283
|
-
id_type: () => this.showStep(
|
|
8420
|
+
id_type: () => this.showStep(countries.length > 1 ? "country" : "welcome"),
|
|
8284
8421
|
country: () => this.showStep("welcome"),
|
|
8285
8422
|
id_input: () => this.showStep("id_type"),
|
|
8423
|
+
confirm_identity: () => this.showStep("id_input"),
|
|
8286
8424
|
document_capture: () => {
|
|
8287
8425
|
this.docCapture?.destroy();
|
|
8288
8426
|
this.showStep("id_type");
|
|
8427
|
+
},
|
|
8428
|
+
document_confirm: () => {
|
|
8429
|
+
this.docCapture?.destroy();
|
|
8430
|
+
this.showStep("document_capture");
|
|
8289
8431
|
}
|
|
8290
8432
|
};
|
|
8291
8433
|
const backAction = backTargets[this.currentStep];
|
|
@@ -8352,6 +8494,8 @@ var SmartComplySDK = (() => {
|
|
|
8352
8494
|
document.head.appendChild(style2);
|
|
8353
8495
|
}
|
|
8354
8496
|
};
|
|
8497
|
+
_SmartComplyFlow.MAX_RETRIES = 3;
|
|
8498
|
+
var SmartComplyFlow = _SmartComplyFlow;
|
|
8355
8499
|
|
|
8356
8500
|
// src/utils/logger.ts
|
|
8357
8501
|
var PREFIX = "[SmartComply SDK]";
|