kryptoexpress-sdk 0.1.1
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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/index.cjs +702 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +248 -0
- package/dist/index.d.ts +248 -0
- package/dist/index.js +653 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
// src/core/config.ts
|
|
2
|
+
var DEFAULT_RETRY_STATUS_CODES = [408, 409, 429, 500, 502, 503, 504];
|
|
3
|
+
function resolveClientOptions(options = {}) {
|
|
4
|
+
const normalizedBaseUrl = (options.baseUrl ?? "https://kryptoexpress.pro/api").replace(/\/+$/, "");
|
|
5
|
+
return {
|
|
6
|
+
apiKey: options.apiKey,
|
|
7
|
+
baseUrl: normalizedBaseUrl,
|
|
8
|
+
timeoutMs: options.timeoutMs ?? 1e4,
|
|
9
|
+
retry: {
|
|
10
|
+
maxRetries: options.retry?.maxRetries ?? 2,
|
|
11
|
+
retryStatusCodes: options.retry?.retryStatusCodes ?? DEFAULT_RETRY_STATUS_CODES
|
|
12
|
+
},
|
|
13
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
14
|
+
fiatConverter: options.fiatConverter
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/domain/errors.ts
|
|
19
|
+
var SDKError = class extends Error {
|
|
20
|
+
cause;
|
|
21
|
+
constructor(message, options = {}) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = new.target.name;
|
|
24
|
+
this.cause = options.cause;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var ValidationError = class extends SDKError {
|
|
28
|
+
details;
|
|
29
|
+
constructor(message, options = {}) {
|
|
30
|
+
super(message, options);
|
|
31
|
+
this.details = options.details;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var UnsupportedPaymentModeError = class extends ValidationError {
|
|
35
|
+
};
|
|
36
|
+
var MinimumAmountError = class extends ValidationError {
|
|
37
|
+
minimumUsdAmount;
|
|
38
|
+
constructor(message, minimumUsdAmount = 1, options = {}) {
|
|
39
|
+
super(message, options);
|
|
40
|
+
this.minimumUsdAmount = minimumUsdAmount;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var CurrencyConversionError = class extends ValidationError {
|
|
44
|
+
};
|
|
45
|
+
var APIError = class extends SDKError {
|
|
46
|
+
statusCode;
|
|
47
|
+
code;
|
|
48
|
+
details;
|
|
49
|
+
constructor(message, statusCode, options = {}) {
|
|
50
|
+
super(message, options);
|
|
51
|
+
this.statusCode = statusCode;
|
|
52
|
+
this.code = options.code;
|
|
53
|
+
this.details = options.details;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var AuthError = class extends APIError {
|
|
57
|
+
};
|
|
58
|
+
var RateLimitError = class extends APIError {
|
|
59
|
+
};
|
|
60
|
+
var NotFoundError = class extends APIError {
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/core/http.ts
|
|
64
|
+
var HttpClient = class {
|
|
65
|
+
options;
|
|
66
|
+
constructor(options) {
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
async request(request) {
|
|
70
|
+
const url = buildUrl(this.options.baseUrl, request.path, request.query);
|
|
71
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs);
|
|
74
|
+
try {
|
|
75
|
+
const requestInit = {
|
|
76
|
+
method: request.method ?? "GET",
|
|
77
|
+
headers: this.buildHeaders(request.requiresAuth ?? false, request.body !== void 0),
|
|
78
|
+
signal: controller.signal
|
|
79
|
+
};
|
|
80
|
+
if (request.body !== void 0) {
|
|
81
|
+
requestInit.body = JSON.stringify(request.body);
|
|
82
|
+
}
|
|
83
|
+
const response = await this.options.fetch(url, requestInit);
|
|
84
|
+
clearTimeout(timeout);
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
if (attempt < this.options.retry.maxRetries && this.options.retry.retryStatusCodes.includes(response.status)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
throw await toApiError(response);
|
|
90
|
+
}
|
|
91
|
+
if (response.status === 204) {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
return await response.json();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
if (!(error instanceof APIError) && attempt < this.options.retry.maxRetries) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
101
|
+
throw new SDKError(`Request timed out after ${this.options.timeoutMs}ms.`, { cause: error });
|
|
102
|
+
}
|
|
103
|
+
if (error instanceof SDKError) {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
throw new SDKError("Unexpected HTTP client error.", { cause: error });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
buildHeaders(requiresAuth, hasJsonBody) {
|
|
111
|
+
const headers = new Headers();
|
|
112
|
+
headers.set("Accept", "application/json");
|
|
113
|
+
if (hasJsonBody) {
|
|
114
|
+
headers.set("Content-Type", "application/json");
|
|
115
|
+
}
|
|
116
|
+
if (requiresAuth) {
|
|
117
|
+
if (!this.options.apiKey) {
|
|
118
|
+
throw new AuthError("This endpoint requires an API key.", 401);
|
|
119
|
+
}
|
|
120
|
+
headers.set("X-Api-Key", this.options.apiKey);
|
|
121
|
+
}
|
|
122
|
+
return headers;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
function buildUrl(baseUrl, path, query = {}) {
|
|
126
|
+
const url = new URL(`${baseUrl}${path}`);
|
|
127
|
+
for (const [key, value] of Object.entries(query)) {
|
|
128
|
+
if (value === void 0) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
url.searchParams.set(key, String(value));
|
|
132
|
+
}
|
|
133
|
+
return url.toString();
|
|
134
|
+
}
|
|
135
|
+
async function toApiError(response) {
|
|
136
|
+
const bodyText = await response.text();
|
|
137
|
+
let payload;
|
|
138
|
+
try {
|
|
139
|
+
payload = bodyText ? JSON.parse(bodyText) : void 0;
|
|
140
|
+
} catch {
|
|
141
|
+
payload = void 0;
|
|
142
|
+
}
|
|
143
|
+
const message = typeof payload?.message === "string" && payload.message || typeof payload?.error === "string" && payload.error || `Request failed with status ${response.status}.`;
|
|
144
|
+
const code = typeof payload?.code === "string" ? payload.code : void 0;
|
|
145
|
+
const options = code === void 0 ? { details: payload ?? bodyText } : { code, details: payload ?? bodyText };
|
|
146
|
+
if (response.status === 401 || response.status === 403) {
|
|
147
|
+
return new AuthError(message, response.status, options);
|
|
148
|
+
}
|
|
149
|
+
if (response.status === 404) {
|
|
150
|
+
return new NotFoundError(message, response.status, options);
|
|
151
|
+
}
|
|
152
|
+
if (response.status === 429) {
|
|
153
|
+
return new RateLimitError(message, response.status, options);
|
|
154
|
+
}
|
|
155
|
+
return new APIError(message, response.status, options);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/domain/enums.ts
|
|
159
|
+
var FIAT_CURRENCIES = [
|
|
160
|
+
"USD",
|
|
161
|
+
"EUR",
|
|
162
|
+
"GBP",
|
|
163
|
+
"JPY",
|
|
164
|
+
"CHF",
|
|
165
|
+
"AUD",
|
|
166
|
+
"CAD",
|
|
167
|
+
"CNY",
|
|
168
|
+
"HKD",
|
|
169
|
+
"SGD",
|
|
170
|
+
"SEK",
|
|
171
|
+
"NOK",
|
|
172
|
+
"DKK",
|
|
173
|
+
"PLN",
|
|
174
|
+
"CZK",
|
|
175
|
+
"HUF",
|
|
176
|
+
"TRY",
|
|
177
|
+
"INR",
|
|
178
|
+
"KRW",
|
|
179
|
+
"THB",
|
|
180
|
+
"IDR",
|
|
181
|
+
"MYR",
|
|
182
|
+
"PHP",
|
|
183
|
+
"VND",
|
|
184
|
+
"AED",
|
|
185
|
+
"SAR",
|
|
186
|
+
"ZAR",
|
|
187
|
+
"NGN",
|
|
188
|
+
"KES",
|
|
189
|
+
"GHS",
|
|
190
|
+
"BRL",
|
|
191
|
+
"MXN",
|
|
192
|
+
"ARS",
|
|
193
|
+
"CLP",
|
|
194
|
+
"COP",
|
|
195
|
+
"PEN",
|
|
196
|
+
"RUB",
|
|
197
|
+
"UAH",
|
|
198
|
+
"ILS",
|
|
199
|
+
"PKR",
|
|
200
|
+
"BDT",
|
|
201
|
+
"LKR",
|
|
202
|
+
"TWD",
|
|
203
|
+
"BHD",
|
|
204
|
+
"KWD",
|
|
205
|
+
"RON",
|
|
206
|
+
"NZD"
|
|
207
|
+
];
|
|
208
|
+
var NATIVE_CRYPTO_CURRENCIES = ["BTC", "LTC", "ETH", "SOL", "BNB", "DOGE"];
|
|
209
|
+
var STABLE_CRYPTO_CURRENCIES = [
|
|
210
|
+
"USDC_ERC20",
|
|
211
|
+
"USDT_ERC20",
|
|
212
|
+
"USDT_BEP20",
|
|
213
|
+
"USDC_BEP20",
|
|
214
|
+
"USDT_SOL",
|
|
215
|
+
"USDC_SOL"
|
|
216
|
+
];
|
|
217
|
+
var ALL_CRYPTO_CURRENCIES = [
|
|
218
|
+
...NATIVE_CRYPTO_CURRENCIES,
|
|
219
|
+
...STABLE_CRYPTO_CURRENCIES
|
|
220
|
+
];
|
|
221
|
+
var PAYMENT_TYPES = ["PAYMENT", "DEPOSIT"];
|
|
222
|
+
var WITHDRAW_TYPES = ["ALL", "SINGLE"];
|
|
223
|
+
var stableCurrencySet = new Set(STABLE_CRYPTO_CURRENCIES);
|
|
224
|
+
function isStableCryptoCurrency(value) {
|
|
225
|
+
return stableCurrencySet.has(value);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/domain/conversion.ts
|
|
229
|
+
var StaticRateFiatConverter = class {
|
|
230
|
+
rates = /* @__PURE__ */ new Map();
|
|
231
|
+
constructor(quotes) {
|
|
232
|
+
for (const quote of quotes) {
|
|
233
|
+
this.rates.set(`${quote.from}:${quote.to}`, quote.rate);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
convert(amount, from, to) {
|
|
237
|
+
if (from === to) {
|
|
238
|
+
return Promise.resolve(amount);
|
|
239
|
+
}
|
|
240
|
+
const direct = this.rates.get(`${from}:${to}`);
|
|
241
|
+
if (direct !== void 0) {
|
|
242
|
+
return Promise.resolve(amount * direct);
|
|
243
|
+
}
|
|
244
|
+
const inverse = this.rates.get(`${to}:${from}`);
|
|
245
|
+
if (inverse !== void 0 && inverse !== 0) {
|
|
246
|
+
return Promise.resolve(amount / inverse);
|
|
247
|
+
}
|
|
248
|
+
throw new CurrencyConversionError(`No fiat conversion rate available for ${from} -> ${to}.`);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
var MinimumAmountPolicy = class {
|
|
252
|
+
converter;
|
|
253
|
+
minimumUsdAmount;
|
|
254
|
+
constructor(options = {}) {
|
|
255
|
+
this.converter = options.converter;
|
|
256
|
+
this.minimumUsdAmount = options.minimumUsdAmount ?? 1;
|
|
257
|
+
}
|
|
258
|
+
async assertFiatAmountEligible(amount, fiatCurrency) {
|
|
259
|
+
if (amount <= 0) {
|
|
260
|
+
throw new MinimumAmountError("Payment amount must be greater than zero.", this.minimumUsdAmount);
|
|
261
|
+
}
|
|
262
|
+
if (fiatCurrency !== "USD") {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const amountInUsd = amount;
|
|
266
|
+
if (amountInUsd < this.minimumUsdAmount) {
|
|
267
|
+
throw new MinimumAmountError(
|
|
268
|
+
`Payment amount must be at least the fiat equivalent of ${this.minimumUsdAmount} USD.`,
|
|
269
|
+
this.minimumUsdAmount,
|
|
270
|
+
{ details: { amount, fiatCurrency, amountInUsd } }
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
function assertPaymentModeSupported(paymentType, cryptoCurrency) {
|
|
276
|
+
if (paymentType === "DEPOSIT" && isStableCryptoCurrency(cryptoCurrency)) {
|
|
277
|
+
throw new UnsupportedPaymentModeError(
|
|
278
|
+
`Stablecoin ${cryptoCurrency} is not supported for DEPOSIT payments.`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/core/validation.ts
|
|
284
|
+
function assertObject(value, label) {
|
|
285
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
286
|
+
throw new ValidationError(`${label} must be an object.`, { details: value });
|
|
287
|
+
}
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
function assertString(value, label) {
|
|
291
|
+
if (typeof value !== "string") {
|
|
292
|
+
throw new ValidationError(`${label} must be a string.`, { details: value });
|
|
293
|
+
}
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
function assertNumberOrNull(value, label) {
|
|
297
|
+
if (value === null) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
301
|
+
throw new ValidationError(`${label} must be a number or null.`, { details: value });
|
|
302
|
+
}
|
|
303
|
+
return value;
|
|
304
|
+
}
|
|
305
|
+
function assertNumber(value, label) {
|
|
306
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
307
|
+
throw new ValidationError(`${label} must be a number.`, { details: value });
|
|
308
|
+
}
|
|
309
|
+
return value;
|
|
310
|
+
}
|
|
311
|
+
function assertBoolean(value, label) {
|
|
312
|
+
if (typeof value !== "boolean") {
|
|
313
|
+
throw new ValidationError(`${label} must be a boolean.`, { details: value });
|
|
314
|
+
}
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
function parsePaymentRecord(value) {
|
|
318
|
+
const record = assertObject(value, "payment");
|
|
319
|
+
const paymentType = assertString(record.paymentType, "payment.paymentType");
|
|
320
|
+
if (!PAYMENT_TYPES.includes(paymentType)) {
|
|
321
|
+
throw new ValidationError("payment.paymentType is invalid.", { details: paymentType });
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
id: assertNumber(record.id, "payment.id"),
|
|
325
|
+
paymentType,
|
|
326
|
+
fiatCurrency: assertString(record.fiatCurrency, "payment.fiatCurrency"),
|
|
327
|
+
fiatAmount: assertNumberOrNull(record.fiatAmount, "payment.fiatAmount"),
|
|
328
|
+
cryptoAmount: assertNumberOrNull(record.cryptoAmount, "payment.cryptoAmount"),
|
|
329
|
+
cryptoCurrency: assertString(
|
|
330
|
+
record.cryptoCurrency,
|
|
331
|
+
"payment.cryptoCurrency"
|
|
332
|
+
),
|
|
333
|
+
expireDatetime: assertNumber(record.expireDatetime, "payment.expireDatetime"),
|
|
334
|
+
createDatetime: assertNumber(record.createDatetime, "payment.createDatetime"),
|
|
335
|
+
paidAt: assertNumberOrNull(record.paidAt, "payment.paidAt"),
|
|
336
|
+
address: assertString(record.address, "payment.address"),
|
|
337
|
+
isPaid: assertBoolean(record.isPaid, "payment.isPaid"),
|
|
338
|
+
isWithdrawn: assertBoolean(record.isWithdrawn, "payment.isWithdrawn"),
|
|
339
|
+
hash: assertString(record.hash, "payment.hash"),
|
|
340
|
+
callbackUrl: record.callbackUrl === null ? null : assertString(record.callbackUrl, "payment.callbackUrl")
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function parseWalletBalances(value) {
|
|
344
|
+
const record = assertObject(value, "wallet");
|
|
345
|
+
const result = {};
|
|
346
|
+
for (const [currency, amount] of Object.entries(record)) {
|
|
347
|
+
result[currency] = assertNumber(amount, `wallet.${currency}`);
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
function parseWithdrawalResponse(value) {
|
|
352
|
+
const record = assertObject(value, "withdrawal");
|
|
353
|
+
const withdrawType = assertString(record.withdrawType, "withdrawal.withdrawType");
|
|
354
|
+
if (!WITHDRAW_TYPES.includes(withdrawType)) {
|
|
355
|
+
throw new ValidationError("withdrawal.withdrawType is invalid.", { details: withdrawType });
|
|
356
|
+
}
|
|
357
|
+
const txIdList = record.txIdList;
|
|
358
|
+
if (!Array.isArray(txIdList) || txIdList.some((item) => typeof item !== "string")) {
|
|
359
|
+
throw new ValidationError("withdrawal.txIdList must be a string array.", { details: txIdList });
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
id: assertNumber(record.id, "withdrawal.id"),
|
|
363
|
+
withdrawType,
|
|
364
|
+
paymentId: record.paymentId === null ? null : assertNumber(record.paymentId, "withdrawal.paymentId"),
|
|
365
|
+
cryptoCurrency: assertString(
|
|
366
|
+
record.cryptoCurrency,
|
|
367
|
+
"withdrawal.cryptoCurrency"
|
|
368
|
+
),
|
|
369
|
+
toAddress: assertString(record.toAddress, "withdrawal.toAddress"),
|
|
370
|
+
txIdList,
|
|
371
|
+
receivingAmount: assertNumber(record.receivingAmount, "withdrawal.receivingAmount"),
|
|
372
|
+
blockchainFeeAmount: assertNumber(
|
|
373
|
+
record.blockchainFeeAmount,
|
|
374
|
+
"withdrawal.blockchainFeeAmount"
|
|
375
|
+
),
|
|
376
|
+
serviceFeeAmount: assertNumber(record.serviceFeeAmount, "withdrawal.serviceFeeAmount"),
|
|
377
|
+
onlyCalculate: assertBoolean(record.onlyCalculate, "withdrawal.onlyCalculate"),
|
|
378
|
+
totalWithdrawalAmount: assertNumber(
|
|
379
|
+
record.totalWithdrawalAmount,
|
|
380
|
+
"withdrawal.totalWithdrawalAmount"
|
|
381
|
+
),
|
|
382
|
+
createDatetime: assertNumber(record.createDatetime, "withdrawal.createDatetime")
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function parseStringArray(value, label) {
|
|
386
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
387
|
+
throw new ValidationError(`${label} must be a string array.`, { details: value });
|
|
388
|
+
}
|
|
389
|
+
return value;
|
|
390
|
+
}
|
|
391
|
+
function parseCryptoPrices(value) {
|
|
392
|
+
if (!Array.isArray(value)) {
|
|
393
|
+
throw new ValidationError("prices must be an array.", { details: value });
|
|
394
|
+
}
|
|
395
|
+
const items = value;
|
|
396
|
+
return items.map((item, index) => {
|
|
397
|
+
const record = assertObject(item, `prices[${index}]`);
|
|
398
|
+
return {
|
|
399
|
+
cryptoCurrency: assertString(record.cryptoCurrency, `prices[${index}].cryptoCurrency`),
|
|
400
|
+
fiatCurrency: assertString(record.fiatCurrency, `prices[${index}].fiatCurrency`),
|
|
401
|
+
price: assertNumber(record.price, `prices[${index}].price`)
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/resources/currencies.ts
|
|
407
|
+
var CurrenciesResource = class {
|
|
408
|
+
httpClient;
|
|
409
|
+
constructor(httpClient) {
|
|
410
|
+
this.httpClient = httpClient;
|
|
411
|
+
}
|
|
412
|
+
async listNative() {
|
|
413
|
+
const response = await this.httpClient.request({
|
|
414
|
+
path: "/cryptocurrency"
|
|
415
|
+
});
|
|
416
|
+
return parseStringArray(response, "cryptocurrency");
|
|
417
|
+
}
|
|
418
|
+
async listAll() {
|
|
419
|
+
const response = await this.httpClient.request({
|
|
420
|
+
path: "/cryptocurrency/all"
|
|
421
|
+
});
|
|
422
|
+
return parseStringArray(response, "cryptocurrency/all");
|
|
423
|
+
}
|
|
424
|
+
async listStable() {
|
|
425
|
+
const response = await this.httpClient.request({
|
|
426
|
+
path: "/cryptocurrency/stable"
|
|
427
|
+
});
|
|
428
|
+
return parseStringArray(response, "cryptocurrency/stable");
|
|
429
|
+
}
|
|
430
|
+
async getPrices(input) {
|
|
431
|
+
if (input.cryptoCurrency.length === 0) {
|
|
432
|
+
throw new ValidationError("At least one cryptoCurrency is required.", { details: input });
|
|
433
|
+
}
|
|
434
|
+
const response = await this.httpClient.request({
|
|
435
|
+
path: "/cryptocurrency/price",
|
|
436
|
+
query: {
|
|
437
|
+
cryptoCurrency: input.cryptoCurrency.join(","),
|
|
438
|
+
fiatCurrency: input.fiatCurrency
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
return parseCryptoPrices(response);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// src/resources/fiat.ts
|
|
446
|
+
var FiatResource = class {
|
|
447
|
+
httpClient;
|
|
448
|
+
constructor(httpClient) {
|
|
449
|
+
this.httpClient = httpClient;
|
|
450
|
+
}
|
|
451
|
+
async list() {
|
|
452
|
+
const response = await this.httpClient.request({
|
|
453
|
+
path: "/currency"
|
|
454
|
+
});
|
|
455
|
+
return parseStringArray(response, "currency");
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// src/resources/payments.ts
|
|
460
|
+
var PaymentsResource = class {
|
|
461
|
+
httpClient;
|
|
462
|
+
minimumAmountPolicy;
|
|
463
|
+
constructor(httpClient, minimumAmountPolicy) {
|
|
464
|
+
this.httpClient = httpClient;
|
|
465
|
+
this.minimumAmountPolicy = minimumAmountPolicy;
|
|
466
|
+
}
|
|
467
|
+
async create(input) {
|
|
468
|
+
if (input.paymentType === "PAYMENT") {
|
|
469
|
+
return this.createPayment(input);
|
|
470
|
+
}
|
|
471
|
+
return this.createDeposit(input);
|
|
472
|
+
}
|
|
473
|
+
async createPayment(input) {
|
|
474
|
+
const request = {
|
|
475
|
+
...input,
|
|
476
|
+
paymentType: "PAYMENT"
|
|
477
|
+
};
|
|
478
|
+
if (request.fiatAmount <= 0) {
|
|
479
|
+
throw new ValidationError("fiatAmount must be greater than zero.", { details: request });
|
|
480
|
+
}
|
|
481
|
+
assertPaymentModeSupported(request.paymentType, request.cryptoCurrency);
|
|
482
|
+
await this.minimumAmountPolicy.assertFiatAmountEligible(request.fiatAmount, request.fiatCurrency);
|
|
483
|
+
const response = await this.httpClient.request({
|
|
484
|
+
path: "/payment",
|
|
485
|
+
method: "POST",
|
|
486
|
+
body: request,
|
|
487
|
+
requiresAuth: true
|
|
488
|
+
});
|
|
489
|
+
return parsePaymentRecord(response);
|
|
490
|
+
}
|
|
491
|
+
async createDeposit(input) {
|
|
492
|
+
const request = {
|
|
493
|
+
...input,
|
|
494
|
+
paymentType: "DEPOSIT"
|
|
495
|
+
};
|
|
496
|
+
if (isStableCryptoCurrency(request.cryptoCurrency)) {
|
|
497
|
+
throw new UnsupportedPaymentModeError(
|
|
498
|
+
`Stablecoin ${request.cryptoCurrency} supports only PAYMENT, not DEPOSIT.`,
|
|
499
|
+
{ details: request }
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
const response = await this.httpClient.request({
|
|
503
|
+
path: "/payment",
|
|
504
|
+
method: "POST",
|
|
505
|
+
body: request,
|
|
506
|
+
requiresAuth: true
|
|
507
|
+
});
|
|
508
|
+
return parsePaymentRecord(response);
|
|
509
|
+
}
|
|
510
|
+
async getByHash(hash) {
|
|
511
|
+
if (!hash) {
|
|
512
|
+
throw new ValidationError("hash is required.");
|
|
513
|
+
}
|
|
514
|
+
const response = await this.httpClient.request({
|
|
515
|
+
path: "/payment",
|
|
516
|
+
query: { hash },
|
|
517
|
+
requiresAuth: false
|
|
518
|
+
});
|
|
519
|
+
return parsePaymentRecord(response);
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// src/resources/wallet.ts
|
|
524
|
+
var WalletResource = class {
|
|
525
|
+
httpClient;
|
|
526
|
+
constructor(httpClient) {
|
|
527
|
+
this.httpClient = httpClient;
|
|
528
|
+
}
|
|
529
|
+
async get() {
|
|
530
|
+
const response = await this.httpClient.request({
|
|
531
|
+
path: "/wallet",
|
|
532
|
+
requiresAuth: true
|
|
533
|
+
});
|
|
534
|
+
return parseWalletBalances(response);
|
|
535
|
+
}
|
|
536
|
+
async withdraw(input) {
|
|
537
|
+
this.assertWithdrawalInput(input);
|
|
538
|
+
const response = await this.httpClient.request({
|
|
539
|
+
path: "/wallet/withdrawal",
|
|
540
|
+
method: "POST",
|
|
541
|
+
body: input,
|
|
542
|
+
requiresAuth: true
|
|
543
|
+
});
|
|
544
|
+
return parseWithdrawalResponse(response);
|
|
545
|
+
}
|
|
546
|
+
async calculate(input) {
|
|
547
|
+
return this.withdraw({
|
|
548
|
+
...input,
|
|
549
|
+
onlyCalculate: true
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
async withdrawAll(input) {
|
|
553
|
+
return this.withdraw({
|
|
554
|
+
...input,
|
|
555
|
+
withdrawType: "ALL",
|
|
556
|
+
onlyCalculate: false
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
async withdrawSingle(input) {
|
|
560
|
+
return this.withdraw({
|
|
561
|
+
...input,
|
|
562
|
+
withdrawType: "SINGLE",
|
|
563
|
+
onlyCalculate: false
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
async calculateAll(input) {
|
|
567
|
+
return this.calculate({
|
|
568
|
+
...input,
|
|
569
|
+
withdrawType: "ALL"
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
async calculateSingle(input) {
|
|
573
|
+
return this.calculate({
|
|
574
|
+
...input,
|
|
575
|
+
withdrawType: "SINGLE"
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
assertWithdrawalInput(input) {
|
|
579
|
+
if (!input.toAddress) {
|
|
580
|
+
throw new ValidationError("toAddress is required.", { details: input });
|
|
581
|
+
}
|
|
582
|
+
if (input.withdrawType === "SINGLE" && typeof input.paymentId !== "number") {
|
|
583
|
+
throw new ValidationError("paymentId is required for SINGLE withdrawals.", {
|
|
584
|
+
details: input
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/client.ts
|
|
591
|
+
var KryptoExpressClient = class {
|
|
592
|
+
payments;
|
|
593
|
+
wallet;
|
|
594
|
+
currencies;
|
|
595
|
+
fiat;
|
|
596
|
+
constructor(options = {}) {
|
|
597
|
+
const resolved = resolveClientOptions(options);
|
|
598
|
+
const httpClient = new HttpClient(resolved);
|
|
599
|
+
const minimumAmountPolicy = resolved.fiatConverter ? new MinimumAmountPolicy({ converter: resolved.fiatConverter }) : new MinimumAmountPolicy();
|
|
600
|
+
this.payments = new PaymentsResource(httpClient, minimumAmountPolicy);
|
|
601
|
+
this.wallet = new WalletResource(httpClient);
|
|
602
|
+
this.currencies = new CurrenciesResource(httpClient);
|
|
603
|
+
this.fiat = new FiatResource(httpClient);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/webhooks/signature.ts
|
|
608
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
609
|
+
function compactJson(rawBody) {
|
|
610
|
+
return JSON.stringify(JSON.parse(rawBody));
|
|
611
|
+
}
|
|
612
|
+
function computeCallbackSignature(rawBody, callbackSecret) {
|
|
613
|
+
const compactBody = compactJson(rawBody);
|
|
614
|
+
return createHmac("sha512", callbackSecret).update(compactBody).digest("hex");
|
|
615
|
+
}
|
|
616
|
+
function verifyCallbackSignature(input) {
|
|
617
|
+
if (!input.signature) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
const expectedSignature = computeCallbackSignature(input.rawBody, input.callbackSecret);
|
|
621
|
+
const actual = Buffer.from(input.signature, "hex");
|
|
622
|
+
const expected = Buffer.from(expectedSignature, "hex");
|
|
623
|
+
if (actual.length !== expected.length) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
return timingSafeEqual(actual, expected);
|
|
627
|
+
}
|
|
628
|
+
export {
|
|
629
|
+
ALL_CRYPTO_CURRENCIES,
|
|
630
|
+
APIError,
|
|
631
|
+
AuthError,
|
|
632
|
+
CurrencyConversionError,
|
|
633
|
+
FIAT_CURRENCIES,
|
|
634
|
+
KryptoExpressClient,
|
|
635
|
+
MinimumAmountError,
|
|
636
|
+
MinimumAmountPolicy,
|
|
637
|
+
NATIVE_CRYPTO_CURRENCIES,
|
|
638
|
+
NotFoundError,
|
|
639
|
+
PAYMENT_TYPES,
|
|
640
|
+
RateLimitError,
|
|
641
|
+
SDKError,
|
|
642
|
+
STABLE_CRYPTO_CURRENCIES,
|
|
643
|
+
StaticRateFiatConverter,
|
|
644
|
+
UnsupportedPaymentModeError,
|
|
645
|
+
ValidationError,
|
|
646
|
+
WITHDRAW_TYPES,
|
|
647
|
+
assertPaymentModeSupported,
|
|
648
|
+
compactJson,
|
|
649
|
+
computeCallbackSignature,
|
|
650
|
+
isStableCryptoCurrency,
|
|
651
|
+
verifyCallbackSignature
|
|
652
|
+
};
|
|
653
|
+
//# sourceMappingURL=index.js.map
|