smartcomply-web-sdk 1.0.43 → 1.0.45
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 +2 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +532 -254
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts +9 -5
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +36 -10
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts +2 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +532 -254
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts +9 -5
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +36 -10
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +444 -221
- package/dist/smartcomply.browser.js.map +3 -3
- package/package.json +1 -1
|
@@ -5125,14 +5125,20 @@ var SmartComplySDK = (() => {
|
|
|
5125
5125
|
* 1. Take autoshot selfie from camera
|
|
5126
5126
|
* 2. Create liveness entry on backend (with challenge actions + selfie + optional doc)
|
|
5127
5127
|
* 3. Run action detection loop (camera + MediaPipe)
|
|
5128
|
-
* 4. Record video
|
|
5129
|
-
* 5.
|
|
5128
|
+
* 4. Record video (document/liveness flows) OR skip recording (data verification)
|
|
5129
|
+
* 5. Submit to backend and return response (status: "processing")
|
|
5130
5130
|
*
|
|
5131
|
-
*
|
|
5132
|
-
*
|
|
5133
|
-
*
|
|
5131
|
+
* For data_verification the backend uses COMPARISON mode (autoshot vs govt DB photo)
|
|
5132
|
+
* so video recording is unnecessary — we skip it to save bandwidth and latency.
|
|
5133
|
+
*
|
|
5134
|
+
* @param container - DOM element to mount the liveness UI into
|
|
5135
|
+
* @param params - identifier info and optional document image
|
|
5136
|
+
* @param actions - override challenge actions (default: BLINK, TURN_HEAD, OPEN_MOUTH)
|
|
5137
|
+
* @param existingEntryId - reuse an already-created LivenessEntry (avoids double charge)
|
|
5134
5138
|
*/
|
|
5135
5139
|
async startCheck(container, params, actions, existingEntryId) {
|
|
5140
|
+
const isDataVerification = params.verification_type === "data_verification" || !params.document && !params.document_front && !params.id_file && !!params.identity_check;
|
|
5141
|
+
const skipVideo = isDataVerification;
|
|
5136
5142
|
const camera = new CameraManager();
|
|
5137
5143
|
const recorder = new VideoRecorder();
|
|
5138
5144
|
const faceEngine = new FaceDetectorEngine();
|
|
@@ -5255,7 +5261,9 @@ var SmartComplySDK = (() => {
|
|
|
5255
5261
|
videoEl.srcObject = stream;
|
|
5256
5262
|
await videoEl.play();
|
|
5257
5263
|
await new Promise((r2) => setTimeout(r2, 1e3));
|
|
5258
|
-
|
|
5264
|
+
if (!skipVideo) {
|
|
5265
|
+
recorder.start(stream);
|
|
5266
|
+
}
|
|
5259
5267
|
ui2.updateInstruction(uiChallenge.instruction);
|
|
5260
5268
|
const actionDetector = new ActionDetector(entry.challenge_actions);
|
|
5261
5269
|
let lastAction = null;
|
|
@@ -5329,14 +5337,30 @@ var SmartComplySDK = (() => {
|
|
|
5329
5337
|
faceEngine.destroy();
|
|
5330
5338
|
camera.stop();
|
|
5331
5339
|
ui2.unmount();
|
|
5332
|
-
this.startCheck(
|
|
5340
|
+
this.startCheck(
|
|
5341
|
+
container,
|
|
5342
|
+
{ ...params, id_file: capturedDocBlob },
|
|
5343
|
+
actions,
|
|
5344
|
+
entry.id
|
|
5345
|
+
).then(resolveRetry).catch(rejectRetry);
|
|
5333
5346
|
});
|
|
5334
5347
|
});
|
|
5335
5348
|
}
|
|
5336
5349
|
ui2.updateInstruction("Almost done \u2014 hold still...");
|
|
5337
5350
|
await new Promise((r2) => setTimeout(r2, 800));
|
|
5338
5351
|
const snapshotBlob = await this.captureFrame(videoEl);
|
|
5339
|
-
|
|
5352
|
+
let videoBlob;
|
|
5353
|
+
let durationSeconds;
|
|
5354
|
+
if (skipVideo) {
|
|
5355
|
+
recorder.stop().catch(() => {
|
|
5356
|
+
});
|
|
5357
|
+
videoBlob = autoshotBlob;
|
|
5358
|
+
durationSeconds = void 0;
|
|
5359
|
+
} else {
|
|
5360
|
+
const recorded = await recorder.stop();
|
|
5361
|
+
videoBlob = recorded.blob;
|
|
5362
|
+
durationSeconds = recorded.durationSeconds;
|
|
5363
|
+
}
|
|
5340
5364
|
const submitResult = await this.submit(
|
|
5341
5365
|
entry.id,
|
|
5342
5366
|
videoBlob,
|
|
@@ -6629,6 +6653,62 @@ var SmartComplySDK = (() => {
|
|
|
6629
6653
|
requiresBack: false
|
|
6630
6654
|
};
|
|
6631
6655
|
}
|
|
6656
|
+
function _sanitizeUserMessage(raw) {
|
|
6657
|
+
if (!raw) return null;
|
|
6658
|
+
const lower = raw.toLowerCase();
|
|
6659
|
+
const blocklist = [
|
|
6660
|
+
"smartcomply",
|
|
6661
|
+
"face comparison service",
|
|
6662
|
+
"session id",
|
|
6663
|
+
"session_id",
|
|
6664
|
+
"provider",
|
|
6665
|
+
"ocr",
|
|
6666
|
+
"textract",
|
|
6667
|
+
"rekognition",
|
|
6668
|
+
"deepface",
|
|
6669
|
+
"ivs",
|
|
6670
|
+
"celery",
|
|
6671
|
+
"exception",
|
|
6672
|
+
"traceback",
|
|
6673
|
+
"str(e)",
|
|
6674
|
+
"error:",
|
|
6675
|
+
"failed:",
|
|
6676
|
+
"django",
|
|
6677
|
+
"python",
|
|
6678
|
+
"http ",
|
|
6679
|
+
"comparison mode",
|
|
6680
|
+
"liveness mode",
|
|
6681
|
+
"callback",
|
|
6682
|
+
"face_comparison",
|
|
6683
|
+
"face_match",
|
|
6684
|
+
"confidence_percentage",
|
|
6685
|
+
"threshold_percentage",
|
|
6686
|
+
"polling_fallback",
|
|
6687
|
+
"resolved_by"
|
|
6688
|
+
];
|
|
6689
|
+
for (const blocked of blocklist) {
|
|
6690
|
+
if (lower.includes(blocked)) return null;
|
|
6691
|
+
}
|
|
6692
|
+
if (/[a-z]+_[a-z]+/.test(raw) && !/\s/.test(raw.trim())) return null;
|
|
6693
|
+
if (raw.trim()[0] === raw.trim()[0].toLowerCase() && /^[a-z]/.test(raw.trim())) return null;
|
|
6694
|
+
return raw.trim();
|
|
6695
|
+
}
|
|
6696
|
+
function _formatDocType(code) {
|
|
6697
|
+
const map = {
|
|
6698
|
+
passport: "Passport",
|
|
6699
|
+
national_id: "National ID",
|
|
6700
|
+
nin_slip: "NIN Slip",
|
|
6701
|
+
nin_with_face: "National Identity Number",
|
|
6702
|
+
drivers_license: "Driver's Licence",
|
|
6703
|
+
voters_card: "Voter's Card",
|
|
6704
|
+
kenya_id: "Kenya National ID",
|
|
6705
|
+
ghana_id: "Ghana Card"
|
|
6706
|
+
};
|
|
6707
|
+
if (!code) return "";
|
|
6708
|
+
const key = code.toLowerCase().replace(/[\s-]/g, "_");
|
|
6709
|
+
if (map[key]) return map[key];
|
|
6710
|
+
return code.replace(/_/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase());
|
|
6711
|
+
}
|
|
6632
6712
|
var style = document.createElement("style");
|
|
6633
6713
|
style.textContent = `
|
|
6634
6714
|
@keyframes sc-spin {
|
|
@@ -6677,6 +6757,7 @@ var SmartComplySDK = (() => {
|
|
|
6677
6757
|
this._pollStatusTimer = null;
|
|
6678
6758
|
// Components
|
|
6679
6759
|
this.docCapture = null;
|
|
6760
|
+
this._livenessSubmitResult = null;
|
|
6680
6761
|
this.options = options;
|
|
6681
6762
|
this.sdk = new SmartComply({
|
|
6682
6763
|
apiKey: options.apiKey,
|
|
@@ -6749,6 +6830,9 @@ var SmartComplySDK = (() => {
|
|
|
6749
6830
|
`;
|
|
6750
6831
|
this.modal = document.createElement("div");
|
|
6751
6832
|
this.modal.id = "sc-flow-modal";
|
|
6833
|
+
this.modal.setAttribute("role", "dialog");
|
|
6834
|
+
this.modal.setAttribute("aria-modal", "true");
|
|
6835
|
+
this.modal.setAttribute("aria-label", "Identity verification");
|
|
6752
6836
|
this.modal.style.cssText = `
|
|
6753
6837
|
width:100%;max-width:580px;max-height:95vh;
|
|
6754
6838
|
background:${this.theme.bg};
|
|
@@ -6771,6 +6855,9 @@ var SmartComplySDK = (() => {
|
|
|
6771
6855
|
this.modal.appendChild(progressWrap);
|
|
6772
6856
|
this.contentArea = document.createElement("div");
|
|
6773
6857
|
this.contentArea.id = "sc-flow-content";
|
|
6858
|
+
this.contentArea.setAttribute("role", "main");
|
|
6859
|
+
this.contentArea.setAttribute("aria-live", "polite");
|
|
6860
|
+
this.contentArea.setAttribute("aria-atomic", "false");
|
|
6774
6861
|
this.contentArea.style.cssText = `
|
|
6775
6862
|
flex:1;overflow-y:auto;padding:16px 20px;
|
|
6776
6863
|
-webkit-overflow-scrolling:touch;
|
|
@@ -6855,7 +6942,7 @@ var SmartComplySDK = (() => {
|
|
|
6855
6942
|
this.renderDocumentCapture(wrapper);
|
|
6856
6943
|
break;
|
|
6857
6944
|
case "processing":
|
|
6858
|
-
this.renderProcessing(wrapper);
|
|
6945
|
+
this.renderProcessing(wrapper, this.livenessEntryId || void 0);
|
|
6859
6946
|
break;
|
|
6860
6947
|
case "liveness":
|
|
6861
6948
|
this.renderLiveness(wrapper);
|
|
@@ -7116,12 +7203,44 @@ var SmartComplySDK = (() => {
|
|
|
7116
7203
|
container.appendChild(autoBtn);
|
|
7117
7204
|
return;
|
|
7118
7205
|
}
|
|
7206
|
+
const _FIELD_RULES = [
|
|
7207
|
+
{ match: "BVN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "BVN must be exactly 11 digits" } },
|
|
7208
|
+
{ match: "NIN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
|
|
7209
|
+
{ 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\u201320 alphanumeric characters" } },
|
|
7211
|
+
{ match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3\u201330 alphanumeric characters" } },
|
|
7212
|
+
{ match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6\u201330 alphanumeric characters" } },
|
|
7213
|
+
{ match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8\u201320 alphanumeric characters" } },
|
|
7214
|
+
{ match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7\u201315 alphanumeric characters" } },
|
|
7215
|
+
{ match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8\u201320 characters" } },
|
|
7216
|
+
{ match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10\u201312 characters" } }
|
|
7217
|
+
];
|
|
7218
|
+
function getFieldRule(label) {
|
|
7219
|
+
const upper = label.toUpperCase();
|
|
7220
|
+
for (const entry of _FIELD_RULES) {
|
|
7221
|
+
if (upper.includes(entry.match)) return entry.rule;
|
|
7222
|
+
}
|
|
7223
|
+
return null;
|
|
7224
|
+
}
|
|
7225
|
+
function validateFieldValue(label, value) {
|
|
7226
|
+
const rule = getFieldRule(label);
|
|
7227
|
+
if (!rule) return null;
|
|
7228
|
+
const v2 = value.trim();
|
|
7229
|
+
if (rule.numeric && !/^\d+$/.test(v2)) return `${label} must contain digits only`;
|
|
7230
|
+
if (rule.alphanumeric && !/^[A-Za-z0-9]+$/.test(v2)) return `${label} must be alphanumeric (letters and digits only)`;
|
|
7231
|
+
if (rule.minLen && v2.length < rule.minLen) return rule.hint;
|
|
7232
|
+
if (rule.maxLen && v2.length > rule.maxLen) return rule.hint;
|
|
7233
|
+
return null;
|
|
7234
|
+
}
|
|
7119
7235
|
const inputEls = [];
|
|
7120
7236
|
for (const field of dataFields) {
|
|
7121
7237
|
const fieldKey = field.label;
|
|
7122
7238
|
const fieldWrap = document.createElement("div");
|
|
7123
7239
|
fieldWrap.style.cssText = "display:flex;flex-direction:column;gap:6px;";
|
|
7240
|
+
const labelId = `sc-field-label-${fieldKey.replace(/\s+/g, "-").toLowerCase()}`;
|
|
7124
7241
|
const label = document.createElement("label");
|
|
7242
|
+
label.id = labelId;
|
|
7243
|
+
label.htmlFor = `sc-field-input-${fieldKey.replace(/\s+/g, "-").toLowerCase()}`;
|
|
7125
7244
|
label.style.cssText = `color:${this.theme.textMuted};font-size:10px;font-weight:700;letter-spacing:0.6px;text-transform:uppercase;`;
|
|
7126
7245
|
label.textContent = field.label;
|
|
7127
7246
|
fieldWrap.appendChild(label);
|
|
@@ -7194,10 +7313,17 @@ var SmartComplySDK = (() => {
|
|
|
7194
7313
|
fieldWrap.appendChild(fileInput);
|
|
7195
7314
|
inputEls.push({ field, el: fileInput });
|
|
7196
7315
|
} else {
|
|
7316
|
+
const inputId = `sc-field-input-${fieldKey.replace(/\s+/g, "-").toLowerCase()}`;
|
|
7317
|
+
const hintId = `sc-field-hint-${fieldKey.replace(/\s+/g, "-").toLowerCase()}`;
|
|
7318
|
+
const rule = getFieldRule(field.label);
|
|
7197
7319
|
const input = document.createElement("input");
|
|
7198
7320
|
input.type = "text";
|
|
7321
|
+
input.id = inputId;
|
|
7322
|
+
input.setAttribute("aria-labelledby", labelId);
|
|
7323
|
+
input.setAttribute("aria-describedby", hintId);
|
|
7199
7324
|
input.placeholder = `Enter ${field.label}`;
|
|
7200
|
-
input.maxLength = 50;
|
|
7325
|
+
input.maxLength = rule?.maxLen ? rule.maxLen + 2 : 50;
|
|
7326
|
+
if (rule?.numeric) input.inputMode = "numeric";
|
|
7201
7327
|
input.style.cssText = `
|
|
7202
7328
|
width:100%;padding:14px 16px;border-radius:12px;
|
|
7203
7329
|
font-size:16px;font-weight:500;
|
|
@@ -7208,17 +7334,46 @@ var SmartComplySDK = (() => {
|
|
|
7208
7334
|
box-sizing:border-box;
|
|
7209
7335
|
letter-spacing:0.5px;
|
|
7210
7336
|
`;
|
|
7337
|
+
const hintEl = document.createElement("div");
|
|
7338
|
+
hintEl.id = hintId;
|
|
7339
|
+
hintEl.setAttribute("aria-live", "polite");
|
|
7340
|
+
hintEl.style.cssText = `font-size:11px;min-height:14px;margin-top:-2px;`;
|
|
7341
|
+
if (rule) {
|
|
7342
|
+
hintEl.style.color = this.theme.textMuted;
|
|
7343
|
+
hintEl.textContent = rule.hint;
|
|
7344
|
+
}
|
|
7211
7345
|
input.addEventListener("focus", () => {
|
|
7212
7346
|
input.style.borderColor = this.theme.primary;
|
|
7213
7347
|
});
|
|
7214
7348
|
input.addEventListener("blur", () => {
|
|
7215
|
-
|
|
7349
|
+
const v2 = input.value.trim();
|
|
7350
|
+
if (v2) {
|
|
7351
|
+
const err = validateFieldValue(field.label, v2);
|
|
7352
|
+
if (err) {
|
|
7353
|
+
input.style.borderColor = this.theme.error;
|
|
7354
|
+
hintEl.style.color = this.theme.error;
|
|
7355
|
+
hintEl.textContent = err;
|
|
7356
|
+
input.setAttribute("aria-invalid", "true");
|
|
7357
|
+
} else {
|
|
7358
|
+
input.style.borderColor = this.theme.success || this.theme.primary;
|
|
7359
|
+
hintEl.style.color = this.theme.textMuted;
|
|
7360
|
+
hintEl.textContent = rule?.hint || "";
|
|
7361
|
+
input.setAttribute("aria-invalid", "false");
|
|
7362
|
+
}
|
|
7363
|
+
} else {
|
|
7364
|
+
input.style.borderColor = this.theme.border;
|
|
7365
|
+
}
|
|
7216
7366
|
});
|
|
7217
7367
|
input.addEventListener("input", () => {
|
|
7368
|
+
input.style.borderColor = this.theme.primary;
|
|
7369
|
+
hintEl.style.color = this.theme.textMuted;
|
|
7370
|
+
hintEl.textContent = rule?.hint || "";
|
|
7371
|
+
input.removeAttribute("aria-invalid");
|
|
7218
7372
|
this.collectedFields[fieldKey] = input.value.trim();
|
|
7219
7373
|
updateSubmitState();
|
|
7220
7374
|
});
|
|
7221
7375
|
fieldWrap.appendChild(input);
|
|
7376
|
+
fieldWrap.appendChild(hintEl);
|
|
7222
7377
|
inputEls.push({ field, el: input });
|
|
7223
7378
|
}
|
|
7224
7379
|
container.appendChild(fieldWrap);
|
|
@@ -7235,8 +7390,13 @@ var SmartComplySDK = (() => {
|
|
|
7235
7390
|
submitBtn.style.opacity = "0.5";
|
|
7236
7391
|
const updateSubmitState = () => {
|
|
7237
7392
|
const allFilled = inputEls.every(({ el }) => el.value.trim().length > 0);
|
|
7238
|
-
|
|
7239
|
-
|
|
7393
|
+
const allValid = inputEls.every(({ field, el }) => {
|
|
7394
|
+
if (el.tagName === "SELECT") return el.value.trim().length > 0;
|
|
7395
|
+
return !validateFieldValue(field.label, el.value.trim());
|
|
7396
|
+
});
|
|
7397
|
+
const ok = allFilled && allValid;
|
|
7398
|
+
submitBtn.disabled = !ok;
|
|
7399
|
+
submitBtn.style.opacity = ok ? "1" : "0.5";
|
|
7240
7400
|
};
|
|
7241
7401
|
submitBtn.addEventListener("click", async () => {
|
|
7242
7402
|
submitBtn.disabled = true;
|
|
@@ -7253,7 +7413,7 @@ var SmartComplySDK = (() => {
|
|
|
7253
7413
|
this.identityCheckId = this.verificationResult.data?.identity_check_id || null;
|
|
7254
7414
|
this.showStep("liveness");
|
|
7255
7415
|
} else {
|
|
7256
|
-
errorEl.textContent = this.verificationResult.message || "
|
|
7416
|
+
errorEl.textContent = this.verificationResult.message || "We could not verify your details. Please check and try again.";
|
|
7257
7417
|
errorEl.style.display = "block";
|
|
7258
7418
|
submitBtn.textContent = "Verify & Continue";
|
|
7259
7419
|
submitBtn.disabled = false;
|
|
@@ -7262,10 +7422,16 @@ var SmartComplySDK = (() => {
|
|
|
7262
7422
|
} catch (err) {
|
|
7263
7423
|
const fieldErrors = err.data?.errors;
|
|
7264
7424
|
if (Array.isArray(fieldErrors) && fieldErrors.length > 0) {
|
|
7265
|
-
const
|
|
7266
|
-
errorEl.textContent =
|
|
7425
|
+
const firstMsg = fieldErrors[0]?.message || fieldErrors[0]?.detail || "";
|
|
7426
|
+
errorEl.textContent = _sanitizeUserMessage(firstMsg) || "Please check your details and try again.";
|
|
7427
|
+
} else if (err.code === "VERIFICATION_FAILED") {
|
|
7428
|
+
errorEl.textContent = "We could not verify those details. Please check your ID number and try again.";
|
|
7429
|
+
} else if (err.code === "CHANNEL_NOT_CONFIGURED") {
|
|
7430
|
+
errorEl.textContent = "This ID type is not currently available. Please choose a different option.";
|
|
7431
|
+
} else if (err.code === "INSUFFICIENT_BALANCE") {
|
|
7432
|
+
errorEl.textContent = "Verification is temporarily unavailable. Please contact support.";
|
|
7267
7433
|
} else {
|
|
7268
|
-
errorEl.textContent = err.message || "
|
|
7434
|
+
errorEl.textContent = _sanitizeUserMessage(err.message) || "Something went wrong. Please try again.";
|
|
7269
7435
|
}
|
|
7270
7436
|
errorEl.style.display = "block";
|
|
7271
7437
|
submitBtn.textContent = "Verify & Continue";
|
|
@@ -7315,121 +7481,112 @@ var SmartComplySDK = (() => {
|
|
|
7315
7481
|
this.showErrorStep(err.message || "Document capture failed. Please try again.");
|
|
7316
7482
|
});
|
|
7317
7483
|
}
|
|
7318
|
-
// ── Processing
|
|
7319
|
-
renderProcessing(container) {
|
|
7320
|
-
container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:
|
|
7321
|
-
const
|
|
7322
|
-
|
|
7323
|
-
const
|
|
7324
|
-
|
|
7325
|
-
position:absolute;inset:0;border-radius:50%;
|
|
7326
|
-
border:3px solid ${this.theme.border};
|
|
7327
|
-
border-top-color:${this.theme.primary};
|
|
7328
|
-
animation:sc-spin 0.9s linear infinite;
|
|
7329
|
-
`;
|
|
7330
|
-
animationWrap.appendChild(spinner);
|
|
7484
|
+
// ── Processing (real polling-driven, document flow only) ────────────
|
|
7485
|
+
renderProcessing(container, entryId) {
|
|
7486
|
+
container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:16px;";
|
|
7487
|
+
const spinnerWrap = document.createElement("div");
|
|
7488
|
+
spinnerWrap.style.cssText = "position:relative;width:56px;height:56px;flex-shrink:0;";
|
|
7489
|
+
const ring = document.createElement("div");
|
|
7490
|
+
ring.id = "sc-proc-ring";
|
|
7491
|
+
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;`;
|
|
7331
7492
|
const centerIcon = document.createElement("div");
|
|
7332
|
-
centerIcon.style.cssText =
|
|
7333
|
-
position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
|
|
7334
|
-
font-size:20px;
|
|
7335
|
-
`;
|
|
7493
|
+
centerIcon.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:22px;";
|
|
7336
7494
|
centerIcon.textContent = "\u{1F50D}";
|
|
7337
|
-
|
|
7338
|
-
|
|
7495
|
+
spinnerWrap.appendChild(ring);
|
|
7496
|
+
spinnerWrap.appendChild(centerIcon);
|
|
7497
|
+
container.appendChild(spinnerWrap);
|
|
7339
7498
|
const title = document.createElement("div");
|
|
7340
7499
|
title.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:700;text-align:center;`;
|
|
7341
|
-
title.textContent = "
|
|
7500
|
+
title.textContent = "Verifying your document";
|
|
7342
7501
|
container.appendChild(title);
|
|
7343
|
-
const
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
{ id: "
|
|
7353
|
-
{ id: "
|
|
7354
|
-
{ id: "
|
|
7355
|
-
{ id: "analyzing", text: "Checking quality", icon: "\u{1F52C}" }
|
|
7502
|
+
const statusLine = document.createElement("div");
|
|
7503
|
+
statusLine.id = "sc-proc-status";
|
|
7504
|
+
statusLine.setAttribute("aria-live", "polite");
|
|
7505
|
+
statusLine.setAttribute("aria-atomic", "true");
|
|
7506
|
+
statusLine.style.cssText = `color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:260px;line-height:1.5;`;
|
|
7507
|
+
statusLine.textContent = "Scanning document\u2026";
|
|
7508
|
+
container.appendChild(statusLine);
|
|
7509
|
+
const stages = [
|
|
7510
|
+
{ id: "ocr", label: "Reading document", icon: "\u{1F4C4}" },
|
|
7511
|
+
{ id: "expiry", label: "Checking expiry", icon: "\u{1F4C5}" },
|
|
7512
|
+
{ id: "face", label: "Face match", icon: "\u{1F933}" },
|
|
7513
|
+
{ id: "result", label: "Finalising result", icon: "\u2713" }
|
|
7356
7514
|
];
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
transition:all 0.3s ease
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
width:26px;height:26px;border-radius:7px;flex-shrink:0;
|
|
7368
|
-
display:flex;align-items:center;justify-content:center;
|
|
7369
|
-
font-size:12px;background:${this.theme.shimmer};
|
|
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>
|
|
7370
7525
|
`;
|
|
7371
|
-
|
|
7372
|
-
iconEl.id = `step-icon-${step.id}`;
|
|
7373
|
-
const textEl = document.createElement("div");
|
|
7374
|
-
textEl.style.cssText = `flex:1;color:${this.theme.textSecondary};font-size:11px;font-weight:500;`;
|
|
7375
|
-
textEl.textContent = step.text;
|
|
7376
|
-
textEl.id = `step-text-${step.id}`;
|
|
7377
|
-
const statusEl = document.createElement("div");
|
|
7378
|
-
statusEl.style.cssText = `width:16px;height:16px;border-radius:50%;border:2px solid ${this.theme.border};position:relative;flex-shrink:0;`;
|
|
7379
|
-
statusEl.id = `step-status-${step.id}`;
|
|
7380
|
-
stepEl.appendChild(iconEl);
|
|
7381
|
-
stepEl.appendChild(textEl);
|
|
7382
|
-
stepEl.appendChild(statusEl);
|
|
7383
|
-
stepsContainer.appendChild(stepEl);
|
|
7526
|
+
grid.appendChild(el);
|
|
7384
7527
|
});
|
|
7385
|
-
container.appendChild(
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
const
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
const
|
|
7395
|
-
const statusEl = document.getElementById(`
|
|
7396
|
-
if (
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
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) => {
|
|
7412
7555
|
setTimeout(() => {
|
|
7413
|
-
if (
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
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;
|
|
7563
|
+
let polls = 0;
|
|
7564
|
+
const poll = async () => {
|
|
7565
|
+
if (this.isDestroyed) return;
|
|
7566
|
+
polls++;
|
|
7567
|
+
try {
|
|
7568
|
+
const result = await this.sdk.liveness.pollResult(entryId);
|
|
7569
|
+
if (result.status === "passed" || result.status === "failed") {
|
|
7570
|
+
stages.forEach((s2) => setStageComplete(s2.id));
|
|
7571
|
+
statusLine.textContent = result.status === "passed" ? "Verification complete \u2713" : "Verification finished";
|
|
7572
|
+
setTimeout(() => {
|
|
7573
|
+
if (!this.isDestroyed) {
|
|
7574
|
+
this._livenessSubmitResult = result;
|
|
7575
|
+
this.showResult(result);
|
|
7576
|
+
}
|
|
7577
|
+
}, 800);
|
|
7578
|
+
return;
|
|
7429
7579
|
}
|
|
7430
|
-
}
|
|
7580
|
+
} catch {
|
|
7581
|
+
}
|
|
7582
|
+
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
7583
|
+
this._pollStatusTimer = setTimeout(poll, 3e3);
|
|
7584
|
+
} else if (!this.isDestroyed) {
|
|
7585
|
+
stages.forEach((s2) => setStageComplete(s2.id));
|
|
7586
|
+
statusLine.textContent = "Still processing \u2014 check back soon";
|
|
7587
|
+
}
|
|
7431
7588
|
};
|
|
7432
|
-
|
|
7589
|
+
this._pollStatusTimer = setTimeout(poll, 5e3);
|
|
7433
7590
|
}
|
|
7434
7591
|
/**
|
|
7435
7592
|
* Process document capture result with front/back support
|
|
@@ -7466,7 +7623,7 @@ var SmartComplySDK = (() => {
|
|
|
7466
7623
|
<div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
|
|
7467
7624
|
<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>
|
|
7468
7625
|
</div>`;
|
|
7469
|
-
const identifier = `DOC-${Date.now()}`;
|
|
7626
|
+
const identifier = Object.values(this.collectedFields)[0] || this.idNumber || this.selectedIdType || `DOC-${Date.now()}`;
|
|
7470
7627
|
const COUNTRY_CODES = {
|
|
7471
7628
|
nigeria: "NG",
|
|
7472
7629
|
ghana: "GH",
|
|
@@ -7535,47 +7692,53 @@ var SmartComplySDK = (() => {
|
|
|
7535
7692
|
await attemptProcessing();
|
|
7536
7693
|
}
|
|
7537
7694
|
handleDocumentProcessingError(error, captureContainer) {
|
|
7538
|
-
let
|
|
7695
|
+
let userMessage = "We could not verify your document. Please try again.";
|
|
7539
7696
|
let canRetry = true;
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7697
|
+
const raw = (error?.message || "").toLowerCase();
|
|
7698
|
+
if (raw.includes("expired")) {
|
|
7699
|
+
userMessage = "The document you submitted has expired. Please use a valid, current ID.";
|
|
7700
|
+
canRetry = false;
|
|
7701
|
+
} else if (raw.includes("quality too low") || raw.includes("blurry") || raw.includes("blur")) {
|
|
7702
|
+
userMessage = "The document photo isn't clear enough. Please retake it in good lighting with no glare.";
|
|
7703
|
+
} else if (raw.includes("not detected") || raw.includes("document_not_detected")) {
|
|
7704
|
+
userMessage = "We could not detect a document in the photo. Please ensure the full ID is visible and try again.";
|
|
7705
|
+
} else if (raw.includes("network") || raw.includes("fetch") || raw.includes("connection")) {
|
|
7706
|
+
userMessage = "A connection error occurred. Please check your internet and try again.";
|
|
7707
|
+
} else if (raw.includes("too large") || raw.includes("413")) {
|
|
7708
|
+
userMessage = "The document image is too large. Please retake the photo.";
|
|
7709
|
+
} else if (raw.includes("format") || raw.includes("415")) {
|
|
7710
|
+
userMessage = "Unsupported image format. Please use JPEG or PNG.";
|
|
7711
|
+
} else if (raw.includes("type mismatch") || raw.includes("document_type_mismatch")) {
|
|
7712
|
+
userMessage = "The document does not match the selected ID type. Please choose the correct ID type.";
|
|
7553
7713
|
}
|
|
7554
7714
|
if (captureContainer) {
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7715
|
+
const wrap = document.createElement("div");
|
|
7716
|
+
wrap.style.cssText = "display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;";
|
|
7717
|
+
wrap.setAttribute("role", "alert");
|
|
7718
|
+
const icon = document.createElement("div");
|
|
7719
|
+
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 = "\u{1F50D}";
|
|
7721
|
+
wrap.appendChild(icon);
|
|
7722
|
+
const titleEl = document.createElement("div");
|
|
7723
|
+
titleEl.style.cssText = `color:${this.theme.text};font-size:14px;font-weight:700;`;
|
|
7724
|
+
titleEl.textContent = "Document Could Not Be Verified";
|
|
7725
|
+
wrap.appendChild(titleEl);
|
|
7726
|
+
const msgEl = document.createElement("div");
|
|
7727
|
+
msgEl.style.cssText = `color:${this.theme.textSecondary};font-size:13px;max-width:260px;line-height:1.6;`;
|
|
7728
|
+
msgEl.textContent = userMessage;
|
|
7729
|
+
wrap.appendChild(msgEl);
|
|
7569
7730
|
if (canRetry) {
|
|
7570
|
-
const retryBtn =
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7731
|
+
const retryBtn = document.createElement("button");
|
|
7732
|
+
retryBtn.textContent = "Try Again";
|
|
7733
|
+
retryBtn.setAttribute("aria-label", "Retake document photo");
|
|
7734
|
+
retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:8px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
|
|
7735
|
+
retryBtn.addEventListener("click", () => this.showStep("document_capture"));
|
|
7736
|
+
wrap.appendChild(retryBtn);
|
|
7576
7737
|
}
|
|
7738
|
+
captureContainer.innerHTML = "";
|
|
7739
|
+
captureContainer.appendChild(wrap);
|
|
7577
7740
|
} else {
|
|
7578
|
-
this.showErrorStep(
|
|
7741
|
+
this.showErrorStep(userMessage);
|
|
7579
7742
|
}
|
|
7580
7743
|
}
|
|
7581
7744
|
// ── Liveness ────────────────────────────────────────────────────
|
|
@@ -7590,7 +7753,7 @@ var SmartComplySDK = (() => {
|
|
|
7590
7753
|
const livenessContainer = document.createElement("div");
|
|
7591
7754
|
livenessContainer.style.cssText = "margin:8px 0;width:100%;";
|
|
7592
7755
|
container.appendChild(livenessContainer);
|
|
7593
|
-
const identifier = this.idNumber || `DOC-${Date.now()}`;
|
|
7756
|
+
const identifier = this.idNumber || Object.values(this.collectedFields)[0] || this.selectedIdType || `DOC-${Date.now()}`;
|
|
7594
7757
|
const COUNTRY_CODES = {
|
|
7595
7758
|
nigeria: "NG",
|
|
7596
7759
|
ghana: "GH",
|
|
@@ -7609,12 +7772,20 @@ var SmartComplySDK = (() => {
|
|
|
7609
7772
|
country,
|
|
7610
7773
|
document: this.documentBlob || void 0,
|
|
7611
7774
|
document_back: this.documentBackBlob || void 0,
|
|
7612
|
-
identity_check: this.identityCheckId || void 0
|
|
7775
|
+
identity_check: this.identityCheckId || void 0,
|
|
7776
|
+
// Tell the module which flow we're in so it can skip video for data verification
|
|
7777
|
+
verification_type: this.identityCheckId ? "data_verification" : this.documentBlob ? "document_verification" : "liveness_only"
|
|
7613
7778
|
},
|
|
7614
7779
|
["BLINK", "TURN_HEAD", "OPEN_MOUTH"],
|
|
7615
7780
|
this.livenessEntryId || void 0
|
|
7616
7781
|
).then((result) => {
|
|
7617
|
-
this.
|
|
7782
|
+
const isAsync = !!(this.documentBlob || this.identityCheckId);
|
|
7783
|
+
if (isAsync && result.id) {
|
|
7784
|
+
this.livenessEntryId = result.id;
|
|
7785
|
+
this.showStep("processing");
|
|
7786
|
+
} else {
|
|
7787
|
+
this.showResult(result);
|
|
7788
|
+
}
|
|
7618
7789
|
}).catch((err) => {
|
|
7619
7790
|
this.showErrorStep(err.message || "Liveness verification failed. Please try again.");
|
|
7620
7791
|
});
|
|
@@ -7626,6 +7797,8 @@ var SmartComplySDK = (() => {
|
|
|
7626
7797
|
if (!this.contentArea) return;
|
|
7627
7798
|
this.contentArea.innerHTML = "";
|
|
7628
7799
|
const wrapper = document.createElement("div");
|
|
7800
|
+
wrapper.setAttribute("role", "region");
|
|
7801
|
+
wrapper.setAttribute("aria-label", "Verification result");
|
|
7629
7802
|
wrapper.style.cssText = `
|
|
7630
7803
|
display:flex;flex-direction:column;align-items:center;
|
|
7631
7804
|
text-align:center;gap:16px;padding:20px 0;
|
|
@@ -7638,54 +7811,59 @@ var SmartComplySDK = (() => {
|
|
|
7638
7811
|
font-size:36px;
|
|
7639
7812
|
background:${this.theme.cardBg};
|
|
7640
7813
|
border:3px solid ${this.theme.border};
|
|
7814
|
+
transition:all 0.4s ease;
|
|
7641
7815
|
`;
|
|
7642
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>`;
|
|
7643
7817
|
wrapper.appendChild(iconWrap);
|
|
7644
7818
|
const title = document.createElement("h2");
|
|
7819
|
+
title.setAttribute("aria-live", "polite");
|
|
7645
7820
|
title.style.cssText = `color:${this.theme.text};font-size:20px;font-weight:700;margin:0;`;
|
|
7646
|
-
title.textContent = "
|
|
7821
|
+
title.textContent = "Checking your identity\u2026";
|
|
7647
7822
|
wrapper.appendChild(title);
|
|
7648
7823
|
const desc = document.createElement("p");
|
|
7824
|
+
desc.setAttribute("aria-live", "polite");
|
|
7649
7825
|
desc.style.cssText = `color:${this.theme.textSecondary};font-size:14px;line-height:1.6;margin:0;max-width:300px;`;
|
|
7650
|
-
desc.textContent = "
|
|
7826
|
+
desc.textContent = "This usually takes a few seconds. Please wait.";
|
|
7651
7827
|
wrapper.appendChild(desc);
|
|
7652
|
-
const
|
|
7653
|
-
|
|
7828
|
+
const resultCard = document.createElement("div");
|
|
7829
|
+
resultCard.style.cssText = `
|
|
7654
7830
|
width:100%;padding:16px;border-radius:12px;
|
|
7655
7831
|
background:${this.theme.cardBg};border:1px solid ${this.theme.border};
|
|
7656
|
-
text-align:left;
|
|
7832
|
+
text-align:left;display:flex;flex-direction:column;gap:10px;
|
|
7833
|
+
transition:border-color 0.3s ease;
|
|
7657
7834
|
`;
|
|
7658
7835
|
const statusBadge = document.createElement("span");
|
|
7836
|
+
statusBadge.setAttribute("role", "status");
|
|
7837
|
+
statusBadge.setAttribute("aria-live", "polite");
|
|
7838
|
+
statusBadge.setAttribute("aria-atomic", "true");
|
|
7659
7839
|
statusBadge.style.cssText = `font-size:12px;font-weight:600;color:${this.theme.warning};`;
|
|
7660
7840
|
statusBadge.textContent = "Processing\u2026";
|
|
7661
7841
|
const statusRow = document.createElement("div");
|
|
7662
|
-
statusRow.style.cssText = "display:flex;justify-content:space-between;
|
|
7663
|
-
statusRow.innerHTML = `<span style="color:${this.theme.textMuted};font-size:12px;">Status</span>`;
|
|
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>`;
|
|
7664
7844
|
statusRow.appendChild(statusBadge);
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
submittedRow.innerHTML = `
|
|
7676
|
-
<span style="color:${this.theme.textMuted};font-size:12px;">Submitted</span>
|
|
7677
|
-
<span style="color:${this.theme.text};font-size:12px;">${submitResult.submitted_at ? new Date(submitResult.submitted_at).toLocaleString() : "Just now"}</span>
|
|
7678
|
-
`;
|
|
7679
|
-
details.appendChild(submittedRow);
|
|
7680
|
-
wrapper.appendChild(details);
|
|
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);
|
|
7681
7855
|
const failureMsg = document.createElement("div");
|
|
7856
|
+
failureMsg.setAttribute("role", "alert");
|
|
7682
7857
|
failureMsg.style.cssText = `
|
|
7683
7858
|
display:none;width:100%;padding:12px 14px;border-radius:10px;
|
|
7684
7859
|
background:${this.theme.errorBg};border:1px solid ${this.theme.error};
|
|
7685
|
-
color:${this.theme.error};font-size:
|
|
7860
|
+
color:${this.theme.error};font-size:13px;text-align:left;line-height:1.6;
|
|
7686
7861
|
`;
|
|
7687
7862
|
wrapper.appendChild(failureMsg);
|
|
7863
|
+
const btnRow = document.createElement("div");
|
|
7864
|
+
btnRow.style.cssText = "display:flex;flex-direction:column;gap:8px;width:100%;";
|
|
7688
7865
|
const doneBtn = this.createPrimaryButton("Done");
|
|
7866
|
+
doneBtn.setAttribute("aria-label", "Close verification and continue");
|
|
7689
7867
|
let finalStatus = submitResult.status;
|
|
7690
7868
|
doneBtn.addEventListener("click", () => {
|
|
7691
7869
|
if (this._pollStatusTimer) {
|
|
@@ -7701,7 +7879,19 @@ var SmartComplySDK = (() => {
|
|
|
7701
7879
|
});
|
|
7702
7880
|
this.close();
|
|
7703
7881
|
});
|
|
7704
|
-
|
|
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);
|
|
7705
7895
|
this.contentArea.appendChild(wrapper);
|
|
7706
7896
|
requestAnimationFrame(() => {
|
|
7707
7897
|
wrapper.style.opacity = "1";
|
|
@@ -7713,13 +7903,17 @@ var SmartComplySDK = (() => {
|
|
|
7713
7903
|
statusBadge,
|
|
7714
7904
|
title,
|
|
7715
7905
|
desc,
|
|
7906
|
+
resultCard,
|
|
7907
|
+
nameRow,
|
|
7908
|
+
docTypeRow,
|
|
7716
7909
|
failureMsg,
|
|
7910
|
+
retryBtn,
|
|
7717
7911
|
(resolved) => {
|
|
7718
7912
|
finalStatus = resolved;
|
|
7719
7913
|
}
|
|
7720
7914
|
);
|
|
7721
7915
|
}
|
|
7722
|
-
_startStatusPolling(entryId, iconWrap, statusBadge, title, desc, failureMsg, onResolved) {
|
|
7916
|
+
_startStatusPolling(entryId, iconWrap, statusBadge, title, desc, resultCard, nameRow, docTypeRow, failureMsg, retryBtn, onResolved) {
|
|
7723
7917
|
const MAX_POLLS = 25;
|
|
7724
7918
|
let polls = 0;
|
|
7725
7919
|
let uiTimeoutId = setTimeout(() => {
|
|
@@ -7736,43 +7930,56 @@ var SmartComplySDK = (() => {
|
|
|
7736
7930
|
clearTimeout(this._pollStatusTimer);
|
|
7737
7931
|
this._pollStatusTimer = null;
|
|
7738
7932
|
}
|
|
7739
|
-
iconWrap.innerHTML = "\
|
|
7740
|
-
iconWrap.style.background = this.theme.cardBg;
|
|
7933
|
+
iconWrap.innerHTML = "\u23F3";
|
|
7741
7934
|
iconWrap.style.border = `3px solid ${this.theme.warning}`;
|
|
7742
|
-
iconWrap.style.color = this.theme.warning;
|
|
7743
|
-
iconWrap.style.fontSize = "30px";
|
|
7744
7935
|
statusBadge.style.color = this.theme.textMuted;
|
|
7745
7936
|
statusBadge.textContent = "Still processing\u2026";
|
|
7746
|
-
title.textContent = "
|
|
7747
|
-
desc.textContent = "
|
|
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.";
|
|
7748
7939
|
};
|
|
7749
|
-
const applyPassed = () => {
|
|
7940
|
+
const applyPassed = (result) => {
|
|
7750
7941
|
clearUiTimeout();
|
|
7751
7942
|
iconWrap.innerHTML = "\u2713";
|
|
7752
|
-
iconWrap.style.
|
|
7753
|
-
|
|
7754
|
-
iconWrap.style.color = this.theme.success;
|
|
7755
|
-
iconWrap.style.fontSize = "36px";
|
|
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;
|
|
7756
7945
|
statusBadge.style.color = this.theme.success;
|
|
7757
7946
|
statusBadge.textContent = "Verified \u2713";
|
|
7758
7947
|
title.textContent = "Identity Verified";
|
|
7759
|
-
desc.textContent = "Your identity
|
|
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
|
+
}
|
|
7760
7967
|
};
|
|
7761
7968
|
const applyFailed = (reason) => {
|
|
7762
7969
|
clearUiTimeout();
|
|
7763
7970
|
iconWrap.innerHTML = "\u2715";
|
|
7764
|
-
iconWrap.style.
|
|
7765
|
-
|
|
7766
|
-
iconWrap.style.color = this.theme.error;
|
|
7767
|
-
iconWrap.style.fontSize = "32px";
|
|
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;
|
|
7768
7973
|
statusBadge.style.color = this.theme.error;
|
|
7769
|
-
statusBadge.textContent = "
|
|
7770
|
-
title.textContent = "Verification
|
|
7771
|
-
desc.textContent = "
|
|
7772
|
-
|
|
7773
|
-
|
|
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;
|
|
7774
7980
|
failureMsg.style.display = "block";
|
|
7775
7981
|
}
|
|
7982
|
+
retryBtn.style.display = "block";
|
|
7776
7983
|
};
|
|
7777
7984
|
const poll = async () => {
|
|
7778
7985
|
if (this.isDestroyed) return;
|
|
@@ -7781,13 +7988,12 @@ var SmartComplySDK = (() => {
|
|
|
7781
7988
|
const result = await this.sdk.liveness.pollResult(entryId);
|
|
7782
7989
|
if (result.status === "passed") {
|
|
7783
7990
|
onResolved("passed");
|
|
7784
|
-
applyPassed();
|
|
7991
|
+
applyPassed(result);
|
|
7785
7992
|
return;
|
|
7786
7993
|
}
|
|
7787
7994
|
if (result.status === "failed") {
|
|
7788
|
-
onResolved(
|
|
7789
|
-
|
|
7790
|
-
applyFailed(reason);
|
|
7995
|
+
onResolved("failed");
|
|
7996
|
+
applyFailed(result.failure_reason || null);
|
|
7791
7997
|
return;
|
|
7792
7998
|
}
|
|
7793
7999
|
if (polls < MAX_POLLS && !this.isDestroyed) {
|
|
@@ -7813,20 +8019,25 @@ var SmartComplySDK = (() => {
|
|
|
7813
8019
|
if (!this.contentArea) return;
|
|
7814
8020
|
this.contentArea.innerHTML = "";
|
|
7815
8021
|
const wrapper = document.createElement("div");
|
|
8022
|
+
wrapper.setAttribute("role", "alert");
|
|
7816
8023
|
wrapper.style.cssText = `
|
|
7817
8024
|
display:flex;flex-direction:column;align-items:center;
|
|
7818
8025
|
text-align:center;gap:16px;padding:20px 0;
|
|
7819
8026
|
`;
|
|
7820
8027
|
const isCameraErr = /camera|permission|notallowed|notfound/i.test(message);
|
|
7821
8028
|
const isNetworkErr = /network|timeout|connection|fetch/i.test(message);
|
|
7822
|
-
const isAuthErr = /session|token
|
|
7823
|
-
const
|
|
7824
|
-
const
|
|
8029
|
+
const isAuthErr = /session.*expired|token.*expired|authentication/i.test(message);
|
|
8030
|
+
const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
|
|
8031
|
+
const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
|
|
8032
|
+
const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
|
|
8033
|
+
const errorIcon = isCameraErr ? "\u{1F4F7}" : isNetworkErr ? "\u{1F4E1}" : isAuthErr ? "\u23F1" : isDocQuality ? "\u{1F50D}" : isExpiredDoc ? "\u{1F4C5}" : isFaceMatch ? "\u{1F933}" : "\u2715";
|
|
8034
|
+
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
|
+
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.";
|
|
7825
8036
|
const iconWrap = document.createElement("div");
|
|
7826
8037
|
iconWrap.style.cssText = `
|
|
7827
8038
|
width:72px;height:72px;border-radius:50%;
|
|
7828
8039
|
display:flex;align-items:center;justify-content:center;
|
|
7829
|
-
font-size
|
|
8040
|
+
font-size:28px;
|
|
7830
8041
|
background:${this.theme.errorBg};
|
|
7831
8042
|
border:3px solid ${this.theme.error};
|
|
7832
8043
|
`;
|
|
@@ -7838,18 +8049,16 @@ var SmartComplySDK = (() => {
|
|
|
7838
8049
|
wrapper.appendChild(title);
|
|
7839
8050
|
const desc = document.createElement("p");
|
|
7840
8051
|
desc.style.cssText = `color:${this.theme.textSecondary};font-size:14px;line-height:1.6;margin:0;max-width:300px;`;
|
|
7841
|
-
desc.textContent =
|
|
8052
|
+
desc.textContent = userMessage;
|
|
7842
8053
|
wrapper.appendChild(desc);
|
|
7843
8054
|
const retryBtn = this.createPrimaryButton("Try Again");
|
|
8055
|
+
retryBtn.setAttribute("aria-label", "Try verification again");
|
|
7844
8056
|
retryBtn.addEventListener("click", () => {
|
|
7845
|
-
|
|
7846
|
-
this.showStep("welcome");
|
|
7847
|
-
} else {
|
|
7848
|
-
this.initialize();
|
|
7849
|
-
}
|
|
8057
|
+
this.initialize();
|
|
7850
8058
|
});
|
|
7851
8059
|
wrapper.appendChild(retryBtn);
|
|
7852
8060
|
const closeBtn = this.createSecondaryButton("Close");
|
|
8061
|
+
closeBtn.setAttribute("aria-label", "Close verification widget");
|
|
7853
8062
|
closeBtn.addEventListener("click", () => {
|
|
7854
8063
|
this.options.onError?.(new Error(message));
|
|
7855
8064
|
this.close();
|
|
@@ -7862,6 +8071,7 @@ var SmartComplySDK = (() => {
|
|
|
7862
8071
|
// ─────────────────────────────────────────────────────────────────
|
|
7863
8072
|
createHeader() {
|
|
7864
8073
|
const header = document.createElement("div");
|
|
8074
|
+
header.setAttribute("role", "banner");
|
|
7865
8075
|
header.style.cssText = `
|
|
7866
8076
|
display:flex;align-items:center;justify-content:space-between;
|
|
7867
8077
|
padding:11px 20px;border-bottom:1px solid ${this.theme.border};
|
|
@@ -7870,6 +8080,7 @@ var SmartComplySDK = (() => {
|
|
|
7870
8080
|
leftArea.style.cssText = "display:flex;align-items:center;gap:8px;min-width:0;";
|
|
7871
8081
|
this.headerBackBtn = document.createElement("button");
|
|
7872
8082
|
this.headerBackBtn.innerHTML = "←";
|
|
8083
|
+
this.headerBackBtn.setAttribute("aria-label", "Go back to previous step");
|
|
7873
8084
|
this.headerBackBtn.style.cssText = `
|
|
7874
8085
|
width:30px;height:30px;border-radius:8px;flex-shrink:0;
|
|
7875
8086
|
display:none;align-items:center;justify-content:center;
|
|
@@ -7878,6 +8089,7 @@ var SmartComplySDK = (() => {
|
|
|
7878
8089
|
background:${this.theme.cardBg};color:${this.theme.text};
|
|
7879
8090
|
transition:all 0.2s ease;
|
|
7880
8091
|
`;
|
|
8092
|
+
this.headerBackBtn.style.cssText += `:focus-visible{outline:2px solid ${this.theme.primary};outline-offset:2px;}`;
|
|
7881
8093
|
this.headerBackBtn.addEventListener("mouseenter", () => {
|
|
7882
8094
|
if (this.headerBackBtn) {
|
|
7883
8095
|
this.headerBackBtn.style.background = this.theme.shimmer;
|
|
@@ -7890,6 +8102,7 @@ var SmartComplySDK = (() => {
|
|
|
7890
8102
|
});
|
|
7891
8103
|
leftArea.appendChild(this.headerBackBtn);
|
|
7892
8104
|
this.brandLabel = document.createElement("div");
|
|
8105
|
+
this.brandLabel.setAttribute("aria-label", "Verification provider");
|
|
7893
8106
|
this.brandLabel.style.cssText = `
|
|
7894
8107
|
font-size:15px;font-weight:700;color:${this.theme.text};
|
|
7895
8108
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
|
|
@@ -7898,6 +8111,8 @@ var SmartComplySDK = (() => {
|
|
|
7898
8111
|
leftArea.appendChild(this.brandLabel);
|
|
7899
8112
|
header.appendChild(leftArea);
|
|
7900
8113
|
this.stepLabel = document.createElement("div");
|
|
8114
|
+
this.stepLabel.setAttribute("aria-live", "polite");
|
|
8115
|
+
this.stepLabel.setAttribute("aria-atomic", "true");
|
|
7901
8116
|
this.stepLabel.style.cssText = `
|
|
7902
8117
|
font-size:12px;color:${this.theme.textMuted};
|
|
7903
8118
|
font-variant-numeric:tabular-nums;flex-shrink:0;
|
|
@@ -7905,6 +8120,7 @@ var SmartComplySDK = (() => {
|
|
|
7905
8120
|
header.appendChild(this.stepLabel);
|
|
7906
8121
|
const closeBtn = document.createElement("button");
|
|
7907
8122
|
closeBtn.innerHTML = "\u2715";
|
|
8123
|
+
closeBtn.setAttribute("aria-label", "Close identity verification");
|
|
7908
8124
|
closeBtn.style.cssText = `
|
|
7909
8125
|
width:32px;height:32px;border-radius:8px;flex-shrink:0;
|
|
7910
8126
|
display:flex;align-items:center;justify-content:center;
|
|
@@ -7951,6 +8167,7 @@ var SmartComplySDK = (() => {
|
|
|
7951
8167
|
}
|
|
7952
8168
|
createOptionCard(title, subtitle, onClick) {
|
|
7953
8169
|
const card = document.createElement("button");
|
|
8170
|
+
card.setAttribute("aria-label", subtitle ? `${title}: ${subtitle}` : title);
|
|
7954
8171
|
card.style.cssText = `
|
|
7955
8172
|
width:100%;padding:13px 16px;border-radius:12px;
|
|
7956
8173
|
background:${this.theme.cardBg};
|
|
@@ -8115,6 +8332,12 @@ var SmartComplySDK = (() => {
|
|
|
8115
8332
|
#sc-flow-modal * {
|
|
8116
8333
|
box-sizing: border-box;
|
|
8117
8334
|
}
|
|
8335
|
+
#sc-flow-modal button:focus-visible,
|
|
8336
|
+
#sc-flow-modal select:focus-visible,
|
|
8337
|
+
#sc-flow-modal input:focus-visible {
|
|
8338
|
+
outline: 2px solid #3b82f6;
|
|
8339
|
+
outline-offset: 2px;
|
|
8340
|
+
}
|
|
8118
8341
|
#sc-flow-modal::-webkit-scrollbar {
|
|
8119
8342
|
width: 4px;
|
|
8120
8343
|
}
|