@paybetaby/node-sdk 0.1.1

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 ADDED
@@ -0,0 +1,666 @@
1
+ # @paybetaby/node-sdk
2
+
3
+ Official Node.js SDK for the [Paybeta](https://usepaybeta.com) payments API. Fully typed TypeScript library with zero runtime dependencies, native `fetch`, and dual CJS/ESM output.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Requirements](#requirements)
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
12
+ - [Authentication](#authentication)
13
+ - [Configuration](#configuration)
14
+ - [Resources](#resources)
15
+ - [Transactions](#transactions)
16
+ - [Payments](#payments)
17
+ - [Escrows](#escrows)
18
+ - [Disputes](#disputes)
19
+ - [Webhooks](#webhooks)
20
+ - [Error Handling](#error-handling)
21
+ - [TypeScript](#typescript)
22
+ - [Building from Source](#building-from-source)
23
+
24
+ ---
25
+
26
+ ## Requirements
27
+
28
+ - Node.js **18** or later (uses native `fetch`)
29
+ - A Paybeta merchant account — [sign up at usepaybeta.com](https://usepaybeta.com)
30
+ - An API key from your Paybeta dashboard (`pb_live_…` for production, `pb_test_…` for sandbox)
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ npm install @paybetaby/node-sdk
38
+ # or
39
+ yarn add @paybetaby/node-sdk
40
+ # or
41
+ pnpm add @paybetaby/node-sdk
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ```typescript
49
+ import { PaybetaClient } from '@paybetaby/node-sdk';
50
+
51
+ const paybeta = new PaybetaClient({
52
+ apiKey: process.env.PAYBETA_API_KEY!,
53
+ webhookSecret: process.env.PAYBETA_WEBHOOK_SECRET,
54
+ });
55
+
56
+ // 1. Create a transaction
57
+ const transaction = await paybeta.transactions.create({
58
+ merchantId: 'your-merchant-id',
59
+ buyerEmail: 'buyer@example.com',
60
+ buyerPhone: '+2348012345678',
61
+ sellerEmail: 'seller@example.com',
62
+ amount: 500, // decimal naira (₦500.00) — unlike Payment.amount, which is kobo
63
+ currency: 'NGN',
64
+ });
65
+
66
+ // 2. Initiate payment — get a redirect URL for your customer
67
+ const payment = await paybeta.payments.initiate({
68
+ merchantId: 'your-merchant-id',
69
+ transactionId: transaction.id,
70
+ amount: 50_000,
71
+ currency: 'NGN',
72
+ paymentMethod: 'CARD',
73
+ pspType: 'PAYSTACK',
74
+ customerEmail: 'buyer@example.com',
75
+ });
76
+
77
+ // Redirect the customer to complete payment
78
+ console.log(payment.authorizationUrl);
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Authentication
84
+
85
+ Paybeta uses **API key authentication**. Pass your key once when constructing the client — all subsequent requests carry it automatically via the `X-API-Key` header.
86
+
87
+ ```typescript
88
+ const paybeta = new PaybetaClient({ apiKey: 'pb_live_...' });
89
+ ```
90
+
91
+ | Key prefix | Environment |
92
+ |-------------|----------------------|
93
+ | `pb_live_…` | Production (live) |
94
+ | `pb_test_…` | Sandbox (test mode) |
95
+
96
+ > **Keep your API key secret.** Never embed it in client-side code or commit it to version control. Use environment variables.
97
+
98
+ ---
99
+
100
+ ## Configuration
101
+
102
+ ```typescript
103
+ const paybeta = new PaybetaClient({
104
+ // Required
105
+ apiKey: 'pb_live_...',
106
+
107
+ // Optional — defaults to https://api.usepaybeta.com
108
+ baseUrl: 'https://api.usepaybeta.com',
109
+
110
+ // Optional — required only for webhook.constructEvent()
111
+ webhookSecret: 'your-webhook-secret',
112
+
113
+ // Optional — request timeout in milliseconds (default: 30000)
114
+ timeout: 30_000,
115
+ });
116
+ ```
117
+
118
+ ### `PaybetaClientConfig`
119
+
120
+ | Option | Type | Default | Description |
121
+ |-----------------|----------|--------------------------------|-----------------------------------------------|
122
+ | `apiKey` | `string` | **required** | Your Paybeta API key |
123
+ | `baseUrl` | `string` | `https://api.usepaybeta.com` | Override for staging or self-hosted instances |
124
+ | `webhookSecret` | `string` | `undefined` | HMAC secret for webhook signature verification|
125
+ | `timeout` | `number` | `30000` | Request timeout in milliseconds |
126
+
127
+ ---
128
+
129
+ ## Resources
130
+
131
+ ### Transactions
132
+
133
+ A **transaction** represents the commercial relationship between a buyer and seller. Create one before initiating a payment or creating an escrow.
134
+
135
+ #### `paybeta.transactions.create(params)`
136
+
137
+ ```typescript
138
+ const transaction = await paybeta.transactions.create({
139
+ merchantId: 'your-merchant-id',
140
+ buyerEmail: 'buyer@example.com',
141
+ buyerPhone: '+2348012345678',
142
+ sellerEmail: 'seller@example.com',
143
+ amount: 1_500, // ₦1,500.00 — decimal naira, not kobo
144
+ currency: 'NGN',
145
+ metadata: {
146
+ orderId: 'ORD-001',
147
+ productName: 'MacBook Air',
148
+ },
149
+ });
150
+
151
+ console.log(transaction.id); // UUID
152
+ console.log(transaction.status); // 'INITIATED'
153
+ ```
154
+
155
+ #### `paybeta.transactions.list(params?)`
156
+
157
+ Returns a bare array — there is no pagination envelope on this endpoint. Passing `merchantId` is required for an API-key caller: the API only allows merchant credentials to call `/transactions/merchant/:id`, not the bare `/transactions` list (that one's platform-role only and 403s an API key).
158
+
159
+ ```typescript
160
+ const transactions = await paybeta.transactions.list({
161
+ merchantId: 'your-merchant-id',
162
+ status: 'FUNDED',
163
+ limit: 20,
164
+ offset: 0,
165
+ });
166
+ ```
167
+
168
+ #### `paybeta.transactions.retrieve(id)`
169
+
170
+ ```typescript
171
+ const transaction = await paybeta.transactions.retrieve('txn-uuid');
172
+ ```
173
+
174
+ #### `paybeta.transactions.listHistory(id)`
175
+
176
+ ```typescript
177
+ const events = await paybeta.transactions.listHistory('txn-uuid');
178
+ // Returns TransactionEvent[] — status changes, saga steps, etc.
179
+ ```
180
+
181
+ **Transaction statuses:** `INITIATED` → `FUNDED` → `IN_ESCROW` → `RELEASED` / `DISPUTED` / `REFUNDED`
182
+
183
+ ---
184
+
185
+ ### Payments
186
+
187
+ A **payment** records a customer's attempt to fund a transaction via a PSP (Paystack, Flutterwave).
188
+
189
+ #### `paybeta.payments.initiate(params)`
190
+
191
+ ```typescript
192
+ const payment = await paybeta.payments.initiate({
193
+ merchantId: 'your-merchant-id',
194
+ transactionId: transaction.id,
195
+ amount: 150_000, // kobo — integer minor-unit, unlike Transaction.amount
196
+ currency: 'NGN',
197
+ paymentMethod: 'CARD', // 'CARD' | 'BANK_TRANSFER' | 'USSD' | 'MOBILE_MONEY' | 'BANK_ACCOUNT'
198
+ pspType: 'PAYSTACK', // 'PAYSTACK' | 'FLUTTERWAVE' | 'BANK_DIRECT'
199
+ customerEmail: 'buyer@example.com',
200
+ customerName: 'Jane Doe', // optional
201
+ redirectUrl: 'https://yourapp.com/payment/callback', // optional
202
+ metadata: { sessionId: 'abc' }, // optional
203
+ });
204
+
205
+ // Redirect your customer to complete the payment
206
+ window.location.href = payment.authorizationUrl!;
207
+ ```
208
+
209
+ #### `paybeta.payments.verify(id)`
210
+
211
+ Call this when the customer returns from the PSP redirect to confirm the payment status.
212
+
213
+ ```typescript
214
+ const payment = await paybeta.payments.verify(paymentId);
215
+
216
+ if (payment.status === 'COMPLETED') {
217
+ // fulfil the order
218
+ }
219
+ ```
220
+
221
+ #### `paybeta.payments.retrieve(id)`
222
+
223
+ ```typescript
224
+ const payment = await paybeta.payments.retrieve(paymentId);
225
+ ```
226
+
227
+ #### `paybeta.payments.list(params?)`
228
+
229
+ Returns a bare array. `merchantId` is required for an API-key caller — same reasoning as transactions above.
230
+
231
+ ```typescript
232
+ const payments = await paybeta.payments.list({ merchantId: 'your-merchant-id', limit: 50 });
233
+ ```
234
+
235
+ #### `paybeta.payments.retry(id)`
236
+
237
+ ```typescript
238
+ const retried = await paybeta.payments.retry(paymentId);
239
+ ```
240
+
241
+ #### `paybeta.payments.listAttempts(id)`
242
+
243
+ ```typescript
244
+ const attempts = await paybeta.payments.listAttempts(paymentId);
245
+ ```
246
+
247
+ **Payment statuses:** `PENDING` → `PROCESSING` → `COMPLETED` / `FAILED` / `CANCELLED`
248
+
249
+ ---
250
+
251
+ ### Escrows
252
+
253
+ **Escrows** hold funds securely between buyer and seller until configurable release conditions are met. Available on **Growth** and **Enterprise** plans.
254
+
255
+ #### `paybeta.escrows.create(params)`
256
+
257
+ ```typescript
258
+ const escrow = await paybeta.escrows.create({
259
+ transactionId: transaction.id,
260
+ merchantId: 'your-merchant-id',
261
+ buyerEmail: 'buyer@example.com',
262
+ buyerPhone: '+2348012345678', // required — guaranteed WhatsApp/SMS delivery channel
263
+ sellerEmail: 'seller@example.com',
264
+ amount: 1_500, // decimal naira — the API converts to kobo itself
265
+ currency: 'NGN',
266
+ releasePolicy: { // optional
267
+ conditionLogic: 'AND', // release only when ALL conditions are met
268
+ conditions: [
269
+ { type: 'DELIVERY_CONFIRMATION' },
270
+ { type: 'BUYER_CONFIRMATION' },
271
+ ],
272
+ },
273
+ });
274
+ ```
275
+
276
+ **Condition types:**
277
+
278
+ | Type | Description |
279
+ |-------------------------|-----------------------------------------------------|
280
+ | `DELIVERY_CONFIRMATION` | Seller confirms goods/services delivered |
281
+ | `BUYER_CONFIRMATION` | Buyer explicitly approves release |
282
+ | `TIME_BASED` | Auto-release after a configured time window |
283
+ | `MANUAL_APPROVAL` | Merchant triggers release manually |
284
+
285
+ **Condition logic:**
286
+
287
+ | Value | Meaning |
288
+ |-------|-------------------------------------------|
289
+ | `AND` | All conditions must be met before release |
290
+ | `OR` | Any one condition triggers release |
291
+
292
+ #### `paybeta.escrows.release(id, params?)`
293
+
294
+ `actorId`/`actorType` are accepted for forward-compat only — the API always attributes the action to the authenticated caller, never a client-supplied value.
295
+
296
+ ```typescript
297
+ await paybeta.escrows.release(escrowId, { idempotencyKey: 'release-once' });
298
+ ```
299
+
300
+ #### `paybeta.escrows.confirmDelivery(id, params?)`
301
+
302
+ ```typescript
303
+ await paybeta.escrows.confirmDelivery(escrowId, { trackingReference: 'DHL123456' });
304
+ ```
305
+
306
+ #### `paybeta.escrows.confirmBuyer(id, params?)`
307
+
308
+ ```typescript
309
+ await paybeta.escrows.confirmBuyer(escrowId);
310
+ ```
311
+
312
+ #### `paybeta.escrows.dispute(id)`
313
+
314
+ ```typescript
315
+ await paybeta.escrows.dispute(escrowId);
316
+ ```
317
+
318
+ #### `paybeta.escrows.refund(id)`
319
+
320
+ ```typescript
321
+ await paybeta.escrows.refund(escrowId);
322
+ ```
323
+
324
+ #### `paybeta.escrows.cancel(id)`
325
+
326
+ ```typescript
327
+ await paybeta.escrows.cancel(escrowId);
328
+ ```
329
+
330
+ #### `paybeta.escrows.retrieve(id)`
331
+
332
+ ```typescript
333
+ const escrow = await paybeta.escrows.retrieve(escrowId);
334
+ console.log(escrow.status); // lowercase: 'funded' | 'pending_release' | 'released' | ...
335
+ console.log(escrow.amount); // kobo (integer) — not divided down on the way out, unlike on create
336
+ ```
337
+
338
+ #### `paybeta.escrows.retrieveBalance(id)`
339
+
340
+ All three amounts are decimal strings (already divided from kobo), not numbers.
341
+
342
+ ```typescript
343
+ const { heldAmount, releasedAmount, refundedAmount, currency } = await paybeta.escrows.retrieveBalance(escrowId);
344
+ ```
345
+
346
+ #### `paybeta.escrows.retrieveConditions(id)`
347
+
348
+ Returns an envelope, not a bare array.
349
+
350
+ ```typescript
351
+ const { conditions, canRelease } = await paybeta.escrows.retrieveConditions(escrowId);
352
+ conditions.forEach(c => console.log(c.type, c.isMet));
353
+ ```
354
+
355
+ #### `paybeta.escrows.list(params?)`
356
+
357
+ Unlike payments/transactions/disputes, this returns a `{ escrows, total, limit, offset }` envelope, not a bare array. `merchantId` routes to the merchant-scoped endpoint, same as the other resources.
358
+
359
+ ```typescript
360
+ const { escrows, total } = await paybeta.escrows.list({
361
+ merchantId: 'your-merchant-id',
362
+ status: 'funded',
363
+ limit: 20,
364
+ });
365
+ ```
366
+
367
+ **Escrow statuses (lowercase):** `created` → `funded` → `pending_release` → `released` / `disputed` / `refunded` / `cancelled`
368
+
369
+ ---
370
+
371
+ ### Disputes
372
+
373
+ A **dispute** is opened when buyer and seller cannot agree. Paybeta provides a structured arbitration workflow.
374
+
375
+ #### `paybeta.disputes.open(params)`
376
+
377
+ All of the fields below are required by the API — there's no partial/inferred version of opening a dispute.
378
+
379
+ ```typescript
380
+ const dispute = await paybeta.disputes.open({
381
+ transactionId: transaction.id,
382
+ escrowId: escrow.id,
383
+ merchantId: 'your-merchant-id',
384
+ buyerEmail: 'buyer@example.com',
385
+ sellerEmail: 'seller@example.com',
386
+ disputeType: 'NON_DELIVERY',
387
+ priority: 'HIGH',
388
+ description: 'Item not as described.',
389
+ amount: 150_000, // kobo
390
+ currency: 'NGN',
391
+ openedBy: 'BUYER', // 'BUYER' | 'SELLER'
392
+ });
393
+ ```
394
+
395
+ #### `paybeta.disputes.uploadEvidence(id, params)`
396
+
397
+ Field names match the API's JSON body exactly — not a generic `fileBase64`/`fileType`/`submittedBy` shape.
398
+
399
+ ```typescript
400
+ await paybeta.disputes.uploadEvidence(disputeId, {
401
+ evidenceType: 'IMAGE', // 'IMAGE' | 'DOCUMENT' | 'VIDEO' | 'OTHER'
402
+ uploadedBy: 'BUYER', // 'BUYER' | 'SELLER' | 'ARBITRATOR'
403
+ fileName: 'packaging.jpg',
404
+ fileData: Buffer.from(fileBytes).toString('base64'),
405
+ mimeType: 'image/jpeg',
406
+ description: 'Photo of damaged packaging',
407
+ });
408
+ ```
409
+
410
+ #### `paybeta.disputes.resolve(id, params)`
411
+
412
+ ```typescript
413
+ await paybeta.disputes.resolve(disputeId, {
414
+ outcome: 'BUYER_WINS', // 'BUYER_WINS' | 'SELLER_WINS' | 'PARTIAL_REFUND' | 'PARTIAL_RELEASE' | 'SPLIT' | 'CANCELLED'
415
+ notes: 'Evidence confirmed item was not delivered.',
416
+ });
417
+ ```
418
+
419
+ #### `paybeta.disputes.cancel(id, params)`
420
+
421
+ ```typescript
422
+ await paybeta.disputes.cancel(disputeId, { reason: 'Parties reached mutual agreement.' });
423
+ ```
424
+
425
+ #### `paybeta.disputes.retrieve(id)` / `paybeta.disputes.list(params?)`
426
+
427
+ `list()` returns a bare array. `merchantId` is required for an API-key caller — same reasoning as transactions/payments above.
428
+
429
+ ```typescript
430
+ const dispute = await paybeta.disputes.retrieve(disputeId);
431
+ const disputes = await paybeta.disputes.list({ merchantId: 'your-merchant-id', status: 'OPENED' });
432
+ ```
433
+
434
+ ---
435
+
436
+ ### Webhooks
437
+
438
+ Paybeta sends signed webhook events to your server when key state changes occur (payment completed, escrow released, dispute opened, etc.). Use `webhooks.constructEvent()` to verify the signature and parse the payload safely.
439
+
440
+ #### Setup
441
+
442
+ Configure your client with a `webhookSecret`:
443
+
444
+ ```typescript
445
+ const paybeta = new PaybetaClient({
446
+ apiKey: process.env.PAYBETA_API_KEY!,
447
+ webhookSecret: process.env.PAYBETA_WEBHOOK_SECRET!,
448
+ });
449
+ ```
450
+
451
+ #### Express example
452
+
453
+ ```typescript
454
+ import express from 'express';
455
+ import { PaybetaClient, PaybetaError, type WebhookEvent } from '@paybetaby/node-sdk';
456
+
457
+ const app = express();
458
+ const paybeta = new PaybetaClient({
459
+ apiKey: process.env.PAYBETA_API_KEY!,
460
+ webhookSecret: process.env.PAYBETA_WEBHOOK_SECRET!,
461
+ });
462
+
463
+ // IMPORTANT: use raw body — do NOT use express.json() for this route
464
+ app.post(
465
+ '/webhooks/paybeta',
466
+ express.raw({ type: '*/*' }),
467
+ (req, res) => {
468
+ const signature = req.headers['x-paybeta-signature'] as string;
469
+ const timestamp = req.headers['x-paybeta-timestamp'] as string;
470
+
471
+ let event: WebhookEvent;
472
+ try {
473
+ event = paybeta.webhooks.constructEvent(req.body, signature, timestamp);
474
+ } catch (err) {
475
+ if (err instanceof PaybetaError) {
476
+ console.error('Webhook signature invalid:', err.message);
477
+ return res.status(400).send('Webhook signature verification failed');
478
+ }
479
+ throw err;
480
+ }
481
+
482
+ switch (event.eventType) {
483
+ case 'payment.received':
484
+ console.log('Payment received:', event.data);
485
+ // fulfil order, send confirmation email, etc.
486
+ break;
487
+ case 'transaction.funded':
488
+ console.log('Transaction funded:', event.data);
489
+ break;
490
+ case 'escrow.released':
491
+ console.log('Escrow released:', event.data);
492
+ break;
493
+ case 'dispute.opened':
494
+ console.log('Dispute opened:', event.data);
495
+ break;
496
+ default:
497
+ console.log('Unhandled event type:', event.eventType);
498
+ }
499
+
500
+ res.json({ received: true });
501
+ }
502
+ );
503
+ ```
504
+
505
+ `constructEvent` verifies `HMAC-SHA256(webhookSecret, "${timestamp}.${rawBody}")` against the `X-PayBeta-Signature` header (sent as `sha256=<hex>`) — both the signature *and* timestamp headers are required, since the timestamp is part of what's actually signed, not just metadata.
506
+
507
+ #### Event types
508
+
509
+ Exactly the events PayBeta can emit — there is no `dispute.cancelled` or any `escrow.*` event besides `escrow.released`.
510
+
511
+ | Event type | Description |
512
+ |---------------------------|--------------------------------------------------|
513
+ | `transaction.created` | New transaction created |
514
+ | `transaction.funded` | Customer's payment cleared; funds received |
515
+ | `transaction.escrowed` | Funds moved into escrow hold |
516
+ | `transaction.released` | Funds released to seller |
517
+ | `transaction.disputed` | Dispute opened on transaction |
518
+ | `transaction.refunded` | Transaction refunded to buyer |
519
+ | `payment.received` | Payment confirmed as successful |
520
+ | `payment.failed` | Payment failed or declined |
521
+ | `dispute.opened` | Dispute opened |
522
+ | `dispute.resolved` | Dispute resolved with outcome |
523
+ | `escrow.released` | Escrow funds disbursed to seller |
524
+
525
+ ---
526
+
527
+ ## Error Handling
528
+
529
+ The SDK throws two error types:
530
+
531
+ ### `PaybetaApiError`
532
+
533
+ Thrown when the API returns a non-2xx response.
534
+
535
+ ```typescript
536
+ import { PaybetaApiError } from '@paybetaby/node-sdk';
537
+
538
+ try {
539
+ const escrow = await paybeta.escrows.create({ ... });
540
+ } catch (err) {
541
+ if (err instanceof PaybetaApiError) {
542
+ console.error(err.message); // Human-readable error message
543
+ console.error(err.status); // HTTP status code (e.g. 402, 403, 404)
544
+ console.error(err.code); // Machine-readable code (e.g. 'FEATURE_NOT_AVAILABLE')
545
+ console.error(err.traceId); // Paybeta trace ID for support
546
+ }
547
+ }
548
+ ```
549
+
550
+ **Common error codes:**
551
+
552
+ | Code | Status | Meaning |
553
+ |---------------------------|--------|--------------------------------------------------------------|
554
+ | `FEATURE_NOT_AVAILABLE` | 403 | Feature not enabled on your plan (e.g. escrow on Starter) |
555
+ | `VOLUME_LIMIT_EXCEEDED` | 402 | Monthly volume limit reached — upgrade your plan |
556
+ | `API_KEY_LIMIT_EXCEEDED` | 402 | API key count limit reached for your plan |
557
+ | `NOT_FOUND` | 404 | Resource not found |
558
+ | `UNAUTHORIZED` | 401 | Invalid or missing API key |
559
+ | `TOO_MANY_REQUESTS` | 429 | Rate limit exceeded |
560
+ | `BAD_REQUEST` | 400 | Validation error — check the error message for field details |
561
+
562
+ ### `PaybetaError`
563
+
564
+ Thrown for client-side errors: request timeout, webhook signature failure, missing configuration.
565
+
566
+ ```typescript
567
+ import { PaybetaError } from '@paybetaby/node-sdk';
568
+
569
+ try {
570
+ const event = paybeta.webhooks.constructEvent(rawBody, signature);
571
+ } catch (err) {
572
+ if (err instanceof PaybetaError) {
573
+ // signature mismatch, missing webhookSecret, etc.
574
+ console.error(err.message);
575
+ }
576
+ }
577
+ ```
578
+
579
+ ---
580
+
581
+ ## TypeScript
582
+
583
+ The SDK is written in TypeScript and ships full type declarations. No `@types` package required.
584
+
585
+ All parameter and response types are exported from the package root:
586
+
587
+ ```typescript
588
+ import type {
589
+ // Client
590
+ PaybetaClientConfig,
591
+
592
+ // Common
593
+ RequestOptions,
594
+
595
+ // Transactions
596
+ Transaction,
597
+ TransactionStatus,
598
+ TransactionEvent,
599
+ CreateTransactionParams,
600
+ ListTransactionsParams,
601
+
602
+ // Payments
603
+ Payment,
604
+ PaymentStatus,
605
+ PaymentMethod,
606
+ PSPType,
607
+ InitiatePaymentParams,
608
+
609
+ // Escrows
610
+ Escrow,
611
+ EscrowStatus,
612
+ EscrowBalance,
613
+ EscrowListResponse,
614
+ EscrowConditionsResponse,
615
+ ReleasePolicy,
616
+ ReleaseCondition,
617
+ ConditionType,
618
+ ConditionLogic,
619
+ CreateEscrowParams,
620
+
621
+ // Disputes
622
+ Dispute,
623
+ DisputeStatus,
624
+ DisputeStage,
625
+ DisputeType,
626
+ OpenDisputeParams,
627
+ ResolveDisputeParams,
628
+
629
+ // Webhooks
630
+ WebhookEvent,
631
+ WebhookEventType,
632
+ TransactionWebhookEvent,
633
+ PaymentWebhookEvent,
634
+ EscrowWebhookEvent,
635
+ DisputeWebhookEvent,
636
+ } from '@paybetaby/node-sdk';
637
+ ```
638
+
639
+ ---
640
+
641
+ ## Building from Source
642
+
643
+ ```bash
644
+ git clone https://github.com/Besaiem/paybeta-node-sdk
645
+ cd paybeta-node-sdk
646
+
647
+ npm install
648
+ npm run build # outputs to dist/
649
+ npm run typecheck # type-check without emitting
650
+ npm run dev # watch mode
651
+ ```
652
+
653
+ The build uses [tsup](https://tsup.egoist.dev/) and produces:
654
+
655
+ | File | Format | Description |
656
+ |-----------------------|--------|------------------------------|
657
+ | `dist/index.js` | CJS | CommonJS (require) |
658
+ | `dist/index.mjs` | ESM | ES Modules (import) |
659
+ | `dist/index.d.ts` | DTS | TypeScript declarations |
660
+ | `dist/index.d.mts` | DTS | ESM TypeScript declarations |
661
+
662
+ ---
663
+
664
+ ## License
665
+
666
+ MIT © Paybeta