pkg-sdk-test 0.0.17 → 0.0.19

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.
Files changed (63) hide show
  1. package/dist/cjs/BaseClient.js +2 -2
  2. package/dist/cjs/api/resources/payments/client/requests/CryptopayCreatePaymentRequest.d.ts +1 -1
  3. package/dist/cjs/api/types/CryptopayPaymentResponse.d.ts +1 -1
  4. package/dist/cjs/api/types/WebhookPaymentEvent.d.ts +26 -0
  5. package/dist/cjs/api/types/WebhookPaymentEvent.js +12 -0
  6. package/dist/cjs/api/types/WebhookStaticDepositEvent.d.ts +29 -0
  7. package/dist/cjs/api/types/WebhookStaticDepositEvent.js +11 -0
  8. package/dist/cjs/api/types/index.d.ts +2 -0
  9. package/dist/cjs/api/types/index.js +2 -0
  10. package/dist/cjs/core/index.d.ts +1 -0
  11. package/dist/cjs/core/index.js +1 -0
  12. package/dist/cjs/core/webhooks/computeHmacSignature.d.ts +9 -0
  13. package/dist/cjs/core/webhooks/computeHmacSignature.js +84 -0
  14. package/dist/cjs/core/webhooks/fetchJwks.d.ts +15 -0
  15. package/dist/cjs/core/webhooks/fetchJwks.js +156 -0
  16. package/dist/cjs/core/webhooks/index.d.ts +8 -0
  17. package/dist/cjs/core/webhooks/index.js +11 -0
  18. package/dist/cjs/core/webhooks/timingSafeEqual.d.ts +1 -0
  19. package/dist/cjs/core/webhooks/timingSafeEqual.js +85 -0
  20. package/dist/cjs/core/webhooks/types.d.ts +1 -0
  21. package/dist/cjs/core/webhooks/types.js +2 -0
  22. package/dist/cjs/core/webhooks/verifyAsymmetricSignature.d.ts +10 -0
  23. package/dist/cjs/core/webhooks/verifyAsymmetricSignature.js +210 -0
  24. package/dist/cjs/index.d.ts +1 -0
  25. package/dist/cjs/index.js +2 -1
  26. package/dist/cjs/version.d.ts +1 -1
  27. package/dist/cjs/version.js +1 -1
  28. package/dist/cjs/webhooks/WebhooksHelper.d.ts +9 -0
  29. package/dist/cjs/webhooks/WebhooksHelper.js +87 -0
  30. package/dist/cjs/webhooks/index.d.ts +1 -0
  31. package/dist/cjs/webhooks/index.js +5 -0
  32. package/dist/esm/BaseClient.mjs +2 -2
  33. package/dist/esm/api/resources/payments/client/requests/CryptopayCreatePaymentRequest.d.mts +1 -1
  34. package/dist/esm/api/types/CryptopayPaymentResponse.d.mts +1 -1
  35. package/dist/esm/api/types/WebhookPaymentEvent.d.mts +26 -0
  36. package/dist/esm/api/types/WebhookPaymentEvent.mjs +9 -0
  37. package/dist/esm/api/types/WebhookStaticDepositEvent.d.mts +29 -0
  38. package/dist/esm/api/types/WebhookStaticDepositEvent.mjs +8 -0
  39. package/dist/esm/api/types/index.d.mts +2 -0
  40. package/dist/esm/api/types/index.mjs +2 -0
  41. package/dist/esm/core/index.d.mts +1 -0
  42. package/dist/esm/core/index.mjs +1 -0
  43. package/dist/esm/core/webhooks/computeHmacSignature.d.mts +9 -0
  44. package/dist/esm/core/webhooks/computeHmacSignature.mjs +48 -0
  45. package/dist/esm/core/webhooks/fetchJwks.d.mts +15 -0
  46. package/dist/esm/core/webhooks/fetchJwks.mjs +153 -0
  47. package/dist/esm/core/webhooks/index.d.mts +8 -0
  48. package/dist/esm/core/webhooks/index.mjs +4 -0
  49. package/dist/esm/core/webhooks/timingSafeEqual.d.mts +1 -0
  50. package/dist/esm/core/webhooks/timingSafeEqual.mjs +49 -0
  51. package/dist/esm/core/webhooks/types.d.mts +1 -0
  52. package/dist/esm/core/webhooks/types.mjs +1 -0
  53. package/dist/esm/core/webhooks/verifyAsymmetricSignature.d.mts +10 -0
  54. package/dist/esm/core/webhooks/verifyAsymmetricSignature.mjs +174 -0
  55. package/dist/esm/index.d.mts +1 -0
  56. package/dist/esm/index.mjs +1 -0
  57. package/dist/esm/version.d.mts +1 -1
  58. package/dist/esm/version.mjs +1 -1
  59. package/dist/esm/webhooks/WebhooksHelper.d.mts +9 -0
  60. package/dist/esm/webhooks/WebhooksHelper.mjs +50 -0
  61. package/dist/esm/webhooks/index.d.mts +1 -0
  62. package/dist/esm/webhooks/index.mjs +1 -0
  63. package/package.json +1 -1
@@ -0,0 +1,153 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
11
+ const CACHE_MAX_SIZE = 50;
12
+ const cache = new Map();
13
+ function base64UrlToBase64(base64url) {
14
+ let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
15
+ while (base64.length % 4 !== 0) {
16
+ base64 += "=";
17
+ }
18
+ return base64;
19
+ }
20
+ function jwkToPem(jwk) {
21
+ if (jwk.x5c != null && jwk.x5c.length > 0) {
22
+ const cert = jwk.x5c[0];
23
+ return `-----BEGIN CERTIFICATE-----\n${cert}\n-----END CERTIFICATE-----`;
24
+ }
25
+ if (jwk.kty === "RSA" && jwk.n != null && jwk.e != null) {
26
+ return constructRsaPem(jwk.n, jwk.e);
27
+ }
28
+ throw new Error(`Unsupported JWK key type for PEM conversion: ${jwk.kty}`);
29
+ }
30
+ function base64UrlToBytes(base64url) {
31
+ const binary = atob(base64UrlToBase64(base64url));
32
+ const bytes = new Uint8Array(binary.length);
33
+ for (let i = 0; i < binary.length; i++) {
34
+ bytes[i] = binary.charCodeAt(i);
35
+ }
36
+ return bytes;
37
+ }
38
+ function concatBytes(arrays) {
39
+ const total = arrays.reduce((sum, a) => sum + a.length, 0);
40
+ const result = new Uint8Array(total);
41
+ let offset = 0;
42
+ for (const array of arrays) {
43
+ result.set(array, offset);
44
+ offset += array.length;
45
+ }
46
+ return result;
47
+ }
48
+ function constructRsaPem(nBase64Url, eBase64Url) {
49
+ const nBytes = base64UrlToBytes(nBase64Url);
50
+ const eBytes = base64UrlToBytes(eBase64Url);
51
+ // ASN.1 DER encoding of RSA public key
52
+ const nEncoded = asn1Integer(nBytes);
53
+ const eEncoded = asn1Integer(eBytes);
54
+ const sequence = asn1Sequence(concatBytes([nEncoded, eEncoded]));
55
+ const bitString = asn1BitString(sequence);
56
+ const algorithmIdentifier = asn1Sequence(new Uint8Array([
57
+ // OID for rsaEncryption (1.2.840.113549.1.1.1)
58
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
59
+ // NULL
60
+ 0x05, 0x00,
61
+ ]));
62
+ const spki = asn1Sequence(concatBytes([algorithmIdentifier, bitString]));
63
+ let binary = "";
64
+ for (const byte of spki) {
65
+ binary += String.fromCharCode(byte);
66
+ }
67
+ const base64 = btoa(binary);
68
+ const lines = [];
69
+ for (let i = 0; i < base64.length; i += 64) {
70
+ lines.push(base64.substring(i, i + 64));
71
+ }
72
+ return `-----BEGIN PUBLIC KEY-----\n${lines.join("\n")}\n-----END PUBLIC KEY-----`;
73
+ }
74
+ function asn1Length(length) {
75
+ if (length < 0x80) {
76
+ return new Uint8Array([length]);
77
+ }
78
+ const bytes = [];
79
+ let temp = length;
80
+ while (temp > 0) {
81
+ bytes.unshift(temp & 0xff);
82
+ temp >>= 8;
83
+ }
84
+ return new Uint8Array([0x80 | bytes.length, ...bytes]);
85
+ }
86
+ function asn1Integer(bytes) {
87
+ // Add leading zero if high bit is set (to ensure positive integer)
88
+ const needsPadding = bytes[0] >= 0x80;
89
+ const content = needsPadding ? concatBytes([new Uint8Array([0x00]), bytes]) : bytes;
90
+ return concatBytes([new Uint8Array([0x02]), asn1Length(content.length), content]);
91
+ }
92
+ function asn1Sequence(content) {
93
+ return concatBytes([new Uint8Array([0x30]), asn1Length(content.length), content]);
94
+ }
95
+ function asn1BitString(content) {
96
+ // Prepend a zero byte for unused bits
97
+ return concatBytes([new Uint8Array([0x03]), asn1Length(content.length + 1), new Uint8Array([0x00]), content]);
98
+ }
99
+ function fetchKeys(url) {
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ const cached = cache.get(url);
102
+ if (cached != null && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
103
+ return cached.keys;
104
+ }
105
+ const response = yield fetch(url);
106
+ if (!response.ok) {
107
+ throw new Error(`Failed to fetch JWKS from ${url}: ${response.status} ${response.statusText}`);
108
+ }
109
+ const jwks = (yield response.json());
110
+ if (cache.size >= CACHE_MAX_SIZE) {
111
+ for (const key of cache.keys()) {
112
+ cache.delete(key);
113
+ break;
114
+ }
115
+ }
116
+ cache.set(url, { keys: jwks.keys, fetchedAt: Date.now() });
117
+ return jwks.keys;
118
+ });
119
+ }
120
+ /**
121
+ * Fetches a public key from a JWKS endpoint and returns it as a PEM string.
122
+ *
123
+ * Only RSA keys (reconstructed from `n`/`e`) and keys with an `x5c` certificate chain are supported.
124
+ * EC (kty: "EC") and OKP (kty: "OKP") keys are **not** supported and will throw an error.
125
+ *
126
+ * @throws {Error} If the JWKS endpoint returns a non-OK response.
127
+ * @throws {Error} If no key matching `keyId` is found (after one cache-busting retry).
128
+ * @throws {Error} If the selected key has an unsupported type (e.g. EC or OKP).
129
+ */
130
+ export function fetchJwks(args) {
131
+ return __awaiter(this, void 0, void 0, function* () {
132
+ const keys = yield fetchKeys(args.url);
133
+ let selectedKey;
134
+ if (args.keyId != null) {
135
+ selectedKey = keys.find((k) => k.kid === args.keyId);
136
+ if (selectedKey == null) {
137
+ // Invalidate cache and retry once
138
+ cache.delete(args.url);
139
+ const refreshedKeys = yield fetchKeys(args.url);
140
+ selectedKey = refreshedKeys.find((k) => k.kid === args.keyId);
141
+ }
142
+ }
143
+ else {
144
+ selectedKey = keys[0];
145
+ }
146
+ if (selectedKey == null) {
147
+ throw new Error(args.keyId != null
148
+ ? `No key found with kid "${args.keyId}" in JWKS at ${args.url}`
149
+ : `No keys found in JWKS at ${args.url}`);
150
+ }
151
+ return jwkToPem(selectedKey);
152
+ });
153
+ }
@@ -0,0 +1,8 @@
1
+ export type { ComputeHmacSignatureArgs, HmacAlgorithm } from "./computeHmacSignature.mjs";
2
+ export { computeHmacSignature } from "./computeHmacSignature.mjs";
3
+ export type { FetchJwksArgs } from "./fetchJwks.mjs";
4
+ export { fetchJwks } from "./fetchJwks.mjs";
5
+ export { timingSafeEqual } from "./timingSafeEqual.mjs";
6
+ export type { SignatureEncoding } from "./types.mjs";
7
+ export type { AsymmetricAlgorithm, VerifyAsymmetricSignatureArgs } from "./verifyAsymmetricSignature.mjs";
8
+ export { verifyAsymmetricSignature } from "./verifyAsymmetricSignature.mjs";
@@ -0,0 +1,4 @@
1
+ export { computeHmacSignature } from "./computeHmacSignature.mjs";
2
+ export { fetchJwks } from "./fetchJwks.mjs";
3
+ export { timingSafeEqual } from "./timingSafeEqual.mjs";
4
+ export { verifyAsymmetricSignature } from "./verifyAsymmetricSignature.mjs";
@@ -0,0 +1 @@
1
+ export declare function timingSafeEqual(a: string, b: string): Promise<boolean>;
@@ -0,0 +1,49 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { RUNTIME } from "../runtime/index.mjs";
11
+ export function timingSafeEqual(a, b) {
12
+ return __awaiter(this, void 0, void 0, function* () {
13
+ var _a;
14
+ if (RUNTIME.type === "node") {
15
+ const crypto = yield import("crypto");
16
+ const bufA = Buffer.from(a);
17
+ const bufB = Buffer.from(b);
18
+ if (bufA.length !== bufB.length) {
19
+ // Still perform comparison to avoid leaking length via timing
20
+ const dummy = Buffer.alloc(bufA.length);
21
+ crypto.timingSafeEqual(bufA, dummy);
22
+ return false;
23
+ }
24
+ return crypto.timingSafeEqual(bufA, bufB);
25
+ }
26
+ // Fallback: constant-time XOR comparison using Uint8Array
27
+ const enc = new TextEncoder();
28
+ const bytesA = enc.encode(a);
29
+ const bytesB = enc.encode(b);
30
+ if (bytesA.length !== bytesB.length) {
31
+ // XOR each byte of bytesA against bytesB[0] (a runtime value) so the
32
+ // loop cannot be trivially folded to a constant by the engine. This is
33
+ // best-effort timing-stability: JS has no guarantee, but we avoid an
34
+ // obvious early-exit that would trivially leak length via timing.
35
+ const pivot = (_a = bytesB[0]) !== null && _a !== void 0 ? _a : 0;
36
+ let sink = 0;
37
+ for (let i = 0; i < bytesA.length; i++) {
38
+ sink |= bytesA[i] ^ pivot;
39
+ }
40
+ void sink;
41
+ return false;
42
+ }
43
+ let result = 0;
44
+ for (let i = 0; i < bytesA.length; i++) {
45
+ result |= bytesA[i] ^ bytesB[i];
46
+ }
47
+ return result === 0;
48
+ });
49
+ }
@@ -0,0 +1 @@
1
+ export type SignatureEncoding = "base64" | "hex";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { SignatureEncoding } from "./types.mjs";
2
+ export type AsymmetricAlgorithm = "RSA_SHA256" | "RSA_SHA384" | "RSA_SHA512" | "ECDSA_SHA256" | "ECDSA_SHA384" | "ECDSA_SHA512" | "ED25519";
3
+ export interface VerifyAsymmetricSignatureArgs {
4
+ payload: string;
5
+ signature: string;
6
+ publicKey: string;
7
+ algorithm: AsymmetricAlgorithm;
8
+ encoding: SignatureEncoding;
9
+ }
10
+ export declare function verifyAsymmetricSignature(args: VerifyAsymmetricSignatureArgs): Promise<boolean>;
@@ -0,0 +1,174 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { RUNTIME } from "../runtime/index.mjs";
11
+ function getNodeAlgorithmName(algorithm) {
12
+ switch (algorithm) {
13
+ case "RSA_SHA256":
14
+ return "RSA-SHA256";
15
+ case "RSA_SHA384":
16
+ return "RSA-SHA384";
17
+ case "RSA_SHA512":
18
+ return "RSA-SHA512";
19
+ case "ECDSA_SHA256":
20
+ return "SHA256";
21
+ case "ECDSA_SHA384":
22
+ return "SHA384";
23
+ case "ECDSA_SHA512":
24
+ return "SHA512";
25
+ }
26
+ }
27
+ // Each ECDSA curve has a fixed coordinate size for IEEE P1363 format.
28
+ // Web Crypto requires P1363 (r ∥ s), while Node's createVerify accepts DER (ASN.1 SEQUENCE).
29
+ function ecdsaCoordSize(algorithm) {
30
+ switch (algorithm) {
31
+ case "ECDSA_SHA256":
32
+ return 32; // P-256
33
+ case "ECDSA_SHA384":
34
+ return 48; // P-384
35
+ case "ECDSA_SHA512":
36
+ return 66; // P-521
37
+ }
38
+ }
39
+ // Read a DER length field starting at der[offset]. Returns [length, bytesConsumed].
40
+ // Handles both short form (1 byte) and long form (0x81/0x82 prefix).
41
+ function readDerLength(der, offset) {
42
+ const first = der[offset];
43
+ if (first < 0x80) {
44
+ return [first, 1];
45
+ }
46
+ const numLenBytes = first & 0x7f;
47
+ let length = 0;
48
+ for (let i = 0; i < numLenBytes; i++) {
49
+ length = (length << 8) | der[offset + 1 + i];
50
+ }
51
+ return [length, 1 + numLenBytes];
52
+ }
53
+ // Convert a DER-encoded ECDSA signature (ASN.1 SEQUENCE { INTEGER r, INTEGER s })
54
+ // to IEEE P1363 format (r ∥ s, each zero-padded to coordSize bytes).
55
+ function derToP1363(der, coordSize) {
56
+ // DER structure: 0x30 <len> 0x02 <rLen> <r> 0x02 <sLen> <s>
57
+ // Skip the outer SEQUENCE tag (0x30) and its length (may be long-form for P-521).
58
+ let offset = 1; // skip 0x30
59
+ const [, seqLenBytes] = readDerLength(der, offset);
60
+ offset += seqLenBytes;
61
+ if (der[offset] !== 0x02) {
62
+ throw new Error("Invalid DER signature: expected INTEGER tag for r");
63
+ }
64
+ offset++; // skip 0x02 tag
65
+ const [rLen, rLenBytes] = readDerLength(der, offset);
66
+ offset += rLenBytes;
67
+ const r = der.slice(offset, offset + rLen);
68
+ offset += rLen;
69
+ if (der[offset] !== 0x02) {
70
+ throw new Error("Invalid DER signature: expected INTEGER tag for s");
71
+ }
72
+ offset++; // skip 0x02 tag
73
+ const [sLen, sLenBytes] = readDerLength(der, offset);
74
+ offset += sLenBytes;
75
+ const s = der.slice(offset, offset + sLen);
76
+ const result = new Uint8Array(new ArrayBuffer(coordSize * 2));
77
+ // r and s may have a leading 0x00 padding byte (DER positive integer) — strip it,
78
+ // then right-align into the fixed-size output.
79
+ const rStripped = r[0] === 0x00 ? r.slice(1) : r;
80
+ const sStripped = s[0] === 0x00 ? s.slice(1) : s;
81
+ result.set(rStripped, coordSize - rStripped.length);
82
+ result.set(sStripped, coordSize * 2 - sStripped.length);
83
+ return result;
84
+ }
85
+ function pemToBytes(pem) {
86
+ const lines = pem.replace(/-----[A-Z ]+-----/g, "").replace(/\s+/g, "");
87
+ const binary = atob(lines);
88
+ const bytes = new Uint8Array(new ArrayBuffer(binary.length));
89
+ for (let i = 0; i < binary.length; i++) {
90
+ bytes[i] = binary.charCodeAt(i);
91
+ }
92
+ return bytes;
93
+ }
94
+ function signatureToBytes(signature, encoding) {
95
+ if (encoding === "base64") {
96
+ const binary = atob(signature);
97
+ const bytes = new Uint8Array(new ArrayBuffer(binary.length));
98
+ for (let i = 0; i < binary.length; i++) {
99
+ bytes[i] = binary.charCodeAt(i);
100
+ }
101
+ return bytes;
102
+ }
103
+ // hex
104
+ const bytes = new Uint8Array(new ArrayBuffer(signature.length / 2));
105
+ for (let i = 0; i < bytes.length; i++) {
106
+ bytes[i] = parseInt(signature.slice(i * 2, i * 2 + 2), 16);
107
+ }
108
+ return bytes;
109
+ }
110
+ function getSubtleAlgorithms(algorithm) {
111
+ switch (algorithm) {
112
+ case "RSA_SHA256":
113
+ return {
114
+ importAlgorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
115
+ verifyAlgorithm: { name: "RSASSA-PKCS1-v1_5" },
116
+ };
117
+ case "RSA_SHA384":
118
+ return {
119
+ importAlgorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-384" },
120
+ verifyAlgorithm: { name: "RSASSA-PKCS1-v1_5" },
121
+ };
122
+ case "RSA_SHA512":
123
+ return {
124
+ importAlgorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-512" },
125
+ verifyAlgorithm: { name: "RSASSA-PKCS1-v1_5" },
126
+ };
127
+ case "ECDSA_SHA256":
128
+ return {
129
+ importAlgorithm: { name: "ECDSA", namedCurve: "P-256" },
130
+ verifyAlgorithm: { name: "ECDSA", hash: "SHA-256" },
131
+ };
132
+ case "ECDSA_SHA384":
133
+ return {
134
+ importAlgorithm: { name: "ECDSA", namedCurve: "P-384" },
135
+ verifyAlgorithm: { name: "ECDSA", hash: "SHA-384" },
136
+ };
137
+ case "ECDSA_SHA512":
138
+ return {
139
+ importAlgorithm: { name: "ECDSA", namedCurve: "P-521" },
140
+ verifyAlgorithm: { name: "ECDSA", hash: "SHA-512" },
141
+ };
142
+ case "ED25519":
143
+ return {
144
+ importAlgorithm: { name: "Ed25519" },
145
+ verifyAlgorithm: { name: "Ed25519" },
146
+ };
147
+ }
148
+ }
149
+ export function verifyAsymmetricSignature(args) {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ if (RUNTIME.type === "node") {
152
+ const crypto = yield import("crypto");
153
+ if (args.algorithm === "ED25519") {
154
+ const signatureBytes = Uint8Array.from(Buffer.from(args.signature, args.encoding));
155
+ return crypto.verify(null, Uint8Array.from(Buffer.from(args.payload)), args.publicKey, signatureBytes);
156
+ }
157
+ const verifier = crypto.createVerify(getNodeAlgorithmName(args.algorithm));
158
+ verifier.update(args.payload);
159
+ return verifier.verify(args.publicKey, args.signature, args.encoding);
160
+ }
161
+ const subtle = globalThis.crypto.subtle;
162
+ const { importAlgorithm, verifyAlgorithm } = getSubtleAlgorithms(args.algorithm);
163
+ const keyBytes = pemToBytes(args.publicKey);
164
+ const key = yield subtle.importKey("spki", keyBytes, importAlgorithm, false, ["verify"]);
165
+ let signatureBytes = signatureToBytes(args.signature, args.encoding);
166
+ // Web Crypto requires IEEE P1363 format for ECDSA (raw r ∥ s), but the
167
+ // incoming signature is DER-encoded (the format Node and most libraries produce).
168
+ if (args.algorithm === "ECDSA_SHA256" || args.algorithm === "ECDSA_SHA384" || args.algorithm === "ECDSA_SHA512") {
169
+ signatureBytes = derToP1363(signatureBytes, ecdsaCoordSize(args.algorithm));
170
+ }
171
+ const payloadBytes = new TextEncoder().encode(args.payload);
172
+ return subtle.verify(verifyAlgorithm, key, signatureBytes, payloadBytes);
173
+ });
174
+ }
@@ -4,3 +4,4 @@ export { SuwardSDKClient } from "./Client.mjs";
4
4
  export { SuwardSDKEnvironment } from "./environments.mjs";
5
5
  export { SuwardSDKError, SuwardSDKTimeoutError } from "./errors/index.mjs";
6
6
  export * from "./exports.mjs";
7
+ export * as webhooks from "./webhooks/index.mjs";
@@ -3,3 +3,4 @@ export { SuwardSDKClient } from "./Client.mjs";
3
3
  export { SuwardSDKEnvironment } from "./environments.mjs";
4
4
  export { SuwardSDKError, SuwardSDKTimeoutError } from "./errors/index.mjs";
5
5
  export * from "./exports.mjs";
6
+ export * as webhooks from "./webhooks/index.mjs";
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.0.17";
1
+ export declare const SDK_VERSION = "0.0.19";
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.0.17";
1
+ export const SDK_VERSION = "0.0.19";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Verify an asymmetric webhook signature.
3
+ *
4
+ * Extract the signature from the "X-Suward-Signature" header and pass it as the signatureHeader parameter.
5
+ * Extract the timestamp from the "X-Suward-Timestamp" header and pass it as the timestampHeader parameter.
6
+ */
7
+ export declare class WebhooksHelper {
8
+ static verifySignature(requestBody: string, signatureHeader: string, publicKey: string, timestampHeader: string): Promise<boolean>;
9
+ }
@@ -0,0 +1,50 @@
1
+ // This file was auto-generated by Fern from our API Definition.
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ import * as core from "../core/index.mjs";
12
+ const TIMESTAMP_TOLERANCE_SECONDS = 300;
13
+ const SIGNATURE_PREFIX = "ed25519=";
14
+ /**
15
+ * Verify an asymmetric webhook signature.
16
+ *
17
+ * Extract the signature from the "X-Suward-Signature" header and pass it as the signatureHeader parameter.
18
+ * Extract the timestamp from the "X-Suward-Timestamp" header and pass it as the timestampHeader parameter.
19
+ */
20
+ export class WebhooksHelper {
21
+ static verifySignature(requestBody, signatureHeader, publicKey, timestampHeader) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ if (requestBody == null || signatureHeader == null || publicKey == null) {
24
+ throw new Error("Missing required parameters for webhook signature verification");
25
+ }
26
+ if (timestampHeader == null || timestampHeader === "") {
27
+ throw new Error("Missing timestamp header 'X-Suward-Timestamp' for webhook signature verification");
28
+ }
29
+ const timestampValue = parseInt(timestampHeader, 10);
30
+ if (Number.isNaN(timestampValue)) {
31
+ throw new Error("Invalid timestamp format: expected unix milliseconds");
32
+ }
33
+ const timestampMs = timestampValue;
34
+ if (Math.abs(Date.now() - timestampMs) > TIMESTAMP_TOLERANCE_SECONDS * 1000) {
35
+ return false;
36
+ }
37
+ const sig = signatureHeader.startsWith(SIGNATURE_PREFIX)
38
+ ? signatureHeader.slice(SIGNATURE_PREFIX.length)
39
+ : signatureHeader;
40
+ const payload = requestBody;
41
+ return yield core.verifyAsymmetricSignature({
42
+ payload: payload,
43
+ signature: sig,
44
+ publicKey: publicKey,
45
+ algorithm: "ED25519",
46
+ encoding: "hex",
47
+ });
48
+ });
49
+ }
50
+ }
@@ -0,0 +1 @@
1
+ export { WebhooksHelper } from "./WebhooksHelper.mjs";
@@ -0,0 +1 @@
1
+ export { WebhooksHelper } from "./WebhooksHelper.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pkg-sdk-test",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",