smartcomply-web-sdk 1.0.3
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/LICENSE +15 -0
- package/README.md +482 -0
- package/dist/camera/ActionDetector.d.ts +26 -0
- package/dist/camera/ActionDetector.d.ts.map +1 -0
- package/dist/camera/ActionDetector.js +114 -0
- package/dist/camera/ActionDetector.js.map +1 -0
- package/dist/camera/CameraManager.d.ts +7 -0
- package/dist/camera/CameraManager.d.ts.map +1 -0
- package/dist/camera/CameraManager.js +31 -0
- package/dist/camera/CameraManager.js.map +1 -0
- package/dist/camera/FaceDetector.d.ts +12 -0
- package/dist/camera/FaceDetector.d.ts.map +1 -0
- package/dist/camera/FaceDetector.js +55 -0
- package/dist/camera/FaceDetector.js.map +1 -0
- package/dist/camera/Permission.d.ts +42 -0
- package/dist/camera/Permission.d.ts.map +1 -0
- package/dist/camera/Permission.js +107 -0
- package/dist/camera/Permission.js.map +1 -0
- package/dist/camera/VideoRecorder.d.ts +12 -0
- package/dist/camera/VideoRecorder.d.ts.map +1 -0
- package/dist/camera/VideoRecorder.js +51 -0
- package/dist/camera/VideoRecorder.js.map +1 -0
- package/dist/client/Config.d.ts +9 -0
- package/dist/client/Config.d.ts.map +1 -0
- package/dist/client/Config.js +8 -0
- package/dist/client/Config.js.map +1 -0
- package/dist/client/HttpClient.d.ts +44 -0
- package/dist/client/HttpClient.d.ts.map +1 -0
- package/dist/client/HttpClient.js +149 -0
- package/dist/client/HttpClient.js.map +1 -0
- package/dist/client/Smartcomply.d.ts +50 -0
- package/dist/client/Smartcomply.d.ts.map +1 -0
- package/dist/client/Smartcomply.js +77 -0
- package/dist/client/Smartcomply.js.map +1 -0
- package/dist/components/CameraView.d.ts +59 -0
- package/dist/components/CameraView.d.ts.map +1 -0
- package/dist/components/CameraView.js +242 -0
- package/dist/components/CameraView.js.map +1 -0
- package/dist/components/DocumentUpload.d.ts +21 -0
- package/dist/components/DocumentUpload.d.ts.map +1 -0
- package/dist/components/DocumentUpload.js +220 -0
- package/dist/components/DocumentUpload.js.map +1 -0
- package/dist/errors/AuthError.d.ts +8 -0
- package/dist/errors/AuthError.d.ts.map +1 -0
- package/dist/errors/AuthError.js +15 -0
- package/dist/errors/AuthError.js.map +1 -0
- package/dist/errors/NetworkError.d.ts +8 -0
- package/dist/errors/NetworkError.d.ts.map +1 -0
- package/dist/errors/NetworkError.js +15 -0
- package/dist/errors/NetworkError.js.map +1 -0
- package/dist/errors/SDKError.d.ts +7 -0
- package/dist/errors/SDKError.d.ts.map +1 -0
- package/dist/errors/SDKError.js +14 -0
- package/dist/errors/SDKError.js.map +1 -0
- package/dist/flow/DocumentCapture.d.ts +54 -0
- package/dist/flow/DocumentCapture.d.ts.map +1 -0
- package/dist/flow/DocumentCapture.js +801 -0
- package/dist/flow/DocumentCapture.js.map +1 -0
- package/dist/flow/SmartComplyFlow.d.ts +104 -0
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -0
- package/dist/flow/SmartComplyFlow.js +1726 -0
- package/dist/flow/SmartComplyFlow.js.map +1 -0
- package/dist/flow/theme.d.ts +30 -0
- package/dist/flow/theme.d.ts.map +1 -0
- package/dist/flow/theme.js +103 -0
- package/dist/flow/theme.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/liveness/LivenessUI.d.ts +46 -0
- package/dist/modules/liveness/LivenessUI.d.ts.map +1 -0
- package/dist/modules/liveness/LivenessUI.js +609 -0
- package/dist/modules/liveness/LivenessUI.js.map +1 -0
- package/dist/modules/liveness/index.d.ts +5 -0
- package/dist/modules/liveness/index.d.ts.map +1 -0
- package/dist/modules/liveness/index.js +10 -0
- package/dist/modules/liveness/index.js.map +1 -0
- package/dist/modules/liveness/liveness.d.ts +85 -0
- package/dist/modules/liveness/liveness.d.ts.map +1 -0
- package/dist/modules/liveness/liveness.js +425 -0
- package/dist/modules/liveness/liveness.js.map +1 -0
- package/dist/modules/liveness/uploader.d.ts +34 -0
- package/dist/modules/liveness/uploader.d.ts.map +1 -0
- package/dist/modules/liveness/uploader.js +106 -0
- package/dist/modules/liveness/uploader.js.map +1 -0
- package/dist/modules/onboarding/index.d.ts +2 -0
- package/dist/modules/onboarding/index.d.ts.map +1 -0
- package/dist/modules/onboarding/index.js +6 -0
- package/dist/modules/onboarding/index.js.map +1 -0
- package/dist/modules/onboarding/onboarding.d.ts +25 -0
- package/dist/modules/onboarding/onboarding.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.js +33 -0
- package/dist/modules/onboarding/onboarding.js.map +1 -0
- package/dist/smartcomply.browser.js +8147 -0
- package/dist/smartcomply.browser.js.map +7 -0
- package/dist/types/common.d.ts +27 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +3 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/liveness.d.ts +142 -0
- package/dist/types/liveness.d.ts.map +1 -0
- package/dist/types/liveness.js +3 -0
- package/dist/types/liveness.js.map +1 -0
- package/dist/types/onboarding.d.ts +53 -0
- package/dist/types/onboarding.d.ts.map +1 -0
- package/dist/types/onboarding.js +3 -0
- package/dist/types/onboarding.js.map +1 -0
- package/dist/types/quality.d.ts +23 -0
- package/dist/types/quality.d.ts.map +1 -0
- package/dist/types/quality.js +6 -0
- package/dist/types/quality.js.map +1 -0
- package/dist/utils/ImageQuality.d.ts +48 -0
- package/dist/utils/ImageQuality.d.ts.map +1 -0
- package/dist/utils/ImageQuality.js +186 -0
- package/dist/utils/ImageQuality.js.map +1 -0
- package/dist/utils/constants.d.ts +59 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +70 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/device.d.ts +14 -0
- package/dist/utils/device.d.ts.map +1 -0
- package/dist/utils/device.js +55 -0
- package/dist/utils/device.js.map +1 -0
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +67 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validator.d.ts +35 -0
- package/dist/utils/validator.d.ts.map +1 -0
- package/dist/utils/validator.js +116 -0
- package/dist/utils/validator.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SmartComply
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
# SmartComply Web SDK
|
|
2
|
+
|
|
3
|
+
Drop-in identity verification (KYC) widget for web applications. Embed one line of code and let your users verify their identity through facial liveness detection, government ID checks (BVN/NIN), and international document verification (passport, national ID, driver's license).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
SmartComply SDK is a **KYC-as-a-service** widget that businesses embed in their web apps to verify end-user identities. It handles the full verification flow — from collecting ID details to live face matching — and delivers results asynchronously via webhook.
|
|
8
|
+
|
|
9
|
+
### Target Audience
|
|
10
|
+
|
|
11
|
+
- **Fintechs, banks, and lending platforms** that need to onboard customers securely
|
|
12
|
+
- **Any business** required to perform Know Your Customer (KYC) checks before granting account access
|
|
13
|
+
|
|
14
|
+
### What It Does
|
|
15
|
+
|
|
16
|
+
The SDK verifies that a person is **real**, **alive**, and **who they claim to be**:
|
|
17
|
+
|
|
18
|
+
1. **Identity verification** — Validates the user's BVN/NIN against government databases, or extracts data from uploaded documents via OCR
|
|
19
|
+
2. **Liveness detection** — Camera-based challenge-response (blink, turn head, open mouth) to prove the person is physically present — not a photo or video replay
|
|
20
|
+
3. **Face matching** — Compares the user's live selfie against:
|
|
21
|
+
- The government-returned photo (BVN/NIN flow), or
|
|
22
|
+
- The face on their uploaded document (passport/license flow)
|
|
23
|
+
|
|
24
|
+
### Two Verification Flows
|
|
25
|
+
|
|
26
|
+
| Flow | How it works | ID Types |
|
|
27
|
+
|------|-------------|----------|
|
|
28
|
+
| **Data verification** | User enters an ID number → backend validates against government database → face match against government photo | BVN, NIN |
|
|
29
|
+
| **Document verification** | User uploads/captures a document photo → OCR extracts data + expiry check → face match against document photo | Passport, National ID, Driver's License |
|
|
30
|
+
|
|
31
|
+
### End-to-End Flow
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
35
|
+
│ YOUR APP │
|
|
36
|
+
│ ┌───────────────────────────────────────────────────────────────┐ │
|
|
37
|
+
│ │ SmartComply Widget (modal) │ │
|
|
38
|
+
│ │ │ │
|
|
39
|
+
│ │ Welcome → Country → ID Type → Enter ID / Upload Doc │ │
|
|
40
|
+
│ │ → Face Liveness (camera) → Done │ │
|
|
41
|
+
│ └───────────────────────────────────────────────────────────────┘ │
|
|
42
|
+
│ │ │
|
|
43
|
+
│ submit │ │
|
|
44
|
+
│ ▼ │
|
|
45
|
+
│ Adhere Backend │
|
|
46
|
+
│ │ │
|
|
47
|
+
│ ┌─────────┼──────────┐ │
|
|
48
|
+
│ ▼ ▼ ▼ │
|
|
49
|
+
│ Face Match OCR Gov DB Check │
|
|
50
|
+
│ (DeepFace) (document) (BVN/NIN) │
|
|
51
|
+
│ └─────────┼──────────┘ │
|
|
52
|
+
│ ▼ │
|
|
53
|
+
│ Webhook POST ──────────► YOUR SERVER │
|
|
54
|
+
│ (signed with HMAC) (result handler) │
|
|
55
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install smartcomply-web-sdk
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Option 1: Drop-in Widget (Recommended)
|
|
67
|
+
|
|
68
|
+
The fastest way to add KYC. Opens a complete, branded verification modal — handles everything automatically.
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<button id="verify-btn">Verify Identity</button>
|
|
72
|
+
|
|
73
|
+
<script type="module">
|
|
74
|
+
import { SmartComplyFlow } from "smartcomply-web-sdk";
|
|
75
|
+
|
|
76
|
+
document.getElementById("verify-btn").onclick = () => {
|
|
77
|
+
SmartComplyFlow.open({
|
|
78
|
+
apiKey: "pk_live_your_api_key",
|
|
79
|
+
clientId: "your-sdk-config-uuid",
|
|
80
|
+
environment: "production",
|
|
81
|
+
|
|
82
|
+
onComplete(result) {
|
|
83
|
+
// Verification submitted! Final result arrives via webhook.
|
|
84
|
+
console.log("Entry ID:", result.entryId);
|
|
85
|
+
console.log("Status:", result.status); // "processing"
|
|
86
|
+
// Update your UI — tell the user to wait for confirmation
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
onError(err) {
|
|
90
|
+
console.error("Verification failed:", err.message);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
onClose() {
|
|
94
|
+
// User closed the modal without completing
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
</script>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
That's it. The SDK will:
|
|
102
|
+
|
|
103
|
+
1. Create a secure session with the Adhere backend
|
|
104
|
+
2. Show a branded welcome screen (your brand name + theme from dashboard)
|
|
105
|
+
3. Let the user select their country and ID type
|
|
106
|
+
4. Collect their BVN/NIN number **or** capture their document photo
|
|
107
|
+
5. Run face liveness detection (camera + challenge actions)
|
|
108
|
+
6. Record a video and submit everything to the backend
|
|
109
|
+
7. Show a result screen and call your `onComplete` callback
|
|
110
|
+
|
|
111
|
+
### What the User Sees
|
|
112
|
+
|
|
113
|
+
| Step | Screen | Description |
|
|
114
|
+
|------|--------|-------------|
|
|
115
|
+
| 1 | Welcome | Your brand name, description, and what to expect |
|
|
116
|
+
| 2 | Country | Select country (auto-skipped if only one country configured) |
|
|
117
|
+
| 3 | ID Type | Choose from the channels you configured (e.g., BVN, NIN, Passport) |
|
|
118
|
+
| 4a | ID Input | Enter BVN/NIN number → instant backend verification against government DB |
|
|
119
|
+
| 4b | Document | Capture a photo of passport/ID card (camera or file upload) |
|
|
120
|
+
| 5 | Liveness | Camera opens — user completes challenge actions (blink, turn head, open mouth) |
|
|
121
|
+
| 6 | Done | "Verification Submitted" — user clicks Done, result arrives via webhook |
|
|
122
|
+
|
|
123
|
+
### `SmartComplyFlow.open()` Options
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
SmartComplyFlow.open({
|
|
127
|
+
// Required
|
|
128
|
+
apiKey: string, // Your API key from the Adhere dashboard
|
|
129
|
+
clientId: string, // UUID from your SDK Config (created in dashboard)
|
|
130
|
+
|
|
131
|
+
// Optional
|
|
132
|
+
environment: "production" | "sandbox", // Default: "sandbox"
|
|
133
|
+
timeout: number, // Request timeout in ms (default: 30000)
|
|
134
|
+
|
|
135
|
+
// Callbacks
|
|
136
|
+
onComplete: (result) => void, // Verification submitted successfully
|
|
137
|
+
onError: (error) => void, // Unrecoverable error
|
|
138
|
+
onClose: () => void, // User closed the modal
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `onComplete` Result
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
{
|
|
146
|
+
entryId: 42, // Use this to track the verification
|
|
147
|
+
status: "processing", // Always "processing" — final result via webhook
|
|
148
|
+
submittedAt: "2026-04-13T...", // ISO timestamp
|
|
149
|
+
verificationResult: { // Only for data verification (BVN/NIN)
|
|
150
|
+
status: "success",
|
|
151
|
+
code: "VERIFICATION_COMPLETE",
|
|
152
|
+
data: { first_name: "Amara", last_name: "Okafor", identity_check_id: 123, ... }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Option 2: Headless (Custom UI)
|
|
160
|
+
|
|
161
|
+
For full control over the user interface, use the SDK's API methods directly.
|
|
162
|
+
|
|
163
|
+
### Initialize
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { SmartComply } from "smartcomply-web-sdk";
|
|
167
|
+
|
|
168
|
+
const sdk = new SmartComply({
|
|
169
|
+
apiKey: "pk_live_your_api_key",
|
|
170
|
+
clientId: "your-sdk-config-uuid",
|
|
171
|
+
environment: "production",
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Create Session
|
|
176
|
+
|
|
177
|
+
Every verification flow starts with a session. Sessions last 30 minutes and are single-use (revoked after liveness submission).
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const session = await sdk.createSession();
|
|
181
|
+
// session.token — used internally for all subsequent API calls
|
|
182
|
+
// session.expires_at — ISO timestamp
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Load Configuration
|
|
186
|
+
|
|
187
|
+
Fetch your SDK config (brand name, theme, available ID types per country):
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const config = await sdk.initializeConfig();
|
|
191
|
+
|
|
192
|
+
console.log(config.brand_name); // "Your Company Name"
|
|
193
|
+
console.log(config.verification_type); // ["data_verification"]
|
|
194
|
+
|
|
195
|
+
// Channels are grouped by country with field definitions:
|
|
196
|
+
console.log(config.channels);
|
|
197
|
+
// {
|
|
198
|
+
// "nigeria": [
|
|
199
|
+
// { id: 8, name: "National Identity Number (NIN)", fields: [{ type: "input", label: "Identification Number" }] },
|
|
200
|
+
// { id: 2, name: "Bank Verification Number Advanced (BVN)", fields: [{ type: "input", label: "Bank Verification Number" }] }
|
|
201
|
+
// ]
|
|
202
|
+
// }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Use `config.channels` to build your own country/ID type selector. Each channel's `fields` array tells you what inputs to render.
|
|
206
|
+
|
|
207
|
+
### Verify Identity (Data Verification)
|
|
208
|
+
|
|
209
|
+
For BVN/NIN — validates against the government database:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const result = await sdk.onboarding.verify({
|
|
213
|
+
identity_type_id: 8, // Channel ID from config.channels
|
|
214
|
+
fields: { identification_number: "12345678901" }, // Field keys from channel's fields definition
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (result.status === "success") {
|
|
218
|
+
console.log(result.data.identity_check_id); // Save this — needed for face matching in liveness
|
|
219
|
+
console.log(result.data.first_name); // "Amara"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Liveness Check (with built-in camera UI)
|
|
224
|
+
|
|
225
|
+
Mount the SDK's liveness UI into any container element:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const container = document.getElementById("liveness-container");
|
|
229
|
+
|
|
230
|
+
const result = await sdk.liveness.startCheck(container, {
|
|
231
|
+
identifier: "12345678901", // The ID number entered by the user
|
|
232
|
+
identifier_type: "NIN",
|
|
233
|
+
country: "NG",
|
|
234
|
+
id_file: documentBlob, // Optional: document photo (for document flow)
|
|
235
|
+
identity_check: identityCheckId, // Optional: from verify response (for data flow)
|
|
236
|
+
}, ["BLINK", "TURN_LEFT", "OPEN_MOUTH"]);
|
|
237
|
+
|
|
238
|
+
console.log(result.status); // "processing"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The SDK handles camera access, face detection, action prompts, video recording, and submission.
|
|
242
|
+
|
|
243
|
+
### Liveness Check (fully manual)
|
|
244
|
+
|
|
245
|
+
If you want to handle camera and recording yourself:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// 1. Create entry
|
|
249
|
+
const entry = await sdk.liveness.create({
|
|
250
|
+
identifier: "A12345678",
|
|
251
|
+
identifier_type: "Passport",
|
|
252
|
+
country: "US",
|
|
253
|
+
challenge_actions: ["BLINK", "TURN_LEFT", "OPEN_MOUTH"],
|
|
254
|
+
autoshot_file: selfieBlob, // Captured selfie (JPEG/PNG, max 5MB)
|
|
255
|
+
id_file: passportPhotoBlob, // Optional: document photo
|
|
256
|
+
identity_check: identityCheckId, // Optional: from verify response
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// 2. Run your own camera/detection/recording UI
|
|
260
|
+
// ...
|
|
261
|
+
|
|
262
|
+
// 3. Submit the recorded video + snapshot
|
|
263
|
+
const result = await sdk.liveness.submit(entry.id, videoBlob, snapshotBlob);
|
|
264
|
+
// result.status === "processing"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**After submission, the session is revoked.** Each `clientId` can only complete one full verification.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Receiving Results (Webhook)
|
|
272
|
+
|
|
273
|
+
Verification is processed asynchronously. After the user submits, the backend runs face matching using AI (DeepFace with RetinaFace detection) and delivers results via webhook to the URL configured in your SDK Config.
|
|
274
|
+
|
|
275
|
+
### Webhook Payload — Data Verification (BVN/NIN)
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
POST https://your-server.com/webhook
|
|
279
|
+
Content-Type: application/json
|
|
280
|
+
X-Adhere-Signature: <hmac-sha256-hex>
|
|
281
|
+
|
|
282
|
+
{
|
|
283
|
+
"event": "liveness.completed",
|
|
284
|
+
"data": {
|
|
285
|
+
"entry_id": 42,
|
|
286
|
+
"status": "passed",
|
|
287
|
+
"is_verified": true,
|
|
288
|
+
"match_score": 0.15,
|
|
289
|
+
"confidence_percentage": 62.5,
|
|
290
|
+
"identity_face_match": {
|
|
291
|
+
"verified": true,
|
|
292
|
+
"distance": 0.12,
|
|
293
|
+
"threshold": 0.4,
|
|
294
|
+
"confidence_percentage": 70.0
|
|
295
|
+
},
|
|
296
|
+
"metadata": { "ip_address": "...", "user_agent": "..." }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Webhook Payload — Document Verification (Passport/License)
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"event": "liveness.completed",
|
|
306
|
+
"data": {
|
|
307
|
+
"entry_id": 43,
|
|
308
|
+
"status": "passed",
|
|
309
|
+
"is_verified": true,
|
|
310
|
+
"match_score": 0.18,
|
|
311
|
+
"confidence_percentage": 55.0,
|
|
312
|
+
"document_verification": {
|
|
313
|
+
"status": "verified",
|
|
314
|
+
"document_type": "passport",
|
|
315
|
+
"is_expired": false,
|
|
316
|
+
"face_match_verified": true,
|
|
317
|
+
"face_match_score": 0.22,
|
|
318
|
+
"extracted_name": "AMARA OKAFOR",
|
|
319
|
+
"extracted_expiry_date": "2030-06-15"
|
|
320
|
+
},
|
|
321
|
+
"metadata": { "ip_address": "...", "user_agent": "..." }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Understanding the Scores
|
|
327
|
+
|
|
328
|
+
- **`match_score`** (distance) — Raw distance between face embeddings. Lower = more similar. Threshold is 0.4 (Facenet default).
|
|
329
|
+
- **`confidence_percentage`** — Human-readable score: how close the match is to perfect. 100% = identical, 0% = at the threshold boundary.
|
|
330
|
+
- **`identity_face_match`** — Only present in data verification (BVN/NIN). Confirms the person doing liveness matches the face on the government database.
|
|
331
|
+
- **`document_verification`** — Only present in document verification. Includes OCR results and document-to-selfie face match.
|
|
332
|
+
|
|
333
|
+
### Possible Statuses
|
|
334
|
+
|
|
335
|
+
| Status | Meaning |
|
|
336
|
+
|--------|---------|
|
|
337
|
+
| `passed` | All checks passed — person is verified |
|
|
338
|
+
| `failed` | Face mismatch, expired document, or processing error |
|
|
339
|
+
| `processing` | Still being processed (rare — usually completes in seconds) |
|
|
340
|
+
|
|
341
|
+
### Verify Signature
|
|
342
|
+
|
|
343
|
+
**Always verify the webhook signature** to prevent spoofing:
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
const crypto = require("crypto");
|
|
347
|
+
|
|
348
|
+
app.post("/webhook/smartcomply", express.json(), (req, res) => {
|
|
349
|
+
const signature = req.headers["x-adhere-signature"];
|
|
350
|
+
const secret = process.env.WEBHOOK_SECRET.replace(/-/g, "");
|
|
351
|
+
const expected = crypto
|
|
352
|
+
.createHmac("sha256", secret)
|
|
353
|
+
.update(JSON.stringify(req.body))
|
|
354
|
+
.digest("hex");
|
|
355
|
+
|
|
356
|
+
const isValid = crypto.timingSafeEqual(
|
|
357
|
+
Buffer.from(signature, "hex"),
|
|
358
|
+
Buffer.from(expected, "hex")
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (!isValid) return res.status(401).send("Bad signature");
|
|
362
|
+
|
|
363
|
+
// Handle the event
|
|
364
|
+
const { event, data } = req.body;
|
|
365
|
+
|
|
366
|
+
if (event === "liveness.completed" && data.is_verified) {
|
|
367
|
+
// ✓ User is verified — update your database
|
|
368
|
+
markUserAsVerified(data.entry_id);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
res.json({ received: true });
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Challenge Actions
|
|
378
|
+
|
|
379
|
+
| Action | What the user does |
|
|
380
|
+
|--------|-------------------|
|
|
381
|
+
| `BLINK` | Blink both eyes |
|
|
382
|
+
| `TURN_LEFT` | Turn head to the left |
|
|
383
|
+
| `TURN_RIGHT` | Turn head to the right |
|
|
384
|
+
| `TURN_HEAD` | Turn head in any direction |
|
|
385
|
+
| `OPEN_MOUTH` | Open mouth wide |
|
|
386
|
+
|
|
387
|
+
Actions must be **UPPERCASE**. We recommend using 3 actions: `["BLINK", "TURN_LEFT", "OPEN_MOUTH"]`.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Error Handling
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { SDKError, AuthError, NetworkError } from "smartcomply-web-sdk";
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
await sdk.createSession();
|
|
398
|
+
} catch (err) {
|
|
399
|
+
if (err instanceof AuthError) {
|
|
400
|
+
// 401: Invalid API key, expired session, or disabled branch
|
|
401
|
+
} else if (err instanceof NetworkError) {
|
|
402
|
+
// No internet, timeout, DNS failure
|
|
403
|
+
} else if (err instanceof SDKError) {
|
|
404
|
+
// Backend error: validation, insufficient balance, etc.
|
|
405
|
+
console.log(err.statusCode); // HTTP status
|
|
406
|
+
console.log(err.errorCode); // Machine-readable code
|
|
407
|
+
console.log(err.errorData); // Additional error details
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
| Error Code | HTTP | Meaning |
|
|
413
|
+
|------------|------|---------|
|
|
414
|
+
| `INVALID_API_KEY` | 401 | Bad API key or expired session |
|
|
415
|
+
| `SDK_CONFIG_NOT_FOUND` | 404 | Invalid `clientId` |
|
|
416
|
+
| `VALIDATION_ERROR` | 400 | Missing or invalid fields |
|
|
417
|
+
| `INSUFFICIENT_BALANCE` | 402 | Top up your wallet |
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Theming
|
|
422
|
+
|
|
423
|
+
The widget automatically uses the theme from your SDK Config (set in the Adhere dashboard):
|
|
424
|
+
|
|
425
|
+
- `default` — Clean blue on white
|
|
426
|
+
- `midnight_blue` — Dark mode with blue accents
|
|
427
|
+
- `sunset_gold` — Warm gold on cream
|
|
428
|
+
- `forest_emerald` — Green on mint
|
|
429
|
+
|
|
430
|
+
No client-side theme configuration required.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Browser Support
|
|
435
|
+
|
|
436
|
+
Requires camera access (`getUserMedia`), video recording (`MediaRecorder`), and WebAssembly.
|
|
437
|
+
|
|
438
|
+
| Browser | Version |
|
|
439
|
+
|---------|---------|
|
|
440
|
+
| Chrome | 80+ |
|
|
441
|
+
| Firefox | 75+ |
|
|
442
|
+
| Safari | 14+ |
|
|
443
|
+
| Edge | 80+ |
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Prerequisites
|
|
448
|
+
|
|
449
|
+
1. **Adhere account** — Sign up at the dashboard
|
|
450
|
+
2. **API Key** — Format: `pk_live_...` (from dashboard → API Keys)
|
|
451
|
+
3. **SDK Config** — Create in dashboard with your brand name, theme, verification types, channels, and webhook URL
|
|
452
|
+
4. **Client ID** — The UUID shown on your SDK Config
|
|
453
|
+
5. **Funded wallet** — Each verification deducts from your balance
|
|
454
|
+
6. **Webhook endpoint** — A URL on your server to receive verification results
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## TypeScript Support
|
|
459
|
+
|
|
460
|
+
Full TypeScript definitions are included. Key types:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import type {
|
|
464
|
+
FlowOptions,
|
|
465
|
+
FlowResult,
|
|
466
|
+
SDKConfig,
|
|
467
|
+
SDKInitConfig,
|
|
468
|
+
SessionResponse,
|
|
469
|
+
VerifyIdentityResponse,
|
|
470
|
+
LivenessCreateResponse,
|
|
471
|
+
LivenessSubmitResponse,
|
|
472
|
+
ChallengeAction,
|
|
473
|
+
LivenessWebhookPayload,
|
|
474
|
+
ApiResponse,
|
|
475
|
+
} from "smartcomply-web-sdk";
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## License
|
|
481
|
+
|
|
482
|
+
ISC
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FaceDetectionResult } from "./FaceDetector";
|
|
2
|
+
export interface ActionState {
|
|
3
|
+
action: string;
|
|
4
|
+
detected: boolean;
|
|
5
|
+
active: boolean;
|
|
6
|
+
confidence: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
10
|
+
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
11
|
+
*/
|
|
12
|
+
export declare class ActionDetector {
|
|
13
|
+
private requiredActions;
|
|
14
|
+
private completedActions;
|
|
15
|
+
private holdCounters;
|
|
16
|
+
constructor(requiredActions: string[]);
|
|
17
|
+
check(result: FaceDetectionResult): ActionState[];
|
|
18
|
+
getCurrentAction(): string | null;
|
|
19
|
+
private buildStates;
|
|
20
|
+
private getConfidence;
|
|
21
|
+
private getHeadYaw;
|
|
22
|
+
allCompleted(): boolean;
|
|
23
|
+
completedCount(): number;
|
|
24
|
+
reset(): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ActionDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD;;;GAGG;AACH,qBAAa,cAAc;IAIb,OAAO,CAAC,eAAe;IAHnC,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,YAAY,CAA6B;gBAE7B,eAAe,EAAE,MAAM,EAAE;IAE7C,KAAK,CAAC,MAAM,EAAE,mBAAmB,GAAG,WAAW,EAAE;IAgCjD,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAOjC,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,aAAa;IAoCrB,OAAO,CAAC,UAAU;IAKlB,YAAY,IAAI,OAAO;IAIvB,cAAc,IAAI,MAAM;IAIxB,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ActionDetector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Per-action detection tuning.
|
|
6
|
+
* - threshold : minimum confidence score required to start counting.
|
|
7
|
+
* - holdFrames: consecutive frames above threshold before action is confirmed.
|
|
8
|
+
*
|
|
9
|
+
* BLINK is a fast reflex (~100-200ms) so we use a low threshold and only 2
|
|
10
|
+
* frames. Head-turn and mouth-open are sustained movements so they need a
|
|
11
|
+
* slightly longer hold to avoid false positives.
|
|
12
|
+
*/
|
|
13
|
+
const ACTION_CONFIGS = {
|
|
14
|
+
BLINK: { threshold: 0.30, holdFrames: 2 },
|
|
15
|
+
TURN_LEFT: { threshold: 0.45, holdFrames: 6 },
|
|
16
|
+
TURN_RIGHT: { threshold: 0.45, holdFrames: 6 },
|
|
17
|
+
TURN_HEAD: { threshold: 0.45, holdFrames: 6 },
|
|
18
|
+
OPEN_MOUTH: { threshold: 0.40, holdFrames: 3 },
|
|
19
|
+
};
|
|
20
|
+
const DEFAULT_CONFIG = { threshold: 0.55, holdFrames: 8 };
|
|
21
|
+
/**
|
|
22
|
+
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
23
|
+
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
24
|
+
*/
|
|
25
|
+
class ActionDetector {
|
|
26
|
+
constructor(requiredActions) {
|
|
27
|
+
this.requiredActions = requiredActions;
|
|
28
|
+
this.completedActions = new Set();
|
|
29
|
+
this.holdCounters = new Map();
|
|
30
|
+
}
|
|
31
|
+
check(result) {
|
|
32
|
+
const currentAction = this.getCurrentAction();
|
|
33
|
+
if (!result.faceDetected) {
|
|
34
|
+
return this.buildStates(currentAction, 0);
|
|
35
|
+
}
|
|
36
|
+
const bs = result.blendshapes;
|
|
37
|
+
// Only check the current active action (sequential flow)
|
|
38
|
+
if (currentAction && !this.completedActions.has(currentAction)) {
|
|
39
|
+
const conf = this.getConfidence(currentAction, bs, result.transformMatrix);
|
|
40
|
+
const cfg = ACTION_CONFIGS[currentAction] ?? DEFAULT_CONFIG;
|
|
41
|
+
if (conf >= cfg.threshold) {
|
|
42
|
+
const count = (this.holdCounters.get(currentAction) || 0) + 1;
|
|
43
|
+
this.holdCounters.set(currentAction, count);
|
|
44
|
+
if (count >= cfg.holdFrames) {
|
|
45
|
+
this.completedActions.add(currentAction);
|
|
46
|
+
this.holdCounters.delete(currentAction);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.holdCounters.set(currentAction, 0);
|
|
51
|
+
}
|
|
52
|
+
return this.buildStates(currentAction, conf);
|
|
53
|
+
}
|
|
54
|
+
return this.buildStates(currentAction, 0);
|
|
55
|
+
}
|
|
56
|
+
getCurrentAction() {
|
|
57
|
+
for (const action of this.requiredActions) {
|
|
58
|
+
if (!this.completedActions.has(action))
|
|
59
|
+
return action;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
buildStates(currentAction, currentConf) {
|
|
64
|
+
return this.requiredActions.map((action) => ({
|
|
65
|
+
action,
|
|
66
|
+
detected: this.completedActions.has(action),
|
|
67
|
+
active: action === currentAction,
|
|
68
|
+
confidence: action === currentAction ? currentConf : 0,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
getConfidence(action, bs, matrix) {
|
|
72
|
+
switch (action) {
|
|
73
|
+
case "BLINK": {
|
|
74
|
+
const left = bs.get("eyeBlinkLeft") || 0;
|
|
75
|
+
const right = bs.get("eyeBlinkRight") || 0;
|
|
76
|
+
return (left + right) / 2;
|
|
77
|
+
}
|
|
78
|
+
case "TURN_LEFT": {
|
|
79
|
+
const yaw = this.getHeadYaw(matrix);
|
|
80
|
+
return yaw < -12 ? Math.min(1, Math.abs(yaw) / 30) : 0;
|
|
81
|
+
}
|
|
82
|
+
case "TURN_RIGHT": {
|
|
83
|
+
const yaw = this.getHeadYaw(matrix);
|
|
84
|
+
return yaw > 12 ? Math.min(1, yaw / 30) : 0;
|
|
85
|
+
}
|
|
86
|
+
case "TURN_HEAD": {
|
|
87
|
+
// Any significant yaw (left OR right) counts
|
|
88
|
+
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
89
|
+
return yaw > 15 ? Math.min(1, yaw / 30) : 0;
|
|
90
|
+
}
|
|
91
|
+
case "OPEN_MOUTH":
|
|
92
|
+
return bs.get("jawOpen") || 0;
|
|
93
|
+
default:
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
getHeadYaw(matrix) {
|
|
98
|
+
if (!matrix || matrix.length < 16)
|
|
99
|
+
return 0;
|
|
100
|
+
return Math.atan2(matrix[8], matrix[0]) * (180 / Math.PI);
|
|
101
|
+
}
|
|
102
|
+
allCompleted() {
|
|
103
|
+
return this.requiredActions.every((a) => this.completedActions.has(a));
|
|
104
|
+
}
|
|
105
|
+
completedCount() {
|
|
106
|
+
return this.completedActions.size;
|
|
107
|
+
}
|
|
108
|
+
reset() {
|
|
109
|
+
this.completedActions.clear();
|
|
110
|
+
this.holdCounters.clear();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.ActionDetector = ActionDetector;
|
|
114
|
+
//# sourceMappingURL=ActionDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CameraManager.d.ts","sourceRoot":"","sources":["../../src/camera/CameraManager.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAEpC,IAAI,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IAiBtE,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B,IAAI,IAAI,IAAI;CAMb"}
|