@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 +623 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,623 @@
|
|
|
1
|
-
|
|
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
|
+
[](https://badge.fury.io/js/@patlink%2Fapi-client)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](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;
|