@flonkid/kyc 1.8.1 → 1.9.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 +108 -2
- package/dist/index.cjs +330 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +128 -4
- package/dist/index.d.ts +128 -4
- package/dist/index.js +325 -88
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +102 -32
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +938 -36
- package/dist/server.d.ts +938 -36
- package/dist/server.js +103 -33
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +950 -34
- package/package.json +78 -49
package/dist/server.cjs
CHANGED
|
@@ -22,9 +22,12 @@ function _interopNamespace(e) {
|
|
|
22
22
|
|
|
23
23
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
24
24
|
|
|
25
|
+
// src/server/index.ts
|
|
26
|
+
|
|
25
27
|
// src/shared/constants.ts
|
|
26
|
-
var SDK_VERSION = "1.
|
|
28
|
+
var SDK_VERSION = "1.9.0";
|
|
27
29
|
var DEFAULT_API_BASE = "https://api.flonk.id/v1";
|
|
30
|
+
var API_VERSION = "2026-06-01";
|
|
28
31
|
|
|
29
32
|
// src/shared/errors.ts
|
|
30
33
|
var FlonkError = class extends Error {
|
|
@@ -36,8 +39,8 @@ var FlonkError = class extends Error {
|
|
|
36
39
|
}
|
|
37
40
|
};
|
|
38
41
|
var FlonkAPIError = class extends FlonkError {
|
|
39
|
-
constructor(message, statusCode, body) {
|
|
40
|
-
super(message,
|
|
42
|
+
constructor(message, statusCode, body, code = "api_error") {
|
|
43
|
+
super(message, code, statusCode);
|
|
41
44
|
this.body = body;
|
|
42
45
|
this.name = "FlonkAPIError";
|
|
43
46
|
}
|
|
@@ -62,56 +65,108 @@ var FlonkWebhookSignatureError = class extends FlonkError {
|
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
// src/server/http-client.ts
|
|
68
|
+
var RETRY_BASE_MS_DEFAULT = 250;
|
|
69
|
+
var RETRY_BACKOFF_CAP_MS = 8e3;
|
|
65
70
|
var HttpClient = class {
|
|
66
|
-
constructor(baseUrl, secretKey, timeout = 3e4) {
|
|
71
|
+
constructor(baseUrl, secretKey, timeout = 3e4, options = {}) {
|
|
67
72
|
this.baseUrl = baseUrl;
|
|
68
73
|
this.secretKey = secretKey;
|
|
69
74
|
this.timeout = timeout;
|
|
75
|
+
this.maxRetries = Math.max(0, options.maxRetries ?? 2);
|
|
76
|
+
this.retryBaseMs = Math.max(0, options.retryBaseMs ?? RETRY_BASE_MS_DEFAULT);
|
|
77
|
+
this.apiVersion = options.apiVersion ?? API_VERSION;
|
|
70
78
|
}
|
|
71
79
|
async get(path) {
|
|
72
80
|
return this.request("GET", path);
|
|
73
81
|
}
|
|
74
|
-
async post(path, body) {
|
|
75
|
-
return this.request("POST", path, body);
|
|
82
|
+
async post(path, body, opts) {
|
|
83
|
+
return this.request("POST", path, body, opts);
|
|
76
84
|
}
|
|
77
|
-
async patch(path, body) {
|
|
78
|
-
return this.request("PATCH", path, body);
|
|
85
|
+
async patch(path, body, opts) {
|
|
86
|
+
return this.request("PATCH", path, body, opts);
|
|
79
87
|
}
|
|
80
|
-
async request(method, path, body) {
|
|
88
|
+
async request(method, path, body, opts) {
|
|
89
|
+
const retriable = method === "GET" || Boolean(opts?.idempotencyKey);
|
|
90
|
+
const headers = {
|
|
91
|
+
"Authorization": `Bearer ${this.secretKey}`,
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
"User-Agent": `flonk-kyc-node/${SDK_VERSION}`,
|
|
94
|
+
"Flonk-Version": this.apiVersion
|
|
95
|
+
};
|
|
96
|
+
if (opts?.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
97
|
+
let lastError = null;
|
|
98
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
99
|
+
let retryAfterMs = null;
|
|
100
|
+
try {
|
|
101
|
+
const { status, json, retryAfter } = await this.fetchOnce(method, path, headers, body);
|
|
102
|
+
retryAfterMs = retryAfter;
|
|
103
|
+
if (status >= 200 && status < 300) return json;
|
|
104
|
+
if (status === 401) throw new FlonkAuthenticationError();
|
|
105
|
+
const code = json?.code;
|
|
106
|
+
const err = new FlonkAPIError(
|
|
107
|
+
json?.message || `Request failed: ${status}`,
|
|
108
|
+
status,
|
|
109
|
+
json,
|
|
110
|
+
typeof code === "string" ? code : void 0
|
|
111
|
+
);
|
|
112
|
+
if (retriable && attempt < this.maxRetries && (status === 429 || status >= 500)) {
|
|
113
|
+
lastError = err;
|
|
114
|
+
} else {
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (err instanceof FlonkAPIError || err instanceof FlonkAuthenticationError) throw err;
|
|
119
|
+
lastError = new FlonkAPIError(err.message, 0);
|
|
120
|
+
if (!retriable || attempt >= this.maxRetries) throw lastError;
|
|
121
|
+
}
|
|
122
|
+
await sleep(this.backoffMs(attempt, retryAfterMs));
|
|
123
|
+
}
|
|
124
|
+
throw lastError ?? new FlonkAPIError("Request failed", 0);
|
|
125
|
+
}
|
|
126
|
+
async fetchOnce(method, path, headers, body) {
|
|
81
127
|
const controller = new AbortController();
|
|
82
128
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
83
129
|
try {
|
|
84
130
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
85
131
|
method,
|
|
86
|
-
headers
|
|
87
|
-
|
|
88
|
-
"Content-Type": "application/json",
|
|
89
|
-
"User-Agent": `flonk-kyc-node/${SDK_VERSION}`
|
|
90
|
-
},
|
|
91
|
-
body: body ? JSON.stringify(body) : void 0,
|
|
132
|
+
headers,
|
|
133
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
92
134
|
signal: controller.signal
|
|
93
135
|
});
|
|
94
136
|
const json = await res.json().catch(() => null);
|
|
95
|
-
|
|
96
|
-
if (res.status === 401) throw new FlonkAuthenticationError();
|
|
97
|
-
throw new FlonkAPIError(
|
|
98
|
-
json?.message || `Request failed: ${res.status}`,
|
|
99
|
-
res.status,
|
|
100
|
-
json
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
return json;
|
|
104
|
-
} catch (err) {
|
|
105
|
-
if (err instanceof FlonkAPIError || err instanceof FlonkAuthenticationError) throw err;
|
|
106
|
-
throw new FlonkAPIError(err.message, 0);
|
|
137
|
+
return { status: res.status, json, retryAfter: parseRetryAfter(res.headers) };
|
|
107
138
|
} finally {
|
|
108
139
|
clearTimeout(timer);
|
|
109
140
|
}
|
|
110
141
|
}
|
|
142
|
+
/** Full-jittered exponential backoff, or the server's Retry-After when given. */
|
|
143
|
+
backoffMs(attempt, retryAfterMs) {
|
|
144
|
+
if (retryAfterMs != null) return retryAfterMs;
|
|
145
|
+
const ceiling = Math.min(RETRY_BACKOFF_CAP_MS, this.retryBaseMs * 2 ** attempt);
|
|
146
|
+
return Math.floor(Math.random() * ceiling);
|
|
147
|
+
}
|
|
111
148
|
};
|
|
149
|
+
function sleep(ms) {
|
|
150
|
+
return ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
151
|
+
}
|
|
152
|
+
function parseRetryAfter(headers) {
|
|
153
|
+
const raw = headers.get("retry-after");
|
|
154
|
+
if (!raw) return null;
|
|
155
|
+
const secs = Number(raw);
|
|
156
|
+
if (Number.isFinite(secs)) return Math.max(0, secs * 1e3);
|
|
157
|
+
const date = Date.parse(raw);
|
|
158
|
+
if (Number.isFinite(date)) return Math.max(0, date - Date.now());
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
112
161
|
var Webhooks = class {
|
|
113
162
|
/**
|
|
114
|
-
* Verify X-Signature-256 header: "sha256=<hex>"
|
|
163
|
+
* Verify X-Signature-256 header: "sha256=<hex>".
|
|
164
|
+
*
|
|
165
|
+
* NOTE: this format carries no timestamp, so it has **no replay protection** —
|
|
166
|
+
* a captured request stays valid forever. Prefer {@link verifyTimestampSignature}
|
|
167
|
+
* (the `t=,v1=` header, sent as `X-Signature`), and dedupe by `event.id` in
|
|
168
|
+
* your own store (a unique index, or Redis `SET id 1 NX EX <ttl>`) so retries
|
|
169
|
+
* are processed once.
|
|
115
170
|
*/
|
|
116
171
|
verifySignature(payload, signature, secret) {
|
|
117
172
|
const expected = "sha256=" + crypto__namespace.createHmac("sha256", secret).update(payload).digest("hex");
|
|
@@ -167,9 +222,18 @@ var Webhooks = class {
|
|
|
167
222
|
}
|
|
168
223
|
return result;
|
|
169
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Constant-time string compare with no length-dependent branch: HMAC both
|
|
227
|
+
* sides with a per-call random key so the compared buffers are always 32
|
|
228
|
+
* bytes regardless of input length, then timingSafeEqual. Equal digests imply
|
|
229
|
+
* equal inputs (HMAC collision resistance), so this leaks neither the result
|
|
230
|
+
* nor the length via timing.
|
|
231
|
+
*/
|
|
170
232
|
safeEqual(a, b) {
|
|
171
|
-
|
|
172
|
-
|
|
233
|
+
const key = crypto__namespace.randomBytes(32);
|
|
234
|
+
const ha = crypto__namespace.createHmac("sha256", key).update(a).digest();
|
|
235
|
+
const hb = crypto__namespace.createHmac("sha256", key).update(b).digest();
|
|
236
|
+
return crypto__namespace.timingSafeEqual(ha, hb);
|
|
173
237
|
}
|
|
174
238
|
};
|
|
175
239
|
|
|
@@ -183,13 +247,19 @@ var FlonkKYCServer = class {
|
|
|
183
247
|
throw new FlonkAuthenticationError("Invalid secret key format. Expected: sk_live_*, sk_sandbox_*, sk_test_sandbox_*, sk_test_live_*");
|
|
184
248
|
}
|
|
185
249
|
const base = (options.apiBase || DEFAULT_API_BASE).replace(/\/$/, "");
|
|
186
|
-
this.http = new HttpClient(base, options.secretKey, options.timeout
|
|
250
|
+
this.http = new HttpClient(base, options.secretKey, options.timeout, {
|
|
251
|
+
maxRetries: options.maxRetries,
|
|
252
|
+
retryBaseMs: options.retryBaseMs,
|
|
253
|
+
apiVersion: options.apiVersion
|
|
254
|
+
});
|
|
187
255
|
}
|
|
188
256
|
static {
|
|
189
257
|
this.version = SDK_VERSION;
|
|
190
258
|
}
|
|
191
259
|
async createSession(params) {
|
|
192
|
-
|
|
260
|
+
const { idempotencyKey, ...body } = params ?? {};
|
|
261
|
+
const key = idempotencyKey ?? crypto__namespace.randomUUID();
|
|
262
|
+
return this.http.post("/sessions", body, { idempotencyKey: key });
|
|
193
263
|
}
|
|
194
264
|
async getSession(sessionId) {
|
|
195
265
|
if (!sessionId) throw new FlonkValidationError("sessionId is required");
|
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/constants.ts","../src/shared/errors.ts","../src/server/http-client.ts","../src/server/webhooks.ts","../src/server/index.ts"],"names":["crypto"],"mappings":"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","file":"server.cjs","sourcesContent":["export const SDK_VERSION = '1.8.1';\r\nexport const DEFAULT_WIDGET_URL = 'https://widget.flonk.id';\r\nexport const DEFAULT_API_BASE = 'https://api.flonk.id/v1';\r\n\r\nexport const WIDGET_EVENTS = {\r\n READY: 'KYC_WIDGET_READY',\r\n COMPLETE: 'KYC_COMPLETE',\r\n CANCEL: 'KYC_CANCEL',\r\n ERROR: 'KYC_ERROR',\r\n} as const;\r\n","export class FlonkError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly code: string,\r\n public readonly statusCode?: number,\r\n ) {\r\n super(message);\r\n this.name = 'FlonkError';\r\n }\r\n}\r\n\r\nexport class FlonkAPIError extends FlonkError {\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n public readonly body?: unknown,\r\n ) {\r\n super(message, 'api_error', statusCode);\r\n this.name = 'FlonkAPIError';\r\n }\r\n}\r\n\r\nexport class FlonkAuthenticationError extends FlonkError {\r\n constructor(message = 'Invalid or missing secret key') {\r\n super(message, 'authentication_error', 401);\r\n this.name = 'FlonkAuthenticationError';\r\n }\r\n}\r\n\r\nexport class FlonkValidationError extends FlonkError {\r\n constructor(message: string) {\r\n super(message, 'validation_error', 400);\r\n this.name = 'FlonkValidationError';\r\n }\r\n}\r\n\r\nexport class FlonkWebhookSignatureError extends FlonkError {\r\n constructor(message = 'Invalid webhook signature') {\r\n super(message, 'webhook_signature_error');\r\n this.name = 'FlonkWebhookSignatureError';\r\n }\r\n}\r\n","import { SDK_VERSION } from '../shared/constants';\r\nimport { FlonkAPIError, FlonkAuthenticationError } from '../shared/errors';\r\n\r\nexport class HttpClient {\r\n constructor(\r\n private readonly baseUrl: string,\r\n private readonly secretKey: string,\r\n private readonly timeout = 30_000,\r\n ) {}\r\n\r\n async get<T>(path: string): Promise<T> {\r\n return this.request<T>('GET', path);\r\n }\r\n\r\n async post<T>(path: string, body?: unknown): Promise<T> {\r\n return this.request<T>('POST', path, body);\r\n }\r\n\r\n async patch<T>(path: string, body?: unknown): Promise<T> {\r\n return this.request<T>('PATCH', path, body);\r\n }\r\n\r\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeout);\r\n\r\n try {\r\n const res = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers: {\r\n 'Authorization': `Bearer ${this.secretKey}`,\r\n 'Content-Type': 'application/json',\r\n 'User-Agent': `flonk-kyc-node/${SDK_VERSION}`,\r\n },\r\n body: body ? JSON.stringify(body) : undefined,\r\n signal: controller.signal,\r\n });\r\n\r\n const json = await res.json().catch(() => null);\r\n\r\n if (!res.ok) {\r\n if (res.status === 401) throw new FlonkAuthenticationError();\r\n throw new FlonkAPIError(\r\n json?.message || `Request failed: ${res.status}`,\r\n res.status,\r\n json,\r\n );\r\n }\r\n\r\n return json as T;\r\n } catch (err) {\r\n if (err instanceof FlonkAPIError || err instanceof FlonkAuthenticationError) throw err;\r\n throw new FlonkAPIError((err as Error).message, 0);\r\n } finally {\r\n clearTimeout(timer);\r\n }\r\n }\r\n}\r\n","import * as crypto from 'crypto';\r\nimport { FlonkWebhookSignatureError } from '../shared/errors';\r\nimport type { WebhookEvent, WebhookVerifyOptions } from '../shared/types';\r\n\r\nexport class Webhooks {\r\n /**\r\n * Verify X-Signature-256 header: \"sha256=<hex>\"\r\n */\r\n verifySignature(payload: string, signature: string, secret: string): WebhookEvent {\r\n const expected =\r\n 'sha256=' + crypto.createHmac('sha256', secret).update(payload).digest('hex');\r\n\r\n if (!this.safeEqual(signature, expected)) {\r\n throw new FlonkWebhookSignatureError();\r\n }\r\n\r\n return this.parsePayload(payload);\r\n }\r\n\r\n /**\r\n * Verify Stripe-like header: \"t=<unix>, v1=<hex>\"\r\n */\r\n verifyTimestampSignature(\r\n payload: string,\r\n header: string,\r\n secret: string,\r\n options?: WebhookVerifyOptions,\r\n ): WebhookEvent {\r\n const maxSkew = options?.maxSkewSeconds ?? 300;\r\n const parts = this.parseHeader(header);\r\n const ts = Number(parts['t']);\r\n const v1 = parts['v1'];\r\n\r\n if (!ts || !v1) {\r\n throw new FlonkWebhookSignatureError('Missing timestamp or signature');\r\n }\r\n\r\n if (Math.abs(Math.floor(Date.now() / 1000) - ts) > maxSkew) {\r\n throw new FlonkWebhookSignatureError(`Timestamp skew exceeds ${maxSkew}s`);\r\n }\r\n\r\n const expected = crypto\r\n .createHmac('sha256', secret)\r\n .update(`${ts}.${payload}`)\r\n .digest('hex');\r\n\r\n if (!this.safeEqual(v1, expected)) {\r\n throw new FlonkWebhookSignatureError();\r\n }\r\n\r\n return this.parsePayload(payload);\r\n }\r\n\r\n /**\r\n * Auto-detect signature format and verify.\r\n */\r\n constructEvent(payload: string, signature: string, secret: string): WebhookEvent {\r\n if (signature.startsWith('sha256=')) {\r\n return this.verifySignature(payload, signature, secret);\r\n }\r\n if (signature.includes('t=') && signature.includes('v1=')) {\r\n return this.verifyTimestampSignature(payload, signature, secret);\r\n }\r\n throw new FlonkWebhookSignatureError('Unrecognized signature format');\r\n }\r\n\r\n private parsePayload(payload: string): WebhookEvent {\r\n try {\r\n return JSON.parse(payload) as WebhookEvent;\r\n } catch {\r\n throw new FlonkWebhookSignatureError('Malformed JSON payload');\r\n }\r\n }\r\n\r\n private parseHeader(header: string): Record<string, string> {\r\n const result: Record<string, string> = {};\r\n for (const part of header.split(',')) {\r\n const [k, v] = part.trim().split('=');\r\n if (k && v) result[k] = v;\r\n }\r\n return result;\r\n }\r\n\r\n private safeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) return false;\r\n return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));\r\n }\r\n}\r\n","import { SDK_VERSION, DEFAULT_API_BASE } from '../shared/constants';\r\nimport { FlonkAuthenticationError, FlonkValidationError } from '../shared/errors';\r\nimport type {\r\n FlonkKYCServerOptions,\r\n CreateSessionParams,\r\n Session,\r\n SessionDetails,\r\n UpdateSessionParams,\r\n} from '../shared/types';\r\nimport { HttpClient } from './http-client';\r\nimport { Webhooks } from './webhooks';\r\n\r\nconst SK_PATTERN = /^sk_(live|sandbox|test_sandbox|test_live)_[A-Za-z0-9]{16,}$/;\r\n\r\nexport class FlonkKYCServer {\r\n static readonly version = SDK_VERSION;\r\n\r\n private readonly http: HttpClient;\r\n public readonly webhooks = new Webhooks();\r\n\r\n constructor(options: FlonkKYCServerOptions) {\r\n if (!options.secretKey) throw new FlonkAuthenticationError('secretKey is required');\r\n if (!SK_PATTERN.test(options.secretKey)) {\r\n throw new FlonkAuthenticationError('Invalid secret key format. Expected: sk_live_*, sk_sandbox_*, sk_test_sandbox_*, sk_test_live_*');\r\n }\r\n\r\n const base = (options.apiBase || DEFAULT_API_BASE).replace(/\\/$/, '');\r\n this.http = new HttpClient(base, options.secretKey, options.timeout);\r\n }\r\n\r\n async createSession(params?: CreateSessionParams): Promise<Session> {\r\n return this.http.post<Session>('/sessions', params ?? {});\r\n }\r\n\r\n async getSession(sessionId: string): Promise<SessionDetails> {\r\n if (!sessionId) throw new FlonkValidationError('sessionId is required');\r\n return this.http.get<SessionDetails>(`/sessions/${sessionId}`);\r\n }\r\n\r\n async updateSession(sessionId: string, params: UpdateSessionParams): Promise<SessionDetails> {\r\n if (!sessionId) throw new FlonkValidationError('sessionId is required');\r\n return this.http.patch<SessionDetails>(`/sessions/${sessionId}`, params);\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/constants.ts","../src/shared/errors.ts","../src/server/http-client.ts","../src/server/webhooks.ts","../src/server/index.ts"],"names":["crypto","crypto2"],"mappings":"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","file":"server.cjs","sourcesContent":["// ── SDK metadata & defaults ─────────────────────────────────────────────\n\nexport const SDK_VERSION = '1.9.0';\nexport const DEFAULT_WIDGET_URL = 'https://widget.flonk.id';\nexport const DEFAULT_API_BASE = 'https://api.flonk.id/v1';\n\n/**\n * REST API version this SDK is built against (date-pinned, separate from the\n * package version). Sent as the `Flonk-Version` header so a future breaking API\n * change can be served the old shape to old SDKs and the new shape to new ones.\n * The API is additive-only within a version; a breaking change mints a new date.\n */\nexport const API_VERSION = '2026-06-01';\n\n// ── SDK ⇄ iframe wire contract ──────────────────────────────────────────\n//\n// The SDK and the widget iframe ship and deploy independently, so the wire\n// between them is versioned SEPARATELY from the npm package (SDK_VERSION).\n// PROTOCOL_VERSION changes ONLY on a contract change — and the contract is\n// ADDITIVE-ONLY: never rename or remove an event/param, only add optional ones.\n// That keeps any SDK build compatible with any iframe build (a newer side just\n// ignores fields the older side doesn't send). This file is the single source\n// of truth for that contract; see CONTRACT.md.\n\nexport const PROTOCOL_VERSION = 1;\n\n/** postMessage `type` values exchanged between the SDK and the iframe. */\nexport const WIDGET_EVENTS = {\n READY: 'KYC_WIDGET_READY',\n COMPLETE: 'KYC_COMPLETE',\n CANCEL: 'KYC_CANCEL',\n ERROR: 'KYC_ERROR',\n CONFIG: 'KYC_WIDGET_CONFIG',\n} as const;\n\n/** Canonical iframe URL param keys the SDK stamps onto the widget src. */\nexport const WIDGET_PARAMS = {\n PROTOCOL_VERSION: 'pv',\n SESSION_ID: 'sessionId',\n EMBED_TOKEN: 'embedToken',\n TOKEN: 'token',\n PUBLISHABLE_KEY: 'publishableKey',\n CLIENT_ID: 'clientId',\n CLIENT_METADATA: 'clientMetadata',\n DESIGN_TOKENS: 'designTokens',\n ALLOW_MANUAL_UPLOAD: 'allowManualUpload',\n LANG: 'lang',\n OVERLAY_COLOR: 'overlayColor',\n MODE: 'mode',\n} as const;\n","/**\r\n * Stable, machine-branchable error codes returned by the API in the `code`\r\n * field of an error body. Open union (`| (string & {})`) so new codes don't\r\n * break consumers — branch on the ones you handle, fall through on the rest.\r\n */\r\nexport type FlonkErrorCode =\r\n | 'authentication_error'\r\n | 'invalid_publishable_key'\r\n | 'session_not_found'\r\n | 'session_expired'\r\n | 'session_invalid_state'\r\n | 'rate_limited'\r\n | 'validation_error'\r\n | 'api_error'\r\n | (string & {});\r\n\r\nexport class FlonkError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly code: FlonkErrorCode,\r\n public readonly statusCode?: number,\r\n ) {\r\n super(message);\r\n this.name = 'FlonkError';\r\n }\r\n}\r\n\r\nexport class FlonkAPIError extends FlonkError {\r\n constructor(\r\n message: string,\r\n statusCode: number,\r\n public readonly body?: unknown,\r\n /** The API's stable error code (from the response body), if it sent one. */\r\n code: FlonkErrorCode = 'api_error',\r\n ) {\r\n super(message, code, statusCode);\r\n this.name = 'FlonkAPIError';\r\n }\r\n}\r\n\r\nexport class FlonkAuthenticationError extends FlonkError {\r\n constructor(message = 'Invalid or missing secret key') {\r\n super(message, 'authentication_error', 401);\r\n this.name = 'FlonkAuthenticationError';\r\n }\r\n}\r\n\r\nexport class FlonkValidationError extends FlonkError {\r\n constructor(message: string) {\r\n super(message, 'validation_error', 400);\r\n this.name = 'FlonkValidationError';\r\n }\r\n}\r\n\r\nexport class FlonkWebhookSignatureError extends FlonkError {\r\n constructor(message = 'Invalid webhook signature') {\r\n super(message, 'webhook_signature_error');\r\n this.name = 'FlonkWebhookSignatureError';\r\n }\r\n}\r\n","import { SDK_VERSION, API_VERSION } from '../shared/constants';\nimport { FlonkAPIError, FlonkAuthenticationError } from '../shared/errors';\n\nexport interface HttpClientOptions {\n /** Max retry attempts for transient failures. Default 2 (→ 3 total tries). */\n maxRetries?: number;\n /** Base backoff in ms; actual delay is full-jittered exponential. Default 250. */\n retryBaseMs?: number;\n /** REST API version to pin (`Flonk-Version` header). Default API_VERSION. */\n apiVersion?: string;\n}\n\nexport interface RequestOptions {\n /**\n * Idempotency key for a write. Its presence both sends the\n * `Idempotency-Key` header AND marks the request safe to retry (the server\n * dedupes, so a retried write can't create a duplicate).\n */\n idempotencyKey?: string;\n}\n\nconst RETRY_BASE_MS_DEFAULT = 250;\nconst RETRY_BACKOFF_CAP_MS = 8_000;\n\nexport class HttpClient {\n private readonly maxRetries: number;\n private readonly retryBaseMs: number;\n private readonly apiVersion: string;\n\n constructor(\n private readonly baseUrl: string,\n private readonly secretKey: string,\n private readonly timeout = 30_000,\n options: HttpClientOptions = {},\n ) {\n this.maxRetries = Math.max(0, options.maxRetries ?? 2);\n this.retryBaseMs = Math.max(0, options.retryBaseMs ?? RETRY_BASE_MS_DEFAULT);\n this.apiVersion = options.apiVersion ?? API_VERSION;\n }\n\n async get<T>(path: string): Promise<T> {\n return this.request<T>('GET', path);\n }\n\n async post<T>(path: string, body?: unknown, opts?: RequestOptions): Promise<T> {\n return this.request<T>('POST', path, body, opts);\n }\n\n async patch<T>(path: string, body?: unknown, opts?: RequestOptions): Promise<T> {\n return this.request<T>('PATCH', path, body, opts);\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n opts?: RequestOptions,\n ): Promise<T> {\n // GET is always idempotent. A write is retry-safe ONLY when it carries an\n // idempotency key (the server dedupes it), never otherwise.\n const retriable = method === 'GET' || Boolean(opts?.idempotencyKey);\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.secretKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': `flonk-kyc-node/${SDK_VERSION}`,\n 'Flonk-Version': this.apiVersion,\n };\n if (opts?.idempotencyKey) headers['Idempotency-Key'] = opts.idempotencyKey;\n\n let lastError: FlonkAPIError | FlonkAuthenticationError | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n let retryAfterMs: number | null = null;\n try {\n const { status, json, retryAfter } = await this.fetchOnce(method, path, headers, body);\n retryAfterMs = retryAfter;\n\n if (status >= 200 && status < 300) return json as T;\n\n if (status === 401) throw new FlonkAuthenticationError();\n const code = (json as { code?: unknown })?.code;\n const err = new FlonkAPIError(\n (json as { message?: string })?.message || `Request failed: ${status}`,\n status,\n json,\n typeof code === 'string' ? code : undefined,\n );\n // Retry only transient statuses (429 + 5xx) on retriable requests;\n // anything else is terminal and propagates.\n if (retriable && attempt < this.maxRetries && (status === 429 || status >= 500)) {\n lastError = err;\n } else {\n throw err;\n }\n } catch (err) {\n // FlonkAPIError reaching here is the terminal throw above (transient\n // statuses set lastError without throwing); auth errors never retry.\n if (err instanceof FlonkAPIError || err instanceof FlonkAuthenticationError) throw err;\n // Network / abort / timeout — transient. Retry if allowed, else surface.\n lastError = new FlonkAPIError((err as Error).message, 0);\n if (!retriable || attempt >= this.maxRetries) throw lastError;\n }\n\n await sleep(this.backoffMs(attempt, retryAfterMs));\n }\n\n // Loop exhausted — surface the last transient error.\n throw lastError ?? new FlonkAPIError('Request failed', 0);\n }\n\n private async fetchOnce(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: unknown,\n ): Promise<{ status: number; json: unknown; retryAfter: number | null }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n const json = await res.json().catch(() => null);\n return { status: res.status, json, retryAfter: parseRetryAfter(res.headers) };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Full-jittered exponential backoff, or the server's Retry-After when given. */\n private backoffMs(attempt: number, retryAfterMs: number | null): number {\n if (retryAfterMs != null) return retryAfterMs;\n const ceiling = Math.min(RETRY_BACKOFF_CAP_MS, this.retryBaseMs * 2 ** attempt);\n return Math.floor(Math.random() * ceiling);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();\n}\n\n/** Parse a Retry-After header (delta-seconds or HTTP-date) into ms, or null. */\nfunction parseRetryAfter(headers: { get(name: string): string | null }): number | null {\n const raw = headers.get('retry-after');\n if (!raw) return null;\n const secs = Number(raw);\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000);\n const date = Date.parse(raw);\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\n return null;\n}\n","import * as crypto from 'crypto';\r\nimport { FlonkWebhookSignatureError } from '../shared/errors';\r\nimport type { WebhookEvent, WebhookVerifyOptions } from '../shared/types';\r\n\r\nexport class Webhooks {\r\n /**\r\n * Verify X-Signature-256 header: \"sha256=<hex>\".\r\n *\r\n * NOTE: this format carries no timestamp, so it has **no replay protection** —\r\n * a captured request stays valid forever. Prefer {@link verifyTimestampSignature}\r\n * (the `t=,v1=` header, sent as `X-Signature`), and dedupe by `event.id` in\r\n * your own store (a unique index, or Redis `SET id 1 NX EX <ttl>`) so retries\r\n * are processed once.\r\n */\r\n verifySignature(payload: string, signature: string, secret: string): WebhookEvent {\r\n const expected =\r\n 'sha256=' + crypto.createHmac('sha256', secret).update(payload).digest('hex');\r\n\r\n if (!this.safeEqual(signature, expected)) {\r\n throw new FlonkWebhookSignatureError();\r\n }\r\n\r\n return this.parsePayload(payload);\r\n }\r\n\r\n /**\r\n * Verify Stripe-like header: \"t=<unix>, v1=<hex>\"\r\n */\r\n verifyTimestampSignature(\r\n payload: string,\r\n header: string,\r\n secret: string,\r\n options?: WebhookVerifyOptions,\r\n ): WebhookEvent {\r\n const maxSkew = options?.maxSkewSeconds ?? 300;\r\n const parts = this.parseHeader(header);\r\n const ts = Number(parts['t']);\r\n const v1 = parts['v1'];\r\n\r\n if (!ts || !v1) {\r\n throw new FlonkWebhookSignatureError('Missing timestamp or signature');\r\n }\r\n\r\n if (Math.abs(Math.floor(Date.now() / 1000) - ts) > maxSkew) {\r\n throw new FlonkWebhookSignatureError(`Timestamp skew exceeds ${maxSkew}s`);\r\n }\r\n\r\n const expected = crypto\r\n .createHmac('sha256', secret)\r\n .update(`${ts}.${payload}`)\r\n .digest('hex');\r\n\r\n if (!this.safeEqual(v1, expected)) {\r\n throw new FlonkWebhookSignatureError();\r\n }\r\n\r\n return this.parsePayload(payload);\r\n }\r\n\r\n /**\r\n * Auto-detect signature format and verify.\r\n */\r\n constructEvent(payload: string, signature: string, secret: string): WebhookEvent {\r\n if (signature.startsWith('sha256=')) {\r\n return this.verifySignature(payload, signature, secret);\r\n }\r\n if (signature.includes('t=') && signature.includes('v1=')) {\r\n return this.verifyTimestampSignature(payload, signature, secret);\r\n }\r\n throw new FlonkWebhookSignatureError('Unrecognized signature format');\r\n }\r\n\r\n private parsePayload(payload: string): WebhookEvent {\r\n try {\r\n return JSON.parse(payload) as WebhookEvent;\r\n } catch {\r\n throw new FlonkWebhookSignatureError('Malformed JSON payload');\r\n }\r\n }\r\n\r\n private parseHeader(header: string): Record<string, string> {\r\n const result: Record<string, string> = {};\r\n for (const part of header.split(',')) {\r\n const [k, v] = part.trim().split('=');\r\n if (k && v) result[k] = v;\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * Constant-time string compare with no length-dependent branch: HMAC both\r\n * sides with a per-call random key so the compared buffers are always 32\r\n * bytes regardless of input length, then timingSafeEqual. Equal digests imply\r\n * equal inputs (HMAC collision resistance), so this leaks neither the result\r\n * nor the length via timing.\r\n */\r\n private safeEqual(a: string, b: string): boolean {\r\n const key = crypto.randomBytes(32);\r\n const ha = crypto.createHmac('sha256', key).update(a).digest();\r\n const hb = crypto.createHmac('sha256', key).update(b).digest();\r\n return crypto.timingSafeEqual(ha, hb);\r\n }\r\n}\r\n","import * as crypto from 'crypto';\r\nimport { SDK_VERSION, DEFAULT_API_BASE } from '../shared/constants';\r\nimport { FlonkAuthenticationError, FlonkValidationError } from '../shared/errors';\r\nimport type {\r\n FlonkKYCServerOptions,\r\n CreateSessionParams,\r\n Session,\r\n SessionDetails,\r\n UpdateSessionParams,\r\n} from '../shared/types';\r\nimport { HttpClient } from './http-client';\r\nimport { Webhooks } from './webhooks';\r\n\r\nconst SK_PATTERN = /^sk_(live|sandbox|test_sandbox|test_live)_[A-Za-z0-9]{16,}$/;\r\n\r\nexport class FlonkKYCServer {\r\n static readonly version = SDK_VERSION;\r\n\r\n private readonly http: HttpClient;\r\n public readonly webhooks = new Webhooks();\r\n\r\n constructor(options: FlonkKYCServerOptions) {\r\n if (!options.secretKey) throw new FlonkAuthenticationError('secretKey is required');\r\n if (!SK_PATTERN.test(options.secretKey)) {\r\n throw new FlonkAuthenticationError('Invalid secret key format. Expected: sk_live_*, sk_sandbox_*, sk_test_sandbox_*, sk_test_live_*');\r\n }\r\n\r\n const base = (options.apiBase || DEFAULT_API_BASE).replace(/\\/$/, '');\r\n this.http = new HttpClient(base, options.secretKey, options.timeout, {\r\n maxRetries: options.maxRetries,\r\n retryBaseMs: options.retryBaseMs,\r\n apiVersion: options.apiVersion,\r\n });\r\n }\r\n\r\n async createSession(params?: CreateSessionParams): Promise<Session> {\r\n // Idempotency: a retried create returns the original session, never a\r\n // duplicate. Auto-generate a key per call (overridable) and send it — this\r\n // also makes the write safe for the HttpClient to retry.\r\n const { idempotencyKey, ...body } = params ?? {};\r\n const key = idempotencyKey ?? crypto.randomUUID();\r\n return this.http.post<Session>('/sessions', body, { idempotencyKey: key });\r\n }\r\n\r\n async getSession(sessionId: string): Promise<SessionDetails> {\r\n if (!sessionId) throw new FlonkValidationError('sessionId is required');\r\n return this.http.get<SessionDetails>(`/sessions/${sessionId}`);\r\n }\r\n\r\n async updateSession(sessionId: string, params: UpdateSessionParams): Promise<SessionDetails> {\r\n if (!sessionId) throw new FlonkValidationError('sessionId is required');\r\n return this.http.patch<SessionDetails>(`/sessions/${sessionId}`, params);\r\n }\r\n}\r\n"]}
|