@patalink/node-api-client
v1.0.5
Published
Official PataLink API client - Simple, secure payment integration
Maintainers
Readme
Here's a professional, detailed documentation for your @patlink/node-api-client package:
# @patlink/node-api-client - Official PataLink API Client
**Server-side API client for Node.js. Accept payments programmatically with automatic encryption.**
[](https://badge.fury.io/js/@patlink%2Fapi-client)
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org/)
---
## 📖 Table of Contents
- [Quick Start](#rocket-quick-start)
- [Installation](#package-installation)
- [Configuration](#gear-configuration)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [Error Handling](#error-handling)
- [TypeScript Support](#typescript-support)
- [Browser Support](#browser-support)
- [FAQ](#faq)
---
## 🚀 Quick Start
Add payment processing to your Node.js backend in 3 simple steps:
### Step 1: Install the package
```bash
npm install @patlink/node-api-clientStep 2: Initialize the client
import { createClient } from '@patlink/node-api-client';
const client = createClient({
apiKey: 'pk_live_abc123...',
encryptionKey: 'xK8j3Hd9Fq2Wm5R...',
baseUrl: 'https://patalink.me'
});Step 3: Create a payment
const payment = await client.createPayment({
amount: 25000,
paymentMethod: 'MTN',
phoneNumber: '0788888888',
customerName: 'John Doe',
customerEmail: '[email protected]'
});
console.log('Transaction ID:', payment.transactionId);📦 Installation
Node.js (Recommended)
npm install @patlink/node-api-clientWith yarn
yarn add @patlink/node-api-clientWith pnpm
pnpm add @patlink/node-api-clientRequirements
- Node.js 16.x or higher
- TypeScript 4.x or higher (optional)
⚙️ Configuration
Client Configuration Options
interface ClientConfig {
apiKey: string; // Required: Your API key from dashboard
encryptionKey: string; // Required: Your encryption key from dashboard
baseUrl: string; // Required: API endpoint URL
timeout?: number; // Optional: Request timeout in ms (default: 30000)
}Getting Your Keys
- Sign up at patalink.me
- Navigate to Settings → API Keys
- Click Generate API Key and Generate Encryption Key
- Copy both keys to your environment variables
Environment Setup (.env)
PATALINK_API_KEY=pk_live_abc123def456...
PATALINK_ENCRYPTION_KEY=xK8j3Hd9Fq2Wm5R...
PATALINK_BASE_URL=https://patalink.meimport { createClient } from '@patlink/node-api-client';
const client = createClient({
apiKey: process.env.PATALINK_API_KEY,
encryptionKey: process.env.PATALINK_ENCRYPTION_KEY,
baseUrl: process.env.PATALINK_BASE_URL,
timeout: 30000 // optional
});📚 API Reference
createPayment(request)
Create a new payment transaction.
Parameters:
interface PaymentRequest {
amount: number; // Required: Amount in RWF (min 100, max 2,000,000 for mobile money)
currency?: string; // Optional: Currency code (default: 'RWF')
paymentMethod: 'MTN' | 'Airtel' | 'PesaPal'; // Required: Payment method
phoneNumber?: string; // Required for MTN/Airtel
customerName?: string; // Optional: Customer's full name
customerEmail?: string; // Optional: Customer's email address
}Returns:
interface PaymentResponse {
transactionId: string; // Unique transaction identifier
status: 'pending' | 'success' | 'failed'; // Current payment status
providerRef?: string; // Payment provider reference
redirectUrl?: string; // For PesaPal card payments
message?: string; // Additional response message
}Example:
// MTN Mobile Money
const payment = await client.createPayment({
amount: 25000,
paymentMethod: 'MTN',
phoneNumber: '0788888888',
customerName: 'John Doe',
customerEmail: '[email protected]'
});
// Card Payment (PesaPal)
const cardPayment = await client.createPayment({
amount: 50000,
paymentMethod: 'PesaPal',
customerName: 'Jane Smith',
customerEmail: '[email protected]'
});
// Handle redirect for card payments
if (cardPayment.redirectUrl) {
// Redirect user to payment page
res.redirect(cardPayment.redirectUrl);
}getTransactionStatus(transactionId)
Get the current status of a transaction.
Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| transactionId | string | Transaction ID from createPayment response |
Returns:
interface TransactionStatus {
id: string; // Transaction ID
status: 'pending' | 'success' | 'failed'; // Current status
amount: number; // Transaction amount
currency: string; // Currency code
paymentMethod: string; // Payment method used
createdAt: string; // ISO timestamp of creation
completedAt?: string; // ISO timestamp of completion
providerRef?: string; // Provider reference
}Example:
const status = await client.getTransactionStatus('txn_abc123');
if (status.status === 'success') {
console.log('Payment completed at:', status.completedAt);
} else if (status.status === 'pending') {
console.log('Still waiting for confirmation...');
} else {
console.log('Payment failed');
}💡 Usage Examples
Webhook Endpoint (Express.js)
import express from 'express';
import { createClient } from '@patlink/node-api-client';
const app = express();
const client = createClient({
apiKey: process.env.PATALINK_API_KEY,
encryptionKey: process.env.PATALINK_ENCRYPTION_KEY,
baseUrl: process.env.PATALINK_BASE_URL
});
app.post('/api/create-payment', async (req, res) => {
try {
const { amount, phoneNumber, customerName, customerEmail } = req.body;
const payment = await client.createPayment({
amount,
paymentMethod: 'MTN',
phoneNumber,
customerName,
customerEmail
});
res.json({
success: true,
transactionId: payment.transactionId,
status: payment.status
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});
app.get('/api/payment-status/:transactionId', async (req, res) => {
try {
const status = await client.getTransactionStatus(req.params.transactionId);
res.json(status);
} catch (error) {
res.status(404).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Polling Implementation
async function pollPaymentStatus(transactionId, maxAttempts = 30, interval = 2000) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
console.log(`Checking status (attempt ${attempt}/${maxAttempts})...`);
const status = await client.getTransactionStatus(transactionId);
if (status.status === 'success') {
console.log('✅ Payment successful!');
return status;
}
if (status.status === 'failed') {
console.log('❌ Payment failed!');
return status;
}
// Wait before next attempt
await new Promise(resolve => setTimeout(resolve, interval));
}
throw new Error('Payment timeout - still pending after maximum attempts');
}
// Usage
const transaction = await client.createPayment({
amount: 25000,
paymentMethod: 'MTN',
phoneNumber: '0788888888'
});
const result = await pollPaymentStatus(transaction.transactionId);
console.log('Final status:', result.status);Next.js API Route
// app/api/payments/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@patlink/node-api-client';
const client = createClient({
apiKey: process.env.PATALINK_API_KEY!,
encryptionKey: process.env.PATALINK_ENCRYPTION_KEY!,
baseUrl: process.env.PATALINK_BASE_URL!
});
export async function POST(request: Request) {
try {
const body = await request.json();
const payment = await client.createPayment({
amount: body.amount,
paymentMethod: body.paymentMethod,
phoneNumber: body.phoneNumber,
customerName: body.customerName,
customerEmail: body.customerEmail
});
return NextResponse.json(payment);
} catch (error: any) {
return NextResponse.json(
{ error: error.message },
{ status: 400 }
);
}
}
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const transactionId = searchParams.get('transactionId');
if (!transactionId) {
return NextResponse.json(
{ error: 'transactionId is required' },
{ status: 400 }
);
}
try {
const status = await client.getTransactionStatus(transactionId);
return NextResponse.json(status);
} catch (error: any) {
return NextResponse.json(
{ error: error.message },
{ status: 404 }
);
}
}Bulk Payment Processing
async function processBulkPayments(payments) {
const results = [];
for (const payment of payments) {
try {
const result = await client.createPayment(payment);
results.push({
success: true,
transactionId: result.transactionId,
amount: payment.amount,
phoneNumber: payment.phoneNumber
});
} catch (error) {
results.push({
success: false,
error: error.message,
amount: payment.amount,
phoneNumber: payment.phoneNumber
});
}
// Rate limiting - avoid overwhelming the API
await new Promise(resolve => setTimeout(resolve, 500));
}
return results;
}
// Usage
const payments = [
{ amount: 10000, paymentMethod: 'MTN', phoneNumber: '0788888888' },
{ amount: 25000, paymentMethod: 'Airtel', phoneNumber: '0789999999' },
{ amount: 5000, paymentMethod: 'MTN', phoneNumber: '0787777777' }
];
const results = await processBulkPayments(payments);
console.log(results);🚨 Error Handling
Common Errors
try {
const payment = await client.createPayment({
amount: 500,
paymentMethod: 'MTN',
phoneNumber: '0788888888'
});
} catch (error) {
switch (error.message) {
case 'Amount must be at least 100 RWF':
// Handle minimum amount error
break;
case 'Mobile Money limit is 2,000,000 RWF':
// Handle maximum amount error
break;
case 'phoneNumber is required for mobile money payments':
// Handle missing phone number
break;
case 'Request timeout':
// Handle timeout - implement retry logic
break;
default:
// Handle other errors
console.error('Payment failed:', error.message);
}
}Retry Logic with Exponential Backoff
async function createPaymentWithRetry(request, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client.createPayment(request);
} catch (error) {
lastError = error;
if (attempt === maxRetries) break;
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt - 1) * 1000;
console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage
const payment = await createPaymentWithRetry({
amount: 25000,
paymentMethod: 'MTN',
phoneNumber: '0788888888'
});🔷 TypeScript Support
Full TypeScript support with complete type definitions.
Type Definitions
import { createClient, PaymentRequest, PaymentResponse, TransactionStatus } from '@patlink/node-api-client';
const client = createClient({
apiKey: process.env.PATALINK_API_KEY!,
encryptionKey: process.env.PATALINK_ENCRYPTION_KEY!,
baseUrl: process.env.PATALINK_BASE_URL!
});
async function processPayment(request: PaymentRequest): Promise<PaymentResponse> {
const payment = await client.createPayment(request);
return payment;
}
async function checkStatus(transactionId: string): Promise<TransactionStatus> {
const status = await client.getTransactionStatus(transactionId);
return status;
}🌐 Browser Support
This package is designed for Node.js server-side use only. For browser/client-side integration, use our HTML web component:
<script type="module">
import 'https://esm.sh/@patlink/html-payment-modal';
</script>
<payment-modal
base-url="https://patalink.me"
api-key="your_api_key"
encryption-key="your_encryption_key"
></payment-modal>❓ FAQ
Can I use this in the browser?
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.
How do I get my API and encryption keys?
- Sign up at dashboard.patalink.me
- Go to Settings → API Keys
- Click Generate API Key and Generate Encryption Key
- Store both keys securely in environment variables
What's the difference between API key and encryption key?
- API Key: Identifies your account (authentication)
- Encryption Key: Encrypts payment data (security)
Both are required and should be kept secret.
What payment methods are supported?
- MTN Mobile Money
- Airtel Money
- Credit/Debit Cards (via PesaPal)
What's the minimum and maximum payment amount?
- Minimum: 100 RWF
- Maximum for Mobile Money: 2,000,000 RWF
- Maximum for Cards: No limit (subject to card issuer)
How do I handle card payment redirects?
For PesaPal card payments, the response includes a redirectUrl. You need to redirect your user to this URL to complete the payment.
const payment = await client.createPayment({
amount: 50000,
paymentMethod: 'PesaPal',
customerEmail: '[email protected]'
});
if (payment.redirectUrl) {
res.redirect(payment.redirectUrl);
}Is the encryption automatic?
Yes. The client automatically encrypts all payment data before sending to our API. You don't need to implement any encryption logic.
Can I test without real money?
Yes. Use your test keys from the dashboard (they start with pk_test_). Test transactions are processed in sandbox mode.
What happens if a request times out?
The client will throw a timeout error after the configured timeout period (default 30 seconds). You should implement retry logic with exponential backoff.
How do I know if a payment is complete?
- Check the
statusfield in thecreatePaymentresponse - For pending payments, use
getTransactionStatusto poll - Or set up webhooks to receive real-time updates
📞 Support
📄 License
MIT © PataLink
Secure, encrypted payments for your Node.js backend. 🚀
