smartcomply-web-sdk 1.0.90 → 1.0.91
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/camera/ActionDetector.d.ts +2 -1
- package/dist/camera/ActionDetector.d.ts.map +1 -1
- package/dist/camera/ActionDetector.js +21 -5
- package/dist/camera/ActionDetector.js.map +1 -1
- package/dist/client/Smartcomply.d.ts +2 -2
- package/dist/client/Smartcomply.js +2 -2
- package/dist/esm/camera/ActionDetector.d.ts +2 -1
- package/dist/esm/camera/ActionDetector.d.ts.map +1 -1
- package/dist/esm/camera/ActionDetector.js +21 -5
- package/dist/esm/camera/ActionDetector.js.map +1 -1
- package/dist/esm/client/Smartcomply.d.ts +2 -2
- package/dist/esm/client/Smartcomply.js +2 -2
- package/dist/esm/flow/SmartComplyFlow.d.ts +10 -0
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +92 -32
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/flow/spinner.js +14 -14
- package/dist/esm/modules/liveness/LivenessUI.js +83 -83
- package/dist/flow/SmartComplyFlow.d.ts +10 -0
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +93 -32
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/flow/spinner.js +14 -14
- package/dist/modules/liveness/LivenessUI.js +83 -83
- package/dist/smartcomply.browser.js +46 -15
- package/dist/smartcomply.browser.js.map +2 -2
- package/package.json +1 -1
- package/dist/esm/components/CameraView.d.ts +0 -59
- package/dist/esm/components/CameraView.d.ts.map +0 -1
- package/dist/esm/components/CameraView.js +0 -238
- package/dist/esm/components/CameraView.js.map +0 -1
- package/dist/esm/components/DocumentUpload.d.ts +0 -21
- package/dist/esm/components/DocumentUpload.d.ts.map +0 -1
- package/dist/esm/components/DocumentUpload.js +0 -216
- package/dist/esm/components/DocumentUpload.js.map +0 -1
|
@@ -6,30 +6,30 @@ function injectLivenessKeyframes() {
|
|
|
6
6
|
return;
|
|
7
7
|
const s = document.createElement("style");
|
|
8
8
|
s.id = "sc-liveness-kf";
|
|
9
|
-
s.textContent = `
|
|
10
|
-
@keyframes sc-pop-in {
|
|
11
|
-
0% { transform:scale(0.7); opacity:0; }
|
|
12
|
-
60% { transform:scale(1.05); }
|
|
13
|
-
100% { transform:scale(1); opacity:1; }
|
|
14
|
-
}
|
|
15
|
-
@keyframes sc-scan-dot {
|
|
16
|
-
0%,100% { opacity:0.3; transform:scale(0.85); }
|
|
17
|
-
50% { opacity:1; transform:scale(1); }
|
|
18
|
-
}
|
|
19
|
-
@keyframes sc-scanline-sweep {
|
|
20
|
-
0% { transform:translateY(-4%); opacity:0; }
|
|
21
|
-
8% { opacity:0.9; }
|
|
22
|
-
92% { opacity:0.9; }
|
|
23
|
-
100% { transform:translateY(104%); opacity:0; }
|
|
24
|
-
}
|
|
25
|
-
@keyframes sc-corner-pulse {
|
|
26
|
-
0%,100% { opacity:0.55; }
|
|
27
|
-
50% { opacity:1; }
|
|
28
|
-
}
|
|
29
|
-
@keyframes sc-oval-breathe {
|
|
30
|
-
0%,100% { opacity:0.5; }
|
|
31
|
-
50% { opacity:0.85; }
|
|
32
|
-
}
|
|
9
|
+
s.textContent = `
|
|
10
|
+
@keyframes sc-pop-in {
|
|
11
|
+
0% { transform:scale(0.7); opacity:0; }
|
|
12
|
+
60% { transform:scale(1.05); }
|
|
13
|
+
100% { transform:scale(1); opacity:1; }
|
|
14
|
+
}
|
|
15
|
+
@keyframes sc-scan-dot {
|
|
16
|
+
0%,100% { opacity:0.3; transform:scale(0.85); }
|
|
17
|
+
50% { opacity:1; transform:scale(1); }
|
|
18
|
+
}
|
|
19
|
+
@keyframes sc-scanline-sweep {
|
|
20
|
+
0% { transform:translateY(-4%); opacity:0; }
|
|
21
|
+
8% { opacity:0.9; }
|
|
22
|
+
92% { opacity:0.9; }
|
|
23
|
+
100% { transform:translateY(104%); opacity:0; }
|
|
24
|
+
}
|
|
25
|
+
@keyframes sc-corner-pulse {
|
|
26
|
+
0%,100% { opacity:0.55; }
|
|
27
|
+
50% { opacity:1; }
|
|
28
|
+
}
|
|
29
|
+
@keyframes sc-oval-breathe {
|
|
30
|
+
0%,100% { opacity:0.5; }
|
|
31
|
+
50% { opacity:0.85; }
|
|
32
|
+
}
|
|
33
33
|
`;
|
|
34
34
|
document.head.appendChild(s);
|
|
35
35
|
}
|
|
@@ -59,38 +59,38 @@ class LivenessUI {
|
|
|
59
59
|
mount(container, challenge) {
|
|
60
60
|
injectLivenessKeyframes();
|
|
61
61
|
this.root = document.createElement("div");
|
|
62
|
-
this.root.style.cssText = `
|
|
63
|
-
position:relative;width:100%;max-width:420px;margin:0 auto;
|
|
64
|
-
background:#0a0a0a;border-radius:16px;overflow:hidden;
|
|
65
|
-
font-family:system-ui,-apple-system,sans-serif;
|
|
66
|
-
box-shadow:0 8px 32px rgba(0,0,0,0.4);
|
|
67
|
-
aspect-ratio:3/4;
|
|
62
|
+
this.root.style.cssText = `
|
|
63
|
+
position:relative;width:100%;max-width:420px;margin:0 auto;
|
|
64
|
+
background:#0a0a0a;border-radius:16px;overflow:hidden;
|
|
65
|
+
font-family:system-ui,-apple-system,sans-serif;
|
|
66
|
+
box-shadow:0 8px 32px rgba(0,0,0,0.4);
|
|
67
|
+
aspect-ratio:3/4;
|
|
68
68
|
`;
|
|
69
69
|
this.videoEl = document.createElement("video");
|
|
70
70
|
this.videoEl.autoplay = true;
|
|
71
71
|
this.videoEl.playsInline = true;
|
|
72
72
|
this.videoEl.muted = true;
|
|
73
73
|
this.videoEl.setAttribute("aria-label", "Live camera preview for face verification");
|
|
74
|
-
this.videoEl.style.cssText = `
|
|
75
|
-
width:100%;height:100%;display:block;
|
|
76
|
-
filter:brightness(1.03) contrast(1.03);
|
|
77
|
-
transform:scaleX(-1);object-fit:cover;
|
|
74
|
+
this.videoEl.style.cssText = `
|
|
75
|
+
width:100%;height:100%;display:block;
|
|
76
|
+
filter:brightness(1.03) contrast(1.03);
|
|
77
|
+
transform:scaleX(-1);object-fit:cover;
|
|
78
78
|
`;
|
|
79
79
|
// Dim everything outside the oval so the guide reads as a scan aperture
|
|
80
80
|
// rather than a shape floating over the raw feed.
|
|
81
81
|
const vignette = document.createElement("div");
|
|
82
|
-
vignette.style.cssText = `
|
|
83
|
-
position:absolute;inset:0;pointer-events:none;
|
|
84
|
-
background:radial-gradient(ellipse 34% 44% at 50% 47%, transparent 55%, rgba(0,0,0,0.55) 100%);
|
|
82
|
+
vignette.style.cssText = `
|
|
83
|
+
position:absolute;inset:0;pointer-events:none;
|
|
84
|
+
background:radial-gradient(ellipse 34% 44% at 50% 47%, transparent 55%, rgba(0,0,0,0.55) 100%);
|
|
85
85
|
`;
|
|
86
86
|
// Oval guide — centred
|
|
87
87
|
const guideWrap = document.createElement("div");
|
|
88
|
-
guideWrap.style.cssText = `
|
|
89
|
-
position:absolute;top:50%;left:50%;
|
|
90
|
-
transform:translate(-50%,-52%);
|
|
91
|
-
width:64%;aspect-ratio:140/180;pointer-events:none;
|
|
92
|
-
filter:drop-shadow(0 0 18px rgba(59,130,246,0.35));
|
|
93
|
-
transition:filter 0.3s ease;
|
|
88
|
+
guideWrap.style.cssText = `
|
|
89
|
+
position:absolute;top:50%;left:50%;
|
|
90
|
+
transform:translate(-50%,-52%);
|
|
91
|
+
width:64%;aspect-ratio:140/180;pointer-events:none;
|
|
92
|
+
filter:drop-shadow(0 0 18px rgba(59,130,246,0.35));
|
|
93
|
+
transition:filter 0.3s ease;
|
|
94
94
|
`;
|
|
95
95
|
this.guideWrapEl = guideWrap;
|
|
96
96
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
@@ -179,49 +179,49 @@ class LivenessUI {
|
|
|
179
179
|
// Scan-line sweep — clipped to the oval, gives a visible "actively
|
|
180
180
|
// scanning" motion cue instead of a static outline.
|
|
181
181
|
const scanlineClipWrap = document.createElement("div");
|
|
182
|
-
scanlineClipWrap.style.cssText = `
|
|
183
|
-
position:absolute;inset:0;
|
|
184
|
-
clip-path:ellipse(45.7% 47.8% at 50% 50%);
|
|
185
|
-
overflow:hidden;
|
|
182
|
+
scanlineClipWrap.style.cssText = `
|
|
183
|
+
position:absolute;inset:0;
|
|
184
|
+
clip-path:ellipse(45.7% 47.8% at 50% 50%);
|
|
185
|
+
overflow:hidden;
|
|
186
186
|
`;
|
|
187
187
|
const scanline = document.createElement("div");
|
|
188
|
-
scanline.style.cssText = `
|
|
189
|
-
position:absolute;left:0;right:0;height:26%;top:0;
|
|
190
|
-
background:linear-gradient(180deg,transparent 0%,rgba(59,130,246,0.32) 45%,rgba(147,197,253,0.55) 50%,rgba(59,130,246,0.32) 55%,transparent 100%);
|
|
191
|
-
animation:sc-scanline-sweep 2.6s ease-in-out infinite;
|
|
188
|
+
scanline.style.cssText = `
|
|
189
|
+
position:absolute;left:0;right:0;height:26%;top:0;
|
|
190
|
+
background:linear-gradient(180deg,transparent 0%,rgba(59,130,246,0.32) 45%,rgba(147,197,253,0.55) 50%,rgba(59,130,246,0.32) 55%,transparent 100%);
|
|
191
|
+
animation:sc-scanline-sweep 2.6s ease-in-out infinite;
|
|
192
192
|
`;
|
|
193
193
|
this.scanlineEl = scanline;
|
|
194
194
|
scanlineClipWrap.appendChild(scanline);
|
|
195
195
|
guideWrap.appendChild(scanlineClipWrap);
|
|
196
196
|
// Instruction — top, gradient scrim
|
|
197
197
|
const camTop = document.createElement("div");
|
|
198
|
-
camTop.style.cssText = `
|
|
199
|
-
position:absolute;top:0;left:0;right:0;
|
|
200
|
-
background:linear-gradient(180deg,rgba(0,0,0,0.8) 0%,transparent 100%);
|
|
201
|
-
padding:14px 16px 32px;
|
|
198
|
+
camTop.style.cssText = `
|
|
199
|
+
position:absolute;top:0;left:0;right:0;
|
|
200
|
+
background:linear-gradient(180deg,rgba(0,0,0,0.8) 0%,transparent 100%);
|
|
201
|
+
padding:14px 16px 32px;
|
|
202
202
|
`;
|
|
203
203
|
this.instructionEl = document.createElement("div");
|
|
204
204
|
this.instructionEl.setAttribute("aria-live", "polite");
|
|
205
205
|
this.instructionEl.setAttribute("role", "status");
|
|
206
|
-
this.instructionEl.style.cssText = `
|
|
207
|
-
color:#fff;font-size:14px;font-weight:600;text-align:center;line-height:1.4;
|
|
206
|
+
this.instructionEl.style.cssText = `
|
|
207
|
+
color:#fff;font-size:14px;font-weight:600;text-align:center;line-height:1.4;
|
|
208
208
|
`;
|
|
209
209
|
this.instructionEl.textContent = challenge.instruction;
|
|
210
210
|
camTop.appendChild(this.instructionEl);
|
|
211
211
|
// Scan progress dots — bottom
|
|
212
212
|
const camBottom = document.createElement("div");
|
|
213
|
-
camBottom.style.cssText = `
|
|
214
|
-
position:absolute;bottom:0;left:0;right:0;
|
|
215
|
-
background:linear-gradient(0deg,rgba(0,0,0,0.7) 0%,transparent 100%);
|
|
216
|
-
padding:28px 10px 18px;display:flex;justify-content:center;
|
|
213
|
+
camBottom.style.cssText = `
|
|
214
|
+
position:absolute;bottom:0;left:0;right:0;
|
|
215
|
+
background:linear-gradient(0deg,rgba(0,0,0,0.7) 0%,transparent 100%);
|
|
216
|
+
padding:28px 10px 18px;display:flex;justify-content:center;
|
|
217
217
|
`;
|
|
218
218
|
this.dotsEl = document.createElement("div");
|
|
219
219
|
this.dotsEl.style.cssText = "display:flex;gap:6px;";
|
|
220
220
|
for (let i = 0; i < 3; i++) {
|
|
221
221
|
const dot = document.createElement("div");
|
|
222
|
-
dot.style.cssText = `
|
|
223
|
-
width:6px;height:6px;border-radius:50%;background:${this.colors.primary};
|
|
224
|
-
animation:sc-scan-dot 1.1s ease-in-out ${i * 0.18}s infinite;
|
|
222
|
+
dot.style.cssText = `
|
|
223
|
+
width:6px;height:6px;border-radius:50%;background:${this.colors.primary};
|
|
224
|
+
animation:sc-scan-dot 1.1s ease-in-out ${i * 0.18}s infinite;
|
|
225
225
|
`;
|
|
226
226
|
this.dotsEl.appendChild(dot);
|
|
227
227
|
}
|
|
@@ -233,10 +233,10 @@ class LivenessUI {
|
|
|
233
233
|
this.root.appendChild(camBottom);
|
|
234
234
|
// Result overlay
|
|
235
235
|
this.overlayEl = document.createElement("div");
|
|
236
|
-
this.overlayEl.style.cssText = `
|
|
237
|
-
position:absolute;inset:0;display:none;align-items:center;
|
|
238
|
-
justify-content:center;z-index:10;
|
|
239
|
-
background:rgba(0,0,0,0.88);backdrop-filter:blur(8px);
|
|
236
|
+
this.overlayEl.style.cssText = `
|
|
237
|
+
position:absolute;inset:0;display:none;align-items:center;
|
|
238
|
+
justify-content:center;z-index:10;
|
|
239
|
+
background:rgba(0,0,0,0.88);backdrop-filter:blur(8px);
|
|
240
240
|
`;
|
|
241
241
|
this.root.appendChild(this.overlayEl);
|
|
242
242
|
container.appendChild(this.root);
|
|
@@ -297,15 +297,15 @@ class LivenessUI {
|
|
|
297
297
|
}
|
|
298
298
|
const ok = status === "processing";
|
|
299
299
|
this.overlayEl.style.display = "flex";
|
|
300
|
-
this.overlayEl.innerHTML = `
|
|
301
|
-
<div style="text-align:center;color:#fff;animation:sc-pop-in 0.35s ease;padding:24px;">
|
|
302
|
-
<div style="width:72px;height:72px;border-radius:50%;margin:0 auto 16px;display:flex;align-items:center;justify-content:center;font-size:32px;
|
|
303
|
-
background:${ok ? "rgba(34,197,94,0.2)" : "rgba(239,68,68,0.2)"};
|
|
304
|
-
border:3px solid ${ok ? this.colors.success : this.colors.error};">
|
|
305
|
-
${ok ? "✓" : "✕"}
|
|
306
|
-
</div>
|
|
307
|
-
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">${ok ? "Verification submitted" : "Submission failed"}</div>
|
|
308
|
-
<div style="font-size:12px;opacity:0.6;line-height:1.6;">${ok ? "Hold on while we wrap up…" : "Something went wrong. Please try again."}</div>
|
|
300
|
+
this.overlayEl.innerHTML = `
|
|
301
|
+
<div style="text-align:center;color:#fff;animation:sc-pop-in 0.35s ease;padding:24px;">
|
|
302
|
+
<div style="width:72px;height:72px;border-radius:50%;margin:0 auto 16px;display:flex;align-items:center;justify-content:center;font-size:32px;
|
|
303
|
+
background:${ok ? "rgba(34,197,94,0.2)" : "rgba(239,68,68,0.2)"};
|
|
304
|
+
border:3px solid ${ok ? this.colors.success : this.colors.error};">
|
|
305
|
+
${ok ? "✓" : "✕"}
|
|
306
|
+
</div>
|
|
307
|
+
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">${ok ? "Verification submitted" : "Submission failed"}</div>
|
|
308
|
+
<div style="font-size:12px;opacity:0.6;line-height:1.6;">${ok ? "Hold on while we wrap up…" : "Something went wrong. Please try again."}</div>
|
|
309
309
|
</div>`;
|
|
310
310
|
setTimeout(resolve, 1800);
|
|
311
311
|
});
|
|
@@ -317,12 +317,12 @@ class LivenessUI {
|
|
|
317
317
|
return;
|
|
318
318
|
}
|
|
319
319
|
this.overlayEl.style.display = "flex";
|
|
320
|
-
this.overlayEl.innerHTML = `
|
|
321
|
-
<div style="text-align:center;color:#fff;animation:sc-pop-in 0.35s ease;padding:24px;">
|
|
322
|
-
<div style="width:72px;height:72px;border-radius:50%;margin:0 auto 14px;display:flex;align-items:center;justify-content:center;font-size:32px;background:rgba(251,191,36,0.15);border:3px solid ${this.colors.warning};">⏱</div>
|
|
323
|
-
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">Time's Up</div>
|
|
324
|
-
<div style="font-size:12px;opacity:0.6;margin-bottom:18px;">We couldn't confirm liveness in time. Please try again.</div>
|
|
325
|
-
<button id="sc-retry-btn" style="padding:11px 28px;border-radius:10px;background:${this.colors.primary};color:#fff;border:none;cursor:pointer;font-size:14px;font-weight:600;width:100%;max-width:180px;">Try Again</button>
|
|
320
|
+
this.overlayEl.innerHTML = `
|
|
321
|
+
<div style="text-align:center;color:#fff;animation:sc-pop-in 0.35s ease;padding:24px;">
|
|
322
|
+
<div style="width:72px;height:72px;border-radius:50%;margin:0 auto 14px;display:flex;align-items:center;justify-content:center;font-size:32px;background:rgba(251,191,36,0.15);border:3px solid ${this.colors.warning};">⏱</div>
|
|
323
|
+
<div style="font-size:17px;font-weight:700;margin-bottom:8px;">Time's Up</div>
|
|
324
|
+
<div style="font-size:12px;opacity:0.6;margin-bottom:18px;">We couldn't confirm liveness in time. Please try again.</div>
|
|
325
|
+
<button id="sc-retry-btn" style="padding:11px 28px;border-radius:10px;background:${this.colors.primary};color:#fff;border:none;cursor:pointer;font-size:14px;font-weight:600;width:100%;max-width:180px;">Try Again</button>
|
|
326
326
|
</div>`;
|
|
327
327
|
const btn = this.overlayEl.querySelector("#sc-retry-btn");
|
|
328
328
|
if (btn) {
|
|
@@ -4632,7 +4632,10 @@ var SmartComplySDK = (() => {
|
|
|
4632
4632
|
this.blinkHoldCount = 0;
|
|
4633
4633
|
this.blinkDetected = false;
|
|
4634
4634
|
this.motionDetected = false;
|
|
4635
|
-
|
|
4635
|
+
// Running min/max yaw seen so far in the scan, not a single fixed
|
|
4636
|
+
// first-frame baseline (see check() below for why).
|
|
4637
|
+
this.minYaw = null;
|
|
4638
|
+
this.maxYaw = null;
|
|
4636
4639
|
}
|
|
4637
4640
|
check(result) {
|
|
4638
4641
|
if (!result.faceDetected) {
|
|
@@ -4652,9 +4655,9 @@ var SmartComplySDK = (() => {
|
|
|
4652
4655
|
}
|
|
4653
4656
|
if (!this.motionDetected) {
|
|
4654
4657
|
const yaw = this.getHeadYaw(result.transformMatrix);
|
|
4655
|
-
if (this.
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
+
if (this.minYaw === null || yaw < this.minYaw) this.minYaw = yaw;
|
|
4659
|
+
if (this.maxYaw === null || yaw > this.maxYaw) this.maxYaw = yaw;
|
|
4660
|
+
if (this.maxYaw - this.minYaw >= YAW_MOTION_THRESHOLD_DEG) {
|
|
4658
4661
|
this.motionDetected = true;
|
|
4659
4662
|
}
|
|
4660
4663
|
}
|
|
@@ -5779,7 +5782,7 @@ var SmartComplySDK = (() => {
|
|
|
5779
5782
|
* the request, so the resume_token has to be the one in use throughout.
|
|
5780
5783
|
*
|
|
5781
5784
|
* No expires_at is set here — the resume_token's own issuance response
|
|
5782
|
-
* only carries resume_expires_at (a
|
|
5785
|
+
* only carries resume_expires_at (a 24h invite-link window), not the
|
|
5783
5786
|
* session's own runtime expiry the way createSession() returns one. The
|
|
5784
5787
|
* backend still enforces the real expiry server-side regardless; this
|
|
5785
5788
|
* only means the client-side "session about to expire" warning timer
|
|
@@ -5834,7 +5837,7 @@ var SmartComplySDK = (() => {
|
|
|
5834
5837
|
* CDN tag they installed with (e.g. @1, which floats to the latest
|
|
5835
5838
|
* matching release) actually resolved to the version they expect.
|
|
5836
5839
|
*/
|
|
5837
|
-
_SmartComply.VERSION = "1.0.
|
|
5840
|
+
_SmartComply.VERSION = "1.0.91";
|
|
5838
5841
|
var SmartComply = _SmartComply;
|
|
5839
5842
|
|
|
5840
5843
|
// src/flow/theme.ts
|
|
@@ -7358,6 +7361,9 @@ var SmartComplySDK = (() => {
|
|
|
7358
7361
|
function isUploadFieldType(type) {
|
|
7359
7362
|
return type === "upload" || type === "image" || type === "file";
|
|
7360
7363
|
}
|
|
7364
|
+
function isResumeLinkErrorMessage(message) {
|
|
7365
|
+
return /resume link|resubmit link/i.test(message);
|
|
7366
|
+
}
|
|
7361
7367
|
function resolveIdTypeInfo(typeName, isDocumentFlow) {
|
|
7362
7368
|
const u2 = typeName.toUpperCase();
|
|
7363
7369
|
const advanced = u2.includes("ADVANCED");
|
|
@@ -7835,7 +7841,12 @@ var SmartComplySDK = (() => {
|
|
|
7835
7841
|
this.brandLogoImg.style.display = "block";
|
|
7836
7842
|
}
|
|
7837
7843
|
if (this.isDestroyed) return;
|
|
7838
|
-
|
|
7844
|
+
if (this.options.resumeToken) {
|
|
7845
|
+
const resumeStep = await this._resolveResumeStep();
|
|
7846
|
+
if (resumeStep !== null) this.showStep(resumeStep);
|
|
7847
|
+
} else {
|
|
7848
|
+
this.showStep("welcome");
|
|
7849
|
+
}
|
|
7839
7850
|
} catch (err) {
|
|
7840
7851
|
this.showErrorStep(err.message || "Failed to initialize. Please try again.");
|
|
7841
7852
|
}
|
|
@@ -7845,9 +7856,23 @@ var SmartComplySDK = (() => {
|
|
|
7845
7856
|
// same state renderCountrySelect/renderIdTypeSelect's own click handlers
|
|
7846
7857
|
// set) so document_capture/liveness render correctly instead of assuming
|
|
7847
7858
|
// state that was never set. Falls back to "welcome" on anything that
|
|
7848
|
-
// doesn't resolve cleanly
|
|
7849
|
-
//
|
|
7850
|
-
//
|
|
7859
|
+
// doesn't resolve cleanly for an UNCERTAIN reason (network error, timeout)
|
|
7860
|
+
// — this is a UX improvement, not a requirement: the backend's own
|
|
7861
|
+
// resume-match at create/ time still works correctly even if the widget
|
|
7862
|
+
// can't figure out which screen to pre-select.
|
|
7863
|
+
//
|
|
7864
|
+
// Returns null instead when the lookup fails for a CERTAIN, known reason
|
|
7865
|
+
// — the resume link itself is provably expired or already used
|
|
7866
|
+
// (RESUME_TARGET_UNAVAILABLE) or was never a resume link at all
|
|
7867
|
+
// (NOT_A_RESUME_TOKEN). Unlike a transient failure, there is no "try the
|
|
7868
|
+
// real request and it'll still work" fallback for these: create/'s own
|
|
7869
|
+
// resume-match reads the exact same resume_target_entry binding this
|
|
7870
|
+
// lookup just found missing/invalid, so falling through to a generic
|
|
7871
|
+
// welcome screen would silently walk the customer into a brand-new,
|
|
7872
|
+
// unrelated verification attempt with no explanation — worse than telling
|
|
7873
|
+
// them plainly that their link no longer works. The caller shows this
|
|
7874
|
+
// error itself (via showErrorStep, called here directly) rather than
|
|
7875
|
+
// rendering a step.
|
|
7851
7876
|
async _resolveResumeStep() {
|
|
7852
7877
|
try {
|
|
7853
7878
|
const info = await this.sdk.getResumeInfo();
|
|
@@ -7886,7 +7911,12 @@ var SmartComplySDK = (() => {
|
|
|
7886
7911
|
}
|
|
7887
7912
|
}
|
|
7888
7913
|
return "welcome";
|
|
7889
|
-
} catch {
|
|
7914
|
+
} catch (err) {
|
|
7915
|
+
const code = err instanceof SDKError ? err.errorCode : void 0;
|
|
7916
|
+
if (code === "RESUME_TARGET_UNAVAILABLE" || code === "NOT_A_RESUME_TOKEN") {
|
|
7917
|
+
this.showErrorStep("This resume link has expired or is no longer valid.");
|
|
7918
|
+
return null;
|
|
7919
|
+
}
|
|
7890
7920
|
return "welcome";
|
|
7891
7921
|
}
|
|
7892
7922
|
}
|
|
@@ -9209,14 +9239,15 @@ var SmartComplySDK = (() => {
|
|
|
9209
9239
|
`;
|
|
9210
9240
|
const isCameraErr = /camera|permission|notallowed|notfound/i.test(message);
|
|
9211
9241
|
const isNetworkErr = /network|timeout|connection|fetch/i.test(message);
|
|
9212
|
-
const
|
|
9242
|
+
const isResumeLinkErr = isResumeLinkErrorMessage(message);
|
|
9243
|
+
const isAuthErr = !isResumeLinkErr && /session.*expired|token.*expired|authentication/i.test(message);
|
|
9213
9244
|
const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
|
|
9214
9245
|
const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
|
|
9215
9246
|
const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
|
|
9216
9247
|
const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
|
|
9217
|
-
const errorIconName = isCameraErr ? "camera" : isNetworkErr ? "network" : isAuthErr ? "clock" : isDocQuality ? "search" : isExpiredDoc ? "calendar" : isFaceMatch ? "face" : isBillingErr ? "warning" : "close";
|
|
9218
|
-
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";
|
|
9219
|
-
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.";
|
|
9248
|
+
const errorIconName = isCameraErr ? "camera" : isNetworkErr ? "network" : isResumeLinkErr ? "clock" : isAuthErr ? "clock" : isDocQuality ? "search" : isExpiredDoc ? "calendar" : isFaceMatch ? "face" : isBillingErr ? "warning" : "close";
|
|
9249
|
+
const errorTitle = isCameraErr ? "Camera Access Required" : isNetworkErr ? "Connection Problem" : isResumeLinkErr ? "Link No Longer Valid" : isAuthErr ? "Session Expired" : isDocQuality ? "Document Photo Unclear" : isExpiredDoc ? "Document Expired" : isFaceMatch ? "Face Match Failed" : isBillingErr ? "Verification Unavailable" : "Verification Unsuccessful";
|
|
9250
|
+
const userMessage = isCameraErr ? "Please allow camera access in your browser settings and try again." : isNetworkErr ? "Check your internet connection and try again." : isResumeLinkErr ? "This link has expired or has already been used. Please contact us to request a new one." : 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.";
|
|
9220
9251
|
const iconWrap = document.createElement("div");
|
|
9221
9252
|
iconWrap.style.cssText = `
|
|
9222
9253
|
width:72px;height:72px;border-radius:50%;
|