API Documentation

Complete guide to integrating with the KLUSTAR payment gateway. Learn how to implement secure payment collection with our comprehensive API.

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/payins

Encryption Steps

  1. Build base string: apiKey + nonce + timestamp + method + requestPath + SHA256Hex(canonicalBody)
  2. Encrypt base string using HMACSHA256 with secret key
  3. Pass resulting string as X-Signature header

Required Headers

HeaderDescription
X-Tenant-IDtest-tenant
X-API-KEYYour API key
X-NonceUnique number (UUID)
X-TimestampEpoch seconds
X-SignatureHMACSHA256 hash
X-Merchant-IDYour merchant ID

Request Parameters

ParameterTypeRequiredDescription
referenceStringYesUnique transaction reference
amountDecimalYesTransaction amount
currencyStringYesNGN
customerNameStringNoCustomer full name
customerEmailStringNoCustomer email
redirectUrlStringNoRedirect after payment
gatewayStringNoPayment gateway (PALMPAY)
notifyUrlStringNoWebhook URL
narrationStringNoTransaction description

2.0 Payin Query Status

Endpoint

GET https://payment.klustar.io/payment/api/payins/status?reference=YOUR_REFERENCE

Query 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&currency=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.

GEThttps://payment.klustar.io/payment/merchant-api/banks

Response

[
  { "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.

POSThttps://payment.klustar.io/payment/merchant-api/account/inquiry

Headers: 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.

POSThttps://payment.klustar.io/payment/merchant-api/payout

Headers: 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.

POSThttps://payment.klustar.io/payment/merchant-api/payout/status

Headers: 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

POSThttps://merchant.klustar.io/api/auth/signup

Request

{
  "email": "merchant@example.com",
  "password": "Str0ng!Passw0rd",
  "businessName": "Acme Ltd",
  "phoneNumber": "+2348012345678"
}

Login — Step 1 (Send OTP)

POSThttps://merchant.klustar.io/api/auth/login

Request

{ "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)

POSThttps://merchant.klustar.io/api/auth/verify-otp

Request

{ "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)

  1. POST /api/auth/forgot-password — body { "email": "..." }
  2. POST /api/auth/verify-reset-otp — body { "email", "otp" }
  3. 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

GEThttps://payment.klustar.io/payment/api/business-profiles/:businessProfileId/config

Headers: 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

POSThttps://payment.klustar.io/payment/api/business-profiles/:businessProfileId/config

Regenerating invalidates the previous apiKey and secretKey.

Setup webhook

POSThttps://payment.klustar.io/payment/api/business-profiles/webhook/setup

Request

{ "appId": "uuid", "webhook": "https://yourdomain.com/webhook" }

Setup IP whitelist

POSThttps://payment.klustar.io/payment/api/business-profiles/ip/setup

Request

{ "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.

POSThttps://merchant.klustar.io/api/environment/check-switch?targetEnv=LIVE

Checks 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.

GEThttps://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 appId used 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.