@paymos/sdk 1.0.0 → 2.0.2
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 +26 -9
- package/LICENSE +21 -21
- package/README.md +117 -117
- package/dist/index.cjs +69 -11
- package/dist/index.d.cts +85 -80
- package/dist/index.d.ts +85 -80
- package/dist/index.js +69 -11
- package/package.json +69 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [2.0.2] - 2026-07-12
|
|
4
|
+
|
|
5
|
+
- fix(typescript-sdk): retry trusted npm publication
|
|
6
|
+
|
|
7
|
+
## [2.0.1] - 2026-07-12
|
|
8
|
+
|
|
9
|
+
- fix(release): align Ruby metadata and retry npm publication
|
|
10
|
+
|
|
11
|
+
## [2.0.0] - 2026-07-12
|
|
12
|
+
|
|
13
|
+
- feat(sdk): harden official package release gates
|
|
14
|
+
- feat(typescript-sdk): harden typed merchant contracts
|
|
15
|
+
- test(sdks): align webhook conformance envelope
|
|
16
|
+
- refactor(sdk): expose idiomatic camelCase contracts
|
|
17
|
+
- feat(sdk): add official Merchant API clients
|
|
18
|
+
- feat(ecosystem): automate SDK and plugin releases
|
|
19
|
+
|
|
20
|
+
## 1.0.0
|
|
21
|
+
|
|
22
|
+
- Initial official TypeScript and JavaScript SDK.
|
|
23
|
+
- Invoice create, get, list, iterate, cancel, confirm, and sandbox simulation.
|
|
24
|
+
- Withdrawal create, get, list, iterate, cancel, and sandbox simulation.
|
|
25
|
+
- Balance retrieval.
|
|
26
|
+
- HMAC-SHA256 request signing, typed RFC 9457 errors, bounded retries, and webhook verification.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Paymos Labs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paymos Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
# Paymos TypeScript SDK
|
|
2
|
-
|
|
3
|
-
Official server-side TypeScript and JavaScript SDK for the [Paymos Merchant API](https://paymos.io/docs/
|
|
4
|
-
|
|
5
|
-
> Do not use an API secret in browser or mobile code. This package targets trusted Node.js backends and serverless runtimes with Node.js crypto support.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @paymos/sdk
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Node.js 22.12 or newer is required. The package ships ESM, CommonJS, and TypeScript declarations and has no runtime dependencies.
|
|
14
|
-
|
|
15
|
-
## Quick start
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
import { Paymos, externalOrderId } from "@paymos/sdk";
|
|
19
|
-
|
|
20
|
-
const paymos = new Paymos({
|
|
21
|
-
apiKey: process.env.PAYMOS_API_KEY!,
|
|
22
|
-
apiSecret: process.env.PAYMOS_API_SECRET!,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const invoice = await paymos.invoices.create({
|
|
26
|
-
|
|
27
|
-
amount: "49.95",
|
|
28
|
-
currency: "USD",
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log(invoice.
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Use decimal strings for money. Never convert API amounts to JavaScript `number`.
|
|
36
|
-
|
|
37
|
-
## List and iterate
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
const page = await paymos.invoices.list({
|
|
41
|
-
|
|
42
|
-
status: ["paid", "paid_over"],
|
|
43
|
-
limit: 50,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
for await (const invoice of paymos.invoices.iterate({ status: ["paid"] }, 10)) {
|
|
47
|
-
console.log(invoice.
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
The API uses opaque forward-only cursors. The second `iterate` argument is a client-side maximum page count.
|
|
52
|
-
|
|
53
|
-
## Withdrawals and balances
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
const withdrawal = await paymos.withdrawals.create({
|
|
57
|
-
|
|
58
|
-
network: "
|
|
59
|
-
currency: "USDT",
|
|
60
|
-
amount: "50.00",
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const balances = await paymos.balances.get();
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Use a Payout key (`rk_test_...` or `rk_live_...`) for withdrawals. Destinations must already be whitelisted.
|
|
68
|
-
|
|
69
|
-
## Webhooks
|
|
70
|
-
|
|
71
|
-
Always verify the exact raw request body before parsing JSON:
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import { WebhookVerifier } from "@paymos/sdk";
|
|
75
|
-
|
|
76
|
-
const verifier = new WebhookVerifier(process.env.PAYMOS_WEBHOOK_SECRET!);
|
|
77
|
-
const event = verifier.constructEvent(
|
|
78
|
-
request.headers.get("x-webhook-signature") ?? "",
|
|
79
|
-
rawBody,
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
switch (event.
|
|
83
|
-
case "invoice.paid":
|
|
84
|
-
// Fulfil idempotently by event.
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
The verifier accepts multiple `v1` signatures during secret rotation, uses constant-time comparison, and rejects timestamps outside the default five-minute tolerance.
|
|
90
|
-
|
|
91
|
-
## Errors and retries
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
import { RateLimitError, ValidationError } from "@paymos/sdk";
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
await paymos.balances.get();
|
|
98
|
-
} catch (error) {
|
|
99
|
-
if (error instanceof ValidationError) console.error(error.code, error.field, error.errors);
|
|
100
|
-
if (error instanceof RateLimitError) console.error(error.retryAfterSeconds);
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
The SDK retries `429` responses and retries `5xx` only for idempotent methods. It never blindly retries a side-effecting POST after a server error.
|
|
106
|
-
|
|
107
|
-
## Links
|
|
108
|
-
|
|
109
|
-
- [API documentation](https://paymos.io/docs/
|
|
110
|
-
- [Invoices](https://paymos.io/docs/invoices/create)
|
|
111
|
-
- [Withdrawals](https://paymos.io/docs/withdrawals/create)
|
|
112
|
-
- [Webhooks](https://paymos.io/docs/webhooks)
|
|
113
|
-
- [Issues](https://github.com/Paymos-labs/typescript-sdk/issues)
|
|
114
|
-
|
|
115
|
-
## License
|
|
116
|
-
|
|
117
|
-
MIT
|
|
1
|
+
# Paymos TypeScript SDK
|
|
2
|
+
|
|
3
|
+
Official server-side TypeScript and JavaScript SDK for the [Paymos Merchant API](https://paymos.io/docs/server-sdks).
|
|
4
|
+
|
|
5
|
+
> Do not use an API secret in browser or mobile code. This package targets trusted Node.js backends and serverless runtimes with Node.js crypto support.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @paymos/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Node.js 22.12 or newer is required. The package ships ESM, CommonJS, and TypeScript declarations and has no runtime dependencies.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Paymos, externalOrderId } from "@paymos/sdk";
|
|
19
|
+
|
|
20
|
+
const paymos = new Paymos({
|
|
21
|
+
apiKey: process.env.PAYMOS_API_KEY!,
|
|
22
|
+
apiSecret: process.env.PAYMOS_API_SECRET!,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const invoice = await paymos.invoices.create({
|
|
26
|
+
projectId: "prj_xxxxxxxxxxxx",
|
|
27
|
+
amount: "49.95",
|
|
28
|
+
currency: "USD",
|
|
29
|
+
externalOrderId: externalOrderId("order"),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log(invoice.paymentUrl);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use decimal strings for money. Never convert API amounts to JavaScript `number`.
|
|
36
|
+
|
|
37
|
+
## List and iterate
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const page = await paymos.invoices.list({
|
|
41
|
+
projectId: "prj_xxxxxxxxxxxx",
|
|
42
|
+
status: ["paid", "paid_over"],
|
|
43
|
+
limit: 50,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
for await (const invoice of paymos.invoices.iterate({ status: ["paid"] }, 10)) {
|
|
47
|
+
console.log(invoice.invoiceId);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The API uses opaque forward-only cursors. The second `iterate` argument is a client-side maximum page count.
|
|
52
|
+
|
|
53
|
+
## Withdrawals and balances
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const withdrawal = await paymos.withdrawals.create({
|
|
57
|
+
destinationAddress: "TRX...whitelisted...address",
|
|
58
|
+
network: "TRC20",
|
|
59
|
+
currency: "USDT",
|
|
60
|
+
amount: "50.00",
|
|
61
|
+
externalOrderId: externalOrderId("payout"),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const balances = await paymos.balances.get();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use a Payout key (`rk_test_...` or `rk_live_...`) for withdrawals. Destinations must already be whitelisted.
|
|
68
|
+
|
|
69
|
+
## Webhooks
|
|
70
|
+
|
|
71
|
+
Always verify the exact raw request body before parsing JSON:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { WebhookVerifier } from "@paymos/sdk";
|
|
75
|
+
|
|
76
|
+
const verifier = new WebhookVerifier(process.env.PAYMOS_WEBHOOK_SECRET!);
|
|
77
|
+
const event = verifier.constructEvent(
|
|
78
|
+
request.headers.get("x-webhook-signature") ?? "",
|
|
79
|
+
rawBody,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
switch (event.eventType) {
|
|
83
|
+
case "invoice.paid":
|
|
84
|
+
// Fulfil idempotently by event.eventId.
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The verifier accepts multiple `v1` signatures during secret rotation, uses constant-time comparison, and rejects timestamps outside the default five-minute tolerance.
|
|
90
|
+
|
|
91
|
+
## Errors and retries
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { RateLimitError, ValidationError } from "@paymos/sdk";
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await paymos.balances.get();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (error instanceof ValidationError) console.error(error.code, error.field, error.errors);
|
|
100
|
+
if (error instanceof RateLimitError) console.error(error.retryAfterSeconds);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The SDK retries `429` responses and retries `5xx` only for idempotent methods. It never blindly retries a side-effecting POST after a server error.
|
|
106
|
+
|
|
107
|
+
## Links
|
|
108
|
+
|
|
109
|
+
- [API documentation](https://paymos.io/docs/server-sdks)
|
|
110
|
+
- [Invoices](https://paymos.io/docs/invoices/create)
|
|
111
|
+
- [Withdrawals](https://paymos.io/docs/withdrawals/create)
|
|
112
|
+
- [Webhooks](https://paymos.io/docs/webhooks)
|
|
113
|
+
- [Issues](https://github.com/Paymos-labs/typescript-sdk/issues)
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,37 @@ __export(index_exports, {
|
|
|
47
47
|
module.exports = __toCommonJS(index_exports);
|
|
48
48
|
var import_node_crypto3 = require("crypto");
|
|
49
49
|
|
|
50
|
+
// src/wire.ts
|
|
51
|
+
function toWire(value) {
|
|
52
|
+
return mapKeys(value, (key) => key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`));
|
|
53
|
+
}
|
|
54
|
+
function fromWire(value) {
|
|
55
|
+
return mapKeys(value, (key) => key.replace(/_([a-z0-9])/g, (_, letter) => letter.toUpperCase()));
|
|
56
|
+
}
|
|
57
|
+
function mapKeys(value, rename) {
|
|
58
|
+
if (Array.isArray(value)) return value.map((item) => mapKeys(item, rename));
|
|
59
|
+
if (!isPlainObject(value)) return value;
|
|
60
|
+
const mapped = {};
|
|
61
|
+
for (const [key, item] of Object.entries(value)) {
|
|
62
|
+
const mappedKey = rename(key);
|
|
63
|
+
if (Object.hasOwn(mapped, mappedKey)) {
|
|
64
|
+
throw new TypeError(`Duplicate Paymos field after case conversion: ${mappedKey}`);
|
|
65
|
+
}
|
|
66
|
+
Object.defineProperty(mapped, mappedKey, {
|
|
67
|
+
value: mapKeys(item, rename),
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return mapped;
|
|
74
|
+
}
|
|
75
|
+
function isPlainObject(value) {
|
|
76
|
+
if (value === null || typeof value !== "object") return false;
|
|
77
|
+
const prototype = Object.getPrototypeOf(value);
|
|
78
|
+
return prototype === Object.prototype || prototype === null;
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
// src/errors.ts
|
|
51
82
|
var PaymosError = class extends Error {
|
|
52
83
|
constructor(message, options) {
|
|
@@ -122,7 +153,7 @@ function parseProblem(body) {
|
|
|
122
153
|
if (!body) return null;
|
|
123
154
|
try {
|
|
124
155
|
const parsed = JSON.parse(body);
|
|
125
|
-
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
156
|
+
return parsed !== null && typeof parsed === "object" ? fromWire(parsed) : null;
|
|
126
157
|
} catch {
|
|
127
158
|
return null;
|
|
128
159
|
}
|
|
@@ -173,7 +204,7 @@ function rfc3986(value) {
|
|
|
173
204
|
}
|
|
174
205
|
|
|
175
206
|
// src/version.ts
|
|
176
|
-
var SDK_VERSION = "
|
|
207
|
+
var SDK_VERSION = "2.0.2";
|
|
177
208
|
|
|
178
209
|
// src/http.ts
|
|
179
210
|
var HttpClient = class {
|
|
@@ -182,7 +213,7 @@ var HttpClient = class {
|
|
|
182
213
|
this.options = normalizeOptions(options);
|
|
183
214
|
}
|
|
184
215
|
async request(method, path, payload, query = "") {
|
|
185
|
-
const body = payload === void 0 ? "" : JSON.stringify(payload);
|
|
216
|
+
const body = payload === void 0 ? "" : JSON.stringify(toWire(payload));
|
|
186
217
|
const url = `${this.options.baseUrl}${path}${query}`;
|
|
187
218
|
let attempt = 0;
|
|
188
219
|
while (true) {
|
|
@@ -224,6 +255,7 @@ var HttpClient = class {
|
|
|
224
255
|
}
|
|
225
256
|
if (shouldRetry(method, response.status) && attempt < this.options.maxRetries) {
|
|
226
257
|
const retryAfter = retryAfterMs(response.headers.get("retry-after"));
|
|
258
|
+
await response.body?.cancel();
|
|
227
259
|
await sleep(Math.max(backoff(this.options.baseDelayMs, ++attempt), retryAfter ?? 0));
|
|
228
260
|
continue;
|
|
229
261
|
}
|
|
@@ -231,7 +263,7 @@ var HttpClient = class {
|
|
|
231
263
|
if (!response.ok) throw apiErrorFromResponse(response.status, responseBody, response.headers);
|
|
232
264
|
if (responseBody === "") return {};
|
|
233
265
|
try {
|
|
234
|
-
return JSON.parse(responseBody);
|
|
266
|
+
return fromWire(JSON.parse(responseBody));
|
|
235
267
|
} catch (error) {
|
|
236
268
|
throw new PaymosError("Paymos API returned invalid JSON.", { cause: error });
|
|
237
269
|
}
|
|
@@ -309,7 +341,7 @@ var InvoicesResource = class {
|
|
|
309
341
|
return this.http.request("GET", `/v1/invoices/${encodePathSegment(invoiceId)}`);
|
|
310
342
|
}
|
|
311
343
|
list(params = {}) {
|
|
312
|
-
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(params));
|
|
344
|
+
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(toWire(params)));
|
|
313
345
|
}
|
|
314
346
|
async *iterate(params = {}, maxPages = 100) {
|
|
315
347
|
assertMaxPages(maxPages);
|
|
@@ -317,7 +349,7 @@ var InvoicesResource = class {
|
|
|
317
349
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
318
350
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
319
351
|
yield* page.items;
|
|
320
|
-
const next = page.
|
|
352
|
+
const next = page.nextCursor || void 0;
|
|
321
353
|
if (next === void 0) return;
|
|
322
354
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
323
355
|
cursor = next;
|
|
@@ -348,7 +380,7 @@ var WithdrawalsResource = class {
|
|
|
348
380
|
return this.http.request("GET", `/v1/withdrawals/${encodePathSegment(withdrawalId)}`);
|
|
349
381
|
}
|
|
350
382
|
list(params = {}) {
|
|
351
|
-
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(params));
|
|
383
|
+
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(toWire(params)));
|
|
352
384
|
}
|
|
353
385
|
async *iterate(params = {}, maxPages = 100) {
|
|
354
386
|
assertMaxPages(maxPages);
|
|
@@ -356,7 +388,7 @@ var WithdrawalsResource = class {
|
|
|
356
388
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
357
389
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
358
390
|
yield* page.items;
|
|
359
|
-
const next = page.
|
|
391
|
+
const next = page.nextCursor || void 0;
|
|
360
392
|
if (next === void 0) return;
|
|
361
393
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
362
394
|
cursor = next;
|
|
@@ -442,8 +474,11 @@ var WebhookVerifier = class {
|
|
|
442
474
|
constructEvent(signatureHeader, rawBody, now = Math.floor(Date.now() / 1e3)) {
|
|
443
475
|
this.assertValid(signatureHeader, rawBody, now);
|
|
444
476
|
try {
|
|
445
|
-
|
|
477
|
+
const event = fromWire(JSON.parse(rawBody.toString()));
|
|
478
|
+
assertEventEnvelope(event);
|
|
479
|
+
return event;
|
|
446
480
|
} catch (error) {
|
|
481
|
+
if (error instanceof TypeError) throw error;
|
|
447
482
|
throw new TypeError("Webhook payload is not valid JSON.", { cause: error });
|
|
448
483
|
}
|
|
449
484
|
}
|
|
@@ -451,16 +486,39 @@ var WebhookVerifier = class {
|
|
|
451
486
|
function parseHeader(header) {
|
|
452
487
|
if (typeof header !== "string") return null;
|
|
453
488
|
let timestamp;
|
|
489
|
+
let timestampCount = 0;
|
|
454
490
|
const signatures = [];
|
|
455
491
|
for (const part of header.split(",")) {
|
|
456
492
|
const separator = part.indexOf("=");
|
|
457
493
|
if (separator < 1) continue;
|
|
458
494
|
const key = part.slice(0, separator).trim();
|
|
459
495
|
const value = part.slice(separator + 1).trim();
|
|
460
|
-
if (key === "t" && /^\d+$/.test(value))
|
|
496
|
+
if (key === "t" && /^\d+$/.test(value)) {
|
|
497
|
+
timestamp = Number(value);
|
|
498
|
+
timestampCount += 1;
|
|
499
|
+
}
|
|
461
500
|
if (key === "v1" && value !== "") signatures.push(value);
|
|
462
501
|
}
|
|
463
|
-
return timestamp === void 0 || signatures.length === 0 ? null : { timestamp, signatures };
|
|
502
|
+
return timestamp === void 0 || timestampCount !== 1 || !Number.isSafeInteger(timestamp) || signatures.length === 0 ? null : { timestamp, signatures };
|
|
503
|
+
}
|
|
504
|
+
function assertEventEnvelope(value) {
|
|
505
|
+
if (value === null || typeof value !== "object") throw new TypeError("Webhook payload must be an object.");
|
|
506
|
+
const event = value;
|
|
507
|
+
if (typeof event.eventId !== "string" || event.eventId.trim() === "") {
|
|
508
|
+
throw new TypeError("Webhook eventId must be a non-empty string.");
|
|
509
|
+
}
|
|
510
|
+
if (typeof event.eventType !== "string" || event.eventType.trim() === "") {
|
|
511
|
+
throw new TypeError("Webhook eventType must be a non-empty string.");
|
|
512
|
+
}
|
|
513
|
+
if (!Number.isSafeInteger(event.version) || event.version < 1) {
|
|
514
|
+
throw new TypeError("Webhook version must be a positive integer.");
|
|
515
|
+
}
|
|
516
|
+
if (!Number.isSafeInteger(event.occurredAt) || event.occurredAt < 0) {
|
|
517
|
+
throw new TypeError("Webhook occurredAt must be a non-negative Unix timestamp.");
|
|
518
|
+
}
|
|
519
|
+
if (event.data === null || typeof event.data !== "object") {
|
|
520
|
+
throw new TypeError("Webhook data must be an object.");
|
|
521
|
+
}
|
|
464
522
|
}
|
|
465
523
|
|
|
466
524
|
// src/index.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -19,126 +19,130 @@ declare class HttpClient {
|
|
|
19
19
|
|
|
20
20
|
type InvoiceStatus = "awaiting_client" | "awaiting_payment" | "confirming" | "underpaid_waiting" | "paid" | "paid_over" | "underpaid" | "expired" | "cancelled";
|
|
21
21
|
type WithdrawalStatus = "created" | "pending_review" | "signed" | "cancelling" | "completed" | "failed" | "cancelled";
|
|
22
|
+
type NetworkCode = "TRC20" | "ERC20" | "BEP20" | "POLYGON" | "ARBITRUM" | "OPTIMISM" | "BASE" | "TON" | "AVAX" | "SOL" | "NEAR" | "SUI" | "PLASMA" | (string & {});
|
|
23
|
+
type InvoiceEventType = "invoice.awaiting_payment" | "invoice.confirming" | "invoice.underpaid_waiting" | "invoice.paid" | "invoice.paid_over" | "invoice.underpaid" | "invoice.expired" | "invoice.cancelled";
|
|
24
|
+
type WithdrawalEventType = "withdrawal.created" | "withdrawal.processing" | "withdrawal.completed" | "withdrawal.failed" | "withdrawal.cancelled";
|
|
25
|
+
type WebhookEventType = InvoiceEventType | WithdrawalEventType | (string & {});
|
|
22
26
|
interface CreateInvoiceParams {
|
|
23
|
-
|
|
27
|
+
projectId: string;
|
|
24
28
|
amount: string;
|
|
25
29
|
currency: string;
|
|
26
|
-
|
|
27
|
-
network?:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
externalOrderId: string;
|
|
31
|
+
network?: NetworkCode | null;
|
|
32
|
+
allowMultiplePayments?: boolean;
|
|
33
|
+
customerFeePercent?: number | null;
|
|
34
|
+
clientId?: string | null;
|
|
31
35
|
}
|
|
32
36
|
interface ConfirmPaymentParams {
|
|
33
37
|
currency: string;
|
|
34
|
-
network:
|
|
38
|
+
network: NetworkCode;
|
|
35
39
|
}
|
|
36
40
|
interface Order {
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
externalId: string;
|
|
42
|
+
clientId?: string;
|
|
39
43
|
amount: string;
|
|
40
44
|
currency: string;
|
|
41
|
-
network
|
|
45
|
+
network?: NetworkCode;
|
|
42
46
|
}
|
|
43
47
|
interface Transfer {
|
|
44
|
-
|
|
48
|
+
txHash: string;
|
|
45
49
|
amount: string;
|
|
46
|
-
status:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
status: "confirming" | "confirmed";
|
|
51
|
+
createdAt: number;
|
|
52
|
+
confirmedAt?: number;
|
|
53
|
+
requiredConfirmations?: number;
|
|
54
|
+
estimatedConfirmationAt?: number;
|
|
55
|
+
explorerUrl?: string;
|
|
52
56
|
}
|
|
53
57
|
interface Payment {
|
|
54
58
|
currency: string;
|
|
55
|
-
network:
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
network: NetworkCode;
|
|
60
|
+
chainId: number;
|
|
61
|
+
contractAddress?: string;
|
|
58
62
|
expected: string;
|
|
59
|
-
address
|
|
60
|
-
|
|
61
|
-
paid
|
|
62
|
-
remaining
|
|
63
|
-
fee
|
|
64
|
-
net
|
|
65
|
-
transfers
|
|
63
|
+
address?: string;
|
|
64
|
+
exchangeRate?: string;
|
|
65
|
+
paid?: string;
|
|
66
|
+
remaining?: string;
|
|
67
|
+
fee?: string;
|
|
68
|
+
net?: string;
|
|
69
|
+
transfers?: Transfer[];
|
|
66
70
|
}
|
|
67
71
|
interface Invoice {
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
invoiceId: string;
|
|
73
|
+
projectId: string;
|
|
70
74
|
status: InvoiceStatus;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
isFinal: boolean;
|
|
76
|
+
isTest: boolean;
|
|
77
|
+
paymentUrl: string;
|
|
74
78
|
order: Order;
|
|
75
|
-
payment
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
payment?: Payment;
|
|
80
|
+
createdAt: number;
|
|
81
|
+
updatedAt: number;
|
|
82
|
+
expiresAt?: number;
|
|
83
|
+
completedAt?: number;
|
|
80
84
|
}
|
|
81
85
|
interface InvoiceListItem {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
invoiceId: string;
|
|
87
|
+
projectId: string;
|
|
88
|
+
externalOrderId: string;
|
|
89
|
+
clientId?: string;
|
|
86
90
|
status: InvoiceStatus;
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
isFinal: boolean;
|
|
92
|
+
isTest: boolean;
|
|
89
93
|
amount: string;
|
|
90
94
|
currency: string;
|
|
91
|
-
network
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
network?: NetworkCode;
|
|
96
|
+
createdAt: number;
|
|
97
|
+
expiresAt?: number;
|
|
98
|
+
completedAt?: number;
|
|
95
99
|
}
|
|
96
100
|
interface ListInvoicesParams {
|
|
97
101
|
limit?: number;
|
|
98
102
|
cursor?: string;
|
|
99
103
|
status?: InvoiceStatus[];
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
externalOrderId?: string;
|
|
105
|
+
projectId?: string;
|
|
106
|
+
createdFrom?: number;
|
|
107
|
+
createdTo?: number;
|
|
104
108
|
}
|
|
105
109
|
interface CreateWithdrawalParams {
|
|
106
|
-
|
|
107
|
-
network:
|
|
110
|
+
destinationAddress: string;
|
|
111
|
+
network: NetworkCode;
|
|
108
112
|
currency: string;
|
|
109
113
|
amount: string;
|
|
110
|
-
|
|
114
|
+
externalOrderId: string;
|
|
111
115
|
}
|
|
112
116
|
interface Withdrawal {
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
withdrawalId: string;
|
|
118
|
+
externalOrderId: string;
|
|
115
119
|
status: WithdrawalStatus;
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
isFinal: boolean;
|
|
121
|
+
isTest: boolean;
|
|
118
122
|
amount: string;
|
|
119
|
-
fee
|
|
123
|
+
fee?: string;
|
|
120
124
|
currency: string;
|
|
121
|
-
network:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
type WithdrawalListItem = Omit<Withdrawal, "
|
|
125
|
+
network: NetworkCode;
|
|
126
|
+
destinationAddress: string;
|
|
127
|
+
txHash?: string;
|
|
128
|
+
explorerUrl?: string;
|
|
129
|
+
createdAt: number;
|
|
130
|
+
completedAt?: number;
|
|
131
|
+
failedAt?: number;
|
|
132
|
+
cancelledAt?: number;
|
|
133
|
+
}
|
|
134
|
+
type WithdrawalListItem = Omit<Withdrawal, "txHash" | "explorerUrl">;
|
|
131
135
|
interface ListWithdrawalsParams {
|
|
132
136
|
limit?: number;
|
|
133
137
|
cursor?: string;
|
|
134
138
|
status?: WithdrawalStatus[];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
externalOrderId?: string;
|
|
140
|
+
createdFrom?: number;
|
|
141
|
+
createdTo?: number;
|
|
138
142
|
}
|
|
139
143
|
interface CursorPage<T> {
|
|
140
144
|
items: T[];
|
|
141
|
-
|
|
145
|
+
nextCursor: string | null;
|
|
142
146
|
}
|
|
143
147
|
interface Balance {
|
|
144
148
|
currency: string;
|
|
@@ -157,12 +161,13 @@ interface ProblemDetails {
|
|
|
157
161
|
code?: string;
|
|
158
162
|
field?: string | null;
|
|
159
163
|
errors?: ProblemError[];
|
|
160
|
-
|
|
164
|
+
traceId?: string;
|
|
161
165
|
}
|
|
162
166
|
interface WebhookEvent<T = unknown> {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
eventId: string;
|
|
168
|
+
eventType: WebhookEventType;
|
|
169
|
+
version: number;
|
|
170
|
+
occurredAt: number;
|
|
166
171
|
data: T;
|
|
167
172
|
}
|
|
168
173
|
|
|
@@ -196,7 +201,7 @@ declare class SystemResource {
|
|
|
196
201
|
private readonly http;
|
|
197
202
|
constructor(http: HttpClient);
|
|
198
203
|
time(): Promise<{
|
|
199
|
-
|
|
204
|
+
serverTime: number;
|
|
200
205
|
}>;
|
|
201
206
|
}
|
|
202
207
|
|
|
@@ -253,7 +258,7 @@ declare class WebhookVerifier {
|
|
|
253
258
|
constructEvent<T = unknown>(signatureHeader: string, rawBody: string | Buffer, now?: number): WebhookEvent<T>;
|
|
254
259
|
}
|
|
255
260
|
|
|
256
|
-
declare const SDK_VERSION = "
|
|
261
|
+
declare const SDK_VERSION = "2.0.2";
|
|
257
262
|
|
|
258
263
|
declare class Paymos {
|
|
259
264
|
readonly invoices: InvoicesResource;
|
|
@@ -264,4 +269,4 @@ declare class Paymos {
|
|
|
264
269
|
}
|
|
265
270
|
declare function externalOrderId(prefix?: string): string;
|
|
266
271
|
|
|
267
|
-
export { ApiError, AuthenticationError, type Balance, type ClientOptions, ConfigurationError, type ConfirmPaymentParams, ConflictError, type CreateInvoiceParams, type CreateWithdrawalParams, type CursorPage, GoneError, type Invoice, type InvoiceListItem, type InvoiceStatus, type ListInvoicesParams, type ListWithdrawalsParams, NotFoundError, type Order, type Payment, Paymos, PaymosError, type ProblemDetails, type ProblemError, RateLimitError, type RetryOptions, SDK_VERSION, ServerError, SignatureMismatchError, TimestampSkewError, type Transfer, UnavailableError, ValidationError, type WebhookEvent, WebhookVerifier, type Withdrawal, type WithdrawalListItem, type WithdrawalStatus, apiErrorFromResponse, authorizationHeader, buildQuery, encodePathSegment, externalOrderId, sign, stringToSign };
|
|
272
|
+
export { ApiError, AuthenticationError, type Balance, type ClientOptions, ConfigurationError, type ConfirmPaymentParams, ConflictError, type CreateInvoiceParams, type CreateWithdrawalParams, type CursorPage, GoneError, type Invoice, type InvoiceEventType, type InvoiceListItem, type InvoiceStatus, type ListInvoicesParams, type ListWithdrawalsParams, type NetworkCode, NotFoundError, type Order, type Payment, Paymos, PaymosError, type ProblemDetails, type ProblemError, RateLimitError, type RetryOptions, SDK_VERSION, ServerError, SignatureMismatchError, TimestampSkewError, type Transfer, UnavailableError, ValidationError, type WebhookEvent, type WebhookEventType, WebhookVerifier, type Withdrawal, type WithdrawalEventType, type WithdrawalListItem, type WithdrawalStatus, apiErrorFromResponse, authorizationHeader, buildQuery, encodePathSegment, externalOrderId, sign, stringToSign };
|
package/dist/index.d.ts
CHANGED
|
@@ -19,126 +19,130 @@ declare class HttpClient {
|
|
|
19
19
|
|
|
20
20
|
type InvoiceStatus = "awaiting_client" | "awaiting_payment" | "confirming" | "underpaid_waiting" | "paid" | "paid_over" | "underpaid" | "expired" | "cancelled";
|
|
21
21
|
type WithdrawalStatus = "created" | "pending_review" | "signed" | "cancelling" | "completed" | "failed" | "cancelled";
|
|
22
|
+
type NetworkCode = "TRC20" | "ERC20" | "BEP20" | "POLYGON" | "ARBITRUM" | "OPTIMISM" | "BASE" | "TON" | "AVAX" | "SOL" | "NEAR" | "SUI" | "PLASMA" | (string & {});
|
|
23
|
+
type InvoiceEventType = "invoice.awaiting_payment" | "invoice.confirming" | "invoice.underpaid_waiting" | "invoice.paid" | "invoice.paid_over" | "invoice.underpaid" | "invoice.expired" | "invoice.cancelled";
|
|
24
|
+
type WithdrawalEventType = "withdrawal.created" | "withdrawal.processing" | "withdrawal.completed" | "withdrawal.failed" | "withdrawal.cancelled";
|
|
25
|
+
type WebhookEventType = InvoiceEventType | WithdrawalEventType | (string & {});
|
|
22
26
|
interface CreateInvoiceParams {
|
|
23
|
-
|
|
27
|
+
projectId: string;
|
|
24
28
|
amount: string;
|
|
25
29
|
currency: string;
|
|
26
|
-
|
|
27
|
-
network?:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
externalOrderId: string;
|
|
31
|
+
network?: NetworkCode | null;
|
|
32
|
+
allowMultiplePayments?: boolean;
|
|
33
|
+
customerFeePercent?: number | null;
|
|
34
|
+
clientId?: string | null;
|
|
31
35
|
}
|
|
32
36
|
interface ConfirmPaymentParams {
|
|
33
37
|
currency: string;
|
|
34
|
-
network:
|
|
38
|
+
network: NetworkCode;
|
|
35
39
|
}
|
|
36
40
|
interface Order {
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
externalId: string;
|
|
42
|
+
clientId?: string;
|
|
39
43
|
amount: string;
|
|
40
44
|
currency: string;
|
|
41
|
-
network
|
|
45
|
+
network?: NetworkCode;
|
|
42
46
|
}
|
|
43
47
|
interface Transfer {
|
|
44
|
-
|
|
48
|
+
txHash: string;
|
|
45
49
|
amount: string;
|
|
46
|
-
status:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
status: "confirming" | "confirmed";
|
|
51
|
+
createdAt: number;
|
|
52
|
+
confirmedAt?: number;
|
|
53
|
+
requiredConfirmations?: number;
|
|
54
|
+
estimatedConfirmationAt?: number;
|
|
55
|
+
explorerUrl?: string;
|
|
52
56
|
}
|
|
53
57
|
interface Payment {
|
|
54
58
|
currency: string;
|
|
55
|
-
network:
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
network: NetworkCode;
|
|
60
|
+
chainId: number;
|
|
61
|
+
contractAddress?: string;
|
|
58
62
|
expected: string;
|
|
59
|
-
address
|
|
60
|
-
|
|
61
|
-
paid
|
|
62
|
-
remaining
|
|
63
|
-
fee
|
|
64
|
-
net
|
|
65
|
-
transfers
|
|
63
|
+
address?: string;
|
|
64
|
+
exchangeRate?: string;
|
|
65
|
+
paid?: string;
|
|
66
|
+
remaining?: string;
|
|
67
|
+
fee?: string;
|
|
68
|
+
net?: string;
|
|
69
|
+
transfers?: Transfer[];
|
|
66
70
|
}
|
|
67
71
|
interface Invoice {
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
invoiceId: string;
|
|
73
|
+
projectId: string;
|
|
70
74
|
status: InvoiceStatus;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
isFinal: boolean;
|
|
76
|
+
isTest: boolean;
|
|
77
|
+
paymentUrl: string;
|
|
74
78
|
order: Order;
|
|
75
|
-
payment
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
payment?: Payment;
|
|
80
|
+
createdAt: number;
|
|
81
|
+
updatedAt: number;
|
|
82
|
+
expiresAt?: number;
|
|
83
|
+
completedAt?: number;
|
|
80
84
|
}
|
|
81
85
|
interface InvoiceListItem {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
invoiceId: string;
|
|
87
|
+
projectId: string;
|
|
88
|
+
externalOrderId: string;
|
|
89
|
+
clientId?: string;
|
|
86
90
|
status: InvoiceStatus;
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
isFinal: boolean;
|
|
92
|
+
isTest: boolean;
|
|
89
93
|
amount: string;
|
|
90
94
|
currency: string;
|
|
91
|
-
network
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
network?: NetworkCode;
|
|
96
|
+
createdAt: number;
|
|
97
|
+
expiresAt?: number;
|
|
98
|
+
completedAt?: number;
|
|
95
99
|
}
|
|
96
100
|
interface ListInvoicesParams {
|
|
97
101
|
limit?: number;
|
|
98
102
|
cursor?: string;
|
|
99
103
|
status?: InvoiceStatus[];
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
externalOrderId?: string;
|
|
105
|
+
projectId?: string;
|
|
106
|
+
createdFrom?: number;
|
|
107
|
+
createdTo?: number;
|
|
104
108
|
}
|
|
105
109
|
interface CreateWithdrawalParams {
|
|
106
|
-
|
|
107
|
-
network:
|
|
110
|
+
destinationAddress: string;
|
|
111
|
+
network: NetworkCode;
|
|
108
112
|
currency: string;
|
|
109
113
|
amount: string;
|
|
110
|
-
|
|
114
|
+
externalOrderId: string;
|
|
111
115
|
}
|
|
112
116
|
interface Withdrawal {
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
withdrawalId: string;
|
|
118
|
+
externalOrderId: string;
|
|
115
119
|
status: WithdrawalStatus;
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
isFinal: boolean;
|
|
121
|
+
isTest: boolean;
|
|
118
122
|
amount: string;
|
|
119
|
-
fee
|
|
123
|
+
fee?: string;
|
|
120
124
|
currency: string;
|
|
121
|
-
network:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
type WithdrawalListItem = Omit<Withdrawal, "
|
|
125
|
+
network: NetworkCode;
|
|
126
|
+
destinationAddress: string;
|
|
127
|
+
txHash?: string;
|
|
128
|
+
explorerUrl?: string;
|
|
129
|
+
createdAt: number;
|
|
130
|
+
completedAt?: number;
|
|
131
|
+
failedAt?: number;
|
|
132
|
+
cancelledAt?: number;
|
|
133
|
+
}
|
|
134
|
+
type WithdrawalListItem = Omit<Withdrawal, "txHash" | "explorerUrl">;
|
|
131
135
|
interface ListWithdrawalsParams {
|
|
132
136
|
limit?: number;
|
|
133
137
|
cursor?: string;
|
|
134
138
|
status?: WithdrawalStatus[];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
externalOrderId?: string;
|
|
140
|
+
createdFrom?: number;
|
|
141
|
+
createdTo?: number;
|
|
138
142
|
}
|
|
139
143
|
interface CursorPage<T> {
|
|
140
144
|
items: T[];
|
|
141
|
-
|
|
145
|
+
nextCursor: string | null;
|
|
142
146
|
}
|
|
143
147
|
interface Balance {
|
|
144
148
|
currency: string;
|
|
@@ -157,12 +161,13 @@ interface ProblemDetails {
|
|
|
157
161
|
code?: string;
|
|
158
162
|
field?: string | null;
|
|
159
163
|
errors?: ProblemError[];
|
|
160
|
-
|
|
164
|
+
traceId?: string;
|
|
161
165
|
}
|
|
162
166
|
interface WebhookEvent<T = unknown> {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
eventId: string;
|
|
168
|
+
eventType: WebhookEventType;
|
|
169
|
+
version: number;
|
|
170
|
+
occurredAt: number;
|
|
166
171
|
data: T;
|
|
167
172
|
}
|
|
168
173
|
|
|
@@ -196,7 +201,7 @@ declare class SystemResource {
|
|
|
196
201
|
private readonly http;
|
|
197
202
|
constructor(http: HttpClient);
|
|
198
203
|
time(): Promise<{
|
|
199
|
-
|
|
204
|
+
serverTime: number;
|
|
200
205
|
}>;
|
|
201
206
|
}
|
|
202
207
|
|
|
@@ -253,7 +258,7 @@ declare class WebhookVerifier {
|
|
|
253
258
|
constructEvent<T = unknown>(signatureHeader: string, rawBody: string | Buffer, now?: number): WebhookEvent<T>;
|
|
254
259
|
}
|
|
255
260
|
|
|
256
|
-
declare const SDK_VERSION = "
|
|
261
|
+
declare const SDK_VERSION = "2.0.2";
|
|
257
262
|
|
|
258
263
|
declare class Paymos {
|
|
259
264
|
readonly invoices: InvoicesResource;
|
|
@@ -264,4 +269,4 @@ declare class Paymos {
|
|
|
264
269
|
}
|
|
265
270
|
declare function externalOrderId(prefix?: string): string;
|
|
266
271
|
|
|
267
|
-
export { ApiError, AuthenticationError, type Balance, type ClientOptions, ConfigurationError, type ConfirmPaymentParams, ConflictError, type CreateInvoiceParams, type CreateWithdrawalParams, type CursorPage, GoneError, type Invoice, type InvoiceListItem, type InvoiceStatus, type ListInvoicesParams, type ListWithdrawalsParams, NotFoundError, type Order, type Payment, Paymos, PaymosError, type ProblemDetails, type ProblemError, RateLimitError, type RetryOptions, SDK_VERSION, ServerError, SignatureMismatchError, TimestampSkewError, type Transfer, UnavailableError, ValidationError, type WebhookEvent, WebhookVerifier, type Withdrawal, type WithdrawalListItem, type WithdrawalStatus, apiErrorFromResponse, authorizationHeader, buildQuery, encodePathSegment, externalOrderId, sign, stringToSign };
|
|
272
|
+
export { ApiError, AuthenticationError, type Balance, type ClientOptions, ConfigurationError, type ConfirmPaymentParams, ConflictError, type CreateInvoiceParams, type CreateWithdrawalParams, type CursorPage, GoneError, type Invoice, type InvoiceEventType, type InvoiceListItem, type InvoiceStatus, type ListInvoicesParams, type ListWithdrawalsParams, type NetworkCode, NotFoundError, type Order, type Payment, Paymos, PaymosError, type ProblemDetails, type ProblemError, RateLimitError, type RetryOptions, SDK_VERSION, ServerError, SignatureMismatchError, TimestampSkewError, type Transfer, UnavailableError, ValidationError, type WebhookEvent, type WebhookEventType, WebhookVerifier, type Withdrawal, type WithdrawalEventType, type WithdrawalListItem, type WithdrawalStatus, apiErrorFromResponse, authorizationHeader, buildQuery, encodePathSegment, externalOrderId, sign, stringToSign };
|
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) {
|
|
@@ -76,7 +107,7 @@ function parseProblem(body) {
|
|
|
76
107
|
if (!body) return null;
|
|
77
108
|
try {
|
|
78
109
|
const parsed = JSON.parse(body);
|
|
79
|
-
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
110
|
+
return parsed !== null && typeof parsed === "object" ? fromWire(parsed) : null;
|
|
80
111
|
} catch {
|
|
81
112
|
return null;
|
|
82
113
|
}
|
|
@@ -127,7 +158,7 @@ function rfc3986(value) {
|
|
|
127
158
|
}
|
|
128
159
|
|
|
129
160
|
// src/version.ts
|
|
130
|
-
var SDK_VERSION = "
|
|
161
|
+
var SDK_VERSION = "2.0.2";
|
|
131
162
|
|
|
132
163
|
// src/http.ts
|
|
133
164
|
var HttpClient = class {
|
|
@@ -136,7 +167,7 @@ var HttpClient = class {
|
|
|
136
167
|
this.options = normalizeOptions(options);
|
|
137
168
|
}
|
|
138
169
|
async request(method, path, payload, query = "") {
|
|
139
|
-
const body = payload === void 0 ? "" : JSON.stringify(payload);
|
|
170
|
+
const body = payload === void 0 ? "" : JSON.stringify(toWire(payload));
|
|
140
171
|
const url = `${this.options.baseUrl}${path}${query}`;
|
|
141
172
|
let attempt = 0;
|
|
142
173
|
while (true) {
|
|
@@ -178,6 +209,7 @@ var HttpClient = class {
|
|
|
178
209
|
}
|
|
179
210
|
if (shouldRetry(method, response.status) && attempt < this.options.maxRetries) {
|
|
180
211
|
const retryAfter = retryAfterMs(response.headers.get("retry-after"));
|
|
212
|
+
await response.body?.cancel();
|
|
181
213
|
await sleep(Math.max(backoff(this.options.baseDelayMs, ++attempt), retryAfter ?? 0));
|
|
182
214
|
continue;
|
|
183
215
|
}
|
|
@@ -185,7 +217,7 @@ var HttpClient = class {
|
|
|
185
217
|
if (!response.ok) throw apiErrorFromResponse(response.status, responseBody, response.headers);
|
|
186
218
|
if (responseBody === "") return {};
|
|
187
219
|
try {
|
|
188
|
-
return JSON.parse(responseBody);
|
|
220
|
+
return fromWire(JSON.parse(responseBody));
|
|
189
221
|
} catch (error) {
|
|
190
222
|
throw new PaymosError("Paymos API returned invalid JSON.", { cause: error });
|
|
191
223
|
}
|
|
@@ -263,7 +295,7 @@ var InvoicesResource = class {
|
|
|
263
295
|
return this.http.request("GET", `/v1/invoices/${encodePathSegment(invoiceId)}`);
|
|
264
296
|
}
|
|
265
297
|
list(params = {}) {
|
|
266
|
-
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(params));
|
|
298
|
+
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(toWire(params)));
|
|
267
299
|
}
|
|
268
300
|
async *iterate(params = {}, maxPages = 100) {
|
|
269
301
|
assertMaxPages(maxPages);
|
|
@@ -271,7 +303,7 @@ var InvoicesResource = class {
|
|
|
271
303
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
272
304
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
273
305
|
yield* page.items;
|
|
274
|
-
const next = page.
|
|
306
|
+
const next = page.nextCursor || void 0;
|
|
275
307
|
if (next === void 0) return;
|
|
276
308
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
277
309
|
cursor = next;
|
|
@@ -302,7 +334,7 @@ var WithdrawalsResource = class {
|
|
|
302
334
|
return this.http.request("GET", `/v1/withdrawals/${encodePathSegment(withdrawalId)}`);
|
|
303
335
|
}
|
|
304
336
|
list(params = {}) {
|
|
305
|
-
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(params));
|
|
337
|
+
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(toWire(params)));
|
|
306
338
|
}
|
|
307
339
|
async *iterate(params = {}, maxPages = 100) {
|
|
308
340
|
assertMaxPages(maxPages);
|
|
@@ -310,7 +342,7 @@ var WithdrawalsResource = class {
|
|
|
310
342
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
311
343
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
312
344
|
yield* page.items;
|
|
313
|
-
const next = page.
|
|
345
|
+
const next = page.nextCursor || void 0;
|
|
314
346
|
if (next === void 0) return;
|
|
315
347
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
316
348
|
cursor = next;
|
|
@@ -396,8 +428,11 @@ var WebhookVerifier = class {
|
|
|
396
428
|
constructEvent(signatureHeader, rawBody, now = Math.floor(Date.now() / 1e3)) {
|
|
397
429
|
this.assertValid(signatureHeader, rawBody, now);
|
|
398
430
|
try {
|
|
399
|
-
|
|
431
|
+
const event = fromWire(JSON.parse(rawBody.toString()));
|
|
432
|
+
assertEventEnvelope(event);
|
|
433
|
+
return event;
|
|
400
434
|
} catch (error) {
|
|
435
|
+
if (error instanceof TypeError) throw error;
|
|
401
436
|
throw new TypeError("Webhook payload is not valid JSON.", { cause: error });
|
|
402
437
|
}
|
|
403
438
|
}
|
|
@@ -405,16 +440,39 @@ var WebhookVerifier = class {
|
|
|
405
440
|
function parseHeader(header) {
|
|
406
441
|
if (typeof header !== "string") return null;
|
|
407
442
|
let timestamp;
|
|
443
|
+
let timestampCount = 0;
|
|
408
444
|
const signatures = [];
|
|
409
445
|
for (const part of header.split(",")) {
|
|
410
446
|
const separator = part.indexOf("=");
|
|
411
447
|
if (separator < 1) continue;
|
|
412
448
|
const key = part.slice(0, separator).trim();
|
|
413
449
|
const value = part.slice(separator + 1).trim();
|
|
414
|
-
if (key === "t" && /^\d+$/.test(value))
|
|
450
|
+
if (key === "t" && /^\d+$/.test(value)) {
|
|
451
|
+
timestamp = Number(value);
|
|
452
|
+
timestampCount += 1;
|
|
453
|
+
}
|
|
415
454
|
if (key === "v1" && value !== "") signatures.push(value);
|
|
416
455
|
}
|
|
417
|
-
return timestamp === void 0 || signatures.length === 0 ? null : { timestamp, signatures };
|
|
456
|
+
return timestamp === void 0 || timestampCount !== 1 || !Number.isSafeInteger(timestamp) || signatures.length === 0 ? null : { timestamp, signatures };
|
|
457
|
+
}
|
|
458
|
+
function assertEventEnvelope(value) {
|
|
459
|
+
if (value === null || typeof value !== "object") throw new TypeError("Webhook payload must be an object.");
|
|
460
|
+
const event = value;
|
|
461
|
+
if (typeof event.eventId !== "string" || event.eventId.trim() === "") {
|
|
462
|
+
throw new TypeError("Webhook eventId must be a non-empty string.");
|
|
463
|
+
}
|
|
464
|
+
if (typeof event.eventType !== "string" || event.eventType.trim() === "") {
|
|
465
|
+
throw new TypeError("Webhook eventType must be a non-empty string.");
|
|
466
|
+
}
|
|
467
|
+
if (!Number.isSafeInteger(event.version) || event.version < 1) {
|
|
468
|
+
throw new TypeError("Webhook version must be a positive integer.");
|
|
469
|
+
}
|
|
470
|
+
if (!Number.isSafeInteger(event.occurredAt) || event.occurredAt < 0) {
|
|
471
|
+
throw new TypeError("Webhook occurredAt must be a non-negative Unix timestamp.");
|
|
472
|
+
}
|
|
473
|
+
if (event.data === null || typeof event.data !== "object") {
|
|
474
|
+
throw new TypeError("Webhook data must be an object.");
|
|
475
|
+
}
|
|
418
476
|
}
|
|
419
477
|
|
|
420
478
|
// 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.2",
|
|
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
|
+
}
|