paylode-node
v1.0.0
Published
Official Node.js SDK for Paylode Services Limited — CBN Licensed PSSP
Downloads
139
Maintainers
Readme
paylode-node
Official Node.js SDK for Paylode Services Limited — CBN Licensed Payment Solution Service Provider (PSSP).
Requirements
- Node.js 14+
- A Paylode secret key (
sk_live_...orsk_test_...) — obtain from your merchant dashboard
Installation
npm install paylode-node
# or
yarn add paylode-nodeZero external dependencies — uses Node.js built-in https and crypto modules only.
Quick start
const Paylode = require('paylode-node');
const client = new Paylode('sk_live_xxxxxxxxxxxxxxxxxxxx');
// 1. Initialize a payment — redirect your customer to authorization_url
const txn = await client.transaction.initialize({
email: '[email protected]',
amount: 500000, // ₦5,000 in kobo (1 kobo = 0.01 naira)
callback_url: 'https://yoursite.com/payment/complete',
channels: ['card', 'bank_transfer'],
metadata: { order_id: 'ORD-9812', customer_name: 'Ada Obi' },
});
res.redirect(txn.data.authorization_url);
// 2. Verify server-side when the customer returns — ALWAYS do this
const result = await client.transaction.verify(req.query.reference);
if (result.data.status === 'success') {
fulfillOrder(result.data.metadata.order_id);
}TypeScript
Full TypeScript definitions are included — no @types/ package needed.
import Paylode, { PaylodeError, TransactionInitParams } from 'paylode-node';
const client = new Paylode('sk_live_xxxxxxxxxxxxxxxxxxxx');
const params: TransactionInitParams = {
email: '[email protected]',
amount: 500_000,
};
const txn = await client.transaction.initialize(params);API reference
new Paylode(secretKey, [options])
| Parameter | Type | Description |
|--------------------|---------|-----------------------------------------------------------|
| secretKey | string | Your sk_live_... or sk_test_... key (required) |
| options.sandbox | boolean | Force sandbox mode (auto-detected from key prefix) |
const client = new Paylode('sk_live_xxxxxxxxxxxxxxxxxxxx');
console.log(client.sandbox); // false for live keys
console.log(client.version); // '1.0.0'client.transaction
.initialize(params) → Promise<ApiResponse>
Initialize a new payment. Returns authorization_url — redirect your customer there.
| Field | Type | Required | Description |
|----------------|------------|----------|---------------------------------------------------------------|
| email | string | Yes | Customer email address |
| amount | number | Yes | Amount in kobo (minimum ₦100 = 10000 kobo) |
| reference | string | No | Unique transaction reference (auto-generated if omitted) |
| currency | string | No | 'NGN' (default) |
| callback_url | string | No | URL to redirect customer after payment |
| channels | string[] | No | ['card', 'bank_transfer', 'ussd', 'direct_debit'] |
| metadata | object | No | Arbitrary key-value pairs — returned in webhooks |
const txn = await client.transaction.initialize({
email: '[email protected]',
amount: 250000, // ₦2,500
reference: 'ORD-2026-00142', // optional, must be unique
callback_url: 'https://myshop.com/confirm',
channels: ['card', 'bank_transfer'],
metadata: { order_id: 'ORD-9812', plan: 'premium' },
});
console.log(txn.data.authorization_url); // redirect customer here
console.log(txn.data.reference); // store this.verify(reference) → Promise<ApiResponse>
Verify a transaction. Always call this server-side before fulfilling any order.
const result = await client.transaction.verify('ORD-2026-00142');
if (result.data.status === 'success') {
// Confirmed paid — fulfill the order
console.log(result.data.amount); // amount in kobo
console.log(result.data.fees); // platform fees deducted
}.list([params]) → Promise<ApiResponse>
const list = await client.transaction.list({
page: 1,
perPage: 50,
status: 'success', // 'success' | 'failed' | 'pending'
from: '2026-01-01',
to: '2026-01-31',
});
console.log(list.data); // array of transactions
console.log(list.meta); // { page, perPage, total }.fetch(id) → Promise<ApiResponse>
const txn = await client.transaction.fetch('txn_01JXYZ123456');.refund(reference, [amount], [reason]) → Promise<ApiResponse>
// Full refund
await client.transaction.refund('ORD-2026-00142');
// Partial refund — ₦2,000
await client.transaction.refund('ORD-2026-00142', 200000, 'Item out of stock');client.customer
// Create
const customer = await client.customer.create({
email: '[email protected]',
first_name: 'Ada',
last_name: 'Obi',
phone: '+2348012345678',
metadata: { plan: 'gold' },
});
// Fetch by email or customer code
const c = await client.customer.fetch('[email protected]');
// List
const list = await client.customer.list({ page: 1, perPage: 50 });
// Update
await client.customer.update('CUS_abc123', { phone: '+2349011112222' });client.subaccount — for aggregators
Subaccounts represent merchants under an aggregator. Paylode uses them to automatically split settlement.
// Register a merchant under your aggregator account
const sub = await client.subaccount.create({
business_name: 'Shoprite Nigeria',
settlement_bank: 'GTB',
account_number: '0123456789',
percentage_charge: 70, // merchant receives 70% of each transaction
description: 'Shoprite Lagos Island branch',
});
console.log(sub.data.subaccount_code); // SUB_xxxx — use in transactions
// Include in a transaction so the merchant gets their share
const txn = await client.transaction.initialize({
email: '[email protected]',
amount: 100000,
subaccount: sub.data.subaccount_code,
// bearer: 'subaccount' — set if the merchant bears the transaction fee
});await client.subaccount.fetch('SUB_abc123');
await client.subaccount.list({ page: 1, perPage: 50 });
await client.subaccount.update('SUB_abc123', { percentage_charge: 75 });client.settlement
// List settlements
const settlements = await client.settlement.list({ page: 1, perPage: 50 });
// Fetch one by ID
const s = await client.settlement.fetch('STL_01JX123456');Paylode.webhooks — static, no instance needed
.verify(rawBody, signature, secret) → boolean
// Express.js webhook handler
app.post('/webhook/paylode', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-paylode-signature'];
const secret = process.env.PAYLODE_WEBHOOK_SECRET;
if (!Paylode.webhooks.verify(req.body, sig, secret)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
switch (event.event) {
case 'payment.success':
fulfillOrder(event.data.metadata.order_id);
break;
case 'payment.failed':
notifyCustomer(event.data.customer.email);
break;
case 'refund.processed':
updateOrderStatus(event.data.reference, 'refunded');
break;
}
res.sendStatus(200);
});.middleware(secret) — Express middleware
app.post(
'/webhook/paylode',
express.raw({ type: 'application/json' }),
Paylode.webhooks.middleware(process.env.PAYLODE_WEBHOOK_SECRET),
(req, res) => {
const event = req.paylodeEvent;
// event is already parsed and verified
res.sendStatus(200);
}
);Paylode.utils — static helpers
Paylode.utils.generateRef('ORD'); // 'ORD-M6X2K1-A3F9B2C1'
Paylode.utils.koboToNaira(500000); // '5000.00'
Paylode.utils.nairaToKobo(5000); // 500000KYC tier limits
const limits = client.kycLimits;
limits.tier_1.single_txn // 5_000_000 kobo (₦50,000)
limits.tier_1.daily // 30_000_000 kobo (₦300,000)
limits.tier_2.single_txn // 100_000_000 kobo (₦1,000,000)
limits.tier_3.single_txn // 500_000_000 kobo (₦5,000,000)Error handling
All SDK errors are instances of PaylodeError.
const { PaylodeError } = require('paylode-node');
try {
const txn = await client.transaction.initialize({ email: 'x', amount: 100 });
} catch (err) {
if (err instanceof PaylodeError) {
console.log(err.message); // Human-readable message
console.log(err.code); // e.g. 'INVALID_AMOUNT', 'MISSING_FIELD'
console.log(err.statusCode); // HTTP status: 400, 401, 422, 500...
console.log(err.raw); // Raw API response object
}
}| Error code | Meaning |
|-------------------|----------------------------------------------|
| MISSING_FIELD | Required parameter not provided |
| INVALID_AMOUNT | Amount below minimum or wrong type |
| INVALID_KEY | Secret key format is wrong |
| MISSING_KEY | No secret key provided |
| NETWORK_ERROR | Could not reach Paylode API |
| PARSE_ERROR | Unexpected API response format |
| API_ERROR | API returned a non-2xx error |
Sandbox / test mode
const client = new Paylode('sk_test_xxxxxxxxxxxxxxxxxxxx');
console.log(client.sandbox); // true — auto-detected from key prefixUse these test card numbers on the checkout page:
| Card number | Result |
|----------------------|--------------------|
| 4084 0841 1111 1111| Success |
| 4084 0841 1111 1112| Insufficient funds |
| 4084 0841 1111 1113| Declined |
Complete integration example (Express.js)
const express = require('express');
const Paylode = require('paylode-node');
const app = express();
const client = new Paylode(process.env.PAYLODE_SECRET_KEY);
// Step 1: Initialize payment
app.post('/checkout', async (req, res) => {
const { email, amount, orderId } = req.body;
try {
const txn = await client.transaction.initialize({
email,
amount,
callback_url: `https://myshop.com/confirm`,
metadata: { order_id: orderId },
});
res.json({ authorization_url: txn.data.authorization_url });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
// Step 2: Verify on return
app.get('/confirm', async (req, res) => {
const result = await client.transaction.verify(req.query.reference);
if (result.data.status === 'success') {
await fulfillOrder(result.data.metadata.order_id);
res.redirect('/order/complete');
} else {
res.redirect('/order/failed');
}
});
// Step 3: Webhook for real-time events
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
if (!Paylode.webhooks.verify(req.body, req.headers['x-paylode-signature'], process.env.PAYLODE_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
if (event.event === 'payment.success') {
fulfillOrder(event.data.metadata.order_id);
}
res.sendStatus(200);
});
app.listen(3000);Paylode Services Limited · CBN/PAY/2024/001847 · docs.paylodeservices.com
