@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 CHANGED
@@ -1,9 +1,26 @@
1
- # Changelog
2
-
3
- ## 1.0.0
4
-
5
- - Initial official TypeScript and JavaScript SDK.
6
- - Invoice create, get, list, iterate, cancel, confirm, and sandbox simulation.
7
- - Withdrawal create, get, list, iterate, cancel, and sandbox simulation.
8
- - Balance retrieval.
9
- - HMAC-SHA256 request signing, typed RFC 9457 errors, bounded retries, and webhook verification.
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/quick-start).
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
- project_id: "prj_xxxxxxxxxxxx",
27
- amount: "49.95",
28
- currency: "USD",
29
- external_order_id: externalOrderId("order"),
30
- });
31
-
32
- console.log(invoice.payment_url);
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
- project_id: "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.invoice_id);
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
- destination_address: "TRX...whitelisted...address",
58
- network: "tron",
59
- currency: "USDT",
60
- amount: "50.00",
61
- external_order_id: 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.event_type) {
83
- case "invoice.paid":
84
- // Fulfil idempotently by event.event_id.
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/quick-start)
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 = "1.0.0";
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.next_cursor || void 0;
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.next_cursor || void 0;
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
- return JSON.parse(rawBody.toString());
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)) timestamp = Number(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
- project_id: string;
27
+ projectId: string;
24
28
  amount: string;
25
29
  currency: string;
26
- external_order_id: string;
27
- network?: string | null;
28
- allow_multiple_payments?: boolean;
29
- customer_fee_percent?: number | null;
30
- client_id?: string | null;
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: string;
38
+ network: NetworkCode;
35
39
  }
36
40
  interface Order {
37
- external_id: string;
38
- client_id: string | null;
41
+ externalId: string;
42
+ clientId?: string;
39
43
  amount: string;
40
44
  currency: string;
41
- network: string | null;
45
+ network?: NetworkCode;
42
46
  }
43
47
  interface Transfer {
44
- tx_hash: string;
48
+ txHash: string;
45
49
  amount: string;
46
- status: string;
47
- created_at: string;
48
- confirmed_at: string | null;
49
- required_confirmations: number | null;
50
- estimated_confirmation_at: string | null;
51
- explorer_url: string | null;
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: string;
56
- chain_id: number;
57
- contract_address: string | null;
59
+ network: NetworkCode;
60
+ chainId: number;
61
+ contractAddress?: string;
58
62
  expected: string;
59
- address: string | null;
60
- exchange_rate: string | null;
61
- paid: string | null;
62
- remaining: string | null;
63
- fee: string | null;
64
- net: string | null;
65
- transfers: Transfer[] | null;
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
- invoice_id: string;
69
- project_id: string;
72
+ invoiceId: string;
73
+ projectId: string;
70
74
  status: InvoiceStatus;
71
- is_final: boolean;
72
- is_test: boolean;
73
- payment_url: string;
75
+ isFinal: boolean;
76
+ isTest: boolean;
77
+ paymentUrl: string;
74
78
  order: Order;
75
- payment: Payment | null;
76
- created_at: string;
77
- updated_at: string;
78
- expires_at: string | null;
79
- completed_at: string | null;
79
+ payment?: Payment;
80
+ createdAt: number;
81
+ updatedAt: number;
82
+ expiresAt?: number;
83
+ completedAt?: number;
80
84
  }
81
85
  interface InvoiceListItem {
82
- invoice_id: string;
83
- project_id: string;
84
- external_order_id: string;
85
- client_id: string | null;
86
+ invoiceId: string;
87
+ projectId: string;
88
+ externalOrderId: string;
89
+ clientId?: string;
86
90
  status: InvoiceStatus;
87
- is_final: boolean;
88
- is_test: boolean;
91
+ isFinal: boolean;
92
+ isTest: boolean;
89
93
  amount: string;
90
94
  currency: string;
91
- network: string | null;
92
- created_at: string;
93
- expires_at: string | null;
94
- completed_at: string | null;
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
- external_order_id?: string;
101
- project_id?: string;
102
- created_from?: number;
103
- created_to?: number;
104
+ externalOrderId?: string;
105
+ projectId?: string;
106
+ createdFrom?: number;
107
+ createdTo?: number;
104
108
  }
105
109
  interface CreateWithdrawalParams {
106
- destination_address: string;
107
- network: string;
110
+ destinationAddress: string;
111
+ network: NetworkCode;
108
112
  currency: string;
109
113
  amount: string;
110
- external_order_id: string;
114
+ externalOrderId: string;
111
115
  }
112
116
  interface Withdrawal {
113
- withdrawal_id: string;
114
- external_order_id: string;
117
+ withdrawalId: string;
118
+ externalOrderId: string;
115
119
  status: WithdrawalStatus;
116
- is_final: boolean;
117
- is_test: boolean;
120
+ isFinal: boolean;
121
+ isTest: boolean;
118
122
  amount: string;
119
- fee: string | null;
123
+ fee?: string;
120
124
  currency: string;
121
- network: string;
122
- destination_address: string;
123
- tx_hash: string | null;
124
- explorer_url: string | null;
125
- created_at: string;
126
- completed_at: string | null;
127
- failed_at: string | null;
128
- cancelled_at: string | null;
129
- }
130
- type WithdrawalListItem = Omit<Withdrawal, "tx_hash" | "explorer_url">;
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
- external_order_id?: string;
136
- created_from?: number;
137
- created_to?: number;
139
+ externalOrderId?: string;
140
+ createdFrom?: number;
141
+ createdTo?: number;
138
142
  }
139
143
  interface CursorPage<T> {
140
144
  items: T[];
141
- next_cursor: string | null;
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
- trace_id?: string;
164
+ traceId?: string;
161
165
  }
162
166
  interface WebhookEvent<T = unknown> {
163
- event_id: string;
164
- event_type: string;
165
- created_at: string;
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
- server_time: number;
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 = "1.0.0";
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
- project_id: string;
27
+ projectId: string;
24
28
  amount: string;
25
29
  currency: string;
26
- external_order_id: string;
27
- network?: string | null;
28
- allow_multiple_payments?: boolean;
29
- customer_fee_percent?: number | null;
30
- client_id?: string | null;
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: string;
38
+ network: NetworkCode;
35
39
  }
36
40
  interface Order {
37
- external_id: string;
38
- client_id: string | null;
41
+ externalId: string;
42
+ clientId?: string;
39
43
  amount: string;
40
44
  currency: string;
41
- network: string | null;
45
+ network?: NetworkCode;
42
46
  }
43
47
  interface Transfer {
44
- tx_hash: string;
48
+ txHash: string;
45
49
  amount: string;
46
- status: string;
47
- created_at: string;
48
- confirmed_at: string | null;
49
- required_confirmations: number | null;
50
- estimated_confirmation_at: string | null;
51
- explorer_url: string | null;
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: string;
56
- chain_id: number;
57
- contract_address: string | null;
59
+ network: NetworkCode;
60
+ chainId: number;
61
+ contractAddress?: string;
58
62
  expected: string;
59
- address: string | null;
60
- exchange_rate: string | null;
61
- paid: string | null;
62
- remaining: string | null;
63
- fee: string | null;
64
- net: string | null;
65
- transfers: Transfer[] | null;
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
- invoice_id: string;
69
- project_id: string;
72
+ invoiceId: string;
73
+ projectId: string;
70
74
  status: InvoiceStatus;
71
- is_final: boolean;
72
- is_test: boolean;
73
- payment_url: string;
75
+ isFinal: boolean;
76
+ isTest: boolean;
77
+ paymentUrl: string;
74
78
  order: Order;
75
- payment: Payment | null;
76
- created_at: string;
77
- updated_at: string;
78
- expires_at: string | null;
79
- completed_at: string | null;
79
+ payment?: Payment;
80
+ createdAt: number;
81
+ updatedAt: number;
82
+ expiresAt?: number;
83
+ completedAt?: number;
80
84
  }
81
85
  interface InvoiceListItem {
82
- invoice_id: string;
83
- project_id: string;
84
- external_order_id: string;
85
- client_id: string | null;
86
+ invoiceId: string;
87
+ projectId: string;
88
+ externalOrderId: string;
89
+ clientId?: string;
86
90
  status: InvoiceStatus;
87
- is_final: boolean;
88
- is_test: boolean;
91
+ isFinal: boolean;
92
+ isTest: boolean;
89
93
  amount: string;
90
94
  currency: string;
91
- network: string | null;
92
- created_at: string;
93
- expires_at: string | null;
94
- completed_at: string | null;
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
- external_order_id?: string;
101
- project_id?: string;
102
- created_from?: number;
103
- created_to?: number;
104
+ externalOrderId?: string;
105
+ projectId?: string;
106
+ createdFrom?: number;
107
+ createdTo?: number;
104
108
  }
105
109
  interface CreateWithdrawalParams {
106
- destination_address: string;
107
- network: string;
110
+ destinationAddress: string;
111
+ network: NetworkCode;
108
112
  currency: string;
109
113
  amount: string;
110
- external_order_id: string;
114
+ externalOrderId: string;
111
115
  }
112
116
  interface Withdrawal {
113
- withdrawal_id: string;
114
- external_order_id: string;
117
+ withdrawalId: string;
118
+ externalOrderId: string;
115
119
  status: WithdrawalStatus;
116
- is_final: boolean;
117
- is_test: boolean;
120
+ isFinal: boolean;
121
+ isTest: boolean;
118
122
  amount: string;
119
- fee: string | null;
123
+ fee?: string;
120
124
  currency: string;
121
- network: string;
122
- destination_address: string;
123
- tx_hash: string | null;
124
- explorer_url: string | null;
125
- created_at: string;
126
- completed_at: string | null;
127
- failed_at: string | null;
128
- cancelled_at: string | null;
129
- }
130
- type WithdrawalListItem = Omit<Withdrawal, "tx_hash" | "explorer_url">;
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
- external_order_id?: string;
136
- created_from?: number;
137
- created_to?: number;
139
+ externalOrderId?: string;
140
+ createdFrom?: number;
141
+ createdTo?: number;
138
142
  }
139
143
  interface CursorPage<T> {
140
144
  items: T[];
141
- next_cursor: string | null;
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
- trace_id?: string;
164
+ traceId?: string;
161
165
  }
162
166
  interface WebhookEvent<T = unknown> {
163
- event_id: string;
164
- event_type: string;
165
- created_at: string;
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
- server_time: number;
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 = "1.0.0";
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 = "1.0.0";
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.next_cursor || void 0;
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.next_cursor || void 0;
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
- return JSON.parse(rawBody.toString());
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)) timestamp = Number(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": "1.0.0",
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/quick-start",
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
- "types": "./dist/index.d.ts",
29
- "import": "./dist/index.js",
30
- "require": "./dist/index.cjs"
31
- }
32
- },
33
- "files": [
34
- "dist",
35
- "README.md",
36
- "LICENSE",
37
- "CHANGELOG.md"
38
- ],
39
- "sideEffects": false,
40
- "engines": {
41
- "node": ">=22.12"
42
- },
43
- "scripts": {
44
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
45
- "check": "tsc --noEmit",
46
- "test": "vitest run",
47
- "test:coverage": "vitest run --coverage",
48
- "prepack": "npm run check && npm test && npm run build"
49
- },
50
- "devDependencies": {
51
- "@types/node": "^22.10.0",
52
- "@vitest/coverage-v8": "^3.2.0",
53
- "tsup": "^8.5.0",
54
- "typescript": "^5.8.0",
55
- "vitest": "^3.2.0"
56
- },
57
- "overrides": {
58
- "esbuild": "0.28.1"
59
- },
60
- "publishConfig": {
61
- "access": "public",
62
- "provenance": true
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
+ }