@finteqhub/sdk-js 0.12.0-beta.1 → 0.12.0-beta.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/README.md +11 -20
- package/dist/src/processing.d.ts +2 -9
- package/dist/src/processing.js +19 -20
- package/dist/src/utils.d.ts +0 -2
- package/dist/src/utils.js +0 -26
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,42 +1,32 @@
|
|
|
1
1
|
# processing-sdk
|
|
2
2
|
|
|
3
|
-
Use `new FinteqHubProcessing(
|
|
3
|
+
Use `new FinteqHubProcessing(apiUrl: string, fingerprintVisitorId: string, merchantId: string, sessionId: string, isSecure?: boolean, retryOptions?: RetryOptions)` to create an instance of the FinteqHubProcessing object. The FinteqHubProcessing object is your entrypoint to FinteqHub processing SDK.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
fingerprintVisitorId: string;
|
|
9
|
-
merchantId: string;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
isSecure?: boolean; // default false
|
|
12
|
-
retryOptions?: RetryOptions;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const processing = new FinteqHubProcessing({
|
|
16
|
-
apiUrl: 'api-url',
|
|
17
|
-
fingerprintVisitorId: 'fingerprint-visitor-id',
|
|
18
|
-
merchantId: 'merchant-id',
|
|
19
|
-
sessionId: 'session-id',
|
|
20
|
-
});
|
|
6
|
+
const processing = new FinteqHubProcessing('api-url', 'fingerprint-visitor-id', 'merchant-id', 'session-id');
|
|
21
7
|
```
|
|
22
8
|
|
|
23
9
|
## Retries and error diagnostics
|
|
24
10
|
|
|
25
|
-
Failed HTTP requests are retried automatically with exponential backoff (`100ms → 200ms → 500ms → 1000ms → 2000ms`; every retry after the fifth waits 2000ms). Retries can be configured via the `retryOptions` constructor
|
|
11
|
+
Failed HTTP requests are retried automatically with exponential backoff (`100ms → 200ms → 500ms → 1000ms → 2000ms`; every retry after the fifth waits 2000ms). Retries can be configured via the optional `retryOptions` constructor argument (the sixth one, after `isSecure`):
|
|
26
12
|
|
|
27
13
|
```
|
|
28
14
|
interface RetryOptions {
|
|
29
15
|
retryCount?: number; // number of retries after the initial attempt, default 5; 0 disables retries
|
|
30
16
|
retryStatusCode?: (statusCode: number) => boolean; // default: statusCode < 200 || statusCode === 408 || statusCode >= 500
|
|
31
17
|
}
|
|
18
|
+
|
|
19
|
+
const processing = new FinteqHubProcessing('api-url', 'fingerprint-visitor-id', 'merchant-id', 'session-id', false, {
|
|
20
|
+
retryCount: 3,
|
|
21
|
+
});
|
|
32
22
|
```
|
|
33
23
|
|
|
34
24
|
Network errors (the browser could not reach the server at all, e.g. `TypeError: Failed to fetch`, or the connection dropped while the response body was being read) are always retried too.
|
|
35
25
|
|
|
36
|
-
Every retry attempt is reported with `console.warn`. When a request finally fails, the SDK logs `console.error` with a full diagnostic dump and rejects with a `RequestError` whose `diagnostics` field carries the same payload — include it in your error reporting. `diagnostics.kind` tells the failure class apart:
|
|
26
|
+
Every retry attempt is reported with `console.warn`. When a request finally fails, the SDK logs `console.error` (`sdk-js: request failed: <message>`) with a full diagnostic dump and rejects with a `RequestError` whose `diagnostics` field carries the same payload — include it in your error reporting. `diagnostics.kind` tells the failure class apart:
|
|
37
27
|
|
|
38
28
|
- `"network"` — the browser never got a complete response (e.g. `TypeError: Failed to fetch`, or the body could not be read); `diagnostics.error` describes the thrown error, `diagnostics.response.status` is present when headers had arrived before the failure, `message` is `request to <url> failed after N attempt(s): ...`;
|
|
39
|
-
- `"http_error"` — an error response (non-200 status, or an `error` field in the body); `message` is the error text from the response body (or `unexpected response status <code>` when the body has none), `diagnostics.response.status` carries the HTTP status;
|
|
29
|
+
- `"http_error"` — an error response (non-200 status, or an `error` field in the body); `message` is the error text from the response body (or `unexpected response status <code>` when the body has none), `diagnostics.response.status` carries the HTTP status and `diagnostics.response.error` the error text from the body;
|
|
40
30
|
- `"invalid_json"` — the response body is not valid JSON; both `diagnostics.error` (the parse error; its message follows the same policy as the body — sanitized for non-200, omitted for 200 responses) and `diagnostics.response` are set.
|
|
41
31
|
|
|
42
32
|
Every `diagnostics` payload also includes:
|
|
@@ -44,6 +34,7 @@ Every `diagnostics` payload also includes:
|
|
|
44
34
|
- `sdkVersion`;
|
|
45
35
|
- `request`: url, method, `x-request-id`, session id, and per-attempt log `attempts: [{ durationMs, status?, error? }]` — attempt durations help tell an instant failure (DNS/connection refused) from a hang (timeout/handshake);
|
|
46
36
|
- `response.body` for non-200 responses — truncated to 500 chars with long digit runs masked (`***`); the body of a 200 response is never included since it may carry session credentials; the request body is never included anywhere;
|
|
37
|
+
- `response.error` — the `error` field of the response body, for any status (including a 200 response with an `error` field); it is a business error text, so it is neither truncated nor masked;
|
|
47
38
|
- `environment`: `navigator.onLine`, `document.visibilityState`, `navigator.connection` (`effectiveType`/`rtt`/`downlink`, Chromium only), timestamp.
|
|
48
39
|
|
|
49
40
|
Diagnostics are collected regardless of whether retries are enabled.
|
|
@@ -93,7 +84,7 @@ import FingerprintJS from "@fingerprintjs/fingerprintjs";
|
|
|
93
84
|
const fp = await FingerprintJS.load();
|
|
94
85
|
const result = await fp.get();
|
|
95
86
|
|
|
96
|
-
const processing = new FinteqHubProcessing(
|
|
87
|
+
const processing = new FinteqHubProcessing(apiUrl, result.visitorId, merchantId, sessionId);
|
|
97
88
|
const session = await processing.getSession();
|
|
98
89
|
|
|
99
90
|
const data = {/** collect data from form and session **/}
|
package/dist/src/processing.d.ts
CHANGED
|
@@ -3,14 +3,6 @@ export interface RetryOptions {
|
|
|
3
3
|
retryCount?: number;
|
|
4
4
|
retryStatusCode?: (statusCode: number) => boolean;
|
|
5
5
|
}
|
|
6
|
-
export interface ProcessingOptions {
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
fingerprintVisitorId: string;
|
|
9
|
-
merchantId: string;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
isSecure?: boolean;
|
|
12
|
-
retryOptions?: RetryOptions;
|
|
13
|
-
}
|
|
14
6
|
export declare type RequestAttempt = {
|
|
15
7
|
durationMs: number;
|
|
16
8
|
status?: number;
|
|
@@ -28,6 +20,7 @@ export declare type RequestDiagnostics = {
|
|
|
28
20
|
response?: {
|
|
29
21
|
status: number;
|
|
30
22
|
body?: string;
|
|
23
|
+
error?: string;
|
|
31
24
|
};
|
|
32
25
|
request: {
|
|
33
26
|
url: string;
|
|
@@ -59,7 +52,7 @@ export declare class FinteqHubProcessing {
|
|
|
59
52
|
private projectId;
|
|
60
53
|
private isSecure;
|
|
61
54
|
private retryOptions;
|
|
62
|
-
constructor(
|
|
55
|
+
constructor(apiUrl: string, fingerprintVisitorId: string, merchantId: string, sessionId: string, isSecure?: boolean, retryOptions?: RetryOptions);
|
|
63
56
|
getSession(): Promise<SessionResponse>;
|
|
64
57
|
submitForm(data: SubmitData): Promise<ProcessOperationRedirectResponse>;
|
|
65
58
|
private processOperation;
|
package/dist/src/processing.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { getDeviceData, uuid
|
|
10
|
+
import { getDeviceData, uuid } from "./utils";
|
|
11
11
|
import { SDK_HEADER_NAME, SDK_HEADER_VALUE, SDK_VERSION } from "./version";
|
|
12
12
|
export class RequestError extends Error {
|
|
13
13
|
constructor(message, diagnostics) {
|
|
@@ -25,15 +25,13 @@ const sanitizeBody = (body) => body.slice(0, MAX_BODY_SNIPPET_LENGTH).replace(/\
|
|
|
25
25
|
// failures and for duplicate-submit rejections — retrying either only delays the error
|
|
26
26
|
const defaultRetryStatusCode = (statusCode) => statusCode < 200 || statusCode === 408 || statusCode >= 500;
|
|
27
27
|
export class FinteqHubProcessing {
|
|
28
|
-
constructor(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.isSecure = (_a = options.isSecure) !== null && _a !== void 0 ? _a : false;
|
|
36
|
-
this.retryOptions = (_b = options.retryOptions) !== null && _b !== void 0 ? _b : {};
|
|
28
|
+
constructor(apiUrl, fingerprintVisitorId, merchantId, sessionId, isSecure = false, retryOptions = {}) {
|
|
29
|
+
this.apiUrl = apiUrl;
|
|
30
|
+
this.fingerprintVisitorId = fingerprintVisitorId;
|
|
31
|
+
this.merchantId = merchantId;
|
|
32
|
+
this.sessionId = sessionId;
|
|
33
|
+
this.isSecure = isSecure;
|
|
34
|
+
this.retryOptions = retryOptions;
|
|
37
35
|
}
|
|
38
36
|
getSession() {
|
|
39
37
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -136,10 +134,10 @@ export class FinteqHubProcessing {
|
|
|
136
134
|
? sanitizeBody(e.message)
|
|
137
135
|
: "parse error message omitted (200 response body may carry credentials)",
|
|
138
136
|
};
|
|
139
|
-
throw this.responseError("invalid_json", `request to ${url} returned invalid JSON (status ${response.status})`, url, options, attempts, response, body, parseError);
|
|
137
|
+
throw this.responseError("invalid_json", `request to ${url} returned invalid JSON (status ${response.status})`, url, options, attempts, { status: response.status, body }, parseError);
|
|
140
138
|
}
|
|
141
139
|
if ((result === null || result === void 0 ? void 0 : result.error) || response.status !== 200) {
|
|
142
|
-
throw this.responseError("http_error", (result === null || result === void 0 ? void 0 : result.error) || `unexpected response status ${response.status}`, url, options, attempts, response, body);
|
|
140
|
+
throw this.responseError("http_error", (result === null || result === void 0 ? void 0 : result.error) || `unexpected response status ${response.status}`, url, options, attempts, { status: response.status, body, error: result === null || result === void 0 ? void 0 : result.error });
|
|
143
141
|
}
|
|
144
142
|
return result;
|
|
145
143
|
});
|
|
@@ -166,9 +164,10 @@ export class FinteqHubProcessing {
|
|
|
166
164
|
// status is known when the failure happened while reading the body of a received response
|
|
167
165
|
attempts.push({ durationMs: Date.now() - startedAt, status: response === null || response === void 0 ? void 0 : response.status, error: e.message });
|
|
168
166
|
if (attempt > retryCount) {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
const message = `request to ${url} failed after ${attempt} attempt(s): ${e.message}`;
|
|
168
|
+
const diagnostics = this.collectDiagnostics("network", url, options, attempts, e, response ? { status: response.status } : undefined);
|
|
169
|
+
console.error(`sdk-js: request failed: ${message}`, diagnostics);
|
|
170
|
+
throw new RequestError(message, diagnostics);
|
|
172
171
|
}
|
|
173
172
|
reason = e.message;
|
|
174
173
|
}
|
|
@@ -188,12 +187,12 @@ export class FinteqHubProcessing {
|
|
|
188
187
|
}
|
|
189
188
|
});
|
|
190
189
|
}
|
|
191
|
-
responseError(kind, message, url, options, attempts, response,
|
|
192
|
-
const diagnostics = this.collectDiagnostics(kind, url, options, attempts, error, response
|
|
193
|
-
console.error(
|
|
190
|
+
responseError(kind, message, url, options, attempts, response, error) {
|
|
191
|
+
const diagnostics = this.collectDiagnostics(kind, url, options, attempts, error, response);
|
|
192
|
+
console.error(`sdk-js: request failed: ${message}`, diagnostics);
|
|
194
193
|
return new RequestError(message, diagnostics);
|
|
195
194
|
}
|
|
196
|
-
collectDiagnostics(kind, url, options, attempts, error, response
|
|
195
|
+
collectDiagnostics(kind, url, options, attempts, error, response) {
|
|
197
196
|
const connection = navigator.connection;
|
|
198
197
|
return {
|
|
199
198
|
kind,
|
|
@@ -206,7 +205,7 @@ export class FinteqHubProcessing {
|
|
|
206
205
|
cause: error.cause,
|
|
207
206
|
}
|
|
208
207
|
: undefined,
|
|
209
|
-
response
|
|
208
|
+
response,
|
|
210
209
|
request: {
|
|
211
210
|
url,
|
|
212
211
|
method: options.method,
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ProcessingOptions } from "./processing";
|
|
2
1
|
export declare const DeviceType: {
|
|
3
2
|
Unknown: string;
|
|
4
3
|
Computer: string;
|
|
@@ -10,7 +9,6 @@ export declare const DeviceType: {
|
|
|
10
9
|
};
|
|
11
10
|
export declare function uuid(): string;
|
|
12
11
|
export declare function getDeviceType(): string;
|
|
13
|
-
export declare function validateOptions(options: ProcessingOptions): void;
|
|
14
12
|
export declare function getDeviceData(): {
|
|
15
13
|
device: {
|
|
16
14
|
type: string;
|
package/dist/src/utils.js
CHANGED
|
@@ -40,32 +40,6 @@ export function getDeviceType() {
|
|
|
40
40
|
}
|
|
41
41
|
return DeviceType.Unknown;
|
|
42
42
|
}
|
|
43
|
-
const REQUIRED_OPTIONS = ["apiUrl", "fingerprintVisitorId", "merchantId", "sessionId"];
|
|
44
|
-
export function validateOptions(options) {
|
|
45
|
-
if (typeof options !== "object" || options === null) {
|
|
46
|
-
throw new TypeError("sdk-js: constructor expects an options object: { apiUrl, fingerprintVisitorId, merchantId, sessionId, isSecure?, retryOptions? }");
|
|
47
|
-
}
|
|
48
|
-
for (const key of REQUIRED_OPTIONS) {
|
|
49
|
-
if (typeof options[key] !== "string" || options[key] === "") {
|
|
50
|
-
throw new TypeError(`sdk-js: option "${key}" must be a non-empty string`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (options.isSecure !== undefined && typeof options.isSecure !== "boolean") {
|
|
54
|
-
throw new TypeError('sdk-js: option "isSecure" must be a boolean');
|
|
55
|
-
}
|
|
56
|
-
if (options.retryOptions !== undefined) {
|
|
57
|
-
if (typeof options.retryOptions !== "object" || options.retryOptions === null) {
|
|
58
|
-
throw new TypeError('sdk-js: option "retryOptions" must be an object');
|
|
59
|
-
}
|
|
60
|
-
const { retryCount, retryStatusCode } = options.retryOptions;
|
|
61
|
-
if (retryCount !== undefined && (!Number.isInteger(retryCount) || retryCount < 0)) {
|
|
62
|
-
throw new TypeError('sdk-js: option "retryOptions.retryCount" must be a non-negative integer');
|
|
63
|
-
}
|
|
64
|
-
if (retryStatusCode !== undefined && typeof retryStatusCode !== "function") {
|
|
65
|
-
throw new TypeError('sdk-js: option "retryOptions.retryStatusCode" must be a function');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
43
|
export function getDeviceData() {
|
|
70
44
|
var _a, _b, _c;
|
|
71
45
|
return {
|
package/dist/src/version.d.ts
CHANGED
package/dist/src/version.js
CHANGED