@paymos/sdk 1.0.0 → 2.0.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/CHANGELOG.md +30 -9
- package/LICENSE +21 -21
- package/README.md +117 -117
- package/dist/index.cjs +76 -16
- package/dist/index.d.cts +91 -86
- package/dist/index.d.ts +91 -86
- package/dist/index.js +76 -16
- package/package.json +69 -64
package/dist/index.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
|
|
4
|
+
// src/wire.ts
|
|
5
|
+
function toWire(value) {
|
|
6
|
+
return mapKeys(value, (key) => key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`));
|
|
7
|
+
}
|
|
8
|
+
function fromWire(value) {
|
|
9
|
+
return mapKeys(value, (key) => key.replace(/_([a-z0-9])/g, (_, letter) => letter.toUpperCase()));
|
|
10
|
+
}
|
|
11
|
+
function mapKeys(value, rename) {
|
|
12
|
+
if (Array.isArray(value)) return value.map((item) => mapKeys(item, rename));
|
|
13
|
+
if (!isPlainObject(value)) return value;
|
|
14
|
+
const mapped = {};
|
|
15
|
+
for (const [key, item] of Object.entries(value)) {
|
|
16
|
+
const mappedKey = rename(key);
|
|
17
|
+
if (Object.hasOwn(mapped, mappedKey)) {
|
|
18
|
+
throw new TypeError(`Duplicate Paymos field after case conversion: ${mappedKey}`);
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(mapped, mappedKey, {
|
|
21
|
+
value: mapKeys(item, rename),
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return mapped;
|
|
28
|
+
}
|
|
29
|
+
function isPlainObject(value) {
|
|
30
|
+
if (value === null || typeof value !== "object") return false;
|
|
31
|
+
const prototype = Object.getPrototypeOf(value);
|
|
32
|
+
return prototype === Object.prototype || prototype === null;
|
|
33
|
+
}
|
|
34
|
+
|
|
4
35
|
// src/errors.ts
|
|
5
36
|
var PaymosError = class extends Error {
|
|
6
37
|
constructor(message, options) {
|
|
@@ -20,8 +51,8 @@ var ApiError = class extends PaymosError {
|
|
|
20
51
|
headers;
|
|
21
52
|
problem;
|
|
22
53
|
constructor(status, body, headers) {
|
|
23
|
-
const problem = parseProblem(body);
|
|
24
|
-
const detail = problem?.detail
|
|
54
|
+
const problem = parseProblem(status, body);
|
|
55
|
+
const detail = problem?.detail || problem?.code || problem?.title;
|
|
25
56
|
super(`Paymos API ${status}: ${detail || body || "empty response"}`);
|
|
26
57
|
this.status = status;
|
|
27
58
|
this.body = body;
|
|
@@ -29,10 +60,10 @@ var ApiError = class extends PaymosError {
|
|
|
29
60
|
this.problem = problem;
|
|
30
61
|
}
|
|
31
62
|
get code() {
|
|
32
|
-
return this.problem?.
|
|
63
|
+
return this.problem?.code ?? "";
|
|
33
64
|
}
|
|
34
65
|
get field() {
|
|
35
|
-
return this.problem?.
|
|
66
|
+
return this.problem?.field ?? null;
|
|
36
67
|
}
|
|
37
68
|
get errors() {
|
|
38
69
|
return this.problem?.errors ?? [];
|
|
@@ -72,11 +103,13 @@ function apiErrorFromResponse(status, body, headers) {
|
|
|
72
103
|
if (status >= 500) return new ServerError(status, body, headers);
|
|
73
104
|
return new ApiError(status, body, headers);
|
|
74
105
|
}
|
|
75
|
-
function parseProblem(body) {
|
|
106
|
+
function parseProblem(status, body) {
|
|
76
107
|
if (!body) return null;
|
|
77
108
|
try {
|
|
78
109
|
const parsed = JSON.parse(body);
|
|
79
|
-
|
|
110
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
111
|
+
const problem = fromWire(parsed);
|
|
112
|
+
return typeof problem.type === "string" && typeof problem.title === "string" && typeof problem.status === "number" && Number.isInteger(problem.status) && problem.status === status && typeof problem.detail === "string" && typeof problem.code === "string" ? problem : null;
|
|
80
113
|
} catch {
|
|
81
114
|
return null;
|
|
82
115
|
}
|
|
@@ -127,7 +160,7 @@ function rfc3986(value) {
|
|
|
127
160
|
}
|
|
128
161
|
|
|
129
162
|
// src/version.ts
|
|
130
|
-
var SDK_VERSION = "
|
|
163
|
+
var SDK_VERSION = "2.0.3";
|
|
131
164
|
|
|
132
165
|
// src/http.ts
|
|
133
166
|
var HttpClient = class {
|
|
@@ -136,7 +169,7 @@ var HttpClient = class {
|
|
|
136
169
|
this.options = normalizeOptions(options);
|
|
137
170
|
}
|
|
138
171
|
async request(method, path, payload, query = "") {
|
|
139
|
-
const body = payload === void 0 ? "" : JSON.stringify(payload);
|
|
172
|
+
const body = payload === void 0 ? "" : JSON.stringify(toWire(payload));
|
|
140
173
|
const url = `${this.options.baseUrl}${path}${query}`;
|
|
141
174
|
let attempt = 0;
|
|
142
175
|
while (true) {
|
|
@@ -178,6 +211,7 @@ var HttpClient = class {
|
|
|
178
211
|
}
|
|
179
212
|
if (shouldRetry(method, response.status) && attempt < this.options.maxRetries) {
|
|
180
213
|
const retryAfter = retryAfterMs(response.headers.get("retry-after"));
|
|
214
|
+
await response.body?.cancel();
|
|
181
215
|
await sleep(Math.max(backoff(this.options.baseDelayMs, ++attempt), retryAfter ?? 0));
|
|
182
216
|
continue;
|
|
183
217
|
}
|
|
@@ -185,7 +219,7 @@ var HttpClient = class {
|
|
|
185
219
|
if (!response.ok) throw apiErrorFromResponse(response.status, responseBody, response.headers);
|
|
186
220
|
if (responseBody === "") return {};
|
|
187
221
|
try {
|
|
188
|
-
return JSON.parse(responseBody);
|
|
222
|
+
return fromWire(JSON.parse(responseBody));
|
|
189
223
|
} catch (error) {
|
|
190
224
|
throw new PaymosError("Paymos API returned invalid JSON.", { cause: error });
|
|
191
225
|
}
|
|
@@ -263,7 +297,7 @@ var InvoicesResource = class {
|
|
|
263
297
|
return this.http.request("GET", `/v1/invoices/${encodePathSegment(invoiceId)}`);
|
|
264
298
|
}
|
|
265
299
|
list(params = {}) {
|
|
266
|
-
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(params));
|
|
300
|
+
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(toWire(params)));
|
|
267
301
|
}
|
|
268
302
|
async *iterate(params = {}, maxPages = 100) {
|
|
269
303
|
assertMaxPages(maxPages);
|
|
@@ -271,7 +305,7 @@ var InvoicesResource = class {
|
|
|
271
305
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
272
306
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
273
307
|
yield* page.items;
|
|
274
|
-
const next = page.
|
|
308
|
+
const next = page.nextCursor || void 0;
|
|
275
309
|
if (next === void 0) return;
|
|
276
310
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
277
311
|
cursor = next;
|
|
@@ -302,7 +336,7 @@ var WithdrawalsResource = class {
|
|
|
302
336
|
return this.http.request("GET", `/v1/withdrawals/${encodePathSegment(withdrawalId)}`);
|
|
303
337
|
}
|
|
304
338
|
list(params = {}) {
|
|
305
|
-
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(params));
|
|
339
|
+
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(toWire(params)));
|
|
306
340
|
}
|
|
307
341
|
async *iterate(params = {}, maxPages = 100) {
|
|
308
342
|
assertMaxPages(maxPages);
|
|
@@ -310,7 +344,7 @@ var WithdrawalsResource = class {
|
|
|
310
344
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
311
345
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
312
346
|
yield* page.items;
|
|
313
|
-
const next = page.
|
|
347
|
+
const next = page.nextCursor || void 0;
|
|
314
348
|
if (next === void 0) return;
|
|
315
349
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
316
350
|
cursor = next;
|
|
@@ -396,8 +430,11 @@ var WebhookVerifier = class {
|
|
|
396
430
|
constructEvent(signatureHeader, rawBody, now = Math.floor(Date.now() / 1e3)) {
|
|
397
431
|
this.assertValid(signatureHeader, rawBody, now);
|
|
398
432
|
try {
|
|
399
|
-
|
|
433
|
+
const event = fromWire(JSON.parse(rawBody.toString()));
|
|
434
|
+
assertEventEnvelope(event);
|
|
435
|
+
return event;
|
|
400
436
|
} catch (error) {
|
|
437
|
+
if (error instanceof TypeError) throw error;
|
|
401
438
|
throw new TypeError("Webhook payload is not valid JSON.", { cause: error });
|
|
402
439
|
}
|
|
403
440
|
}
|
|
@@ -405,16 +442,39 @@ var WebhookVerifier = class {
|
|
|
405
442
|
function parseHeader(header) {
|
|
406
443
|
if (typeof header !== "string") return null;
|
|
407
444
|
let timestamp;
|
|
445
|
+
let timestampCount = 0;
|
|
408
446
|
const signatures = [];
|
|
409
447
|
for (const part of header.split(",")) {
|
|
410
448
|
const separator = part.indexOf("=");
|
|
411
449
|
if (separator < 1) continue;
|
|
412
450
|
const key = part.slice(0, separator).trim();
|
|
413
451
|
const value = part.slice(separator + 1).trim();
|
|
414
|
-
if (key === "t" && /^\d+$/.test(value))
|
|
452
|
+
if (key === "t" && /^\d+$/.test(value)) {
|
|
453
|
+
timestamp = Number(value);
|
|
454
|
+
timestampCount += 1;
|
|
455
|
+
}
|
|
415
456
|
if (key === "v1" && value !== "") signatures.push(value);
|
|
416
457
|
}
|
|
417
|
-
return timestamp === void 0 || signatures.length === 0 ? null : { timestamp, signatures };
|
|
458
|
+
return timestamp === void 0 || timestampCount !== 1 || !Number.isSafeInteger(timestamp) || signatures.length === 0 ? null : { timestamp, signatures };
|
|
459
|
+
}
|
|
460
|
+
function assertEventEnvelope(value) {
|
|
461
|
+
if (value === null || typeof value !== "object") throw new TypeError("Webhook payload must be an object.");
|
|
462
|
+
const event = value;
|
|
463
|
+
if (typeof event.eventId !== "string" || event.eventId.trim() === "") {
|
|
464
|
+
throw new TypeError("Webhook eventId must be a non-empty string.");
|
|
465
|
+
}
|
|
466
|
+
if (typeof event.eventType !== "string" || event.eventType.trim() === "") {
|
|
467
|
+
throw new TypeError("Webhook eventType must be a non-empty string.");
|
|
468
|
+
}
|
|
469
|
+
if (!Number.isSafeInteger(event.version) || event.version < 1) {
|
|
470
|
+
throw new TypeError("Webhook version must be a positive integer.");
|
|
471
|
+
}
|
|
472
|
+
if (!Number.isSafeInteger(event.occurredAt) || event.occurredAt < 0) {
|
|
473
|
+
throw new TypeError("Webhook occurredAt must be a non-negative Unix timestamp.");
|
|
474
|
+
}
|
|
475
|
+
if (event.data === null || typeof event.data !== "object") {
|
|
476
|
+
throw new TypeError("Webhook data must be an object.");
|
|
477
|
+
}
|
|
418
478
|
}
|
|
419
479
|
|
|
420
480
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,64 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@paymos/sdk",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Official TypeScript and JavaScript SDK for the Paymos Merchant API",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"paymos",
|
|
7
|
-
"payments",
|
|
8
|
-
"stablecoins",
|
|
9
|
-
"usdt",
|
|
10
|
-
"usdc",
|
|
11
|
-
"crypto",
|
|
12
|
-
"sdk"
|
|
13
|
-
],
|
|
14
|
-
"homepage": "https://paymos.io/docs/
|
|
15
|
-
"bugs": "https://github.com/Paymos-labs/typescript-sdk/issues",
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/Paymos-labs/typescript-sdk.git"
|
|
19
|
-
},
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"author": "Paymos Labs <support@paymos.io>",
|
|
22
|
-
"type": "module",
|
|
23
|
-
"main": "./dist/index.cjs",
|
|
24
|
-
"module": "./dist/index.js",
|
|
25
|
-
"types": "./dist/index.d.ts",
|
|
26
|
-
"exports": {
|
|
27
|
-
".": {
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@paymos/sdk",
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"description": "Official TypeScript and JavaScript SDK for the Paymos Merchant API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"paymos",
|
|
7
|
+
"payments",
|
|
8
|
+
"stablecoins",
|
|
9
|
+
"usdt",
|
|
10
|
+
"usdc",
|
|
11
|
+
"crypto",
|
|
12
|
+
"sdk"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://paymos.io/docs/server-sdks",
|
|
15
|
+
"bugs": "https://github.com/Paymos-labs/typescript-sdk/issues",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/Paymos-labs/typescript-sdk.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Paymos Labs <support@paymos.io>",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"default": "./dist/index.cjs"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE",
|
|
42
|
+
"CHANGELOG.md"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22.12"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
50
|
+
"check": "tsc --noEmit",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:coverage": "vitest run --coverage",
|
|
53
|
+
"prepack": "npm run check && npm test && npm run build"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^22.10.0",
|
|
57
|
+
"@vitest/coverage-v8": "^3.2.0",
|
|
58
|
+
"tsup": "^8.5.0",
|
|
59
|
+
"typescript": "^5.8.0",
|
|
60
|
+
"vitest": "^3.2.0"
|
|
61
|
+
},
|
|
62
|
+
"overrides": {
|
|
63
|
+
"esbuild": "0.28.1"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public",
|
|
67
|
+
"provenance": true
|
|
68
|
+
}
|
|
69
|
+
}
|