lokipay-sdk
v2.4.2
Published
Official SDK for LokiPay VFM - Mobile Money Payment Verification System
Downloads
1,696
Maintainers
Readme
LokiPay SDK
Official JavaScript/TypeScript SDK for LokiPay VFM
Automated Mobile Money Payment Verification for Ghana 🇬🇭
Getting Started • API Reference • Examples • Webhooks
🌟 Features
- ✅ Easy Integration - Simple, intuitive API for payment processing
- ✅ TypeScript Support - Full type definitions included
- ✅ Automatic Verification - Real-time SMS-based payment matching
- ✅ Multi-Provider - Works with MTN MoMo, Telecel Cash, AirtelTigo Money
- ✅ Polling Support - Built-in
waitForPayment()method - ✅ Zero Dependencies - Lightweight, uses native
fetch - ✅ ESM & CJS - Works with any JavaScript environment
📦 Installation
npm install lokipay-sdkyarn add lokipay-sdkpnpm add lokipay-sdk🚀 Quick Start
import { LokiPay } from 'lokipay-sdk';
// Initialize with your secret key from the LokiPay Dashboard
const lokipay = new LokiPay({
secretKey: 'sk_live_your_secret_key' // Get this from your dashboard
});
// Create a payment order
const result = await lokipay.createOrder({
phone: '0241234567',
amount: 150.00,
description: 'Order #12345 - Electronics Store'
});
console.log('Order created:', result.order.orderId);
console.log('Tell customer to pay with reference:', result.order.vfmReference);
// Wait for payment (polls automatically)
const status = await lokipay.waitForPayment(result.order.orderId, {
intervalMs: 5000, // Check every 5 seconds
timeoutMs: 600000, // Wait up to 10 minutes
onStatusChange: (status) => {
console.log('Status:', status.status);
}
});
if (status.status === 'confirmed') {
console.log('✅ Payment confirmed!');
} else {
console.log('❌ Payment not received:', status.status);
}⚛️ React Component
The SDK exports a pre-built LokiPayCheckout component that handles the entire payment flow.
import { LokiPay } from 'lokipay-sdk';
import { LokiPayCheckout } from 'lokipay-sdk/react';
const lokipay = new LokiPay({
secretKey: 'sk_live_...'
});
export default function Payment() {
return (
<div className="flex justify-center p-12">
<LokiPayCheckout
client={lokipay}
amount={150.00}
description="Premium Plan"
onSuccess={(order) => {
console.log('Payment Verified:', order);
alert('Payment successful!');
}}
darkMode={false} // Toggle dark mode
color="#30D158" // Custom accent color
/>
</div>
);
}⚙️ Configuration
API Keys
You get two API keys from your LokiPay Dashboard:
| Key Type | Format | Usage |
|----------|--------|-------|
| Secret Key | sk_live_... | Server-side only. Full access. Keep this private! |
| Public Key | pk_live_... | Can be used client-side for limited operations |
⚠️ Important: Never expose your secret key in frontend code or public repositories.
// Recommended: Use secret key on your backend
const lokipay = new LokiPay({
secretKey: 'sk_live_your_secret_key' // From dashboard Settings > API Keys
});
// Alternative: Use public key for client-side (limited access)
const lokipay = new LokiPay({
secretKey: 'pk_live_your_public_key' // Less sensitive operations only
});Advanced Options
const lokipay = new LokiPay({
// Your API key (required)
secretKey: 'sk_live_your_secret_key',
// Custom API URL (for self-hosted deployments)
apiUrl: 'https://your-api.com/api/vfm',
// Request timeout in milliseconds (default: 30000)
timeout: 60000,
// Merchant configuration for payment instructions
merchant: {
mtnMerchantCode: '190638',
mtnPaymentNumber: '0592353232'
}
});📖 API Reference
createOrder(options)
Create a new payment order.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| phone | string | ✅ | Customer phone number |
| amount | number | ✅ | Payment amount in GHS (e.g., 50.00) |
| description | string | ❌ | Description of the payment/order |
| email | string | ❌ | Customer email for notifications |
| telegramChatId | string | ❌ | Telegram chat ID for notifications |
Returns: Promise<CreateOrderResponse>
const result = await lokipay.createOrder({
phone: '0551234567',
amount: 100.00,
description: 'Invoice #INV-2025-001',
email: '[email protected]'
});
// Response
{
success: true,
message: 'Order created. Use the reference code when making payment.',
order: {
orderId: 'LokiPayABCD',
vfmReference: 'LokiPayABCD',
recipientPhone: '0551234567',
description: 'Invoice #INV-2025-001',
expectedAmount: 100.00,
status: 'pending',
expiresAt: '2025-01-15T10:45:00Z',
instructions: {
step1: 'Copy the reference code below',
step2: 'Send exactly GHS 100.00 via MoMo',
step3: 'Use "LokiPayABCD" as the payment reference/description',
step4: 'After payment, click "I have completed payment" button',
note: 'Order expires in 30 minutes'
}
}
}getOrderStatus(orderId)
Check the current status of an order.
Parameters:
orderId(string) - The order ID to check
Returns: Promise<OrderStatusResponse>
const status = await lokipay.getOrderStatus('LokiPayABCD');
console.log(status.status);
// 'pending' | 'matched' | 'confirmed' | 'expired' | 'failed'| Status | Description |
|--------|-------------|
| pending | Waiting for payment |
| matched | Payment detected, pending confirmation |
| confirmed | Payment verified and confirmed |
| expired | Order expired before payment |
| failed | Payment verification failed |
verifyPayment(orderId)
Manually trigger payment verification.
Parameters:
orderId(string) - The order ID to verify
Returns: Promise<VerifyPaymentResponse>
const result = await lokipay.verifyPayment('LokiPayABCD');
if (result.success) {
console.log('Payment verified!');
console.log('Transaction:', result.transaction);
}waitForPayment(orderId, options)
Poll for payment status until confirmed, expired, or timeout.
Parameters:
orderId(string) - The order ID to polloptions(object) - Polling configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| intervalMs | number | 5000 | Polling interval in ms |
| timeoutMs | number | 300000 | Maximum wait time in ms |
| onStatusChange | function | - | Callback on status change |
Returns: Promise<OrderStatusResponse>
try {
const status = await lokipay.waitForPayment('LokiPayABCD', {
intervalMs: 3000, // Check every 3 seconds
timeoutMs: 600000, // Timeout after 10 minutes
onStatusChange: (status) => {
console.log(`Status changed to: ${status.status}`);
// Update your UI here
updatePaymentUI(status);
}
});
if (status.status === 'confirmed') {
// Process successful payment
await fulfillOrder(status.orderId);
}
} catch (error) {
if (error.message === 'Payment polling timeout') {
console.log('Customer did not complete payment in time');
}
}getConfig()
Get VFM service configuration.
Returns: Promise<VFMConfig>
const config = await lokipay.getConfig();
console.log(config);
// {
// enabled: true,
// minAmount: 1,
// maxAmount: 10000,
// orderExpiryMinutes: 30,
// paymentMethods: [
// { name: 'MTN MoMo', instructions: '...' },
// { name: 'Telecel Cash', instructions: '...' },
// { name: 'AirtelTigo Money', instructions: '...' }
// ]
// }LokiPay.formatPaymentInstructions(order)
Static helper to format payment instructions for display.
const instructions = LokiPay.formatPaymentInstructions(result.order);
console.log(instructions);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 💰 PAYMENT INSTRUCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
// 📝 Invoice #INV-2025-001
// 💵 Amount: GHS 100.00
// 🔑 Reference: LokiPayABCD
// ...🔔 Webhook Integration
Receive real-time payment notifications in your backend:
import type { WebhookPayload } from 'lokipay-sdk';
// Express.js example
app.post('/webhooks/lokipay', express.json(), (req, res) => {
const payload: WebhookPayload = req.body;
console.log('Received webhook:', payload.event);
switch (payload.event) {
case 'payment.matched':
// Payment detected but not yet confirmed
console.log(`Payment matched for order ${payload.orderId}`);
break;
case 'payment.confirmed':
// Payment fully verified
console.log(`✅ Order ${payload.orderId} paid!`);
await fulfillOrder(payload.orderId, payload.transaction);
break;
case 'order.expired':
// Order expired without payment
console.log(`Order ${payload.orderId} expired`);
await cancelOrder(payload.orderId);
break;
}
res.status(200).send('OK');
});Webhook Payload Structure:
interface WebhookPayload {
event: 'payment.matched' | 'payment.confirmed' | 'order.expired';
orderId: string;
vfmReference: string;
amount: number;
status: OrderStatus;
timestamp: string;
transaction?: {
id: string;
sender: string;
reference: string;
};
}⚠️ Error Handling
import { LokiPay, LokiPayError } from 'lokipay-sdk';
try {
const result = await lokipay.createOrder({
phone: '0241234567',
amount: 100
});
} catch (error) {
if (error instanceof LokiPayError) {
console.error(`LokiPay Error [${error.statusCode}]: ${error.message}`);
switch (error.statusCode) {
case 400:
console.log('Invalid request - check your parameters');
break;
case 404:
console.log('Order not found');
break;
case 408:
console.log('Request timeout - try again');
break;
case 500:
console.log('Server error - contact support');
break;
}
} else {
console.error('Unexpected error:', error);
}
}📋 Examples
E-commerce Checkout
async function processCheckout(cart: Cart, customer: Customer) {
const lokipay = new LokiPay();
// Create payment order
const result = await lokipay.createOrder({
phone: customer.phone,
amount: cart.total,
description: `Order #${cart.id} - ${cart.items.length} items`,
email: customer.email
});
// Show payment instructions to customer
showPaymentModal({
amount: result.order.expectedAmount,
reference: result.order.vfmReference,
instructions: result.order.instructions,
expiresAt: result.order.expiresAt
});
// Wait for payment
const status = await lokipay.waitForPayment(result.order.orderId, {
onStatusChange: (status) => updatePaymentStatus(status.status)
});
if (status.status === 'confirmed') {
closePaymentModal();
showSuccessMessage('Payment received! Your order is being processed.');
await createShipment(cart, customer);
} else {
showErrorMessage('Payment was not completed. Please try again.');
}
}React Hook
import { useState, useCallback } from 'react';
import { LokiPay, type Order, type OrderStatus } from 'lokipay-sdk';
const lokipay = new LokiPay();
export function usePayment() {
const [order, setOrder] = useState<Order | null>(null);
const [status, setStatus] = useState<OrderStatus | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const createPayment = useCallback(async (
phone: string,
amount: number,
description: string
) => {
setLoading(true);
setError(null);
try {
const result = await lokipay.createOrder({ phone, amount, description });
setOrder(result.order);
setStatus('pending');
// Start polling
const finalStatus = await lokipay.waitForPayment(result.order.orderId, {
onStatusChange: (s) => setStatus(s.status)
});
return finalStatus;
} catch (err) {
setError(err instanceof Error ? err.message : 'Payment failed');
throw err;
} finally {
setLoading(false);
}
}, []);
return { order, status, loading, error, createPayment };
}🔧 TypeScript Support
All types are exported for use in your TypeScript projects:
import type {
LokiPayConfig,
MerchantConfig,
CustomInstructionsConfig,
NetworkInstructionOverride,
Order,
OrderStatus,
PaymentMethod,
PaymentInstructions,
CreateOrderRequest,
CreateOrderResponse,
OrderStatusResponse,
VerifyPaymentResponse,
VFMConfig,
WebhookPayload,
LokiPayErrorType,
APIResponse
} from 'lokipay-sdk';
import type { LokiPayCheckoutProps } from 'lokipay-sdk/react';📄 License
MIT © LokiPay
Made with ❤️ in Ghana 🇬🇭
