@flonkid/kyc 1.6.1 → 1.8.0
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/README.md +175 -169
- package/dist/index.cjs +319 -363
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -10
- package/dist/index.d.ts +57 -10
- package/dist/index.js +319 -364
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +21 -46
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +21 -46
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +9 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
-
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
5
|
|
|
10
6
|
// src/shared/constants.ts
|
|
11
|
-
var SDK_VERSION = "1.
|
|
7
|
+
var SDK_VERSION = "1.8.0";
|
|
12
8
|
var DEFAULT_WIDGET_URL = "https://widget.flonk.id";
|
|
13
9
|
var DEFAULT_API_BASE = "https://api.flonk.id/v1";
|
|
14
10
|
var WIDGET_EVENTS = {
|
|
@@ -19,43 +15,35 @@ var WIDGET_EVENTS = {
|
|
|
19
15
|
};
|
|
20
16
|
|
|
21
17
|
// src/shared/errors.ts
|
|
22
|
-
var
|
|
18
|
+
var FlonkError = class extends Error {
|
|
23
19
|
constructor(message, code, statusCode) {
|
|
24
20
|
super(message);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.code = code, this.statusCode = statusCode;
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.statusCode = statusCode;
|
|
28
23
|
this.name = "FlonkError";
|
|
29
24
|
}
|
|
30
25
|
};
|
|
31
|
-
|
|
32
|
-
var FlonkError = _FlonkError;
|
|
33
|
-
var _FlonkValidationError = class _FlonkValidationError extends FlonkError {
|
|
26
|
+
var FlonkValidationError = class extends FlonkError {
|
|
34
27
|
constructor(message) {
|
|
35
28
|
super(message, "validation_error", 400);
|
|
36
29
|
this.name = "FlonkValidationError";
|
|
37
30
|
}
|
|
38
31
|
};
|
|
39
|
-
__name(_FlonkValidationError, "FlonkValidationError");
|
|
40
|
-
var FlonkValidationError = _FlonkValidationError;
|
|
41
32
|
|
|
42
33
|
// src/browser/utils.ts
|
|
43
34
|
function getOrigin(url) {
|
|
44
35
|
try {
|
|
45
36
|
return new URL(url).origin;
|
|
46
37
|
} catch {
|
|
47
|
-
return
|
|
38
|
+
return "null";
|
|
48
39
|
}
|
|
49
40
|
}
|
|
50
|
-
__name(getOrigin, "getOrigin");
|
|
51
41
|
function isDesktop() {
|
|
52
42
|
return window.innerWidth >= 1024 && !("ontouchstart" in window || navigator.maxTouchPoints > 0);
|
|
53
43
|
}
|
|
54
|
-
__name(isDesktop, "isDesktop");
|
|
55
44
|
function setStyles(el, styles) {
|
|
56
45
|
Object.assign(el.style, styles);
|
|
57
46
|
}
|
|
58
|
-
__name(setStyles, "setStyles");
|
|
59
47
|
function createEl(tag, styles, attrs) {
|
|
60
48
|
const el = document.createElement(tag);
|
|
61
49
|
if (styles) setStyles(el, styles);
|
|
@@ -64,15 +52,13 @@ function createEl(tag, styles, attrs) {
|
|
|
64
52
|
}
|
|
65
53
|
return el;
|
|
66
54
|
}
|
|
67
|
-
__name(createEl, "createEl");
|
|
68
55
|
function debounce(fn, ms) {
|
|
69
56
|
let timer;
|
|
70
|
-
return (...args) => {
|
|
57
|
+
return ((...args) => {
|
|
71
58
|
clearTimeout(timer);
|
|
72
59
|
timer = setTimeout(() => fn(...args), ms);
|
|
73
|
-
};
|
|
60
|
+
});
|
|
74
61
|
}
|
|
75
|
-
__name(debounce, "debounce");
|
|
76
62
|
function generateSecondaryColor(hex) {
|
|
77
63
|
try {
|
|
78
64
|
const h = hex.replace("#", "");
|
|
@@ -80,21 +66,33 @@ function generateSecondaryColor(hex) {
|
|
|
80
66
|
const g = parseInt(h.substring(2, 4), 16);
|
|
81
67
|
const b = parseInt(h.substring(4, 6), 16);
|
|
82
68
|
const f = 0.6;
|
|
83
|
-
const toHex =
|
|
69
|
+
const toHex = (n) => {
|
|
84
70
|
const s = n.toString(16);
|
|
85
71
|
return s.length === 1 ? "0" + s : s;
|
|
86
|
-
}
|
|
72
|
+
};
|
|
87
73
|
return "#" + toHex(Math.round(r + (255 - r) * f)) + toHex(Math.round(g + (255 - g) * f)) + toHex(Math.round(b + (255 - b) * f));
|
|
88
74
|
} catch {
|
|
89
75
|
return "#93c5fd";
|
|
90
76
|
}
|
|
91
77
|
}
|
|
92
|
-
|
|
78
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 2e4;
|
|
79
|
+
async function fetchWithTimeout(url, init = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
80
|
+
const controller = new AbortController();
|
|
81
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
82
|
+
try {
|
|
83
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err?.name === "AbortError") {
|
|
86
|
+
throw new Error(`Request timed out after ${timeoutMs}ms: ${url}`);
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
} finally {
|
|
90
|
+
clearTimeout(timer);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
93
|
async function fetchWidgetToken(pk, apiBase) {
|
|
94
|
-
const res = await
|
|
95
|
-
headers: {
|
|
96
|
-
"x-kyc-pk": pk
|
|
97
|
-
},
|
|
94
|
+
const res = await fetchWithTimeout(`${apiBase}/public/widget-token`, {
|
|
95
|
+
headers: { "x-kyc-pk": pk },
|
|
98
96
|
credentials: "include"
|
|
99
97
|
});
|
|
100
98
|
if (!res.ok) {
|
|
@@ -108,51 +106,66 @@ async function fetchWidgetToken(pk, apiBase) {
|
|
|
108
106
|
}
|
|
109
107
|
return res.json();
|
|
110
108
|
}
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
var BRANDING_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
110
|
+
var brandingCache = /* @__PURE__ */ new Map();
|
|
111
|
+
function brandingCacheKey(opts) {
|
|
112
|
+
if (opts.pk) return `pk:${opts.pk}`;
|
|
113
|
+
if (opts.sessionId) return `sid:${opts.sessionId}`;
|
|
114
|
+
if (opts.clientId) return `cid:${opts.clientId}`;
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
async function requestDesignTokens(apiBase, opts) {
|
|
113
118
|
const params = [];
|
|
114
119
|
if (opts.sessionId) params.push(`sessionId=${encodeURIComponent(opts.sessionId)}`);
|
|
115
120
|
else if (opts.clientId) params.push(`clientId=${encodeURIComponent(opts.clientId)}`);
|
|
116
121
|
const url = `${apiBase}/public/design-tokens${params.length ? "?" + params.join("&") : ""}`;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
const res = await fetchWithTimeout(
|
|
123
|
+
url,
|
|
124
|
+
{ headers: opts.pk ? { "x-kyc-pk": opts.pk } : {}, credentials: "omit" },
|
|
125
|
+
8e3
|
|
126
|
+
);
|
|
127
|
+
return res.ok ? res.json() : null;
|
|
128
|
+
}
|
|
129
|
+
async function fetchDesignTokens(apiBase, opts) {
|
|
130
|
+
const key = brandingCacheKey(opts);
|
|
131
|
+
if (!key) return null;
|
|
132
|
+
const now = Date.now();
|
|
133
|
+
const cached = brandingCache.get(key);
|
|
134
|
+
if (cached && cached.expiresAt > now) {
|
|
135
|
+
return cached.promise;
|
|
127
136
|
}
|
|
137
|
+
const promise = requestDesignTokens(apiBase, opts).catch(() => null);
|
|
138
|
+
brandingCache.set(key, { promise, expiresAt: now + BRANDING_CACHE_TTL_MS });
|
|
139
|
+
promise.then((tokens) => {
|
|
140
|
+
if (tokens == null) brandingCache.delete(key);
|
|
141
|
+
});
|
|
142
|
+
return promise;
|
|
143
|
+
}
|
|
144
|
+
function preloadDesignTokens(apiBase, opts) {
|
|
145
|
+
return fetchDesignTokens(apiBase, opts);
|
|
128
146
|
}
|
|
129
|
-
__name(fetchDesignTokens, "fetchDesignTokens");
|
|
130
147
|
function validateServerUrl(url) {
|
|
131
148
|
if (url.startsWith("/")) return;
|
|
132
149
|
try {
|
|
133
150
|
const parsed = new URL(url);
|
|
134
151
|
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
135
152
|
if (parsed.protocol !== "https:" && !isLocalhost) {
|
|
136
|
-
throw new Error(
|
|
153
|
+
throw new Error(
|
|
154
|
+
`serverUrl must use HTTPS in production. Got: ${parsed.protocol}//. Use HTTPS ('https://api.myapp.com/...') or a relative path ('/api/...')`
|
|
155
|
+
);
|
|
137
156
|
}
|
|
138
157
|
} catch (e) {
|
|
139
158
|
if (e.message.includes("serverUrl must use HTTPS")) throw e;
|
|
140
159
|
throw new Error(`Invalid serverUrl: ${url}`);
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
|
-
__name(validateServerUrl, "validateServerUrl");
|
|
144
162
|
async function fetchSessionFromServer(serverUrl, clientMetadata, requestHeaders) {
|
|
145
163
|
validateServerUrl(serverUrl);
|
|
146
|
-
const res = await
|
|
164
|
+
const res = await fetchWithTimeout(serverUrl, {
|
|
147
165
|
method: "POST",
|
|
148
|
-
headers: {
|
|
149
|
-
"Content-Type": "application/json",
|
|
150
|
-
...requestHeaders
|
|
151
|
-
},
|
|
166
|
+
headers: { "Content-Type": "application/json", ...requestHeaders },
|
|
152
167
|
credentials: "include",
|
|
153
|
-
body: JSON.stringify({
|
|
154
|
-
clientMetadata
|
|
155
|
-
})
|
|
168
|
+
body: JSON.stringify({ clientMetadata })
|
|
156
169
|
});
|
|
157
170
|
if (!res.ok) {
|
|
158
171
|
let message = `Session request failed (${res.status})`;
|
|
@@ -166,9 +179,8 @@ async function fetchSessionFromServer(serverUrl, clientMetadata, requestHeaders)
|
|
|
166
179
|
}
|
|
167
180
|
return res.json();
|
|
168
181
|
}
|
|
169
|
-
__name(fetchSessionFromServer, "fetchSessionFromServer");
|
|
170
182
|
async function fetchPublicSession(apiBase, sessionId, embedToken) {
|
|
171
|
-
const res = await
|
|
183
|
+
const res = await fetchWithTimeout(`${apiBase}/public/session/${sessionId}`, {
|
|
172
184
|
headers: {
|
|
173
185
|
"Content-Type": "application/json",
|
|
174
186
|
"Authorization": `Bearer ${embedToken}`
|
|
@@ -185,13 +197,10 @@ async function fetchPublicSession(apiBase, sessionId, embedToken) {
|
|
|
185
197
|
}
|
|
186
198
|
return res.json();
|
|
187
199
|
}
|
|
188
|
-
__name(fetchPublicSession, "fetchPublicSession");
|
|
189
200
|
async function exchangeSessionForToken(apiBase, sessionId) {
|
|
190
|
-
const res = await
|
|
201
|
+
const res = await fetchWithTimeout(`${apiBase}/public/session/${sessionId}/token`, {
|
|
191
202
|
method: "POST",
|
|
192
|
-
headers: {
|
|
193
|
-
"Content-Type": "application/json"
|
|
194
|
-
}
|
|
203
|
+
headers: { "Content-Type": "application/json" }
|
|
195
204
|
});
|
|
196
205
|
if (!res.ok) {
|
|
197
206
|
let message = `Token exchange failed (${res.status})`;
|
|
@@ -204,33 +213,36 @@ async function exchangeSessionForToken(apiBase, sessionId) {
|
|
|
204
213
|
}
|
|
205
214
|
return res.json();
|
|
206
215
|
}
|
|
207
|
-
__name(exchangeSessionForToken, "exchangeSessionForToken");
|
|
208
216
|
|
|
209
217
|
// src/browser/iframe-manager.ts
|
|
210
218
|
function createIframe(src) {
|
|
211
219
|
const d = isDesktop();
|
|
212
|
-
const iframe = createEl(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
220
|
+
const iframe = createEl(
|
|
221
|
+
"iframe",
|
|
222
|
+
{
|
|
223
|
+
border: "0",
|
|
224
|
+
width: window.innerWidth + "px",
|
|
225
|
+
height: window.innerHeight + "px",
|
|
226
|
+
position: "fixed",
|
|
227
|
+
top: "0",
|
|
228
|
+
left: "0",
|
|
229
|
+
zIndex: "9999",
|
|
230
|
+
background: "transparent",
|
|
231
|
+
backgroundColor: "transparent",
|
|
232
|
+
opacity: "0",
|
|
233
|
+
visibility: "hidden",
|
|
234
|
+
borderRadius: d ? "0" : "",
|
|
235
|
+
boxShadow: d ? "none" : "",
|
|
236
|
+
colorScheme: "normal"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
src,
|
|
240
|
+
allow: "camera;microphone;clipboard-read;clipboard-write",
|
|
241
|
+
sandbox: "allow-scripts allow-forms allow-same-origin allow-popups",
|
|
242
|
+
"aria-label": "KYC Verification",
|
|
243
|
+
allowtransparency: "true"
|
|
244
|
+
}
|
|
245
|
+
);
|
|
234
246
|
try {
|
|
235
247
|
iframe.style.setProperty("background", "transparent", "important");
|
|
236
248
|
iframe.style.setProperty("background-color", "transparent", "important");
|
|
@@ -239,24 +251,21 @@ function createIframe(src) {
|
|
|
239
251
|
}
|
|
240
252
|
return iframe;
|
|
241
253
|
}
|
|
242
|
-
__name(createIframe, "createIframe");
|
|
243
254
|
function adjustZIndex(loader, iframe) {
|
|
244
255
|
if (!isDesktop()) return;
|
|
245
256
|
const all = Array.from(document.querySelectorAll("*"));
|
|
246
|
-
const maxZ = Math.max(
|
|
257
|
+
const maxZ = Math.max(
|
|
258
|
+
...all.map((el) => parseInt(getComputedStyle(el).zIndex) || 0).filter((z) => z < 999999)
|
|
259
|
+
);
|
|
247
260
|
if (maxZ > 9998) {
|
|
248
261
|
loader.style.zIndex = String(maxZ + 1);
|
|
249
262
|
iframe.style.zIndex = String(maxZ + 2);
|
|
250
263
|
}
|
|
251
264
|
}
|
|
252
|
-
__name(adjustZIndex, "adjustZIndex");
|
|
253
265
|
function transitionLoaderToIframe(loader, iframe, onDone) {
|
|
254
266
|
const d = isDesktop();
|
|
255
267
|
const dur = d ? 300 : 500;
|
|
256
|
-
setStyles(iframe, {
|
|
257
|
-
opacity: "0",
|
|
258
|
-
visibility: "hidden"
|
|
259
|
-
});
|
|
268
|
+
setStyles(iframe, { opacity: "0", visibility: "hidden" });
|
|
260
269
|
const card = loader.querySelector("div");
|
|
261
270
|
if (card) {
|
|
262
271
|
setStyles(card, {
|
|
@@ -274,28 +283,12 @@ function transitionLoaderToIframe(loader, iframe, onDone) {
|
|
|
274
283
|
});
|
|
275
284
|
}, dur);
|
|
276
285
|
}
|
|
277
|
-
__name(transitionLoaderToIframe, "transitionLoaderToIframe");
|
|
278
286
|
|
|
279
287
|
// src/browser/loader.ts
|
|
280
288
|
var LOADER_I18N = {
|
|
281
|
-
en: {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
errorTitle: "Something went wrong",
|
|
285
|
-
close: "Close"
|
|
286
|
-
},
|
|
287
|
-
de: {
|
|
288
|
-
title: "Initialisierung...",
|
|
289
|
-
subtitle: "KYC-Widget wird geladen",
|
|
290
|
-
errorTitle: "Ein Fehler ist aufgetreten",
|
|
291
|
-
close: "Schlie\xDFen"
|
|
292
|
-
},
|
|
293
|
-
uk: {
|
|
294
|
-
title: "\u0406\u043D\u0456\u0446\u0456\u0430\u043B\u0456\u0437\u0430\u0446\u0456\u044F...",
|
|
295
|
-
subtitle: "\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F KYC-\u0432\u0456\u0434\u0436\u0435\u0442\u0430",
|
|
296
|
-
errorTitle: "\u0429\u043E\u0441\u044C \u043F\u0456\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A",
|
|
297
|
-
close: "\u0417\u0430\u043A\u0440\u0438\u0442\u0438"
|
|
298
|
-
}
|
|
289
|
+
en: { title: "Initializing...", subtitle: "Loading KYC widget", errorTitle: "Something went wrong", close: "Close" },
|
|
290
|
+
de: { title: "Initialisierung...", subtitle: "KYC-Widget wird geladen", errorTitle: "Ein Fehler ist aufgetreten", close: "Schlie\xDFen" },
|
|
291
|
+
uk: { title: "\u0406\u043D\u0456\u0446\u0456\u0430\u043B\u0456\u0437\u0430\u0446\u0456\u044F...", subtitle: "\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F KYC-\u0432\u0456\u0434\u0436\u0435\u0442\u0430", errorTitle: "\u0429\u043E\u0441\u044C \u043F\u0456\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A", close: "\u0417\u0430\u043A\u0440\u0438\u0442\u0438" }
|
|
299
292
|
};
|
|
300
293
|
var KEYFRAMES = `
|
|
301
294
|
@keyframes kycspin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
|
|
@@ -303,11 +296,11 @@ var KEYFRAMES = `
|
|
|
303
296
|
@keyframes kycprogress{0%{transform:translateX(-100%)}50%{transform:translateX(0%)}100%{transform:translateX(250%)}}
|
|
304
297
|
`.trim();
|
|
305
298
|
var styleInjected = false;
|
|
306
|
-
var
|
|
299
|
+
var Loader = class {
|
|
307
300
|
constructor() {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
301
|
+
this.overlay = null;
|
|
302
|
+
this.cleanup = null;
|
|
303
|
+
this.origBodyStyles = null;
|
|
311
304
|
}
|
|
312
305
|
show(primaryColor, lang) {
|
|
313
306
|
const color = primaryColor || "#15BA68";
|
|
@@ -386,64 +379,23 @@ var _Loader = class _Loader {
|
|
|
386
379
|
"stroke-dasharray": "62.8",
|
|
387
380
|
"stroke-dashoffset": "15.7"
|
|
388
381
|
})) fg.setAttribute(k, v);
|
|
389
|
-
setStyles(fg, {
|
|
390
|
-
transformOrigin: "center",
|
|
391
|
-
animation: "kycdash 1.5s ease-in-out infinite"
|
|
392
|
-
});
|
|
382
|
+
setStyles(fg, { transformOrigin: "center", animation: "kycdash 1.5s ease-in-out infinite" });
|
|
393
383
|
svg.append(bg, fg);
|
|
394
384
|
wrap.appendChild(svg);
|
|
395
385
|
const font = 'Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif';
|
|
396
|
-
const textBox = createEl("div", {
|
|
397
|
-
|
|
398
|
-
});
|
|
399
|
-
const title = createEl("h3", {
|
|
400
|
-
fontFamily: font,
|
|
401
|
-
fontWeight: "600",
|
|
402
|
-
fontSize: "18px",
|
|
403
|
-
lineHeight: "1.3",
|
|
404
|
-
color: "#1F2937",
|
|
405
|
-
margin: "0 0 4px 0"
|
|
406
|
-
});
|
|
386
|
+
const textBox = createEl("div", { textAlign: "center" });
|
|
387
|
+
const title = createEl("h3", { fontFamily: font, fontWeight: "600", fontSize: "18px", lineHeight: "1.3", color: "#1F2937", margin: "0 0 4px 0" });
|
|
407
388
|
title.textContent = strings.title;
|
|
408
|
-
const subtitle = createEl("p", {
|
|
409
|
-
fontFamily: font,
|
|
410
|
-
fontWeight: "400",
|
|
411
|
-
fontSize: "13px",
|
|
412
|
-
lineHeight: "1.4",
|
|
413
|
-
color: "rgba(31,41,55,0.7)",
|
|
414
|
-
margin: "0"
|
|
415
|
-
});
|
|
389
|
+
const subtitle = createEl("p", { fontFamily: font, fontWeight: "400", fontSize: "13px", lineHeight: "1.4", color: "rgba(31,41,55,0.7)", margin: "0" });
|
|
416
390
|
subtitle.textContent = strings.subtitle;
|
|
417
391
|
textBox.append(title, subtitle);
|
|
418
|
-
const track = createEl("div", {
|
|
419
|
-
|
|
420
|
-
maxWidth: "240px",
|
|
421
|
-
height: "3px",
|
|
422
|
-
backgroundColor: color + "1A",
|
|
423
|
-
borderRadius: "2px",
|
|
424
|
-
overflow: "hidden"
|
|
425
|
-
});
|
|
426
|
-
const bar = createEl("div", {
|
|
427
|
-
width: "40%",
|
|
428
|
-
height: "100%",
|
|
429
|
-
backgroundColor: color,
|
|
430
|
-
borderRadius: "2px",
|
|
431
|
-
transform: "translateX(-100%)",
|
|
432
|
-
animation: "kycprogress 2000ms ease-in-out infinite"
|
|
433
|
-
});
|
|
392
|
+
const track = createEl("div", { width: "100%", maxWidth: "240px", height: "3px", backgroundColor: color + "1A", borderRadius: "2px", overflow: "hidden" });
|
|
393
|
+
const bar = createEl("div", { width: "40%", height: "100%", backgroundColor: color, borderRadius: "2px", transform: "translateX(-100%)", animation: "kycprogress 2000ms ease-in-out infinite" });
|
|
434
394
|
track.appendChild(bar);
|
|
435
395
|
card.append(wrap, textBox, track);
|
|
436
396
|
overlay.appendChild(card);
|
|
437
|
-
this.origBodyStyles = {
|
|
438
|
-
|
|
439
|
-
position: document.body.style.position
|
|
440
|
-
};
|
|
441
|
-
setStyles(document.body, {
|
|
442
|
-
overflow: "hidden",
|
|
443
|
-
position: "fixed",
|
|
444
|
-
width: "100%",
|
|
445
|
-
height: "100%"
|
|
446
|
-
});
|
|
397
|
+
this.origBodyStyles = { overflow: document.body.style.overflow, position: document.body.style.position };
|
|
398
|
+
setStyles(document.body, { overflow: "hidden", position: "fixed", width: "100%", height: "100%" });
|
|
447
399
|
document.body.appendChild(overlay);
|
|
448
400
|
let prevW = window.innerWidth;
|
|
449
401
|
let prevH = window.innerHeight;
|
|
@@ -451,10 +403,7 @@ var _Loader = class _Loader {
|
|
|
451
403
|
const w = window.innerWidth;
|
|
452
404
|
const h = window.innerHeight;
|
|
453
405
|
if (Math.abs(w - prevW) > 1 || Math.abs(h - prevH) > 1) {
|
|
454
|
-
setStyles(overlay, {
|
|
455
|
-
width: w + "px",
|
|
456
|
-
height: h + "px"
|
|
457
|
-
});
|
|
406
|
+
setStyles(overlay, { width: w + "px", height: h + "px" });
|
|
458
407
|
prevW = w;
|
|
459
408
|
prevH = h;
|
|
460
409
|
}
|
|
@@ -464,11 +413,7 @@ var _Loader = class _Loader {
|
|
|
464
413
|
this.cleanup = () => {
|
|
465
414
|
window.removeEventListener("resize", onResize);
|
|
466
415
|
if (this.origBodyStyles) {
|
|
467
|
-
setStyles(document.body, {
|
|
468
|
-
...this.origBodyStyles,
|
|
469
|
-
width: "",
|
|
470
|
-
height: ""
|
|
471
|
-
});
|
|
416
|
+
setStyles(document.body, { ...this.origBodyStyles, width: "", height: "" });
|
|
472
417
|
}
|
|
473
418
|
};
|
|
474
419
|
return overlay;
|
|
@@ -588,25 +533,20 @@ var _Loader = class _Loader {
|
|
|
588
533
|
this.cleanup = null;
|
|
589
534
|
}
|
|
590
535
|
};
|
|
591
|
-
__name(_Loader, "Loader");
|
|
592
|
-
var Loader = _Loader;
|
|
593
536
|
|
|
594
537
|
// src/browser/message-handler.ts
|
|
595
|
-
var
|
|
538
|
+
var MessageHandler = class {
|
|
596
539
|
constructor(iframeSrc, iframe, callbacks) {
|
|
597
|
-
__publicField(this, "iframeSrc");
|
|
598
|
-
__publicField(this, "iframe");
|
|
599
|
-
__publicField(this, "callbacks");
|
|
600
|
-
__publicField(this, "listener", null);
|
|
601
|
-
__publicField(this, "readyListener", null);
|
|
602
|
-
__publicField(this, "completionHandled", false);
|
|
603
540
|
this.iframeSrc = iframeSrc;
|
|
604
541
|
this.iframe = iframe;
|
|
605
542
|
this.callbacks = callbacks;
|
|
543
|
+
this.listener = null;
|
|
544
|
+
this.readyListener = null;
|
|
545
|
+
this.completionHandled = false;
|
|
606
546
|
}
|
|
607
547
|
/**
|
|
608
|
-
|
|
609
|
-
|
|
548
|
+
* Start listening for postMessage events from the widget iframe.
|
|
549
|
+
*/
|
|
610
550
|
listen() {
|
|
611
551
|
const origin = getOrigin(this.iframeSrc);
|
|
612
552
|
this.listener = (e) => {
|
|
@@ -614,34 +554,33 @@ var _MessageHandler = class _MessageHandler {
|
|
|
614
554
|
if (e.source !== this.iframe.contentWindow) return;
|
|
615
555
|
const data = e.data || {};
|
|
616
556
|
const type = data.type;
|
|
617
|
-
if (type === WIDGET_EVENTS.COMPLETE
|
|
557
|
+
if (type === WIDGET_EVENTS.COMPLETE) {
|
|
618
558
|
if (this.completionHandled) return;
|
|
619
559
|
this.completionHandled = true;
|
|
620
|
-
this.callbacks.onSuccess(data.result);
|
|
621
|
-
|
|
622
|
-
} else if (type === WIDGET_EVENTS.CANCEL && this.callbacks.onCancel) {
|
|
560
|
+
this.callbacks.onSuccess?.(data.result);
|
|
561
|
+
} else if (type === WIDGET_EVENTS.CANCEL) {
|
|
623
562
|
if (this.completionHandled) return;
|
|
624
563
|
this.completionHandled = true;
|
|
625
|
-
this.callbacks.onCancel();
|
|
626
|
-
|
|
627
|
-
} else if (type === WIDGET_EVENTS.ERROR && this.callbacks.onError) {
|
|
564
|
+
this.callbacks.onCancel?.();
|
|
565
|
+
} else if (type === WIDGET_EVENTS.ERROR) {
|
|
628
566
|
if (this.completionHandled) return;
|
|
629
567
|
this.completionHandled = true;
|
|
630
|
-
this.callbacks.onError(data.error || "Unknown error");
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
this.callbacks.onReady();
|
|
568
|
+
this.callbacks.onError?.(data.error || "Unknown error");
|
|
569
|
+
} else if (type === WIDGET_EVENTS.READY) {
|
|
570
|
+
this.callbacks.onReady?.();
|
|
634
571
|
}
|
|
635
572
|
};
|
|
636
573
|
window.addEventListener("message", this.listener);
|
|
637
574
|
}
|
|
638
575
|
/**
|
|
639
|
-
|
|
640
|
-
|
|
576
|
+
* Listen for the first READY event, then call the callback once.
|
|
577
|
+
*/
|
|
641
578
|
onReadyOnce(callback) {
|
|
642
579
|
const origin = getOrigin(this.iframeSrc);
|
|
643
580
|
this.readyListener = (e) => {
|
|
644
|
-
if (e.origin !== origin || e.data?.type !== WIDGET_EVENTS.READY)
|
|
581
|
+
if (e.origin !== origin || e.source !== this.iframe.contentWindow || e.data?.type !== WIDGET_EVENTS.READY) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
645
584
|
window.removeEventListener("message", this.readyListener);
|
|
646
585
|
this.readyListener = null;
|
|
647
586
|
callback();
|
|
@@ -659,8 +598,6 @@ var _MessageHandler = class _MessageHandler {
|
|
|
659
598
|
}
|
|
660
599
|
}
|
|
661
600
|
};
|
|
662
|
-
__name(_MessageHandler, "MessageHandler");
|
|
663
|
-
var MessageHandler = _MessageHandler;
|
|
664
601
|
|
|
665
602
|
// src/browser/viewport.ts
|
|
666
603
|
function getVV() {
|
|
@@ -672,14 +609,8 @@ function getVV() {
|
|
|
672
609
|
offsetLeft: window.visualViewport.offsetLeft || 0
|
|
673
610
|
};
|
|
674
611
|
}
|
|
675
|
-
return {
|
|
676
|
-
width: window.innerWidth,
|
|
677
|
-
height: window.innerHeight,
|
|
678
|
-
offsetTop: 0,
|
|
679
|
-
offsetLeft: 0
|
|
680
|
-
};
|
|
612
|
+
return { width: window.innerWidth, height: window.innerHeight, offsetTop: 0, offsetLeft: 0 };
|
|
681
613
|
}
|
|
682
|
-
__name(getVV, "getVV");
|
|
683
614
|
function ensureViewportMeta() {
|
|
684
615
|
try {
|
|
685
616
|
const meta = document.querySelector('meta[name="viewport"]');
|
|
@@ -697,42 +628,31 @@ function ensureViewportMeta() {
|
|
|
697
628
|
} catch {
|
|
698
629
|
}
|
|
699
630
|
}
|
|
700
|
-
__name(ensureViewportMeta, "ensureViewportMeta");
|
|
701
631
|
function setupViewportSizing(overlay, iframe) {
|
|
702
632
|
ensureViewportMeta();
|
|
703
633
|
let baselineHeight = 0;
|
|
704
634
|
let desktop = isDesktop();
|
|
705
635
|
let rafId = null;
|
|
706
|
-
const initBaseline =
|
|
636
|
+
const initBaseline = () => {
|
|
707
637
|
const vv = getVV();
|
|
708
638
|
baselineHeight = window.innerHeight || vv.height || document.documentElement.clientHeight || 0;
|
|
709
639
|
desktop = isDesktop();
|
|
710
640
|
if (rafId) cancelAnimationFrame(rafId);
|
|
711
641
|
rafId = requestAnimationFrame(applySize);
|
|
712
|
-
}
|
|
713
|
-
const applySize =
|
|
642
|
+
};
|
|
643
|
+
const applySize = () => {
|
|
714
644
|
try {
|
|
715
645
|
const vv = getVV();
|
|
716
646
|
const inner = window.innerHeight || vv.height;
|
|
717
647
|
const kbThreshold = desktop ? 200 : 150;
|
|
718
648
|
const isKeyboard = inner - vv.height > kbThreshold;
|
|
719
649
|
const height = isKeyboard ? vv.height : baselineHeight || inner;
|
|
720
|
-
const dims = desktop ? {
|
|
721
|
-
top: "0",
|
|
722
|
-
left: "0",
|
|
723
|
-
width: window.innerWidth + "px",
|
|
724
|
-
height: window.innerHeight + "px"
|
|
725
|
-
} : {
|
|
726
|
-
top: vv.offsetTop + "px",
|
|
727
|
-
left: vv.offsetLeft + "px",
|
|
728
|
-
width: vv.width + "px",
|
|
729
|
-
height: height + "px"
|
|
730
|
-
};
|
|
650
|
+
const dims = desktop ? { top: "0", left: "0", width: window.innerWidth + "px", height: window.innerHeight + "px" } : { top: vv.offsetTop + "px", left: vv.offsetLeft + "px", width: vv.width + "px", height: height + "px" };
|
|
731
651
|
if (overlay) setStyles(overlay, dims);
|
|
732
652
|
if (iframe) setStyles(iframe, dims);
|
|
733
653
|
} catch {
|
|
734
654
|
}
|
|
735
|
-
}
|
|
655
|
+
};
|
|
736
656
|
let prevW = window.innerWidth;
|
|
737
657
|
let prevH = window.innerHeight;
|
|
738
658
|
let prevX = 0;
|
|
@@ -741,7 +661,7 @@ function setupViewportSizing(overlay, iframe) {
|
|
|
741
661
|
if (rafId) cancelAnimationFrame(rafId);
|
|
742
662
|
rafId = requestAnimationFrame(applySize);
|
|
743
663
|
}, desktop ? 50 : 150);
|
|
744
|
-
const handleResize =
|
|
664
|
+
const handleResize = () => {
|
|
745
665
|
const vv = getVV();
|
|
746
666
|
const w = vv.width;
|
|
747
667
|
const h = vv.height;
|
|
@@ -760,12 +680,12 @@ function setupViewportSizing(overlay, iframe) {
|
|
|
760
680
|
} else {
|
|
761
681
|
debouncedApply();
|
|
762
682
|
}
|
|
763
|
-
}
|
|
764
|
-
const handleOrientation =
|
|
683
|
+
};
|
|
684
|
+
const handleOrientation = () => {
|
|
765
685
|
initBaseline();
|
|
766
686
|
if (rafId) cancelAnimationFrame(rafId);
|
|
767
687
|
rafId = requestAnimationFrame(applySize);
|
|
768
|
-
}
|
|
688
|
+
};
|
|
769
689
|
initBaseline();
|
|
770
690
|
applySize();
|
|
771
691
|
window.addEventListener("resize", handleResize);
|
|
@@ -787,26 +707,58 @@ function setupViewportSizing(overlay, iframe) {
|
|
|
787
707
|
}
|
|
788
708
|
};
|
|
789
709
|
}
|
|
790
|
-
__name(setupViewportSizing, "setupViewportSizing");
|
|
791
710
|
|
|
792
711
|
// src/browser/index.ts
|
|
793
|
-
var
|
|
712
|
+
var FALLBACK_PRIMARY = "#15BA68";
|
|
713
|
+
var EARLY_COLOR_BUDGET_MS = 800;
|
|
714
|
+
var primaryFrom = (tokens) => tokens?.colors?.primary?.cannabis || FALLBACK_PRIMARY;
|
|
715
|
+
async function showLoaderWithEarlyColor(tokensPromise, lang) {
|
|
716
|
+
const earlyTokens = await Promise.race([
|
|
717
|
+
tokensPromise,
|
|
718
|
+
new Promise((resolve) => setTimeout(() => resolve(null), EARLY_COLOR_BUDGET_MS))
|
|
719
|
+
]);
|
|
720
|
+
const primaryColor = primaryFrom(earlyTokens);
|
|
721
|
+
const loader = new Loader();
|
|
722
|
+
loader.show(primaryColor, lang);
|
|
723
|
+
return { loader, primaryColor };
|
|
724
|
+
}
|
|
725
|
+
var FlonkKYC = class {
|
|
794
726
|
constructor(options = {}) {
|
|
795
|
-
__publicField(this, "widgetUrl");
|
|
796
|
-
__publicField(this, "apiBase");
|
|
797
727
|
this.widgetUrl = (options.widgetUrl || DEFAULT_WIDGET_URL).replace(/\/$/, "");
|
|
798
728
|
this.apiBase = (options.apiBase || DEFAULT_API_BASE).replace(/\/$/, "");
|
|
799
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Warm the project's branding (colors) ahead of time so the widget paints the
|
|
732
|
+
* brand color on the first frame, with no branding round-trip at click time.
|
|
733
|
+
*
|
|
734
|
+
* Call it early — on page mount, route enter, or hover of the "verify" button
|
|
735
|
+
* — well before `init()`. The result is cached (module-level, 5-min TTL) and
|
|
736
|
+
* every subsequent `init()`/`open()` for the same key reads from it. Safe to
|
|
737
|
+
* call repeatedly; concurrent calls dedupe. Never throws.
|
|
738
|
+
*
|
|
739
|
+
* @example
|
|
740
|
+
* // in a layout effect, long before the user clicks "Verify"
|
|
741
|
+
* FlonkKYC.preloadBranding({ publishableKey: 'pk_live_...' });
|
|
742
|
+
*/
|
|
743
|
+
static preloadBranding(opts) {
|
|
744
|
+
const apiBase = (opts.apiBase || DEFAULT_API_BASE).replace(/\/$/, "");
|
|
745
|
+
return preloadDesignTokens(apiBase, {
|
|
746
|
+
pk: opts.publishableKey,
|
|
747
|
+
sessionId: opts.sessionId
|
|
748
|
+
}).then(() => void 0);
|
|
749
|
+
}
|
|
800
750
|
// ── Public API ───────────────────────────────────────
|
|
801
751
|
/**
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
752
|
+
* Open the KYC verification widget.
|
|
753
|
+
*
|
|
754
|
+
* Flows (pick one; add `publishableKey` to any for an instant branded loader):
|
|
755
|
+
* 1. `{ serverUrl }` — SDK auto-creates the session via your backend (recommended).
|
|
756
|
+
* 2. `{ sessionId, embedToken }` — you created the session; pass its credentials.
|
|
757
|
+
* 3. `{ sessionId }` — **deprecated**: exchanges the sessionId for an embedToken
|
|
758
|
+
* via an extra round-trip. Prefer flow 2 by returning `embedToken` from your
|
|
759
|
+
* backend alongside `sessionId`.
|
|
760
|
+
* 4. `{ publishableKey }` — client-only; SDK mints a short-lived widget token.
|
|
761
|
+
*/
|
|
810
762
|
async init(config) {
|
|
811
763
|
if (!config) throw new FlonkValidationError("config is required");
|
|
812
764
|
if (config.serverUrl) {
|
|
@@ -820,18 +772,17 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
820
772
|
}
|
|
821
773
|
const pk = config.publishableKey;
|
|
822
774
|
if (!pk || !/^pk_/.test(pk)) {
|
|
823
|
-
throw new FlonkValidationError(
|
|
775
|
+
throw new FlonkValidationError(
|
|
776
|
+
"Provide one of: serverUrl, sessionId + embedToken, or publishableKey (pk_*)"
|
|
777
|
+
);
|
|
824
778
|
}
|
|
825
779
|
return this.initWithPublishableKey(config);
|
|
826
780
|
}
|
|
827
781
|
/**
|
|
828
|
-
|
|
829
|
-
|
|
782
|
+
* Preview mode — no API calls, mock data.
|
|
783
|
+
*/
|
|
830
784
|
preview(config = {}) {
|
|
831
|
-
const colors = config.colors || {
|
|
832
|
-
primaryColor: "#3b82f6",
|
|
833
|
-
secondaryColor: "#93c5fd"
|
|
834
|
-
};
|
|
785
|
+
const colors = config.colors || { primaryColor: "#3b82f6", secondaryColor: "#93c5fd" };
|
|
835
786
|
return this.openWidget({
|
|
836
787
|
mode: "preview",
|
|
837
788
|
isPreview: "true",
|
|
@@ -849,20 +800,17 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
849
800
|
});
|
|
850
801
|
}
|
|
851
802
|
/**
|
|
852
|
-
|
|
853
|
-
|
|
803
|
+
* Embed inline preview in a container (for dashboards).
|
|
804
|
+
*/
|
|
854
805
|
embed(config) {
|
|
855
806
|
if (!config?.container) throw new FlonkValidationError("container is required");
|
|
856
807
|
const container = typeof config.container === "string" ? document.querySelector(config.container) : config.container;
|
|
857
808
|
if (!container) throw new FlonkValidationError("Container element not found");
|
|
858
|
-
let colors = config.colors || {
|
|
859
|
-
primaryColor: "#3b82f6",
|
|
860
|
-
secondaryColor: "#93c5fd"
|
|
861
|
-
};
|
|
809
|
+
let colors = config.colors || { primaryColor: "#3b82f6", secondaryColor: "#93c5fd" };
|
|
862
810
|
let device = config.device || "mobile";
|
|
863
811
|
let scale = config.scale ?? 1;
|
|
864
812
|
const lang = config.lang || "de";
|
|
865
|
-
const buildSrc =
|
|
813
|
+
const buildSrc = (c, d) => {
|
|
866
814
|
const p = new URLSearchParams({
|
|
867
815
|
mode: "preview",
|
|
868
816
|
isPreview: "true",
|
|
@@ -873,11 +821,11 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
873
821
|
lang
|
|
874
822
|
});
|
|
875
823
|
return `${this.widgetUrl}/?${p.toString()}`;
|
|
876
|
-
}
|
|
877
|
-
const applyScale =
|
|
824
|
+
};
|
|
825
|
+
const applyScale = () => {
|
|
878
826
|
iframe.style.transform = scale !== 1 ? `scale(${scale})` : "";
|
|
879
827
|
iframe.style.transformOrigin = scale !== 1 ? "top left" : "";
|
|
880
|
-
}
|
|
828
|
+
};
|
|
881
829
|
const iframe = document.createElement("iframe");
|
|
882
830
|
iframe.style.cssText = "border:none;width:100%;height:100%;display:block";
|
|
883
831
|
iframe.src = buildSrc(colors, device);
|
|
@@ -889,10 +837,7 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
889
837
|
return {
|
|
890
838
|
iframe,
|
|
891
839
|
setColors(newColors) {
|
|
892
|
-
colors = {
|
|
893
|
-
...colors,
|
|
894
|
-
...newColors
|
|
895
|
-
};
|
|
840
|
+
colors = { ...colors, ...newColors };
|
|
896
841
|
if (newColors.primaryColor && !newColors.secondaryColor) {
|
|
897
842
|
colors.secondaryColor = generateSecondaryColor(newColors.primaryColor);
|
|
898
843
|
}
|
|
@@ -902,42 +847,34 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
902
847
|
device = d;
|
|
903
848
|
iframe.src = buildSrc(colors, device);
|
|
904
849
|
},
|
|
905
|
-
getColors:
|
|
906
|
-
|
|
907
|
-
}), "getColors"),
|
|
908
|
-
destroy: /* @__PURE__ */ __name(() => iframe.remove(), "destroy")
|
|
850
|
+
getColors: () => ({ ...colors }),
|
|
851
|
+
destroy: () => iframe.remove()
|
|
909
852
|
};
|
|
910
853
|
}
|
|
911
854
|
// ── Private flows ────────────────────────────────────
|
|
912
855
|
/**
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
856
|
+
* Flow 1: serverUrl — POST to client's backend, get sessionId + embedToken.
|
|
857
|
+
*
|
|
858
|
+
* When `publishableKey` is provided, design tokens are fetched in parallel
|
|
859
|
+
* with the session creation request. This shows the branded loader ~200-500ms
|
|
860
|
+
* faster because we don't have to wait for the session to be created first.
|
|
861
|
+
*/
|
|
919
862
|
async initWithServerUrl(config) {
|
|
920
863
|
const pk = config.publishableKey;
|
|
921
|
-
const designTokensPromise = pk ? fetchDesignTokens(this.apiBase, {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
]);
|
|
929
|
-
const primaryColor = earlyTokens?.colors?.primary?.cannabis || "#15BA68";
|
|
930
|
-
const loader = new Loader();
|
|
931
|
-
loader.show(primaryColor, config.lang);
|
|
864
|
+
const designTokensPromise = pk ? fetchDesignTokens(this.apiBase, { pk }) : Promise.resolve(null);
|
|
865
|
+
const sessionPromise = fetchSessionFromServer(
|
|
866
|
+
config.serverUrl,
|
|
867
|
+
config.clientMetadata,
|
|
868
|
+
config.requestHeaders
|
|
869
|
+
);
|
|
870
|
+
const { loader, primaryColor } = await showLoaderWithEarlyColor(designTokensPromise, config.lang);
|
|
932
871
|
try {
|
|
933
872
|
const [{ sessionId, embedToken }, designTokens] = await Promise.all([
|
|
934
873
|
sessionPromise,
|
|
935
874
|
designTokensPromise
|
|
936
875
|
]);
|
|
937
|
-
const finalTokens = designTokens ?? await fetchDesignTokens(this.apiBase, {
|
|
938
|
-
|
|
939
|
-
});
|
|
940
|
-
const finalColor = finalTokens?.colors?.primary?.cannabis || "#15BA68";
|
|
876
|
+
const finalTokens = designTokens ?? await fetchDesignTokens(this.apiBase, { sessionId });
|
|
877
|
+
const finalColor = primaryFrom(finalTokens);
|
|
941
878
|
if (finalColor !== primaryColor) loader.updateColor(finalColor);
|
|
942
879
|
const sessionData = await fetchPublicSession(this.apiBase, sessionId, embedToken);
|
|
943
880
|
const session = {
|
|
@@ -961,17 +898,24 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
961
898
|
}
|
|
962
899
|
}
|
|
963
900
|
/**
|
|
964
|
-
|
|
965
|
-
|
|
901
|
+
* Flow 2: sessionId + embedToken — fetch session data, open widget.
|
|
902
|
+
*/
|
|
966
903
|
async initWithEmbedToken(config) {
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
904
|
+
const pk = config.publishableKey;
|
|
905
|
+
const designTokensPromise = pk ? fetchDesignTokens(this.apiBase, { pk }) : fetchDesignTokens(this.apiBase, { sessionId: config.sessionId });
|
|
906
|
+
const sessionPromise = fetchPublicSession(
|
|
907
|
+
this.apiBase,
|
|
908
|
+
config.sessionId,
|
|
909
|
+
config.embedToken
|
|
910
|
+
);
|
|
911
|
+
const { loader, primaryColor } = await showLoaderWithEarlyColor(designTokensPromise, config.lang);
|
|
973
912
|
try {
|
|
974
|
-
const sessionData = await
|
|
913
|
+
const [sessionData, designTokens] = await Promise.all([
|
|
914
|
+
sessionPromise,
|
|
915
|
+
designTokensPromise
|
|
916
|
+
]);
|
|
917
|
+
const finalColor = primaryFrom(designTokens);
|
|
918
|
+
if (finalColor !== primaryColor) loader.updateColor(finalColor);
|
|
975
919
|
const session = {
|
|
976
920
|
id: sessionData.id,
|
|
977
921
|
allowManualUpload: sessionData.allowManualUpload ?? config.allowManualUpload ?? true,
|
|
@@ -993,17 +937,23 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
993
937
|
}
|
|
994
938
|
}
|
|
995
939
|
/**
|
|
996
|
-
|
|
997
|
-
|
|
940
|
+
* Flow 3: sessionId only — exchange for embedToken, then init.
|
|
941
|
+
*
|
|
942
|
+
* @deprecated Prefer flow 2 (`sessionId` + `embedToken`). Return the
|
|
943
|
+
* `embedToken` from your backend together with the `sessionId` to skip this
|
|
944
|
+
* extra token-exchange round-trip.
|
|
945
|
+
*/
|
|
998
946
|
async initWithSession(config) {
|
|
999
|
-
const
|
|
947
|
+
const designTokensPromise = fetchDesignTokens(this.apiBase, {
|
|
1000
948
|
sessionId: config.sessionId
|
|
1001
949
|
});
|
|
1002
|
-
const
|
|
1003
|
-
const loader =
|
|
1004
|
-
loader.show(primaryColor, config.lang);
|
|
950
|
+
const exchangePromise = exchangeSessionForToken(this.apiBase, config.sessionId);
|
|
951
|
+
const { loader } = await showLoaderWithEarlyColor(designTokensPromise, config.lang);
|
|
1005
952
|
try {
|
|
1006
|
-
const { embedToken, session } = await
|
|
953
|
+
const [{ embedToken, session }, designTokens] = await Promise.all([
|
|
954
|
+
exchangePromise,
|
|
955
|
+
designTokensPromise
|
|
956
|
+
]);
|
|
1007
957
|
return this.buildWidget(embedToken, session, config, loader, designTokens);
|
|
1008
958
|
} catch (err) {
|
|
1009
959
|
const msg = err.message || "Failed to initialize verification";
|
|
@@ -1013,18 +963,15 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1013
963
|
}
|
|
1014
964
|
}
|
|
1015
965
|
/**
|
|
1016
|
-
|
|
1017
|
-
|
|
966
|
+
* Flow 4: publishableKey — fetch widget token, open widget.
|
|
967
|
+
*/
|
|
1018
968
|
async initWithPublishableKey(config) {
|
|
1019
969
|
const pk = config.publishableKey;
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
});
|
|
1023
|
-
const primaryColor = designTokens?.colors?.primary?.cannabis || "#15BA68";
|
|
1024
|
-
const loader = new Loader();
|
|
1025
|
-
loader.show(primaryColor, config.lang);
|
|
970
|
+
const designTokensPromise = fetchDesignTokens(this.apiBase, { pk });
|
|
971
|
+
const widgetTokenPromise = fetchWidgetToken(pk, this.apiBase);
|
|
972
|
+
const { loader, primaryColor } = await showLoaderWithEarlyColor(designTokensPromise, config.lang);
|
|
1026
973
|
try {
|
|
1027
|
-
const data = await
|
|
974
|
+
const data = await widgetTokenPromise;
|
|
1028
975
|
const params = {
|
|
1029
976
|
mode: "embedded",
|
|
1030
977
|
publishableKey: pk,
|
|
@@ -1079,7 +1026,7 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1079
1026
|
if (config.lang) params.lang = config.lang;
|
|
1080
1027
|
if (config.overlayColor) params.overlayColor = config.overlayColor;
|
|
1081
1028
|
return this.openWidget(params, {
|
|
1082
|
-
primaryColor: designTokens
|
|
1029
|
+
primaryColor: primaryFrom(designTokens),
|
|
1083
1030
|
lang: config.lang,
|
|
1084
1031
|
loader,
|
|
1085
1032
|
onSuccess: config.onSuccess,
|
|
@@ -1090,10 +1037,12 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1090
1037
|
});
|
|
1091
1038
|
}
|
|
1092
1039
|
/**
|
|
1093
|
-
|
|
1094
|
-
|
|
1040
|
+
* Core: create iframe, attach listeners, return WidgetInstance.
|
|
1041
|
+
*/
|
|
1095
1042
|
openWidget(params, opts) {
|
|
1096
|
-
const filtered = Object.fromEntries(
|
|
1043
|
+
const filtered = Object.fromEntries(
|
|
1044
|
+
Object.entries(params).filter(([, v]) => v != null)
|
|
1045
|
+
);
|
|
1097
1046
|
const search = new URLSearchParams(filtered);
|
|
1098
1047
|
const src = `${this.widgetUrl}/?${search.toString()}`;
|
|
1099
1048
|
const iframe = createIframe(src);
|
|
@@ -1107,7 +1056,7 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1107
1056
|
mountTarget.appendChild(iframe);
|
|
1108
1057
|
const cleanupViewport = setupViewportSizing(loader.element, iframe);
|
|
1109
1058
|
let cleaned = false;
|
|
1110
|
-
const cleanupAll =
|
|
1059
|
+
const cleanupAll = () => {
|
|
1111
1060
|
if (cleaned) return;
|
|
1112
1061
|
cleaned = true;
|
|
1113
1062
|
try {
|
|
@@ -1117,26 +1066,28 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1117
1066
|
if (iframe.parentNode) iframe.remove();
|
|
1118
1067
|
} catch {
|
|
1119
1068
|
}
|
|
1120
|
-
}
|
|
1121
|
-
const afterCleanup =
|
|
1069
|
+
};
|
|
1070
|
+
const afterCleanup = (delayMs) => setTimeout(cleanupAll, delayMs);
|
|
1122
1071
|
const handler = new MessageHandler(src, iframe, {
|
|
1123
|
-
onSuccess:
|
|
1072
|
+
onSuccess: (r) => {
|
|
1124
1073
|
opts.onSuccess?.(r);
|
|
1125
|
-
afterCleanup(1e3)
|
|
1126
|
-
}
|
|
1127
|
-
onError:
|
|
1074
|
+
afterCleanup(1e3);
|
|
1075
|
+
},
|
|
1076
|
+
onError: (e) => {
|
|
1128
1077
|
opts.onError?.(e);
|
|
1129
|
-
afterCleanup(500)
|
|
1130
|
-
}
|
|
1131
|
-
onCancel:
|
|
1078
|
+
afterCleanup(500);
|
|
1079
|
+
},
|
|
1080
|
+
onCancel: () => {
|
|
1132
1081
|
opts.onCancel?.();
|
|
1133
|
-
afterCleanup(500)
|
|
1134
|
-
}
|
|
1082
|
+
afterCleanup(500);
|
|
1083
|
+
},
|
|
1135
1084
|
onReady: opts.onReady
|
|
1136
1085
|
});
|
|
1137
1086
|
handler.listen();
|
|
1138
1087
|
handler.onReadyOnce(() => {
|
|
1139
|
-
|
|
1088
|
+
if (loader.element) {
|
|
1089
|
+
transitionLoaderToIframe(loader.element, iframe, () => loader.destroy());
|
|
1090
|
+
}
|
|
1140
1091
|
});
|
|
1141
1092
|
return {
|
|
1142
1093
|
iframe,
|
|
@@ -1144,31 +1095,34 @@ var _FlonkKYC = class _FlonkKYC {
|
|
|
1144
1095
|
};
|
|
1145
1096
|
}
|
|
1146
1097
|
};
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1098
|
+
FlonkKYC.version = SDK_VERSION;
|
|
1099
|
+
function FlonkKYCWidget({
|
|
1100
|
+
widgetUrl,
|
|
1101
|
+
apiBase,
|
|
1102
|
+
autoOpen = true,
|
|
1103
|
+
publishableKey,
|
|
1104
|
+
serverUrl,
|
|
1105
|
+
sessionId,
|
|
1106
|
+
embedToken,
|
|
1107
|
+
clientMetadata,
|
|
1108
|
+
lang,
|
|
1109
|
+
overlayColor,
|
|
1110
|
+
allowManualUpload,
|
|
1111
|
+
requestHeaders,
|
|
1112
|
+
onSuccess,
|
|
1113
|
+
onError,
|
|
1114
|
+
onCancel,
|
|
1115
|
+
onReady
|
|
1116
|
+
}) {
|
|
1151
1117
|
const mountRef = react.useRef(null);
|
|
1152
1118
|
const widgetRef = react.useRef(null);
|
|
1153
|
-
const callbacksRef = react.useRef({
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
onSuccess,
|
|
1161
|
-
onError,
|
|
1162
|
-
onCancel,
|
|
1163
|
-
onReady
|
|
1164
|
-
};
|
|
1165
|
-
const sdk = react.useMemo(() => new FlonkKYC({
|
|
1166
|
-
widgetUrl,
|
|
1167
|
-
apiBase
|
|
1168
|
-
}), [
|
|
1169
|
-
widgetUrl,
|
|
1170
|
-
apiBase
|
|
1171
|
-
]);
|
|
1119
|
+
const callbacksRef = react.useRef({ onSuccess, onError, onCancel, onReady });
|
|
1120
|
+
callbacksRef.current = { onSuccess, onError, onCancel, onReady };
|
|
1121
|
+
const sdk = react.useMemo(() => new FlonkKYC({ widgetUrl, apiBase }), [widgetUrl, apiBase]);
|
|
1122
|
+
react.useEffect(() => {
|
|
1123
|
+
if (!publishableKey && !sessionId) return;
|
|
1124
|
+
void FlonkKYC.preloadBranding({ publishableKey, sessionId, apiBase });
|
|
1125
|
+
}, [publishableKey, sessionId, apiBase]);
|
|
1172
1126
|
const generationRef = react.useRef(0);
|
|
1173
1127
|
react.useEffect(() => {
|
|
1174
1128
|
if (!autoOpen) return;
|
|
@@ -1184,10 +1138,10 @@ function FlonkKYCWidget({ widgetUrl, apiBase, autoOpen = true, publishableKey, s
|
|
|
1184
1138
|
allowManualUpload,
|
|
1185
1139
|
requestHeaders,
|
|
1186
1140
|
mount: mountRef.current || void 0,
|
|
1187
|
-
onSuccess:
|
|
1188
|
-
onError:
|
|
1189
|
-
onCancel:
|
|
1190
|
-
onReady:
|
|
1141
|
+
onSuccess: (r) => callbacksRef.current.onSuccess?.(r),
|
|
1142
|
+
onError: (e) => callbacksRef.current.onError?.(e),
|
|
1143
|
+
onCancel: () => callbacksRef.current.onCancel?.(),
|
|
1144
|
+
onReady: () => callbacksRef.current.onReady?.()
|
|
1191
1145
|
};
|
|
1192
1146
|
const timer = setTimeout(() => {
|
|
1193
1147
|
if (generationRef.current !== thisGeneration) return;
|
|
@@ -1208,21 +1162,23 @@ function FlonkKYCWidget({ widgetUrl, apiBase, autoOpen = true, publishableKey, s
|
|
|
1208
1162
|
widgetRef.current?.destroy();
|
|
1209
1163
|
widgetRef.current = null;
|
|
1210
1164
|
};
|
|
1211
|
-
}, [
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1165
|
+
}, [sdk, publishableKey, serverUrl, sessionId, embedToken, lang, overlayColor, allowManualUpload, autoOpen]);
|
|
1166
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: mountRef });
|
|
1167
|
+
}
|
|
1168
|
+
function FlonkKYCBrandingPreloader({
|
|
1169
|
+
publishableKey,
|
|
1170
|
+
sessionId,
|
|
1171
|
+
apiBase
|
|
1172
|
+
}) {
|
|
1173
|
+
react.useEffect(() => {
|
|
1174
|
+
void FlonkKYC.preloadBranding({ publishableKey, sessionId, apiBase });
|
|
1175
|
+
}, [publishableKey, sessionId, apiBase]);
|
|
1176
|
+
return null;
|
|
1221
1177
|
}
|
|
1222
|
-
__name(FlonkKYCWidget, "FlonkKYCWidget");
|
|
1223
1178
|
|
|
1224
1179
|
exports.FlonkError = FlonkError;
|
|
1225
1180
|
exports.FlonkKYC = FlonkKYC;
|
|
1181
|
+
exports.FlonkKYCBrandingPreloader = FlonkKYCBrandingPreloader;
|
|
1226
1182
|
exports.FlonkKYCWidget = FlonkKYCWidget;
|
|
1227
1183
|
exports.FlonkValidationError = FlonkValidationError;
|
|
1228
1184
|
//# sourceMappingURL=index.cjs.map
|