zkp-sdk-node 0.1.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/LICENSE +21 -0
- package/README.md +191 -0
- package/SECURITY.md +34 -0
- package/dist/cjs/client.js +233 -0
- package/dist/cjs/config.js +54 -0
- package/dist/cjs/errors.js +70 -0
- package/dist/cjs/http.js +78 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/invoices.js +74 -0
- package/dist/cjs/models/invoice.js +189 -0
- package/dist/cjs/models/webhook.js +97 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/replay.js +109 -0
- package/dist/cjs/status.js +46 -0
- package/dist/cjs/version.js +5 -0
- package/dist/cjs/webhooks/verifier.js +100 -0
- package/dist/client.d.ts +57 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +228 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +50 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +60 -0
- package/dist/http.d.ts +32 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +73 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/invoices.d.ts +35 -0
- package/dist/invoices.d.ts.map +1 -0
- package/dist/invoices.js +71 -0
- package/dist/models/invoice.d.ts +82 -0
- package/dist/models/invoice.d.ts.map +1 -0
- package/dist/models/invoice.js +184 -0
- package/dist/models/webhook.d.ts +42 -0
- package/dist/models/webhook.d.ts.map +1 -0
- package/dist/models/webhook.js +93 -0
- package/dist/replay.d.ts +52 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +103 -0
- package/dist/status.d.ts +16 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +41 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/webhooks/verifier.d.ts +38 -0
- package/dist/webhooks/verifier.d.ts.map +1 -0
- package/dist/webhooks/verifier.js +96 -0
- package/package.json +66 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client with typed error mapping and a bounded, idempotency-aware
|
|
3
|
+
* retry policy. Adapters never talk to the API directly.
|
|
4
|
+
*
|
|
5
|
+
* Retry rules (identical to the PHP/Python SDKs):
|
|
6
|
+
* - GET: retry NetworkError and selected retryable statuses (429/408/5xx);
|
|
7
|
+
* - POST invoices: retry ONLY when an idempotency key is present;
|
|
8
|
+
* - 429: honors Retry-After up to a 5s cap (plain non-negative integers
|
|
9
|
+
* only; malformed, negative, fractional and HTTP-date values safely map
|
|
10
|
+
* to retryAfter: null and never break error construction);
|
|
11
|
+
* - 400/401/403/422: never retried;
|
|
12
|
+
* - bounded exponential backoff (300ms -> 600ms -> 1200ms), no infinite loops.
|
|
13
|
+
*
|
|
14
|
+
* Requests accept an AbortSignal; a per-request timeout is enforced via
|
|
15
|
+
* AbortController. Secrets never appear in exceptions.
|
|
16
|
+
*/
|
|
17
|
+
import { Config, type ConfigOptions } from "./config.js";
|
|
18
|
+
import { type FetchLike } from "./http.js";
|
|
19
|
+
import { type CreateInvoiceOptions } from "./invoices.js";
|
|
20
|
+
import { CreateInvoiceResponse, Invoice } from "./models/invoice.js";
|
|
21
|
+
import type { WebhookEvent } from "./models/webhook.js";
|
|
22
|
+
import { WebhookVerifier } from "./webhooks/verifier.js";
|
|
23
|
+
/** Stable idempotency key: zerokyc:{platform}:{entity}:{id} (max 120 chars). */
|
|
24
|
+
export declare function idempotencyKey(...parts: Array<string | number>): string;
|
|
25
|
+
export interface RequestCallOptions {
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
/** Injectable delay: sync fire-and-forget or (default) a real Promise-based
|
|
29
|
+
* sleep that actually waits; the retry loop awaits either form. */
|
|
30
|
+
export type Sleeper = (ms: number) => void | Promise<void>;
|
|
31
|
+
export declare class ZeroKYC {
|
|
32
|
+
readonly config: Config;
|
|
33
|
+
private readonly fetchLike;
|
|
34
|
+
private readonly sleeper;
|
|
35
|
+
constructor(options?: ConfigOptions & {
|
|
36
|
+
fetchImpl?: FetchLike;
|
|
37
|
+
/** Injectable for tests; default is a real Promise-based sleep. */
|
|
38
|
+
sleeper?: Sleeper;
|
|
39
|
+
});
|
|
40
|
+
/** Create an invoice; with an idempotency key a timeout+retry returns the
|
|
41
|
+
* same invoice instead of creating a duplicate. */
|
|
42
|
+
createInvoice(options: CreateInvoiceOptions & RequestCallOptions): Promise<CreateInvoiceResponse>;
|
|
43
|
+
/** Reconciliation/recovery: poll a status server-to-server. */
|
|
44
|
+
getInvoice(invoiceId: string, options?: RequestCallOptions): Promise<Invoice>;
|
|
45
|
+
cancelInvoice(invoiceId: string, options?: RequestCallOptions): Promise<Invoice>;
|
|
46
|
+
/** Liveness/configuration probe; returns the decoded /v1/ping body. */
|
|
47
|
+
ping(options?: RequestCallOptions): Promise<Record<string, unknown>>;
|
|
48
|
+
/** Verify a delivery; throws WebhookVerificationError on any failure. */
|
|
49
|
+
verifyWebhook(rawBody: string | Uint8Array, signatureHeader: string, options?: {
|
|
50
|
+
secret?: string;
|
|
51
|
+
}): WebhookEvent;
|
|
52
|
+
verifier(secret?: string): WebhookVerifier;
|
|
53
|
+
/** Await the sleeper; a caller abort during the wait stops the retry loop. */
|
|
54
|
+
private wait;
|
|
55
|
+
private request;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQzD,OAAO,EAAgD,KAAK,SAAS,EAAqB,MAAM,WAAW,CAAC;AAC5G,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzD,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CASvE;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;oEACoE;AACpE,MAAM,MAAM,OAAO,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAO3D,qBAAa,OAAO;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0D;IACpF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAGhC,OAAO,GAAE,aAAa,GAAG;QACvB,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,mEAAmE;QACnE,OAAO,CAAC,EAAE,OAAO,CAAC;KACF;IAOpB;wDACoD;IAC9C,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,kBAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAkBvG,+DAA+D;IACzD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IASjF,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IAe1F,uEAAuE;IACjE,IAAI,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAS9E,yEAAyE;IACzE,aAAa,CACX,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChC,YAAY;IAIf,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe;IAI1C,8EAA8E;YAChE,IAAI;YAgBJ,OAAO;CAoEtB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client with typed error mapping and a bounded, idempotency-aware
|
|
3
|
+
* retry policy. Adapters never talk to the API directly.
|
|
4
|
+
*
|
|
5
|
+
* Retry rules (identical to the PHP/Python SDKs):
|
|
6
|
+
* - GET: retry NetworkError and selected retryable statuses (429/408/5xx);
|
|
7
|
+
* - POST invoices: retry ONLY when an idempotency key is present;
|
|
8
|
+
* - 429: honors Retry-After up to a 5s cap (plain non-negative integers
|
|
9
|
+
* only; malformed, negative, fractional and HTTP-date values safely map
|
|
10
|
+
* to retryAfter: null and never break error construction);
|
|
11
|
+
* - 400/401/403/422: never retried;
|
|
12
|
+
* - bounded exponential backoff (300ms -> 600ms -> 1200ms), no infinite loops.
|
|
13
|
+
*
|
|
14
|
+
* Requests accept an AbortSignal; a per-request timeout is enforced via
|
|
15
|
+
* AbortController. Secrets never appear in exceptions.
|
|
16
|
+
*/
|
|
17
|
+
import { Config } from "./config.js";
|
|
18
|
+
import { APIError, AuthenticationError, NetworkError, RateLimitError, ValidationError, } from "./errors.js";
|
|
19
|
+
import { fetchTransport, sdkUserAgent } from "./http.js";
|
|
20
|
+
import { buildCreatePayload } from "./invoices.js";
|
|
21
|
+
import { CreateInvoiceResponse, Invoice } from "./models/invoice.js";
|
|
22
|
+
import { WebhookVerifier } from "./webhooks/verifier.js";
|
|
23
|
+
const RETRYABLE_STATUSES = new Set([429, 408]);
|
|
24
|
+
const RETRY_AFTER_CAP_SECONDS = 5;
|
|
25
|
+
/** Stable idempotency key: zerokyc:{platform}:{entity}:{id} (max 120 chars). */
|
|
26
|
+
export function idempotencyKey(...parts) {
|
|
27
|
+
const key = `zerokyc:${parts.join(":")}`;
|
|
28
|
+
if (!key) {
|
|
29
|
+
throw new ValidationError("idempotency key must not be empty");
|
|
30
|
+
}
|
|
31
|
+
if (key.length > 120) {
|
|
32
|
+
throw new ValidationError("idempotency key must be at most 120 characters");
|
|
33
|
+
}
|
|
34
|
+
return key;
|
|
35
|
+
}
|
|
36
|
+
const defaultSleeper = (ms) => new Promise((resolve) => {
|
|
37
|
+
setTimeout(resolve, ms);
|
|
38
|
+
});
|
|
39
|
+
export class ZeroKYC {
|
|
40
|
+
config;
|
|
41
|
+
fetchLike;
|
|
42
|
+
sleeper;
|
|
43
|
+
constructor(options = { apiKey: "" }) {
|
|
44
|
+
this.config = new Config(options);
|
|
45
|
+
this.fetchLike = options.fetchImpl ?? fetchTransport;
|
|
46
|
+
this.sleeper = options.sleeper ?? defaultSleeper;
|
|
47
|
+
}
|
|
48
|
+
/** Create an invoice; with an idempotency key a timeout+retry returns the
|
|
49
|
+
* same invoice instead of creating a duplicate. */
|
|
50
|
+
async createInvoice(options) {
|
|
51
|
+
const { signal, ...create } = options;
|
|
52
|
+
const payload = buildCreatePayload(create);
|
|
53
|
+
const headers = { "Content-Type": "application/json" };
|
|
54
|
+
if (create.idempotencyKey !== undefined) {
|
|
55
|
+
headers["Idempotency-Key"] = create.idempotencyKey;
|
|
56
|
+
}
|
|
57
|
+
const response = await this.request("POST", "/v1/invoices", {
|
|
58
|
+
headers,
|
|
59
|
+
body: JSON.stringify(payload),
|
|
60
|
+
mayRetry: create.idempotencyKey !== undefined,
|
|
61
|
+
expectedStatus: 201,
|
|
62
|
+
signal,
|
|
63
|
+
});
|
|
64
|
+
const replay = (response.header("Idempotent-Replay") ?? "").toLowerCase() === "true";
|
|
65
|
+
return new CreateInvoiceResponse(Invoice.fromJson(parseJson(response)), replay);
|
|
66
|
+
}
|
|
67
|
+
/** Reconciliation/recovery: poll a status server-to-server. */
|
|
68
|
+
async getInvoice(invoiceId, options = {}) {
|
|
69
|
+
const response = await this.request("GET", `/v1/invoices/${encodeURIComponent(invoiceId)}`, {
|
|
70
|
+
mayRetry: true,
|
|
71
|
+
expectedStatus: 200,
|
|
72
|
+
signal: options.signal,
|
|
73
|
+
});
|
|
74
|
+
return Invoice.fromJson(parseJson(response));
|
|
75
|
+
}
|
|
76
|
+
async cancelInvoice(invoiceId, options = {}) {
|
|
77
|
+
const response = await this.request("POST", `/v1/invoices/${encodeURIComponent(invoiceId)}/cancel`, {
|
|
78
|
+
headers: { "Content-Type": "application/json" },
|
|
79
|
+
body: "{}",
|
|
80
|
+
mayRetry: false,
|
|
81
|
+
expectedStatus: 200,
|
|
82
|
+
signal: options.signal,
|
|
83
|
+
});
|
|
84
|
+
return Invoice.fromJson(parseJson(response));
|
|
85
|
+
}
|
|
86
|
+
/** Liveness/configuration probe; returns the decoded /v1/ping body. */
|
|
87
|
+
async ping(options = {}) {
|
|
88
|
+
const response = await this.request("GET", "/v1/ping", {
|
|
89
|
+
mayRetry: true,
|
|
90
|
+
expectedStatus: 200,
|
|
91
|
+
signal: options.signal,
|
|
92
|
+
});
|
|
93
|
+
return parseJson(response);
|
|
94
|
+
}
|
|
95
|
+
/** Verify a delivery; throws WebhookVerificationError on any failure. */
|
|
96
|
+
verifyWebhook(rawBody, signatureHeader, options = {}) {
|
|
97
|
+
return this.verifier(options.secret).verify(rawBody, signatureHeader);
|
|
98
|
+
}
|
|
99
|
+
verifier(secret) {
|
|
100
|
+
return new WebhookVerifier(secret ?? this.config.webhookSecret);
|
|
101
|
+
}
|
|
102
|
+
/** Await the sleeper; a caller abort during the wait stops the retry loop. */
|
|
103
|
+
async wait(ms, signal) {
|
|
104
|
+
const sleep = Promise.resolve(this.sleeper(ms));
|
|
105
|
+
if (!signal) {
|
|
106
|
+
await sleep;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (signal.aborted) {
|
|
110
|
+
throw new NetworkError("aborted while waiting to retry");
|
|
111
|
+
}
|
|
112
|
+
await new Promise((resolve, reject) => {
|
|
113
|
+
const onAbort = () => reject(new NetworkError("aborted while waiting to retry"));
|
|
114
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
115
|
+
void sleep.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async request(method, path, opts) {
|
|
119
|
+
let attempt = 0;
|
|
120
|
+
for (;;) {
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await this.fetchLike(this.config.baseUrl + path, {
|
|
124
|
+
method,
|
|
125
|
+
headers: {
|
|
126
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
127
|
+
Accept: "application/json",
|
|
128
|
+
"User-Agent": sdkUserAgent(),
|
|
129
|
+
...opts.headers,
|
|
130
|
+
},
|
|
131
|
+
body: opts.body,
|
|
132
|
+
signal: opts.signal,
|
|
133
|
+
redirect: "manual",
|
|
134
|
+
timeoutMs: this.config.timeoutMs,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
if (!(e instanceof NetworkError)) {
|
|
139
|
+
throw e;
|
|
140
|
+
}
|
|
141
|
+
// the caller cancelled: retrying their own abort is pointless
|
|
142
|
+
if (opts.signal?.aborted) {
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
145
|
+
if (!opts.mayRetry || attempt >= this.config.maxRetries) {
|
|
146
|
+
throw e;
|
|
147
|
+
}
|
|
148
|
+
await this.wait(backoffMs(attempt), opts.signal);
|
|
149
|
+
attempt += 1;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (response.status === opts.expectedStatus) {
|
|
153
|
+
return response;
|
|
154
|
+
}
|
|
155
|
+
if ((RETRYABLE_STATUSES.has(response.status) || response.status >= 500) &&
|
|
156
|
+
opts.mayRetry &&
|
|
157
|
+
attempt < this.config.maxRetries) {
|
|
158
|
+
const delayMs = response.status === 429
|
|
159
|
+
? retryAfterMs(response.header("Retry-After"))
|
|
160
|
+
: backoffMs(attempt);
|
|
161
|
+
if (delayMs !== null) {
|
|
162
|
+
await this.wait(delayMs, opts.signal);
|
|
163
|
+
attempt += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Retry-After beyond the cap: fall through and surface the 429
|
|
167
|
+
}
|
|
168
|
+
throw mapError(response);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function parseJson(response) {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(response.body);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
throw new APIError("response was not valid JSON", response.status);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function mapError(response) {
|
|
181
|
+
let error = {};
|
|
182
|
+
try {
|
|
183
|
+
const decoded = JSON.parse(response.body);
|
|
184
|
+
if (decoded && typeof decoded === "object" && typeof decoded.error === "object") {
|
|
185
|
+
error = decoded.error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// non-JSON error body
|
|
190
|
+
}
|
|
191
|
+
const message = typeof error.message === "string" && error.message
|
|
192
|
+
? error.message
|
|
193
|
+
: `unexpected HTTP ${response.status}`;
|
|
194
|
+
if (response.status === 401 || response.status === 403) {
|
|
195
|
+
return new AuthenticationError(message);
|
|
196
|
+
}
|
|
197
|
+
if (response.status === 429) {
|
|
198
|
+
return new RateLimitError(message, parseRetryAfter(response.header("Retry-After")));
|
|
199
|
+
}
|
|
200
|
+
if (response.status === 400 || response.status === 422) {
|
|
201
|
+
return new ValidationError(message);
|
|
202
|
+
}
|
|
203
|
+
return new APIError(message, response.status, typeof error.code === "string" ? error.code : undefined, typeof error.doc_url === "string" ? error.doc_url : undefined);
|
|
204
|
+
}
|
|
205
|
+
/** Seconds from Retry-After: plain non-negative integers only, else null. */
|
|
206
|
+
function parseRetryAfter(header) {
|
|
207
|
+
if (header === null) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
const value = header.trim();
|
|
211
|
+
return /^\d+$/.test(value) ? Number(value) : null;
|
|
212
|
+
}
|
|
213
|
+
/** Delay for a 429 retry; null means "surface the error immediately". */
|
|
214
|
+
function retryAfterMs(header) {
|
|
215
|
+
if (header === null) {
|
|
216
|
+
return backoffMs(0);
|
|
217
|
+
}
|
|
218
|
+
const seconds = parseRetryAfter(header);
|
|
219
|
+
if (seconds === null) {
|
|
220
|
+
// malformed or HTTP-date: unsupported -> retryAfter reported as null,
|
|
221
|
+
// still retry on the default bounded backoff
|
|
222
|
+
return backoffMs(0);
|
|
223
|
+
}
|
|
224
|
+
return seconds <= RETRY_AFTER_CAP_SECONDS ? seconds * 1000 : null;
|
|
225
|
+
}
|
|
226
|
+
function backoffMs(attempt) {
|
|
227
|
+
return 300 * 2 ** attempt;
|
|
228
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK configuration: sandbox/production, base URL, timeouts, retries.
|
|
3
|
+
*
|
|
4
|
+
* The base URL is defined centrally here; the override exists for tests and
|
|
5
|
+
* local development only - adapters must never hard-code URLs.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_BASE_URL = "https://api.zerokyc-payments.com";
|
|
8
|
+
export declare const SANDBOX = "sandbox";
|
|
9
|
+
export declare const PRODUCTION = "production";
|
|
10
|
+
export type Environment = typeof SANDBOX | typeof PRODUCTION;
|
|
11
|
+
export interface ConfigOptions {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
environment?: Environment;
|
|
14
|
+
webhookSecret?: string;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
maxRetries?: number;
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class Config {
|
|
20
|
+
readonly apiKey: string;
|
|
21
|
+
readonly environment: Environment;
|
|
22
|
+
readonly webhookSecret: string;
|
|
23
|
+
readonly timeoutMs: number;
|
|
24
|
+
readonly maxRetries: number;
|
|
25
|
+
readonly baseUrl: string;
|
|
26
|
+
constructor(options: ConfigOptions);
|
|
27
|
+
get isSandbox(): boolean;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB,qCAAqC,CAAC;AAEnE,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,MAAM,MAAM,WAAW,GAAG,OAAO,OAAO,GAAG,OAAO,UAAU,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,MAAM;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,aAAa;IAqClC,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK configuration: sandbox/production, base URL, timeouts, retries.
|
|
3
|
+
*
|
|
4
|
+
* The base URL is defined centrally here; the override exists for tests and
|
|
5
|
+
* local development only - adapters must never hard-code URLs.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_BASE_URL = "https://api.zerokyc-payments.com";
|
|
8
|
+
export const SANDBOX = "sandbox";
|
|
9
|
+
export const PRODUCTION = "production";
|
|
10
|
+
export class Config {
|
|
11
|
+
apiKey;
|
|
12
|
+
environment;
|
|
13
|
+
webhookSecret;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
maxRetries;
|
|
16
|
+
baseUrl;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
const { apiKey } = options;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
throw new Error("apiKey is required");
|
|
21
|
+
}
|
|
22
|
+
const environment = options.environment ?? (apiKey.startsWith("pk_test_") ? SANDBOX : PRODUCTION);
|
|
23
|
+
if (environment !== SANDBOX && environment !== PRODUCTION) {
|
|
24
|
+
throw new Error(`environment must be 'sandbox' or 'production', got '${environment}'`);
|
|
25
|
+
}
|
|
26
|
+
// Mixing up sandbox and live keys is the classic production incident:
|
|
27
|
+
// refuse the mismatch outright instead of hoping for the best.
|
|
28
|
+
const isTestKey = apiKey.startsWith("pk_test_");
|
|
29
|
+
if (environment === PRODUCTION && isTestKey) {
|
|
30
|
+
throw new Error("environment is production but the apiKey is a sandbox key (pk_test_...); " +
|
|
31
|
+
"use a pk_live_... key or set environment: 'sandbox'");
|
|
32
|
+
}
|
|
33
|
+
if (environment === SANDBOX && !isTestKey) {
|
|
34
|
+
throw new Error("environment is sandbox but the apiKey is not a sandbox key; expected a pk_test_... key");
|
|
35
|
+
}
|
|
36
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
37
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0 || maxRetries > 5) {
|
|
38
|
+
throw new Error("maxRetries must be an integer between 0 and 5");
|
|
39
|
+
}
|
|
40
|
+
this.apiKey = apiKey;
|
|
41
|
+
this.environment = environment;
|
|
42
|
+
this.webhookSecret = options.webhookSecret ?? "";
|
|
43
|
+
this.timeoutMs = options.timeoutMs ?? 15_000;
|
|
44
|
+
this.maxRetries = maxRetries;
|
|
45
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
46
|
+
}
|
|
47
|
+
get isSandbox() {
|
|
48
|
+
return this.environment === SANDBOX;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Mapping: 401/403 -> AuthenticationError, 400/422 -> ValidationError,
|
|
5
|
+
* 429 -> RateLimitError (carries retryAfter seconds when sent), 5xx/402/404
|
|
6
|
+
* -> APIError, transport failures (timeout/DNS/connect/abort) ->
|
|
7
|
+
* NetworkError, webhook verification -> WebhookVerificationError
|
|
8
|
+
* (machine-readable `reason`).
|
|
9
|
+
*
|
|
10
|
+
* Secrets never appear in messages: they carry API-provided text only.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ZeroKYCError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/** 401/403 from the API: missing/invalid key or forbidden scope. Never retried. */
|
|
16
|
+
export declare class AuthenticationError extends ZeroKYCError {
|
|
17
|
+
}
|
|
18
|
+
/** 400/422 from the API (or local request validation). Never retried. */
|
|
19
|
+
export declare class ValidationError extends ZeroKYCError {
|
|
20
|
+
}
|
|
21
|
+
/** 429 from the API; `retryAfter` carries Retry-After seconds when parseable. */
|
|
22
|
+
export declare class RateLimitError extends ZeroKYCError {
|
|
23
|
+
readonly retryAfter: number | null;
|
|
24
|
+
constructor(message: string, retryAfter?: number | null);
|
|
25
|
+
}
|
|
26
|
+
/** Unexpected API error (5xx, 402 cutoff, 404, malformed envelope). */
|
|
27
|
+
export declare class APIError extends ZeroKYCError {
|
|
28
|
+
readonly status: number;
|
|
29
|
+
readonly errorCode?: string | undefined;
|
|
30
|
+
readonly docUrl?: string | undefined;
|
|
31
|
+
constructor(message: string, status?: number, errorCode?: string | undefined, docUrl?: string | undefined);
|
|
32
|
+
}
|
|
33
|
+
/** Transport failure: DNS, connect, TLS, timeout or abort. */
|
|
34
|
+
export declare class NetworkError extends ZeroKYCError {
|
|
35
|
+
}
|
|
36
|
+
/** Webhook verification failed; `reason` is machine-readable. */
|
|
37
|
+
export declare class WebhookVerificationError extends ZeroKYCError {
|
|
38
|
+
readonly reason: string;
|
|
39
|
+
static readonly MISSING_HEADER = "missing_header";
|
|
40
|
+
static readonly MALFORMED_HEADER = "malformed_header";
|
|
41
|
+
static readonly STALE_TIMESTAMP = "stale_timestamp";
|
|
42
|
+
static readonly FUTURE_TIMESTAMP = "future_timestamp";
|
|
43
|
+
static readonly SIGNATURE_MISMATCH = "signature_mismatch";
|
|
44
|
+
static readonly MALFORMED_PAYLOAD = "malformed_payload";
|
|
45
|
+
constructor(message: string, reason: string);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,mFAAmF;AACnF,qBAAa,mBAAoB,SAAQ,YAAY;CAAG;AAExD,yEAAyE;AACzE,qBAAa,eAAgB,SAAQ,YAAY;CAAG;AAEpD,iFAAiF;AACjF,qBAAa,cAAe,SAAQ,YAAY;aAG5B,UAAU,EAAE,MAAM,GAAG,IAAI;gBADzC,OAAO,EAAE,MAAM,EACC,UAAU,GAAE,MAAM,GAAG,IAAW;CAInD;AAED,uEAAuE;AACvE,qBAAa,QAAS,SAAQ,YAAY;aAGtB,MAAM,EAAE,MAAM;aACd,SAAS,CAAC,EAAE,MAAM;aAClB,MAAM,CAAC,EAAE,MAAM;gBAH/B,OAAO,EAAE,MAAM,EACC,MAAM,GAAE,MAAU,EAClB,SAAS,CAAC,EAAE,MAAM,YAAA,EAClB,MAAM,CAAC,EAAE,MAAM,YAAA;CAIlC;AAED,8DAA8D;AAC9D,qBAAa,YAAa,SAAQ,YAAY;CAAG;AAEjD,iEAAiE;AACjE,qBAAa,wBAAyB,SAAQ,YAAY;aAUtC,MAAM,EAAE,MAAM;IAThC,MAAM,CAAC,QAAQ,CAAC,cAAc,oBAAoB;IAClD,MAAM,CAAC,QAAQ,CAAC,gBAAgB,sBAAsB;IACtD,MAAM,CAAC,QAAQ,CAAC,eAAe,qBAAqB;IACpD,MAAM,CAAC,QAAQ,CAAC,gBAAgB,sBAAsB;IACtD,MAAM,CAAC,QAAQ,CAAC,kBAAkB,wBAAwB;IAC1D,MAAM,CAAC,QAAQ,CAAC,iBAAiB,uBAAuB;gBAGtD,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM;CAIjC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Mapping: 401/403 -> AuthenticationError, 400/422 -> ValidationError,
|
|
5
|
+
* 429 -> RateLimitError (carries retryAfter seconds when sent), 5xx/402/404
|
|
6
|
+
* -> APIError, transport failures (timeout/DNS/connect/abort) ->
|
|
7
|
+
* NetworkError, webhook verification -> WebhookVerificationError
|
|
8
|
+
* (machine-readable `reason`).
|
|
9
|
+
*
|
|
10
|
+
* Secrets never appear in messages: they carry API-provided text only.
|
|
11
|
+
*/
|
|
12
|
+
export class ZeroKYCError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = new.target.name;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** 401/403 from the API: missing/invalid key or forbidden scope. Never retried. */
|
|
19
|
+
export class AuthenticationError extends ZeroKYCError {
|
|
20
|
+
}
|
|
21
|
+
/** 400/422 from the API (or local request validation). Never retried. */
|
|
22
|
+
export class ValidationError extends ZeroKYCError {
|
|
23
|
+
}
|
|
24
|
+
/** 429 from the API; `retryAfter` carries Retry-After seconds when parseable. */
|
|
25
|
+
export class RateLimitError extends ZeroKYCError {
|
|
26
|
+
retryAfter;
|
|
27
|
+
constructor(message, retryAfter = null) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.retryAfter = retryAfter;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Unexpected API error (5xx, 402 cutoff, 404, malformed envelope). */
|
|
33
|
+
export class APIError extends ZeroKYCError {
|
|
34
|
+
status;
|
|
35
|
+
errorCode;
|
|
36
|
+
docUrl;
|
|
37
|
+
constructor(message, status = 0, errorCode, docUrl) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.status = status;
|
|
40
|
+
this.errorCode = errorCode;
|
|
41
|
+
this.docUrl = docUrl;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Transport failure: DNS, connect, TLS, timeout or abort. */
|
|
45
|
+
export class NetworkError extends ZeroKYCError {
|
|
46
|
+
}
|
|
47
|
+
/** Webhook verification failed; `reason` is machine-readable. */
|
|
48
|
+
export class WebhookVerificationError extends ZeroKYCError {
|
|
49
|
+
reason;
|
|
50
|
+
static MISSING_HEADER = "missing_header";
|
|
51
|
+
static MALFORMED_HEADER = "malformed_header";
|
|
52
|
+
static STALE_TIMESTAMP = "stale_timestamp";
|
|
53
|
+
static FUTURE_TIMESTAMP = "future_timestamp";
|
|
54
|
+
static SIGNATURE_MISMATCH = "signature_mismatch";
|
|
55
|
+
static MALFORMED_PAYLOAD = "malformed_payload";
|
|
56
|
+
constructor(message, reason) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.reason = reason;
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP transport: built-in fetch with per-request timeout via
|
|
3
|
+
* AbortController, an injectable fetch for tests, and redirect: "manual"
|
|
4
|
+
* (a 3xx must surface - the same rule as the PHP/Python SDKs).
|
|
5
|
+
*/
|
|
6
|
+
export interface HttpResponse {
|
|
7
|
+
status: number;
|
|
8
|
+
body: string;
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
header(name: string): string | null;
|
|
11
|
+
}
|
|
12
|
+
export interface FetchInit {
|
|
13
|
+
method: string;
|
|
14
|
+
headers: Record<string, string>;
|
|
15
|
+
body?: string;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
redirect: "manual";
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
export type FetchLike = (url: string, init: FetchInit) => Promise<HttpResponse>;
|
|
21
|
+
export declare function httpResponse(status: number, body: string, headers?: Record<string, string>): HttpResponse;
|
|
22
|
+
/**
|
|
23
|
+
* Built-in fetch transport: per-request timeout via AbortController, caller
|
|
24
|
+
* aborts forwarded into the same controller (original reason preserved),
|
|
25
|
+
* redirect: "manual" - a 3xx must surface (same rule as the PHP/Python SDKs).
|
|
26
|
+
* Listener and timer lifecycles are leak-free: everything is cleaned in
|
|
27
|
+
* finally, so a reused caller signal never accumulates handlers.
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchTransport(url: string, init: FetchInit): Promise<HttpResponse>;
|
|
30
|
+
/** Identifiable SDK user agent (CDNs commonly block library default UAs). */
|
|
31
|
+
export declare function sdkUserAgent(): string;
|
|
32
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEhF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,YAAY,CAad;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAwCxF;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP transport: built-in fetch with per-request timeout via
|
|
3
|
+
* AbortController, an injectable fetch for tests, and redirect: "manual"
|
|
4
|
+
* (a 3xx must surface - the same rule as the PHP/Python SDKs).
|
|
5
|
+
*/
|
|
6
|
+
import { NetworkError } from "./errors.js";
|
|
7
|
+
import { VERSION } from "./version.js";
|
|
8
|
+
export function httpResponse(status, body, headers = {}) {
|
|
9
|
+
const normalized = {};
|
|
10
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
11
|
+
normalized[k.toLowerCase()] = v;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
status,
|
|
15
|
+
body,
|
|
16
|
+
headers: normalized,
|
|
17
|
+
header(name) {
|
|
18
|
+
return normalized[name.toLowerCase()] ?? null;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Built-in fetch transport: per-request timeout via AbortController, caller
|
|
24
|
+
* aborts forwarded into the same controller (original reason preserved),
|
|
25
|
+
* redirect: "manual" - a 3xx must surface (same rule as the PHP/Python SDKs).
|
|
26
|
+
* Listener and timer lifecycles are leak-free: everything is cleaned in
|
|
27
|
+
* finally, so a reused caller signal never accumulates handlers.
|
|
28
|
+
*/
|
|
29
|
+
export async function fetchTransport(url, init) {
|
|
30
|
+
// ONE request controller: caller aborts are forwarded into it, and the
|
|
31
|
+
// forwarding listener plus the timeout timer are always removed in finally
|
|
32
|
+
// (a reused caller signal must never accumulate listeners).
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
controller.abort(new Error("request timeout"));
|
|
36
|
+
}, init.timeoutMs ?? 15_000);
|
|
37
|
+
const caller = init.signal;
|
|
38
|
+
const onCallerAbort = () => {
|
|
39
|
+
// surface the caller's own abort reason, not a generic one
|
|
40
|
+
controller.abort(caller?.reason);
|
|
41
|
+
};
|
|
42
|
+
if (caller?.aborted) {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
throw new NetworkError(`transport failure: ${String(caller.reason ?? "aborted")}`);
|
|
45
|
+
}
|
|
46
|
+
caller?.addEventListener("abort", onCallerAbort, { once: true });
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(url, {
|
|
49
|
+
method: init.method,
|
|
50
|
+
headers: init.headers,
|
|
51
|
+
body: init.body,
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
redirect: "manual",
|
|
54
|
+
});
|
|
55
|
+
const headers = {};
|
|
56
|
+
response.headers.forEach((value, key) => {
|
|
57
|
+
headers[key] = value;
|
|
58
|
+
});
|
|
59
|
+
return httpResponse(response.status, await response.text(), headers);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
// fetch rejects on network/timeout/abort; DNS/connect arrive as TypeError
|
|
63
|
+
throw new NetworkError(`transport failure: ${e.message}`);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
caller?.removeEventListener("abort", onCallerAbort);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Identifiable SDK user agent (CDNs commonly block library default UAs). */
|
|
71
|
+
export function sdkUserAgent() {
|
|
72
|
+
return `zerokyc-node/${VERSION}`;
|
|
73
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Node.js/TypeScript SDK for the ZeroKYC Pay crypto payment gateway.
|
|
3
|
+
*
|
|
4
|
+
* Server-side only: API keys and webhook verification must never run in a
|
|
5
|
+
* browser.
|
|
6
|
+
*/
|
|
7
|
+
export { ZeroKYC, idempotencyKey } from "./client.js";
|
|
8
|
+
export { Config, DEFAULT_BASE_URL } from "./config.js";
|
|
9
|
+
export { Invoice, CreateInvoiceResponse, type PaymentOption, type InvoiceObservation, type InvoiceData, } from "./models/invoice.js";
|
|
10
|
+
export { WebhookEvent, type VerificationResult } from "./models/webhook.js";
|
|
11
|
+
export { WebhookVerifier, DEFAULT_TOLERANCE_SECONDS } from "./webhooks/verifier.js";
|
|
12
|
+
export { ReplayGuard, compareDecimals, type EventStore, type MatchOrderOptions } from "./replay.js";
|
|
13
|
+
export { StatusMapper, isTerminalStatus, type InvoiceStatus } from "./status.js";
|
|
14
|
+
export { ZeroKYCError, AuthenticationError, ValidationError, RateLimitError, APIError, NetworkError, WebhookVerificationError, } from "./errors.js";
|
|
15
|
+
export { VERSION } from "./version.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EACL,OAAO,EACP,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official Node.js/TypeScript SDK for the ZeroKYC Pay crypto payment gateway.
|
|
3
|
+
*
|
|
4
|
+
* Server-side only: API keys and webhook verification must never run in a
|
|
5
|
+
* browser.
|
|
6
|
+
*/
|
|
7
|
+
export { ZeroKYC, idempotencyKey } from "./client.js";
|
|
8
|
+
export { Config, DEFAULT_BASE_URL } from "./config.js";
|
|
9
|
+
export { Invoice, CreateInvoiceResponse, } from "./models/invoice.js";
|
|
10
|
+
export { WebhookEvent } from "./models/webhook.js";
|
|
11
|
+
export { WebhookVerifier, DEFAULT_TOLERANCE_SECONDS } from "./webhooks/verifier.js";
|
|
12
|
+
export { ReplayGuard, compareDecimals } from "./replay.js";
|
|
13
|
+
export { StatusMapper, isTerminalStatus } from "./status.js";
|
|
14
|
+
export { ZeroKYCError, AuthenticationError, ValidationError, RateLimitError, APIError, NetworkError, WebhookVerificationError, } from "./errors.js";
|
|
15
|
+
export { VERSION } from "./version.js";
|