@patalink/node-api-client 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,623 @@
1
- # patalink_node_api_client
1
+ Here's a professional, detailed documentation for your `@patlink/node-api-client` package:
2
+
3
+ ```markdown
4
+ # @patlink/node-api-client - Official PataLink API Client
5
+
6
+ **Server-side API client for Node.js. Accept payments programmatically with automatic encryption.**
7
+
8
+ [![npm version](https://badge.fury.io/js/@patlink%2Fapi-client.svg)](https://badge.fury.io/js/@patlink%2Fapi-client)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![Node Version](https://img.shields.io/node/v/@patlink/node-api-client)](https://nodejs.org/)
11
+
12
+ ---
13
+
14
+ ## 📖 Table of Contents
15
+
16
+ - [Quick Start](#rocket-quick-start)
17
+ - [Installation](#package-installation)
18
+ - [Configuration](#gear-configuration)
19
+ - [API Reference](#api-reference)
20
+ - [Usage Examples](#usage-examples)
21
+ - [Error Handling](#error-handling)
22
+ - [TypeScript Support](#typescript-support)
23
+ - [Browser Support](#browser-support)
24
+ - [FAQ](#faq)
25
+
26
+ ---
27
+
28
+ ## 🚀 Quick Start
29
+
30
+ Add payment processing to your Node.js backend in 3 simple steps:
31
+
32
+ ### Step 1: Install the package
33
+
34
+ ```bash
35
+ npm install @patlink/node-api-client
36
+ ```
37
+
38
+ ### Step 2: Initialize the client
39
+
40
+ ```javascript
41
+ import { createClient } from '@patlink/node-api-client';
42
+
43
+ const client = createClient({
44
+ apiKey: 'pk_live_abc123...',
45
+ encryptionKey: 'xK8j3Hd9Fq2Wm5R...',
46
+ baseUrl: 'https://api.patalink.me'
47
+ });
48
+ ```
49
+
50
+ ### Step 3: Create a payment
51
+
52
+ ```javascript
53
+ const payment = await client.createPayment({
54
+ amount: 25000,
55
+ paymentMethod: 'MTN',
56
+ phoneNumber: '0788888888',
57
+ customerName: 'John Doe',
58
+ customerEmail: 'john@example.com'
59
+ });
60
+
61
+ console.log('Transaction ID:', payment.transactionId);
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 📦 Installation
67
+
68
+ ### Node.js (Recommended)
69
+
70
+ ```bash
71
+ npm install @patlink/node-api-client
72
+ ```
73
+
74
+ ### With yarn
75
+
76
+ ```bash
77
+ yarn add @patlink/node-api-client
78
+ ```
79
+
80
+ ### With pnpm
81
+
82
+ ```bash
83
+ pnpm add @patlink/node-api-client
84
+ ```
85
+
86
+ ### Requirements
87
+
88
+ - Node.js 16.x or higher
89
+ - TypeScript 4.x or higher (optional)
90
+
91
+ ---
92
+
93
+ ## ⚙️ Configuration
94
+
95
+ ### Client Configuration Options
96
+
97
+ ```typescript
98
+ interface ClientConfig {
99
+ apiKey: string; // Required: Your API key from dashboard
100
+ encryptionKey: string; // Required: Your encryption key from dashboard
101
+ baseUrl: string; // Required: API endpoint URL
102
+ timeout?: number; // Optional: Request timeout in ms (default: 30000)
103
+ }
104
+ ```
105
+
106
+ ### Getting Your Keys
107
+
108
+ 1. Sign up at [dashboard.patalink.me](https://dashboard.patalink.me)
109
+ 2. Navigate to **Settings** → **API Keys**
110
+ 3. Click **Generate API Key** and **Generate Encryption Key**
111
+ 4. Copy both keys to your environment variables
112
+
113
+ ### Environment Setup (.env)
114
+
115
+ ```env
116
+ PATALINK_API_KEY=pk_live_abc123def456...
117
+ PATALINK_ENCRYPTION_KEY=xK8j3Hd9Fq2Wm5R...
118
+ PATALINK_BASE_URL=https://api.patalink.me
119
+ ```
120
+
121
+ ```javascript
122
+ import { createClient } from '@patlink/node-api-client';
123
+
124
+ const client = createClient({
125
+ apiKey: process.env.PATALINK_API_KEY,
126
+ encryptionKey: process.env.PATALINK_ENCRYPTION_KEY,
127
+ baseUrl: process.env.PATALINK_BASE_URL,
128
+ timeout: 30000 // optional
129
+ });
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 📚 API Reference
135
+
136
+ ### `createPayment(request)`
137
+
138
+ Create a new payment transaction.
139
+
140
+ **Parameters:**
141
+
142
+ ```typescript
143
+ interface PaymentRequest {
144
+ amount: number; // Required: Amount in RWF (min 100, max 2,000,000 for mobile money)
145
+ currency?: string; // Optional: Currency code (default: 'RWF')
146
+ paymentMethod: 'MTN' | 'Airtel' | 'PesaPal'; // Required: Payment method
147
+ phoneNumber?: string; // Required for MTN/Airtel
148
+ customerName?: string; // Optional: Customer's full name
149
+ customerEmail?: string; // Optional: Customer's email address
150
+ }
151
+ ```
152
+
153
+ **Returns:**
154
+
155
+ ```typescript
156
+ interface PaymentResponse {
157
+ transactionId: string; // Unique transaction identifier
158
+ status: 'pending' | 'success' | 'failed'; // Current payment status
159
+ providerRef?: string; // Payment provider reference
160
+ redirectUrl?: string; // For PesaPal card payments
161
+ message?: string; // Additional response message
162
+ }
163
+ ```
164
+
165
+ **Example:**
166
+
167
+ ```javascript
168
+ // MTN Mobile Money
169
+ const payment = await client.createPayment({
170
+ amount: 25000,
171
+ paymentMethod: 'MTN',
172
+ phoneNumber: '0788888888',
173
+ customerName: 'John Doe',
174
+ customerEmail: 'john@example.com'
175
+ });
176
+
177
+ // Card Payment (PesaPal)
178
+ const cardPayment = await client.createPayment({
179
+ amount: 50000,
180
+ paymentMethod: 'PesaPal',
181
+ customerName: 'Jane Smith',
182
+ customerEmail: 'jane@example.com'
183
+ });
184
+
185
+ // Handle redirect for card payments
186
+ if (cardPayment.redirectUrl) {
187
+ // Redirect user to payment page
188
+ res.redirect(cardPayment.redirectUrl);
189
+ }
190
+ ```
191
+
192
+ ---
193
+
194
+ ### `getTransactionStatus(transactionId)`
195
+
196
+ Get the current status of a transaction.
197
+
198
+ **Parameters:**
199
+
200
+ | Parameter | Type | Description |
201
+ |-----------|------|-------------|
202
+ | `transactionId` | `string` | Transaction ID from `createPayment` response |
203
+
204
+ **Returns:**
205
+
206
+ ```typescript
207
+ interface TransactionStatus {
208
+ id: string; // Transaction ID
209
+ status: 'pending' | 'success' | 'failed'; // Current status
210
+ amount: number; // Transaction amount
211
+ currency: string; // Currency code
212
+ paymentMethod: string; // Payment method used
213
+ createdAt: string; // ISO timestamp of creation
214
+ completedAt?: string; // ISO timestamp of completion
215
+ providerRef?: string; // Provider reference
216
+ }
217
+ ```
218
+
219
+ **Example:**
220
+
221
+ ```javascript
222
+ const status = await client.getTransactionStatus('txn_abc123');
223
+
224
+ if (status.status === 'success') {
225
+ console.log('Payment completed at:', status.completedAt);
226
+ } else if (status.status === 'pending') {
227
+ console.log('Still waiting for confirmation...');
228
+ } else {
229
+ console.log('Payment failed');
230
+ }
231
+ ```
232
+
233
+ ---
234
+
235
+ ## 💡 Usage Examples
236
+
237
+ ### Webhook Endpoint (Express.js)
238
+
239
+ ```javascript
240
+ import express from 'express';
241
+ import { createClient } from '@patlink/node-api-client';
242
+
243
+ const app = express();
244
+ const client = createClient({
245
+ apiKey: process.env.PATALINK_API_KEY,
246
+ encryptionKey: process.env.PATALINK_ENCRYPTION_KEY,
247
+ baseUrl: process.env.PATALINK_BASE_URL
248
+ });
249
+
250
+ app.post('/api/create-payment', async (req, res) => {
251
+ try {
252
+ const { amount, phoneNumber, customerName, customerEmail } = req.body;
253
+
254
+ const payment = await client.createPayment({
255
+ amount,
256
+ paymentMethod: 'MTN',
257
+ phoneNumber,
258
+ customerName,
259
+ customerEmail
260
+ });
261
+
262
+ res.json({
263
+ success: true,
264
+ transactionId: payment.transactionId,
265
+ status: payment.status
266
+ });
267
+ } catch (error) {
268
+ res.status(400).json({
269
+ success: false,
270
+ error: error.message
271
+ });
272
+ }
273
+ });
274
+
275
+ app.get('/api/payment-status/:transactionId', async (req, res) => {
276
+ try {
277
+ const status = await client.getTransactionStatus(req.params.transactionId);
278
+ res.json(status);
279
+ } catch (error) {
280
+ res.status(404).json({ error: error.message });
281
+ }
282
+ });
283
+
284
+ app.listen(3000, () => {
285
+ console.log('Server running on port 3000');
286
+ });
287
+ ```
288
+
289
+ ### Polling Implementation
290
+
291
+ ```javascript
292
+ async function pollPaymentStatus(transactionId, maxAttempts = 30, interval = 2000) {
293
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
294
+ console.log(`Checking status (attempt ${attempt}/${maxAttempts})...`);
295
+
296
+ const status = await client.getTransactionStatus(transactionId);
297
+
298
+ if (status.status === 'success') {
299
+ console.log('✅ Payment successful!');
300
+ return status;
301
+ }
302
+
303
+ if (status.status === 'failed') {
304
+ console.log('❌ Payment failed!');
305
+ return status;
306
+ }
307
+
308
+ // Wait before next attempt
309
+ await new Promise(resolve => setTimeout(resolve, interval));
310
+ }
311
+
312
+ throw new Error('Payment timeout - still pending after maximum attempts');
313
+ }
314
+
315
+ // Usage
316
+ const transaction = await client.createPayment({
317
+ amount: 25000,
318
+ paymentMethod: 'MTN',
319
+ phoneNumber: '0788888888'
320
+ });
321
+
322
+ const result = await pollPaymentStatus(transaction.transactionId);
323
+ console.log('Final status:', result.status);
324
+ ```
325
+
326
+ ### Next.js API Route
327
+
328
+ ```typescript
329
+ // app/api/payments/route.ts
330
+ import { NextResponse } from 'next/server';
331
+ import { createClient } from '@patlink/node-api-client';
332
+
333
+ const client = createClient({
334
+ apiKey: process.env.PATALINK_API_KEY!,
335
+ encryptionKey: process.env.PATALINK_ENCRYPTION_KEY!,
336
+ baseUrl: process.env.PATALINK_BASE_URL!
337
+ });
338
+
339
+ export async function POST(request: Request) {
340
+ try {
341
+ const body = await request.json();
342
+
343
+ const payment = await client.createPayment({
344
+ amount: body.amount,
345
+ paymentMethod: body.paymentMethod,
346
+ phoneNumber: body.phoneNumber,
347
+ customerName: body.customerName,
348
+ customerEmail: body.customerEmail
349
+ });
350
+
351
+ return NextResponse.json(payment);
352
+ } catch (error: any) {
353
+ return NextResponse.json(
354
+ { error: error.message },
355
+ { status: 400 }
356
+ );
357
+ }
358
+ }
359
+
360
+ export async function GET(request: Request) {
361
+ const { searchParams } = new URL(request.url);
362
+ const transactionId = searchParams.get('transactionId');
363
+
364
+ if (!transactionId) {
365
+ return NextResponse.json(
366
+ { error: 'transactionId is required' },
367
+ { status: 400 }
368
+ );
369
+ }
370
+
371
+ try {
372
+ const status = await client.getTransactionStatus(transactionId);
373
+ return NextResponse.json(status);
374
+ } catch (error: any) {
375
+ return NextResponse.json(
376
+ { error: error.message },
377
+ { status: 404 }
378
+ );
379
+ }
380
+ }
381
+ ```
382
+
383
+ ### Bulk Payment Processing
384
+
385
+ ```javascript
386
+ async function processBulkPayments(payments) {
387
+ const results = [];
388
+
389
+ for (const payment of payments) {
390
+ try {
391
+ const result = await client.createPayment(payment);
392
+ results.push({
393
+ success: true,
394
+ transactionId: result.transactionId,
395
+ amount: payment.amount,
396
+ phoneNumber: payment.phoneNumber
397
+ });
398
+ } catch (error) {
399
+ results.push({
400
+ success: false,
401
+ error: error.message,
402
+ amount: payment.amount,
403
+ phoneNumber: payment.phoneNumber
404
+ });
405
+ }
406
+
407
+ // Rate limiting - avoid overwhelming the API
408
+ await new Promise(resolve => setTimeout(resolve, 500));
409
+ }
410
+
411
+ return results;
412
+ }
413
+
414
+ // Usage
415
+ const payments = [
416
+ { amount: 10000, paymentMethod: 'MTN', phoneNumber: '0788888888' },
417
+ { amount: 25000, paymentMethod: 'Airtel', phoneNumber: '0789999999' },
418
+ { amount: 5000, paymentMethod: 'MTN', phoneNumber: '0787777777' }
419
+ ];
420
+
421
+ const results = await processBulkPayments(payments);
422
+ console.log(results);
423
+ ```
424
+
425
+ ---
426
+
427
+ ## 🚨 Error Handling
428
+
429
+ ### Common Errors
430
+
431
+ ```javascript
432
+ try {
433
+ const payment = await client.createPayment({
434
+ amount: 500,
435
+ paymentMethod: 'MTN',
436
+ phoneNumber: '0788888888'
437
+ });
438
+ } catch (error) {
439
+ switch (error.message) {
440
+ case 'Amount must be at least 100 RWF':
441
+ // Handle minimum amount error
442
+ break;
443
+ case 'Mobile Money limit is 2,000,000 RWF':
444
+ // Handle maximum amount error
445
+ break;
446
+ case 'phoneNumber is required for mobile money payments':
447
+ // Handle missing phone number
448
+ break;
449
+ case 'Request timeout':
450
+ // Handle timeout - implement retry logic
451
+ break;
452
+ default:
453
+ // Handle other errors
454
+ console.error('Payment failed:', error.message);
455
+ }
456
+ }
457
+ ```
458
+
459
+ ### Retry Logic with Exponential Backoff
460
+
461
+ ```javascript
462
+ async function createPaymentWithRetry(request, maxRetries = 3) {
463
+ let lastError;
464
+
465
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
466
+ try {
467
+ return await client.createPayment(request);
468
+ } catch (error) {
469
+ lastError = error;
470
+
471
+ if (attempt === maxRetries) break;
472
+
473
+ // Exponential backoff: 1s, 2s, 4s
474
+ const delay = Math.pow(2, attempt - 1) * 1000;
475
+ console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
476
+ await new Promise(resolve => setTimeout(resolve, delay));
477
+ }
478
+ }
479
+
480
+ throw lastError;
481
+ }
482
+
483
+ // Usage
484
+ const payment = await createPaymentWithRetry({
485
+ amount: 25000,
486
+ paymentMethod: 'MTN',
487
+ phoneNumber: '0788888888'
488
+ });
489
+ ```
490
+
491
+ ---
492
+
493
+ ## 🔷 TypeScript Support
494
+
495
+ Full TypeScript support with complete type definitions.
496
+
497
+ ### Type Definitions
498
+
499
+ ```typescript
500
+ import { createClient, PaymentRequest, PaymentResponse, TransactionStatus } from '@patlink/node-api-client';
501
+
502
+ const client = createClient({
503
+ apiKey: process.env.PATALINK_API_KEY!,
504
+ encryptionKey: process.env.PATALINK_ENCRYPTION_KEY!,
505
+ baseUrl: process.env.PATALINK_BASE_URL!
506
+ });
507
+
508
+ async function processPayment(request: PaymentRequest): Promise<PaymentResponse> {
509
+ const payment = await client.createPayment(request);
510
+ return payment;
511
+ }
512
+
513
+ async function checkStatus(transactionId: string): Promise<TransactionStatus> {
514
+ const status = await client.getTransactionStatus(transactionId);
515
+ return status;
516
+ }
517
+ ```
518
+
519
+ ---
520
+
521
+ ## 🌐 Browser Support
522
+
523
+ This package is designed for **Node.js server-side** use only. For browser/client-side integration, use our HTML web component:
524
+
525
+ ```html
526
+ <script type="module">
527
+ import 'https://esm.sh/@patlink/html-payment-modal';
528
+ </script>
529
+
530
+ <payment-modal
531
+ base-url="https://api.patalink.me"
532
+ api-key="your_api_key"
533
+ encryption-key="your_encryption_key"
534
+ ></payment-modal>
535
+ ```
536
+
537
+ ---
538
+
539
+ ## ❓ FAQ
540
+
541
+ ### Can I use this in the browser?
542
+
543
+ **No.** This package uses server-side encryption and should only be used in Node.js environments. For browser payments, use our HTML web component instead.
544
+
545
+ ### How do I get my API and encryption keys?
546
+
547
+ 1. Sign up at [dashboard.patalink.me](https://dashboard.patalink.me)
548
+ 2. Go to **Settings** → **API Keys**
549
+ 3. Click **Generate API Key** and **Generate Encryption Key**
550
+ 4. Store both keys securely in environment variables
551
+
552
+ ### What's the difference between API key and encryption key?
553
+
554
+ - **API Key**: Identifies your account (authentication)
555
+ - **Encryption Key**: Encrypts payment data (security)
556
+
557
+ Both are required and should be kept secret.
558
+
559
+ ### What payment methods are supported?
560
+
561
+ - MTN Mobile Money
562
+ - Airtel Money
563
+ - Credit/Debit Cards (via PesaPal)
564
+
565
+ ### What's the minimum and maximum payment amount?
566
+
567
+ - **Minimum**: 100 RWF
568
+ - **Maximum for Mobile Money**: 2,000,000 RWF
569
+ - **Maximum for Cards**: No limit (subject to card issuer)
570
+
571
+ ### How do I handle card payment redirects?
572
+
573
+ For PesaPal card payments, the response includes a `redirectUrl`. You need to redirect your user to this URL to complete the payment.
574
+
575
+ ```javascript
576
+ const payment = await client.createPayment({
577
+ amount: 50000,
578
+ paymentMethod: 'PesaPal',
579
+ customerEmail: 'customer@example.com'
580
+ });
581
+
582
+ if (payment.redirectUrl) {
583
+ res.redirect(payment.redirectUrl);
584
+ }
585
+ ```
586
+
587
+ ### Is the encryption automatic?
588
+
589
+ **Yes.** The client automatically encrypts all payment data before sending to our API. You don't need to implement any encryption logic.
590
+
591
+ ### Can I test without real money?
592
+
593
+ **Yes.** Use your test keys from the dashboard (they start with `pk_test_`). Test transactions are processed in sandbox mode.
594
+
595
+ ### What happens if a request times out?
596
+
597
+ The client will throw a timeout error after the configured timeout period (default 30 seconds). You should implement retry logic with exponential backoff.
598
+
599
+ ### How do I know if a payment is complete?
600
+
601
+ 1. Check the `status` field in the `createPayment` response
602
+ 2. For pending payments, use `getTransactionStatus` to poll
603
+ 3. Or set up webhooks to receive real-time updates
604
+
605
+ ---
606
+
607
+ ## 📞 Support
608
+
609
+ - 📖 [Full API Documentation](https://docs.patalink.me)
610
+ - 💬 [Discord Community](https://discord.gg/patlink)
611
+ - 🐛 [GitHub Issues](https://github.com/patlink/api-client/issues)
612
+ - ✉️ [support@patalink.me](mailto:support@patalink.me)
613
+
614
+ ---
615
+
616
+ ## 📄 License
617
+
618
+ MIT © [PataLink](https://patalink.me)
619
+
620
+ ---
621
+
622
+ **Secure, encrypted payments for your Node.js backend.** 🚀
623
+ ```
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ var PataLinkClient = class {
39
39
  constructor(config) {
40
40
  if (!config.apiKey) throw new Error("API key is required");
41
41
  if (!config.encryptionKey) throw new Error("Encryption key is required");
42
+ if (!config.baseUrl) throw new Error("Base url is required");
42
43
  this.apiKey = config.apiKey;
43
44
  this.baseUrl = config.baseUrl;
44
45
  this.encryptionKey = config.encryptionKey;
package/dist/index.mjs CHANGED
@@ -4,6 +4,7 @@ var PataLinkClient = class {
4
4
  constructor(config) {
5
5
  if (!config.apiKey) throw new Error("API key is required");
6
6
  if (!config.encryptionKey) throw new Error("Encryption key is required");
7
+ if (!config.baseUrl) throw new Error("Base url is required");
7
8
  this.apiKey = config.apiKey;
8
9
  this.baseUrl = config.baseUrl;
9
10
  this.encryptionKey = config.encryptionKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patalink/node-api-client",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Official PataLink API client - Simple, secure payment integration",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",