@pague-dev/sdk-node 1.0.0 → 1.2.0
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/README.md +13 -4
- package/dist/index.cjs +51 -1
- package/dist/index.d.cts +68 -1
- package/dist/index.d.mts +68 -1
- package/dist/index.mjs +51 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,9 +19,12 @@ const pdev = new Pdev('pd_live_sua_api_key');
|
|
|
19
19
|
// Opção 2: Usar variável de ambiente PDEV_API_KEY
|
|
20
20
|
const pdev = new Pdev();
|
|
21
21
|
|
|
22
|
-
// PIX
|
|
22
|
+
// PIX Dinâmico
|
|
23
23
|
await pdev.pix.create({ amount, description, customer });
|
|
24
24
|
|
|
25
|
+
// PIX QR Code Estático
|
|
26
|
+
await pdev.pix.createStaticQrCode({ amount, description });
|
|
27
|
+
|
|
25
28
|
// Charges
|
|
26
29
|
await pdev.charges.create({ projectId, name, amount, paymentMethods });
|
|
27
30
|
await pdev.charges.list({ page, limit });
|
|
@@ -43,19 +46,25 @@ await pdev.withdrawals.create({ amount, bankAccountId });
|
|
|
43
46
|
await pdev.transactions.get(id);
|
|
44
47
|
|
|
45
48
|
// Webhooks
|
|
46
|
-
import { parseWebhook } from '@pague-dev/sdk-node';
|
|
49
|
+
import { verifyWebhookSignature, parseWebhook } from '@pague-dev/sdk-node';
|
|
50
|
+
|
|
51
|
+
const signature = req.headers['x-webhook-signature'] as string;
|
|
52
|
+
if (!verifyWebhookSignature(req.body, signature, 'your_webhook_secret')) {
|
|
53
|
+
return res.status(401).send('Invalid signature');
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
const event = parseWebhook(req.body);
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
## Recursos
|
|
51
60
|
|
|
52
|
-
- **PIX** - Cobranças instantâneas
|
|
61
|
+
- **PIX** - Cobranças instantâneas (dinâmico e estático)
|
|
53
62
|
- **Charges** - Links de pagamento
|
|
54
63
|
- **Customers** - Gestão de clientes
|
|
55
64
|
- **Projects** - Organização por projetos
|
|
56
65
|
- **Withdrawals** - Saques via PIX (avulso ou conta salva)
|
|
57
66
|
- **Transactions** - Consulta de transações
|
|
58
|
-
- **Webhooks** -
|
|
67
|
+
- **Webhooks** - Verificação de assinatura e parsing de eventos
|
|
59
68
|
|
|
60
69
|
## Exemplo
|
|
61
70
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
let node_crypto = require("node:crypto");
|
|
1
2
|
|
|
3
|
+
//#region src/account/account.ts
|
|
4
|
+
var Account = class {
|
|
5
|
+
endpoint = "/account";
|
|
6
|
+
constructor(pdev) {
|
|
7
|
+
this.pdev = pdev;
|
|
8
|
+
}
|
|
9
|
+
async get() {
|
|
10
|
+
return this.pdev.get(this.endpoint);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
2
15
|
//#region src/common/utils/build-pagination-query.ts
|
|
3
16
|
function buildPaginationQuery(options) {
|
|
4
17
|
const searchParams = new URLSearchParams();
|
|
@@ -109,6 +122,7 @@ var Pdev = class {
|
|
|
109
122
|
key;
|
|
110
123
|
baseUrl;
|
|
111
124
|
headers;
|
|
125
|
+
account;
|
|
112
126
|
pix;
|
|
113
127
|
customers;
|
|
114
128
|
projects;
|
|
@@ -126,6 +140,7 @@ var Pdev = class {
|
|
|
126
140
|
"X-API-Key": this.key,
|
|
127
141
|
"Content-Type": "application/json"
|
|
128
142
|
});
|
|
143
|
+
this.account = new Account(this);
|
|
129
144
|
this.pix = new Pix(this);
|
|
130
145
|
this.customers = new Customers(this);
|
|
131
146
|
this.projects = new Projects(this);
|
|
@@ -261,6 +276,40 @@ function isValidWebhookEvent(event) {
|
|
|
261
276
|
* });
|
|
262
277
|
* ```
|
|
263
278
|
*/
|
|
279
|
+
/**
|
|
280
|
+
* Verifies the HMAC-SHA256 signature of a webhook payload.
|
|
281
|
+
*
|
|
282
|
+
* @param payload - The raw request body as a string
|
|
283
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
284
|
+
* @param secret - Your webhook secret (plain text, before hashing)
|
|
285
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* import { verifyWebhookSignature, parseWebhook } from '@pague-dev/sdk-node';
|
|
290
|
+
*
|
|
291
|
+
* app.post('/webhook', (req, res) => {
|
|
292
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
293
|
+
* const rawBody = req.body; // raw string body
|
|
294
|
+
*
|
|
295
|
+
* if (!verifyWebhookSignature(rawBody, signature, 'your_webhook_secret')) {
|
|
296
|
+
* return res.status(401).send('Invalid signature');
|
|
297
|
+
* }
|
|
298
|
+
*
|
|
299
|
+
* const event = parseWebhook(rawBody);
|
|
300
|
+
* // handle event...
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
305
|
+
if (!payload || !signature || !secret) return false;
|
|
306
|
+
try {
|
|
307
|
+
const expected = (0, node_crypto.createHmac)("sha256", (0, node_crypto.createHash)("sha256").update(secret).digest("hex")).update(payload).digest("hex");
|
|
308
|
+
return (0, node_crypto.timingSafeEqual)(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
264
313
|
function parseWebhook(payload) {
|
|
265
314
|
let parsed;
|
|
266
315
|
try {
|
|
@@ -274,4 +323,5 @@ function parseWebhook(payload) {
|
|
|
274
323
|
|
|
275
324
|
//#endregion
|
|
276
325
|
exports.Pdev = Pdev;
|
|
277
|
-
exports.parseWebhook = parseWebhook;
|
|
326
|
+
exports.parseWebhook = parseWebhook;
|
|
327
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
//#region src/account/interfaces/account-info.interface.d.ts
|
|
2
|
+
type AccountStatus = 'approved' | 'pending' | 'pending_documents' | 'pending_approval' | 'rejected' | 'suspended';
|
|
3
|
+
type CompanyStatus = 'pending' | 'active' | 'suspended' | 'blocked';
|
|
4
|
+
interface BalanceAmount {
|
|
5
|
+
amount: number;
|
|
6
|
+
amountFormatted: number;
|
|
7
|
+
}
|
|
8
|
+
interface AccountDetail {
|
|
9
|
+
id: string;
|
|
10
|
+
status: AccountStatus;
|
|
11
|
+
}
|
|
12
|
+
interface CompanyDetail {
|
|
13
|
+
razaoSocial: string;
|
|
14
|
+
nomeFantasia?: string;
|
|
15
|
+
cnpj: string;
|
|
16
|
+
email: string;
|
|
17
|
+
phone: string;
|
|
18
|
+
status: CompanyStatus;
|
|
19
|
+
}
|
|
20
|
+
interface BalanceDetail {
|
|
21
|
+
available: BalanceAmount;
|
|
22
|
+
promotional: BalanceAmount;
|
|
23
|
+
total: BalanceAmount;
|
|
24
|
+
currency: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
interface AccountInfo {
|
|
28
|
+
account: AccountDetail;
|
|
29
|
+
company: CompanyDetail | null;
|
|
30
|
+
balance: BalanceDetail;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
1
33
|
//#region src/charges/interfaces/charge.interface.d.ts
|
|
2
34
|
type ChargeStatus = 'active' | 'expired' | 'disabled' | 'paid';
|
|
3
35
|
type PaymentMethod = 'pix' | 'credit_card' | 'boleto';
|
|
@@ -108,6 +140,14 @@ interface ListCustomersOptions extends PaginationOptions {
|
|
|
108
140
|
}
|
|
109
141
|
type ListCustomersResponse = Response<PaginatedResponse<Customer>>;
|
|
110
142
|
//#endregion
|
|
143
|
+
//#region src/account/account.d.ts
|
|
144
|
+
declare class Account {
|
|
145
|
+
private readonly pdev;
|
|
146
|
+
private readonly endpoint;
|
|
147
|
+
constructor(pdev: Pdev);
|
|
148
|
+
get(): Promise<Response<AccountInfo>>;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
111
151
|
//#region src/common/base-resource.d.ts
|
|
112
152
|
/**
|
|
113
153
|
* Base class for API resources that follow CRUD patterns.
|
|
@@ -326,6 +366,7 @@ declare class Pdev {
|
|
|
326
366
|
readonly key: string;
|
|
327
367
|
private readonly baseUrl;
|
|
328
368
|
private readonly headers;
|
|
369
|
+
readonly account: Account;
|
|
329
370
|
readonly pix: Pix;
|
|
330
371
|
readonly customers: Customers;
|
|
331
372
|
readonly projects: Projects;
|
|
@@ -538,6 +579,32 @@ interface WebhookHeaders {
|
|
|
538
579
|
* });
|
|
539
580
|
* ```
|
|
540
581
|
*/
|
|
582
|
+
/**
|
|
583
|
+
* Verifies the HMAC-SHA256 signature of a webhook payload.
|
|
584
|
+
*
|
|
585
|
+
* @param payload - The raw request body as a string
|
|
586
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
587
|
+
* @param secret - Your webhook secret (plain text, before hashing)
|
|
588
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```typescript
|
|
592
|
+
* import { verifyWebhookSignature, parseWebhook } from '@pague-dev/sdk-node';
|
|
593
|
+
*
|
|
594
|
+
* app.post('/webhook', (req, res) => {
|
|
595
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
596
|
+
* const rawBody = req.body; // raw string body
|
|
597
|
+
*
|
|
598
|
+
* if (!verifyWebhookSignature(rawBody, signature, 'your_webhook_secret')) {
|
|
599
|
+
* return res.status(401).send('Invalid signature');
|
|
600
|
+
* }
|
|
601
|
+
*
|
|
602
|
+
* const event = parseWebhook(rawBody);
|
|
603
|
+
* // handle event...
|
|
604
|
+
* });
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
541
608
|
declare function parseWebhook(payload: string): WebhookEvent | null;
|
|
542
609
|
//#endregion
|
|
543
|
-
export { Charge, ChargeStatus, CreateChargeOptions, CreateChargeResponse, CreateCustomerOptions, CreateCustomerResponse, CreatePixCustomer, CreatePixOptions, CreatePixResponse, CreateProjectOptions, CreateProjectResponse, CreateStaticQrCodeOptions, CreateStaticQrCodeResponse, CreateWithdrawalOptions, CreateWithdrawalResponse, Customer, DocumentType, type ErrorResponse, GetChargeResponse, GetTransactionResponse, ListChargesOptions, ListChargesResponse, ListCustomersOptions, ListCustomersResponse, ListProjectsOptions, ListProjectsResponse, NotificationType, type PaginatedResponse, type PaginationOptions, PaymentCompletedData, PaymentCompletedEvent, PaymentMethod, Pdev, PixCharge, PixKeyType, PixStatus, Project, RefundCompletedData, RefundCompletedEvent, type Response, SortBy, SortOrder, StaticQrCode, Transaction, TransactionPaymentMethod, TransactionStatus, TransactionType, WebhookEvent, WebhookEventType, WebhookHeaders, WebhookPayload, Withdrawal, WithdrawalCompletedData, WithdrawalCompletedEvent, WithdrawalFailedData, WithdrawalFailedEvent, WithdrawalStatus, parseWebhook };
|
|
610
|
+
export { AccountDetail, AccountInfo, AccountStatus, BalanceAmount, BalanceDetail, Charge, ChargeStatus, CompanyDetail, CompanyStatus, CreateChargeOptions, CreateChargeResponse, CreateCustomerOptions, CreateCustomerResponse, CreatePixCustomer, CreatePixOptions, CreatePixResponse, CreateProjectOptions, CreateProjectResponse, CreateStaticQrCodeOptions, CreateStaticQrCodeResponse, CreateWithdrawalOptions, CreateWithdrawalResponse, Customer, DocumentType, type ErrorResponse, GetChargeResponse, GetTransactionResponse, ListChargesOptions, ListChargesResponse, ListCustomersOptions, ListCustomersResponse, ListProjectsOptions, ListProjectsResponse, NotificationType, type PaginatedResponse, type PaginationOptions, PaymentCompletedData, PaymentCompletedEvent, PaymentMethod, Pdev, PixCharge, PixKeyType, PixStatus, Project, RefundCompletedData, RefundCompletedEvent, type Response, SortBy, SortOrder, StaticQrCode, Transaction, TransactionPaymentMethod, TransactionStatus, TransactionType, WebhookEvent, WebhookEventType, WebhookHeaders, WebhookPayload, Withdrawal, WithdrawalCompletedData, WithdrawalCompletedEvent, WithdrawalFailedData, WithdrawalFailedEvent, WithdrawalStatus, parseWebhook, verifyWebhookSignature };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
//#region src/account/interfaces/account-info.interface.d.ts
|
|
2
|
+
type AccountStatus = 'approved' | 'pending' | 'pending_documents' | 'pending_approval' | 'rejected' | 'suspended';
|
|
3
|
+
type CompanyStatus = 'pending' | 'active' | 'suspended' | 'blocked';
|
|
4
|
+
interface BalanceAmount {
|
|
5
|
+
amount: number;
|
|
6
|
+
amountFormatted: number;
|
|
7
|
+
}
|
|
8
|
+
interface AccountDetail {
|
|
9
|
+
id: string;
|
|
10
|
+
status: AccountStatus;
|
|
11
|
+
}
|
|
12
|
+
interface CompanyDetail {
|
|
13
|
+
razaoSocial: string;
|
|
14
|
+
nomeFantasia?: string;
|
|
15
|
+
cnpj: string;
|
|
16
|
+
email: string;
|
|
17
|
+
phone: string;
|
|
18
|
+
status: CompanyStatus;
|
|
19
|
+
}
|
|
20
|
+
interface BalanceDetail {
|
|
21
|
+
available: BalanceAmount;
|
|
22
|
+
promotional: BalanceAmount;
|
|
23
|
+
total: BalanceAmount;
|
|
24
|
+
currency: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
interface AccountInfo {
|
|
28
|
+
account: AccountDetail;
|
|
29
|
+
company: CompanyDetail | null;
|
|
30
|
+
balance: BalanceDetail;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
1
33
|
//#region src/charges/interfaces/charge.interface.d.ts
|
|
2
34
|
type ChargeStatus = 'active' | 'expired' | 'disabled' | 'paid';
|
|
3
35
|
type PaymentMethod = 'pix' | 'credit_card' | 'boleto';
|
|
@@ -108,6 +140,14 @@ interface ListCustomersOptions extends PaginationOptions {
|
|
|
108
140
|
}
|
|
109
141
|
type ListCustomersResponse = Response<PaginatedResponse<Customer>>;
|
|
110
142
|
//#endregion
|
|
143
|
+
//#region src/account/account.d.ts
|
|
144
|
+
declare class Account {
|
|
145
|
+
private readonly pdev;
|
|
146
|
+
private readonly endpoint;
|
|
147
|
+
constructor(pdev: Pdev);
|
|
148
|
+
get(): Promise<Response<AccountInfo>>;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
111
151
|
//#region src/common/base-resource.d.ts
|
|
112
152
|
/**
|
|
113
153
|
* Base class for API resources that follow CRUD patterns.
|
|
@@ -326,6 +366,7 @@ declare class Pdev {
|
|
|
326
366
|
readonly key: string;
|
|
327
367
|
private readonly baseUrl;
|
|
328
368
|
private readonly headers;
|
|
369
|
+
readonly account: Account;
|
|
329
370
|
readonly pix: Pix;
|
|
330
371
|
readonly customers: Customers;
|
|
331
372
|
readonly projects: Projects;
|
|
@@ -538,6 +579,32 @@ interface WebhookHeaders {
|
|
|
538
579
|
* });
|
|
539
580
|
* ```
|
|
540
581
|
*/
|
|
582
|
+
/**
|
|
583
|
+
* Verifies the HMAC-SHA256 signature of a webhook payload.
|
|
584
|
+
*
|
|
585
|
+
* @param payload - The raw request body as a string
|
|
586
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
587
|
+
* @param secret - Your webhook secret (plain text, before hashing)
|
|
588
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```typescript
|
|
592
|
+
* import { verifyWebhookSignature, parseWebhook } from '@pague-dev/sdk-node';
|
|
593
|
+
*
|
|
594
|
+
* app.post('/webhook', (req, res) => {
|
|
595
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
596
|
+
* const rawBody = req.body; // raw string body
|
|
597
|
+
*
|
|
598
|
+
* if (!verifyWebhookSignature(rawBody, signature, 'your_webhook_secret')) {
|
|
599
|
+
* return res.status(401).send('Invalid signature');
|
|
600
|
+
* }
|
|
601
|
+
*
|
|
602
|
+
* const event = parseWebhook(rawBody);
|
|
603
|
+
* // handle event...
|
|
604
|
+
* });
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
541
608
|
declare function parseWebhook(payload: string): WebhookEvent | null;
|
|
542
609
|
//#endregion
|
|
543
|
-
export { Charge, ChargeStatus, CreateChargeOptions, CreateChargeResponse, CreateCustomerOptions, CreateCustomerResponse, CreatePixCustomer, CreatePixOptions, CreatePixResponse, CreateProjectOptions, CreateProjectResponse, CreateStaticQrCodeOptions, CreateStaticQrCodeResponse, CreateWithdrawalOptions, CreateWithdrawalResponse, Customer, DocumentType, type ErrorResponse, GetChargeResponse, GetTransactionResponse, ListChargesOptions, ListChargesResponse, ListCustomersOptions, ListCustomersResponse, ListProjectsOptions, ListProjectsResponse, NotificationType, type PaginatedResponse, type PaginationOptions, PaymentCompletedData, PaymentCompletedEvent, PaymentMethod, Pdev, PixCharge, PixKeyType, PixStatus, Project, RefundCompletedData, RefundCompletedEvent, type Response, SortBy, SortOrder, StaticQrCode, Transaction, TransactionPaymentMethod, TransactionStatus, TransactionType, WebhookEvent, WebhookEventType, WebhookHeaders, WebhookPayload, Withdrawal, WithdrawalCompletedData, WithdrawalCompletedEvent, WithdrawalFailedData, WithdrawalFailedEvent, WithdrawalStatus, parseWebhook };
|
|
610
|
+
export { AccountDetail, AccountInfo, AccountStatus, BalanceAmount, BalanceDetail, Charge, ChargeStatus, CompanyDetail, CompanyStatus, CreateChargeOptions, CreateChargeResponse, CreateCustomerOptions, CreateCustomerResponse, CreatePixCustomer, CreatePixOptions, CreatePixResponse, CreateProjectOptions, CreateProjectResponse, CreateStaticQrCodeOptions, CreateStaticQrCodeResponse, CreateWithdrawalOptions, CreateWithdrawalResponse, Customer, DocumentType, type ErrorResponse, GetChargeResponse, GetTransactionResponse, ListChargesOptions, ListChargesResponse, ListCustomersOptions, ListCustomersResponse, ListProjectsOptions, ListProjectsResponse, NotificationType, type PaginatedResponse, type PaginationOptions, PaymentCompletedData, PaymentCompletedEvent, PaymentMethod, Pdev, PixCharge, PixKeyType, PixStatus, Project, RefundCompletedData, RefundCompletedEvent, type Response, SortBy, SortOrder, StaticQrCode, Transaction, TransactionPaymentMethod, TransactionStatus, TransactionType, WebhookEvent, WebhookEventType, WebhookHeaders, WebhookPayload, Withdrawal, WithdrawalCompletedData, WithdrawalCompletedEvent, WithdrawalFailedData, WithdrawalFailedEvent, WithdrawalStatus, parseWebhook, verifyWebhookSignature };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/account/account.ts
|
|
4
|
+
var Account = class {
|
|
5
|
+
endpoint = "/account";
|
|
6
|
+
constructor(pdev) {
|
|
7
|
+
this.pdev = pdev;
|
|
8
|
+
}
|
|
9
|
+
async get() {
|
|
10
|
+
return this.pdev.get(this.endpoint);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
1
15
|
//#region src/common/utils/build-pagination-query.ts
|
|
2
16
|
function buildPaginationQuery(options) {
|
|
3
17
|
const searchParams = new URLSearchParams();
|
|
@@ -108,6 +122,7 @@ var Pdev = class {
|
|
|
108
122
|
key;
|
|
109
123
|
baseUrl;
|
|
110
124
|
headers;
|
|
125
|
+
account;
|
|
111
126
|
pix;
|
|
112
127
|
customers;
|
|
113
128
|
projects;
|
|
@@ -125,6 +140,7 @@ var Pdev = class {
|
|
|
125
140
|
"X-API-Key": this.key,
|
|
126
141
|
"Content-Type": "application/json"
|
|
127
142
|
});
|
|
143
|
+
this.account = new Account(this);
|
|
128
144
|
this.pix = new Pix(this);
|
|
129
145
|
this.customers = new Customers(this);
|
|
130
146
|
this.projects = new Projects(this);
|
|
@@ -260,6 +276,40 @@ function isValidWebhookEvent(event) {
|
|
|
260
276
|
* });
|
|
261
277
|
* ```
|
|
262
278
|
*/
|
|
279
|
+
/**
|
|
280
|
+
* Verifies the HMAC-SHA256 signature of a webhook payload.
|
|
281
|
+
*
|
|
282
|
+
* @param payload - The raw request body as a string
|
|
283
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
284
|
+
* @param secret - Your webhook secret (plain text, before hashing)
|
|
285
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* import { verifyWebhookSignature, parseWebhook } from '@pague-dev/sdk-node';
|
|
290
|
+
*
|
|
291
|
+
* app.post('/webhook', (req, res) => {
|
|
292
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
293
|
+
* const rawBody = req.body; // raw string body
|
|
294
|
+
*
|
|
295
|
+
* if (!verifyWebhookSignature(rawBody, signature, 'your_webhook_secret')) {
|
|
296
|
+
* return res.status(401).send('Invalid signature');
|
|
297
|
+
* }
|
|
298
|
+
*
|
|
299
|
+
* const event = parseWebhook(rawBody);
|
|
300
|
+
* // handle event...
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
305
|
+
if (!payload || !signature || !secret) return false;
|
|
306
|
+
try {
|
|
307
|
+
const expected = createHmac("sha256", createHash("sha256").update(secret).digest("hex")).update(payload).digest("hex");
|
|
308
|
+
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
263
313
|
function parseWebhook(payload) {
|
|
264
314
|
let parsed;
|
|
265
315
|
try {
|
|
@@ -272,4 +322,4 @@ function parseWebhook(payload) {
|
|
|
272
322
|
}
|
|
273
323
|
|
|
274
324
|
//#endregion
|
|
275
|
-
export { Pdev, parseWebhook };
|
|
325
|
+
export { Pdev, parseWebhook, verifyWebhookSignature };
|