smartcomply-web-sdk 1.0.53 → 1.0.57

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.
@@ -6536,7 +6536,6 @@ var SmartComplySDK = (() => {
6536
6536
  "traceback",
6537
6537
  "str(e)",
6538
6538
  "error:",
6539
- "failed:",
6540
6539
  "django",
6541
6540
  "python",
6542
6541
  "http ",
@@ -6548,11 +6547,19 @@ var SmartComplySDK = (() => {
6548
6547
  "confidence_percentage",
6549
6548
  "threshold_percentage",
6550
6549
  "polling_fallback",
6551
- "resolved_by"
6550
+ "resolved_by",
6551
+ // Billing/wallet messages are an operator problem (fund the account,
6552
+ // configure billing) — never the end user's to act on.
6553
+ "wallet",
6554
+ "fund your wallet",
6555
+ "billing is not configured",
6556
+ "insufficient balance",
6557
+ "insufficient fund"
6552
6558
  ];
6553
6559
  for (const blocked of blocklist) {
6554
6560
  if (lower.includes(blocked)) return null;
6555
6561
  }
6562
+ if (/failed:\S/i.test(raw)) return null;
6556
6563
  if (/[a-z]+_[a-z]+/.test(raw) && !/\s/.test(raw.trim())) return null;
6557
6564
  if (raw.trim()[0] === raw.trim()[0].toLowerCase() && /^[a-z]/.test(raw.trim())) return null;
6558
6565
  return raw.trim();
@@ -7161,7 +7168,12 @@ var SmartComplySDK = (() => {
7161
7168
  { match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8–20 alphanumeric characters" } },
7162
7169
  { match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7–15 alphanumeric characters" } },
7163
7170
  { match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8–20 characters" } },
7164
- { match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10–12 characters" } }
7171
+ { match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10–12 characters" } },
7172
+ // Backend accepts DD-MM-YYYY, DD/MM/YYYY, YYYY-MM-DD or YYYY/MM/DD
7173
+ // (see _normalize_date_str in ivs_run_check.py) — DD-MM-YYYY shown here
7174
+ // since it matches the date format printed on NIN/BVN records.
7175
+ { match: "DATE OF BIRTH", rule: { pattern: /^\d{2}[-/]\d{2}[-/]\d{4}$|^\d{4}[-/]\d{2}[-/]\d{2}$/, hint: "Format: DD-MM-YYYY (e.g. 15-06-1996)", placeholder: "DD-MM-YYYY" } },
7176
+ { match: "DOB", rule: { pattern: /^\d{2}[-/]\d{2}[-/]\d{4}$|^\d{4}[-/]\d{2}[-/]\d{2}$/, hint: "Format: DD-MM-YYYY (e.g. 15-06-1996)", placeholder: "DD-MM-YYYY" } }
7165
7177
  ];
7166
7178
  function getFieldRule(label) {
7167
7179
  const upper = label.toUpperCase();
@@ -7176,6 +7188,7 @@ var SmartComplySDK = (() => {
7176
7188
  const v2 = value.trim();
7177
7189
  if (rule.numeric && !/^\d+$/.test(v2)) return `${label} must contain digits only`;
7178
7190
  if (rule.alphanumeric && !/^[A-Za-z0-9]+$/.test(v2)) return `${label} must be alphanumeric (letters and digits only)`;
7191
+ if (rule.pattern && !rule.pattern.test(v2)) return rule.hint;
7179
7192
  if (rule.minLen && v2.length < rule.minLen) return rule.hint;
7180
7193
  if (rule.maxLen && v2.length > rule.maxLen) return rule.hint;
7181
7194
  return null;
@@ -7269,7 +7282,7 @@ var SmartComplySDK = (() => {
7269
7282
  input.id = inputId;
7270
7283
  input.setAttribute("aria-labelledby", labelId);
7271
7284
  input.setAttribute("aria-describedby", hintId);
7272
- input.placeholder = `Enter ${field.label}`;
7285
+ input.placeholder = rule?.placeholder || `Enter ${field.label}`;
7273
7286
  input.maxLength = rule?.maxLen ? rule.maxLen + 2 : 50;
7274
7287
  if (rule?.numeric) input.inputMode = "numeric";
7275
7288
  input.style.cssText = `
@@ -7369,7 +7382,7 @@ var SmartComplySDK = (() => {
7369
7382
  }
7370
7383
  this.showStep("confirm_identity");
7371
7384
  } else {
7372
- errorEl.textContent = this.verificationResult.message || "We could not verify your details. Please check and try again.";
7385
+ errorEl.textContent = _sanitizeUserMessage(this.verificationResult.message) || "We could not verify your details. Please check and try again.";
7373
7386
  errorEl.style.display = "block";
7374
7387
  submitBtn.textContent = "Verify & Continue";
7375
7388
  submitBtn.disabled = false;
@@ -7380,11 +7393,11 @@ var SmartComplySDK = (() => {
7380
7393
  if (Array.isArray(fieldErrors) && fieldErrors.length > 0) {
7381
7394
  const firstMsg = fieldErrors[0]?.message || fieldErrors[0]?.detail || "";
7382
7395
  errorEl.textContent = _sanitizeUserMessage(firstMsg) || "Please check your details and try again.";
7383
- } else if (err.code === "VERIFICATION_FAILED") {
7384
- errorEl.textContent = "We could not verify those details. Please check your ID number and try again.";
7385
- } else if (err.code === "CHANNEL_NOT_CONFIGURED") {
7396
+ } else if (err.errorCode === "VERIFICATION_FAILED") {
7397
+ errorEl.textContent = _sanitizeUserMessage(err.message) || "We could not verify those details. Please check your ID number and try again.";
7398
+ } else if (err.errorCode === "CHANNEL_NOT_CONFIGURED") {
7386
7399
  errorEl.textContent = "This ID type is not currently available. Please choose a different option.";
7387
- } else if (err.code === "INSUFFICIENT_BALANCE") {
7400
+ } else if (err.errorCode === "INSUFFICIENT_BALANCE") {
7388
7401
  errorEl.textContent = "Verification is temporarily unavailable. Please contact support.";
7389
7402
  } else {
7390
7403
  errorEl.textContent = _sanitizeUserMessage(err.message) || "Something went wrong. Please try again.";
@@ -7615,7 +7628,7 @@ var SmartComplySDK = (() => {
7615
7628
  return;
7616
7629
  }
7617
7630
  const entryId = this.livenessEntryId;
7618
- const MAX_POLLS = 10;
7631
+ const MAX_POLLS = 45;
7619
7632
  let polls = 0;
7620
7633
  const poll = async () => {
7621
7634
  if (this.isDestroyed) return;
@@ -7650,6 +7663,10 @@ var SmartComplySDK = (() => {
7650
7663
  this.showStep("document_confirm");
7651
7664
  return;
7652
7665
  }
7666
+ if (polls === 10 && !this.isDestroyed) {
7667
+ statusEl.textContent = "Still reading your document…";
7668
+ subEl.textContent = "Documents with a back side can take a little longer. Please keep waiting.";
7669
+ }
7653
7670
  if (polls < MAX_POLLS && !this.isDestroyed) {
7654
7671
  this._ocrPollTimer = setTimeout(poll, 2e3);
7655
7672
  } else if (!this.isDestroyed) {
@@ -8015,9 +8032,10 @@ var SmartComplySDK = (() => {
8015
8032
  const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
8016
8033
  const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
8017
8034
  const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
8018
- const errorIcon = isCameraErr ? "📷" : isNetworkErr ? "📡" : isAuthErr ? "⏱" : isDocQuality ? "🔍" : isExpiredDoc ? "📅" : isFaceMatch ? "🤳" : "✕";
8019
- 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";
8020
- 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.";
8035
+ const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
8036
+ const errorIcon = isCameraErr ? "📷" : isNetworkErr ? "📡" : isAuthErr ? "⏱" : isDocQuality ? "🔍" : isExpiredDoc ? "📅" : isFaceMatch ? "🤳" : isBillingErr ? "⚠️" : "✕";
8037
+ const errorTitle = isCameraErr ? "Camera Access Required" : isNetworkErr ? "Connection Problem" : isAuthErr ? "Session Expired" : isDocQuality ? "Document Photo Unclear" : isExpiredDoc ? "Document Expired" : isFaceMatch ? "Face Match Failed" : isBillingErr ? "Verification Unavailable" : "Verification Unsuccessful";
8038
+ 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." : isBillingErr ? "Verification is temporarily unavailable. Please contact support." : _sanitizeUserMessage(message) || "We were unable to complete your verification. Please try again.";
8021
8039
  const iconWrap = document.createElement("div");
8022
8040
  iconWrap.style.cssText = `
8023
8041
  width:72px;height:72px;border-radius:50%;