@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/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [2.0.3] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
- fix: align SDK problem details semantics
|
|
6
|
+
|
|
7
|
+
## [2.0.2] - 2026-07-12
|
|
8
|
+
|
|
9
|
+
- fix(typescript-sdk): retry trusted npm publication
|
|
10
|
+
|
|
11
|
+
## [2.0.1] - 2026-07-12
|
|
12
|
+
|
|
13
|
+
- fix(release): align Ruby metadata and retry npm publication
|
|
14
|
+
|
|
15
|
+
## [2.0.0] - 2026-07-12
|
|
16
|
+
|
|
17
|
+
- feat(sdk): harden official package release gates
|
|
18
|
+
- feat(typescript-sdk): harden typed merchant contracts
|
|
19
|
+
- test(sdks): align webhook conformance envelope
|
|
20
|
+
- refactor(sdk): expose idiomatic camelCase contracts
|
|
21
|
+
- feat(sdk): add official Merchant API clients
|
|
22
|
+
- feat(ecosystem): automate SDK and plugin releases
|
|
23
|
+
|
|
24
|
+
## 1.0.0
|
|
25
|
+
|
|
26
|
+
- Initial official TypeScript and JavaScript SDK.
|
|
27
|
+
- Invoice create, get, list, iterate, cancel, confirm, and sandbox simulation.
|
|
28
|
+
- Withdrawal create, get, list, iterate, cancel, and sandbox simulation.
|
|
29
|
+
- Balance retrieval.
|
|
30
|
+
- 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) {
|
|
@@ -66,8 +97,8 @@ var ApiError = class extends PaymosError {
|
|
|
66
97
|
headers;
|
|
67
98
|
problem;
|
|
68
99
|
constructor(status, body, headers) {
|
|
69
|
-
const problem = parseProblem(body);
|
|
70
|
-
const detail = problem?.detail
|
|
100
|
+
const problem = parseProblem(status, body);
|
|
101
|
+
const detail = problem?.detail || problem?.code || problem?.title;
|
|
71
102
|
super(`Paymos API ${status}: ${detail || body || "empty response"}`);
|
|
72
103
|
this.status = status;
|
|
73
104
|
this.body = body;
|
|
@@ -75,10 +106,10 @@ var ApiError = class extends PaymosError {
|
|
|
75
106
|
this.problem = problem;
|
|
76
107
|
}
|
|
77
108
|
get code() {
|
|
78
|
-
return this.problem?.
|
|
109
|
+
return this.problem?.code ?? "";
|
|
79
110
|
}
|
|
80
111
|
get field() {
|
|
81
|
-
return this.problem?.
|
|
112
|
+
return this.problem?.field ?? null;
|
|
82
113
|
}
|
|
83
114
|
get errors() {
|
|
84
115
|
return this.problem?.errors ?? [];
|
|
@@ -118,11 +149,13 @@ function apiErrorFromResponse(status, body, headers) {
|
|
|
118
149
|
if (status >= 500) return new ServerError(status, body, headers);
|
|
119
150
|
return new ApiError(status, body, headers);
|
|
120
151
|
}
|
|
121
|
-
function parseProblem(body) {
|
|
152
|
+
function parseProblem(status, body) {
|
|
122
153
|
if (!body) return null;
|
|
123
154
|
try {
|
|
124
155
|
const parsed = JSON.parse(body);
|
|
125
|
-
|
|
156
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
157
|
+
const problem = fromWire(parsed);
|
|
158
|
+
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;
|
|
126
159
|
} catch {
|
|
127
160
|
return null;
|
|
128
161
|
}
|
|
@@ -173,7 +206,7 @@ function rfc3986(value) {
|
|
|
173
206
|
}
|
|
174
207
|
|
|
175
208
|
// src/version.ts
|
|
176
|
-
var SDK_VERSION = "
|
|
209
|
+
var SDK_VERSION = "2.0.3";
|
|
177
210
|
|
|
178
211
|
// src/http.ts
|
|
179
212
|
var HttpClient = class {
|
|
@@ -182,7 +215,7 @@ var HttpClient = class {
|
|
|
182
215
|
this.options = normalizeOptions(options);
|
|
183
216
|
}
|
|
184
217
|
async request(method, path, payload, query = "") {
|
|
185
|
-
const body = payload === void 0 ? "" : JSON.stringify(payload);
|
|
218
|
+
const body = payload === void 0 ? "" : JSON.stringify(toWire(payload));
|
|
186
219
|
const url = `${this.options.baseUrl}${path}${query}`;
|
|
187
220
|
let attempt = 0;
|
|
188
221
|
while (true) {
|
|
@@ -224,6 +257,7 @@ var HttpClient = class {
|
|
|
224
257
|
}
|
|
225
258
|
if (shouldRetry(method, response.status) && attempt < this.options.maxRetries) {
|
|
226
259
|
const retryAfter = retryAfterMs(response.headers.get("retry-after"));
|
|
260
|
+
await response.body?.cancel();
|
|
227
261
|
await sleep(Math.max(backoff(this.options.baseDelayMs, ++attempt), retryAfter ?? 0));
|
|
228
262
|
continue;
|
|
229
263
|
}
|
|
@@ -231,7 +265,7 @@ var HttpClient = class {
|
|
|
231
265
|
if (!response.ok) throw apiErrorFromResponse(response.status, responseBody, response.headers);
|
|
232
266
|
if (responseBody === "") return {};
|
|
233
267
|
try {
|
|
234
|
-
return JSON.parse(responseBody);
|
|
268
|
+
return fromWire(JSON.parse(responseBody));
|
|
235
269
|
} catch (error) {
|
|
236
270
|
throw new PaymosError("Paymos API returned invalid JSON.", { cause: error });
|
|
237
271
|
}
|
|
@@ -309,7 +343,7 @@ var InvoicesResource = class {
|
|
|
309
343
|
return this.http.request("GET", `/v1/invoices/${encodePathSegment(invoiceId)}`);
|
|
310
344
|
}
|
|
311
345
|
list(params = {}) {
|
|
312
|
-
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(params));
|
|
346
|
+
return this.http.request("GET", "/v1/invoices", void 0, buildQuery(toWire(params)));
|
|
313
347
|
}
|
|
314
348
|
async *iterate(params = {}, maxPages = 100) {
|
|
315
349
|
assertMaxPages(maxPages);
|
|
@@ -317,7 +351,7 @@ var InvoicesResource = class {
|
|
|
317
351
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
318
352
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
319
353
|
yield* page.items;
|
|
320
|
-
const next = page.
|
|
354
|
+
const next = page.nextCursor || void 0;
|
|
321
355
|
if (next === void 0) return;
|
|
322
356
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
323
357
|
cursor = next;
|
|
@@ -348,7 +382,7 @@ var WithdrawalsResource = class {
|
|
|
348
382
|
return this.http.request("GET", `/v1/withdrawals/${encodePathSegment(withdrawalId)}`);
|
|
349
383
|
}
|
|
350
384
|
list(params = {}) {
|
|
351
|
-
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(params));
|
|
385
|
+
return this.http.request("GET", "/v1/withdrawals", void 0, buildQuery(toWire(params)));
|
|
352
386
|
}
|
|
353
387
|
async *iterate(params = {}, maxPages = 100) {
|
|
354
388
|
assertMaxPages(maxPages);
|
|
@@ -356,7 +390,7 @@ var WithdrawalsResource = class {
|
|
|
356
390
|
for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
|
|
357
391
|
const page = await this.list(cursor === void 0 ? params : { ...params, cursor });
|
|
358
392
|
yield* page.items;
|
|
359
|
-
const next = page.
|
|
393
|
+
const next = page.nextCursor || void 0;
|
|
360
394
|
if (next === void 0) return;
|
|
361
395
|
if (next === cursor) throw new Error("Paymos API returned the same pagination cursor twice.");
|
|
362
396
|
cursor = next;
|
|
@@ -442,8 +476,11 @@ var WebhookVerifier = class {
|
|
|
442
476
|
constructEvent(signatureHeader, rawBody, now = Math.floor(Date.now() / 1e3)) {
|
|
443
477
|
this.assertValid(signatureHeader, rawBody, now);
|
|
444
478
|
try {
|
|
445
|
-
|
|
479
|
+
const event = fromWire(JSON.parse(rawBody.toString()));
|
|
480
|
+
assertEventEnvelope(event);
|
|
481
|
+
return event;
|
|
446
482
|
} catch (error) {
|
|
483
|
+
if (error instanceof TypeError) throw error;
|
|
447
484
|
throw new TypeError("Webhook payload is not valid JSON.", { cause: error });
|
|
448
485
|
}
|
|
449
486
|
}
|
|
@@ -451,16 +488,39 @@ var WebhookVerifier = class {
|
|
|
451
488
|
function parseHeader(header) {
|
|
452
489
|
if (typeof header !== "string") return null;
|
|
453
490
|
let timestamp;
|
|
491
|
+
let timestampCount = 0;
|
|
454
492
|
const signatures = [];
|
|
455
493
|
for (const part of header.split(",")) {
|
|
456
494
|
const separator = part.indexOf("=");
|
|
457
495
|
if (separator < 1) continue;
|
|
458
496
|
const key = part.slice(0, separator).trim();
|
|
459
497
|
const value = part.slice(separator + 1).trim();
|
|
460
|
-
if (key === "t" && /^\d+$/.test(value))
|
|
498
|
+
if (key === "t" && /^\d+$/.test(value)) {
|
|
499
|
+
timestamp = Number(value);
|
|
500
|
+
timestampCount += 1;
|
|
501
|
+
}
|
|
461
502
|
if (key === "v1" && value !== "") signatures.push(value);
|
|
462
503
|
}
|
|
463
|
-
return timestamp === void 0 || signatures.length === 0 ? null : { timestamp, signatures };
|
|
504
|
+
return timestamp === void 0 || timestampCount !== 1 || !Number.isSafeInteger(timestamp) || signatures.length === 0 ? null : { timestamp, signatures };
|
|
505
|
+
}
|
|
506
|
+
function assertEventEnvelope(value) {
|
|
507
|
+
if (value === null || typeof value !== "object") throw new TypeError("Webhook payload must be an object.");
|
|
508
|
+
const event = value;
|
|
509
|
+
if (typeof event.eventId !== "string" || event.eventId.trim() === "") {
|
|
510
|
+
throw new TypeError("Webhook eventId must be a non-empty string.");
|
|
511
|
+
}
|
|
512
|
+
if (typeof event.eventType !== "string" || event.eventType.trim() === "") {
|
|
513
|
+
throw new TypeError("Webhook eventType must be a non-empty string.");
|
|
514
|
+
}
|
|
515
|
+
if (!Number.isSafeInteger(event.version) || event.version < 1) {
|
|
516
|
+
throw new TypeError("Webhook version must be a positive integer.");
|
|
517
|
+
}
|
|
518
|
+
if (!Number.isSafeInteger(event.occurredAt) || event.occurredAt < 0) {
|
|
519
|
+
throw new TypeError("Webhook occurredAt must be a non-negative Unix timestamp.");
|
|
520
|
+
}
|
|
521
|
+
if (event.data === null || typeof event.data !== "object") {
|
|
522
|
+
throw new TypeError("Webhook data must be an object.");
|
|
523
|
+
}
|
|
464
524
|
}
|
|
465
525
|
|
|
466
526
|
// src/index.ts
|