@flonkid/kyc 1.4.1 → 1.5.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 CHANGED
@@ -11,45 +11,66 @@ Official SDK for [Flonk](https://flonk.id) identity verification.
11
11
  npm install @flonkid/kyc
12
12
  ```
13
13
 
14
- ## Browser Imperative
14
+ | Import | Use |
15
+ |--------|-----|
16
+ | `@flonkid/kyc` | Browser — widget + React component |
17
+ | `@flonkid/kyc/server` | Node.js — sessions API + webhook verification |
15
18
 
16
- ```typescript
17
- import { FlonkKYC } from '@flonkid/kyc';
19
+ ## React Component (recommended)
18
20
 
19
- const kyc = new FlonkKYC();
20
- const widget = await kyc.init({
21
- publishableKey: 'pk_live_...',
22
- onSuccess: (result) => console.log('Verified:', result),
23
- onError: (err) => console.error(err),
24
- });
21
+ ### Option A: SDK handles session creation
25
22
 
26
- // Later: widget.destroy()
23
+ ```tsx
24
+ import { FlonkKYCWidget } from '@flonkid/kyc';
25
+
26
+ <FlonkKYCWidget
27
+ serverUrl="/api/kyc/create-session"
28
+ clientMetadata={{ email: 'user@example.com', userId: 'user_123' }}
29
+ lang="de"
30
+ onSuccess={(result) => console.log('Verified:', result)}
31
+ onError={(error) => console.error(error)}
32
+ onCancel={() => console.log('Cancelled')}
33
+ />
34
+
35
+ // With authenticated backend
36
+ <FlonkKYCWidget
37
+ serverUrl="/api/kyc/create-session"
38
+ requestHeaders={{ Authorization: `Bearer ${token}` }}
39
+ ...
40
+ />
27
41
  ```
28
42
 
29
- ## Browser React Component
43
+ ### Option B: You control session creation
30
44
 
31
45
  ```tsx
32
46
  import { FlonkKYCWidget } from '@flonkid/kyc';
33
47
 
34
- function App() {
35
- return (
36
- <FlonkKYCWidget
37
- publishableKey="pk_live_..."
38
- onSuccess={(result) => console.log('Verified:', result)}
39
- onError={(err) => console.error(err)}
40
- />
41
- );
42
- }
48
+ // 1. Create session on your backend first
49
+ const { sessionId, embedToken } = await fetch('/api/kyc/create-session', {
50
+ method: 'POST',
51
+ body: JSON.stringify({ email: user.email }),
52
+ }).then(r => r.json());
53
+
54
+ // 2. Pass credentials to widget
55
+ <FlonkKYCWidget
56
+ sessionId={sessionId}
57
+ embedToken={embedToken}
58
+ lang="de"
59
+ onSuccess={(result) => console.log('Verified:', result)}
60
+ onError={(error) => console.error(error)}
61
+ onCancel={() => console.log('Cancelled')}
62
+ />
43
63
  ```
44
64
 
45
65
  ### Props
46
66
 
47
67
  | Prop | Type | Description |
48
68
  |------|------|-------------|
49
- | `publishableKey` | `string` | Client-side key (`pk_live_*` or `pk_sandbox_*`) |
50
- | `serverUrl` | `string` | Your backend endpoint for session creation |
51
- | `sessionId` | `string` | Pre-created session ID |
52
- | `embedToken` | `string` | JWT token from server-to-server flow |
69
+ | `serverUrl` | `string` | Your backend endpoint SDK auto-creates session |
70
+ | `sessionId` | `string` | Pre-created session ID (Option B) |
71
+ | `embedToken` | `string` | JWT token from your backend (Option B) |
72
+ | `requestHeaders` | `Record<string, string>` | Extra headers for serverUrl (e.g. JWT auth) |
73
+ | `clientMetadata` | `Record<string, unknown>` | Custom data passed to session |
53
74
  | `lang` | `'en' \| 'de' \| 'uk'` | Widget language |
54
75
  | `onSuccess` | `(result) => void` | Verification completed |
55
76
  | `onError` | `(error) => void` | Error occurred |
@@ -57,6 +78,26 @@ function App() {
57
78
  | `onReady` | `() => void` | Widget loaded |
58
79
  | `autoOpen` | `boolean` | Open on mount (default: `true`) |
59
80
 
81
+ ## Imperative API (non-React)
82
+
83
+ ```typescript
84
+ import { FlonkKYC } from '@flonkid/kyc';
85
+
86
+ const kyc = new FlonkKYC();
87
+
88
+ const widget = await kyc.init({
89
+ serverUrl: '/api/kyc/create-session',
90
+ clientMetadata: { email: 'user@example.com' },
91
+ lang: 'de',
92
+ onSuccess: (result) => console.log('Verified:', result),
93
+ onError: (error) => console.error(error),
94
+ onCancel: () => console.log('Cancelled'),
95
+ });
96
+
97
+ // Cleanup
98
+ widget.destroy();
99
+ ```
100
+
60
101
  ## Server — Node.js
61
102
 
62
103
  ```typescript
@@ -65,11 +106,14 @@ import { FlonkKYCServer } from '@flonkid/kyc/server';
65
106
  const flonk = new FlonkKYCServer({ secretKey: 'sk_live_...' });
66
107
 
67
108
  // Create session
68
- const session = await flonk.sessions.create({
69
- clientMetadata: { userId: 'user_123' },
109
+ const session = await flonk.createSession({
110
+ clientMetadata: { email: 'user@example.com', userId: 'user_123' },
111
+ expiryMinutes: 30,
112
+ language: 'de',
70
113
  });
114
+ // → { id, embedToken, status, expiresAt, widgetUrl, qrCodeUrl }
71
115
 
72
- // Verify webhook signature
116
+ // Verify webhook
73
117
  const event = flonk.webhooks.constructEvent(rawBody, signature, secret);
74
118
  ```
75
119
 
@@ -78,7 +122,7 @@ const event = flonk.webhooks.constructEvent(rawBody, signature, secret);
78
122
  | Event | Description |
79
123
  |-------|-------------|
80
124
  | `verification.completed` | AI verification finished |
81
- | `verification.status_changed` | Admin changed status |
125
+ | `verification.status_changed` | Admin changed status (approved/rejected) |
82
126
  | `verification.updated` | Admin edited verification data |
83
127
 
84
128
  ```typescript
@@ -86,13 +130,13 @@ const event = flonk.webhooks.constructEvent(rawBody, signature, secret);
86
130
 
87
131
  switch (event.type) {
88
132
  case 'verification.completed':
89
- // Automated decision
133
+ console.log('Extracted:', event.data.object.extracted_data);
90
134
  break;
91
135
  case 'verification.status_changed':
92
- // Admin approved/rejected
136
+ console.log('New status:', event.data.object.status);
93
137
  break;
94
138
  case 'verification.updated':
95
- // Admin edited extracted data
139
+ console.log('Updated by:', event.data.object.updated_by);
96
140
  break;
97
141
  }
98
142
  ```
@@ -100,9 +144,9 @@ switch (event.type) {
100
144
  ## Links
101
145
 
102
146
  - [Full Documentation](https://docs.flonk.id)
103
- - [Dashboard](https://dashboard.flonk.id)
147
+ - [Integration Guide](https://docs.flonk.id/docs/integration-frontend-backend)
104
148
  - [Webhook Guide](https://docs.flonk.id/docs/webhooks)
105
- - [API Reference](https://docs.flonk.id/docs/api-verification)
149
+ - [Dashboard](https://dashboard.flonk.id)
106
150
 
107
151
  ## License
108
152
 
package/dist/index.cjs CHANGED
@@ -121,11 +121,12 @@ function validateServerUrl(url) {
121
121
  throw new Error(`Invalid serverUrl: ${url}`);
122
122
  }
123
123
  }
124
- async function fetchSessionFromServer(serverUrl, clientMetadata) {
124
+ async function fetchSessionFromServer(serverUrl, clientMetadata, requestHeaders) {
125
125
  validateServerUrl(serverUrl);
126
126
  const res = await fetch(serverUrl, {
127
127
  method: "POST",
128
- headers: { "Content-Type": "application/json" },
128
+ headers: { "Content-Type": "application/json", ...requestHeaders },
129
+ credentials: "include",
129
130
  body: JSON.stringify({ clientMetadata })
130
131
  });
131
132
  if (!res.ok) {
@@ -775,7 +776,8 @@ var FlonkKYC = class {
775
776
  try {
776
777
  const { sessionId, embedToken } = await fetchSessionFromServer(
777
778
  config.serverUrl,
778
- config.clientMetadata
779
+ config.clientMetadata,
780
+ config.requestHeaders
779
781
  );
780
782
  loader.destroy();
781
783
  return this.initWithEmbedToken({ ...config, sessionId, embedToken });
@@ -984,6 +986,7 @@ function FlonkKYCWidget({
984
986
  lang,
985
987
  overlayColor,
986
988
  allowManualUpload,
989
+ requestHeaders,
987
990
  onSuccess,
988
991
  onError,
989
992
  onCancel,
@@ -991,50 +994,45 @@ function FlonkKYCWidget({
991
994
  }) {
992
995
  const mountRef = react.useRef(null);
993
996
  const widgetRef = react.useRef(null);
994
- const destroyedRef = react.useRef(false);
995
- const initPromiseRef = react.useRef(null);
996
997
  const callbacksRef = react.useRef({ onSuccess, onError, onCancel, onReady });
997
998
  callbacksRef.current = { onSuccess, onError, onCancel, onReady };
998
999
  const sdk = react.useMemo(() => new FlonkKYC({ widgetUrl, apiBase }), [widgetUrl, apiBase]);
1000
+ const generationRef = react.useRef(0);
999
1001
  react.useEffect(() => {
1000
1002
  if (!autoOpen) return;
1001
- const prev = initPromiseRef.current;
1002
- destroyedRef.current = false;
1003
- const run = async () => {
1004
- if (prev) await prev.catch(() => {
1005
- });
1006
- if (destroyedRef.current) return;
1007
- const config = {
1008
- publishableKey,
1009
- serverUrl,
1010
- sessionId,
1011
- embedToken,
1012
- clientMetadata,
1013
- lang,
1014
- overlayColor,
1015
- allowManualUpload,
1016
- mount: mountRef.current || void 0,
1017
- onSuccess: (r) => callbacksRef.current.onSuccess?.(r),
1018
- onError: (e) => callbacksRef.current.onError?.(e),
1019
- onCancel: () => callbacksRef.current.onCancel?.(),
1020
- onReady: () => callbacksRef.current.onReady?.()
1021
- };
1022
- try {
1023
- const instance = await sdk.init(config);
1024
- if (destroyedRef.current) {
1003
+ const thisGeneration = ++generationRef.current;
1004
+ const config = {
1005
+ publishableKey,
1006
+ serverUrl,
1007
+ sessionId,
1008
+ embedToken,
1009
+ clientMetadata,
1010
+ lang,
1011
+ overlayColor,
1012
+ allowManualUpload,
1013
+ requestHeaders,
1014
+ mount: mountRef.current || void 0,
1015
+ onSuccess: (r) => callbacksRef.current.onSuccess?.(r),
1016
+ onError: (e) => callbacksRef.current.onError?.(e),
1017
+ onCancel: () => callbacksRef.current.onCancel?.(),
1018
+ onReady: () => callbacksRef.current.onReady?.()
1019
+ };
1020
+ const timer = setTimeout(() => {
1021
+ if (generationRef.current !== thisGeneration) return;
1022
+ sdk.init(config).then((instance) => {
1023
+ if (generationRef.current !== thisGeneration) {
1025
1024
  instance.destroy();
1026
1025
  } else {
1027
1026
  widgetRef.current = instance;
1028
1027
  }
1029
- } catch (err) {
1030
- if (!destroyedRef.current) {
1028
+ }).catch((err) => {
1029
+ if (generationRef.current === thisGeneration) {
1031
1030
  callbacksRef.current.onError?.(err.message);
1032
1031
  }
1033
- }
1034
- };
1035
- initPromiseRef.current = run();
1032
+ });
1033
+ }, 0);
1036
1034
  return () => {
1037
- destroyedRef.current = true;
1035
+ clearTimeout(timer);
1038
1036
  widgetRef.current?.destroy();
1039
1037
  widgetRef.current = null;
1040
1038
  };