KLUSTAR PAYIN API DOCUMENTATION
This document explains the requirement for integrating to the KLUSTAR payment gateway. The KLUSTAR payment gateway provides you with a safe, convenient and the choice of three (3) payment channels.
1.0 Payin
The API uses Hash-based Message Authentication Code (HMAC) with SHA256 encryption. Required credentials: merchant ID, API key, and secret key (available on merchant portal).
Endpoint
POST https://payment.klustar.io/payment/api/payinsEncryption Steps
- Build base string: apiKey + nonce + timestamp + method + requestPath + SHA256Hex(canonicalBody)
- Encrypt base string using HMACSHA256 with secret key
- Pass resulting string as X-Signature header
Required Headers
| Header | Description |
|---|---|
| X-Tenant-ID | test-tenant |
| X-API-KEY | Your API key |
| X-Nonce | Unique number (UUID) |
| X-Timestamp | Epoch seconds |
| X-Signature | HMACSHA256 hash |
| X-Merchant-ID | Your merchant ID |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| reference | String | Yes | Unique transaction reference |
| amount | Decimal | Yes | Transaction amount |
| currency | String | Yes | NGN |
| customerName | String | No | Customer full name |
| customerEmail | String | No | Customer email |
| redirectUrl | String | No | Redirect after payment |
| gateway | String | No | Payment gateway (PALMPAY) |
| notifyUrl | String | No | Webhook URL |
| narration | String | No | Transaction description |
2.0 Payin Query Status
Endpoint
GET https://payment.klustar.io/payment/api/payins/status?reference=YOUR_REFERENCEQuery the status of a payin transaction using the transaction reference.
3.0 Payin Webhook Notification
Webhook notifications are sent to your notifyUrl upon transaction completion. Maximum 5 attempts at 2-minute intervals.
Webhook Payload
{
"reference": "e7c3784d5e2242fc00afe89ac1234",
"merchantId": "AppId123456",
"currency": "NGN",
"amount": 2000.00,
"fee": 20.00,
"vat": 7.50,
"status": "successful",
"gatewayReference": "100033240509135230000500932911",
"completeTime": 1658574095184,
"sign": "IDjwDpLTJXqbJh0PmC74R0HuEBM7oIpHdmfnIn6V14..."
}Your webhook should respond with HTTP 200 status code.
Code Examples
1. Base String Formation
baseString = apiKey + "\n" + nonce + "\n" + timestamp + "\n" + "POST" + "\n" + "/payment/api/payins" + "\n" + sha256Hex(canonicalBody)
2. Canonical Body Sorting
// Sort parameters alphabetically and format as key=value&key=value amount=1500.00¤cy=NGN&customerEmail=john@example.com&...
3. HMAC Signature
signature = HMACSHA256(baseString, secretKey) // Convert to hex string for X-Signature header
Payout APIs
Transfer funds to beneficiary bank accounts and check payout status.
Get Banks
Retrieve the list of supported banks with their codes.
https://payment.klustar.io/payment/merchant-api/banksResponse
[
{ "name": "5TT MFB", "code": "KBC-0001", "logo": null },
{ "name": "78 FINANCE COMPANY LIMITED", "code": "KBC-0002", "logo": null },
{ "name": "9 PSB", "code": "KBC-0003", "logo": null },
{ "name": "9jaPay", "code": "KBC-0004", "logo": null },
{ "name": "AAA FINANCE", "code": "KBC-0005", "logo": null }
]Account Enquiry
Verify a beneficiary account before initiating a payout.
https://payment.klustar.io/payment/merchant-api/account/inquiryHeaders: X-Merchant-ID, X-Timestamp, X-Nonce, X-API-KEY, X-Signature, X-Tenant-ID
Request
{
"bankCode": "KBC-0618",
"accountNumber": "5900415110"
}Success Response
{
"success": true,
"code": "00000000",
"raw": "{Status=Success, accountName=QUADRI AZEEZ OLALEKAN}",
"accountName": "QUADRI AZEEZ OLALEKAN",
"message": "Success"
}Failure Response
{
"success": false,
"raw": "{Status=Failed, errorMessage=Invalid account number, please check and try again}"
}Initiate Payout
Transfer funds to a beneficiary bank account.
https://payment.klustar.io/payment/merchant-api/payoutHeaders: X-Merchant-ID, X-Timestamp, X-Nonce, X-API-KEY, X-Signature, X-Tenant-ID
Request
{
"amount": "10000",
"currency": "NGN",
"beneficiaryAccount": "5900415110",
"beneficiaryName": "QUADRI AZEEZ OLALEKAN",
"beneficiaryBankCode": "KBC-0618",
"beneficiaryPhone": "08065602845",
"notifyUrl": "",
"narration": "payout",
"reference": "REF-27fc7903-726b-4fb6-9f35-590624ecb95b"
}Response
{
"success": true,
"status": "success",
"code": "00000000",
"message": "",
"raw": "{\"data\":{\"amount\":10000,\"orderNo\":\"41260419190628629310\",\"orderId\":\"REF-27fc7903-726b-4fb6-9f35-5oPl78\",\"fee\":{\"fee\":1000,\"vat\":0},\"orderStatus\":2,\"currency\":\"NGN\",\"sessionId\":\"100033260419190629812590223519\",\"payId\":\"20260419A02330307510000728381072002\",\"message\":\"success\",\"status\":1},\"respMsg\":\"success\",\"respCode\":\"00000000\"}"
}Query Payout Status
Check the status of a previously initiated payout.
https://payment.klustar.io/payment/merchant-api/payout/statusHeaders: X-Merchant-ID
Request
{
"reference": "REF-27fc7903-726b-4fb6-9f35-590624ecb95b"
}Response
{
"success": true,
"status": "success",
"transferNo": null,
"raw": "{\"data\":{},\"respMsg\":\"success\",\"respCode\":\"00000000\"}"
}Authentication & Onboarding
Sign up, log in (2-step with OTP), and recover passwords. All endpoints accept and return JSON.
Sign up
https://merchant.klustar.io/api/auth/signupRequest
{
"email": "merchant@example.com",
"password": "Str0ng!Passw0rd",
"businessName": "Acme Ltd",
"phoneNumber": "+2348012345678"
}Login — Step 1 (Send OTP)
https://merchant.klustar.io/api/auth/loginRequest
{ "email": "merchant@example.com", "password": "Str0ng!Passw0rd" }Returns 200 with a message indicating an OTP has been emailed to the merchant.
Login — Step 2 (Verify OTP)
https://merchant.klustar.io/api/auth/verify-otpRequest
{ "email": "merchant@example.com", "otp": "123456" }Response
{
"code": 200,
"status": true,
"message": "Login successful",
"data": {
"token": "eyJhbGc...",
"userId": "uuid",
"email": "merchant@example.com",
"businessProfile": { "id": "uuid", "businessName": "Acme Ltd", "ip": null, "webhookUrl": null, ... }
}
}Use Authorization: Bearer <token> for all subsequent merchant API calls.
Forgot Password (3-step)
- POST
/api/auth/forgot-password— body{ "email": "..." } - POST
/api/auth/verify-reset-otp— body{ "email", "otp" } - POST
/api/auth/reset-password— body{ "email", "otp", "newPassword" }
API Key Management
Generate the API key, secret, webhook, and IP whitelist for your business profile. Use the businessProfile.id returned from login.
Get API config
https://payment.klustar.io/payment/api/business-profiles/:businessProfileId/configHeaders: X-Tenant-ID
Response
{
"appId": "uuid",
"apiKey": "qduaTClFA3DTfakbIQGlTA",
"secretKey": "PZef9RE76HD-ptSJ7gMXmC9Crq9P0w9nYb_a-ieq_0I",
"webhook": "https://example.com/webhook",
"ipString": "192.168.19.20"
}Generate / Regenerate API config
https://payment.klustar.io/payment/api/business-profiles/:businessProfileId/configRegenerating invalidates the previous apiKey and secretKey.
Setup webhook
https://payment.klustar.io/payment/api/business-profiles/webhook/setupRequest
{ "appId": "uuid", "webhook": "https://yourdomain.com/webhook" }Setup IP whitelist
https://payment.klustar.io/payment/api/business-profiles/ip/setupRequest
{ "appId": "uuid", "ipString": "192.168.1.10,203.0.113.5" }Comma-separated. Leave empty to allow all IPs.
Pagination
List endpoints accept page (0-indexed) and size query parameters. Most endpoints accept additional filters as query params (e.g. status, method).
GET /api/transactions/pay-ins?page=0&size=20&status=COMPLETED
Response shape
{
"data": {
"content": [ /* array of items */ ],
"page": 0,
"size": 20,
"totalElements": 134,
"totalPages": 7
}
}Sandbox vs Live
Merchants can switch between SANDBOX and LIVE environments. Use sandbox for integration testing — transactions will not move real money.
https://merchant.klustar.io/api/environment/check-switch?targetEnv=LIVEChecks readiness to switch (e.g. KYC complete, bank account verified). Toggle persisted in the dashboard.
Settlement & Bank Accounts
Configure where settled funds are paid out and manage bank accounts.
- GET
/api/settlement— current settlement settings - PUT
/api/settlement— update settings - POST
/api/settlement/bank-accounts— add a bank account - PUT
/api/settlement/bank-accounts/:id— update - PUT
/api/settlement/bank-accounts/:id/primary— set primary - DELETE
/api/settlement/bank-accounts/:id— remove
Transaction History
List previously initiated payins and payouts. Both endpoints accept pagination and filtering.
- GET
/api/transactions/pay-ins?page=0&size=20&status=&method= - GET
/api/transactions/pay-outs?page=0&size=20&status=&method=
Disputes
Read-only access to disputes raised on your transactions.
https://merchant.klustar.io/api/disputes?page=0&size=20&status=&priority=SDK Examples
End-to-end example: initiate a payin with HMAC signature.
cURL
curl -X POST https://payment.klustar.io/payment/api/payins \
-H "X-Tenant-ID: test-tenant" \
-H "X-API-KEY: $API_KEY" \
-H "X-Merchant-ID: $MERCHANT_ID" \
-H "X-Nonce: $(uuidgen)" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d '{"reference":"REF-123","amount":2000,"currency":"NGN","customerEmail":"a@b.com"}'Node.js
import crypto from 'crypto'
import { randomUUID } from 'crypto'
const apiKey = process.env.KLUSTAR_API_KEY
const secret = process.env.KLUSTAR_SECRET
const body = { reference: 'REF-123', amount: 2000, currency: 'NGN', customerEmail: 'a@b.com' }
const canonical = Object.keys(body).sort().map(k => `${k}=${(body as any)[k]}`).join('&')
const nonce = randomUUID()
const ts = Math.floor(Date.now() / 1000).toString()
const bodyHash = crypto.createHash('sha256').update(canonical).digest('hex')
const baseString = [apiKey, nonce, ts, 'POST', '/payment/api/payins', bodyHash].join('\n')
const signature = crypto.createHmac('sha256', secret).update(baseString).digest('hex')
await fetch('https://payment.klustar.io/payment/api/payins', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Tenant-ID': 'test-tenant',
'X-API-KEY': apiKey, 'X-Merchant-ID': merchantId,
'X-Nonce': nonce, 'X-Timestamp': ts, 'X-Signature': signature,
},
body: JSON.stringify(body),
})Python
import hashlib, hmac, json, os, time, uuid, requests
api_key = os.environ['KLUSTAR_API_KEY']
secret = os.environ['KLUSTAR_SECRET'].encode()
body = {'reference': 'REF-123', 'amount': 2000, 'currency': 'NGN', 'customerEmail': 'a@b.com'}
canonical = '&'.join(f'{k}={body[k]}' for k in sorted(body))
nonce, ts = str(uuid.uuid4()), str(int(time.time()))
body_hash = hashlib.sha256(canonical.encode()).hexdigest()
base = '\n'.join([api_key, nonce, ts, 'POST', '/payment/api/payins', body_hash])
sig = hmac.new(secret, base.encode(), hashlib.sha256).hexdigest()
requests.post('https://payment.klustar.io/payment/api/payins',
headers={'X-Tenant-ID':'test-tenant','X-API-KEY':api_key,'X-Merchant-ID':merchant_id,
'X-Nonce':nonce,'X-Timestamp':ts,'X-Signature':sig,'Content-Type':'application/json'},
data=json.dumps(body))Glossary
- appId
- — unique identifier of your business profile; same as
businessProfile.id. - merchantId
- — alias of
appIdused in payin/payout headers. - apiKey / secretKey
- — public/private credential pair used to sign requests.
- reference
- — your unique transaction reference. Echoed back in callbacks.
- orderNo / payId
- — Klustar's internal IDs for the transaction; useful for support enquiries.
- tenant
- — multi-tenant identifier; passed as
X-Tenant-ID. - notifyUrl
- — your webhook endpoint for asynchronous transaction updates.
Postman Collection
A full Postman collection containing all merchant and payment endpoints is available on request from support@klustar.io.