airtelmoneycg
v0.1.1
Published
Node.js server-side Airtel Money package for collection, account, disbursement, banking, and operations flows.
Maintainers
Readme
airtelmoneycg
Node.js server-side package for Airtel Money integrations.
It covers:
- collection:
collect,collectStatus,refund - account:
balance,kyc - disbursement:
disburse,disburseStatus - bank-to-wallet:
bankToWallet,bankToWalletStatus - operations:
webhook,reconcile,healthCheck
This package is intended for backend usage only: Node.js APIs, workers, cron jobs, and webhook handlers.
Requirements
- Node.js
>=18 - Airtel Money API credentials
Installation
npm install airtelmoneycgQuick Start
import { AirtelMoneyClient, createAirtelConfigFromEnv } from 'airtelmoneycg'
const airtel = new AirtelMoneyClient(createAirtelConfigFromEnv(process.env))
const payment = await airtel.collection.collect({
msisdn: '055123456',
amount: 1000,
reference: 'ORDER1001',
transactionId: 'tx-1001',
})
console.log(payment)Configuration
You can build the config manually or from environment variables.
From environment variables
import { createAirtelConfigFromEnv } from 'airtelmoneycg'
const config = createAirtelConfigFromEnv(process.env)Example .env
AIRTEL_MONEY_ENV=uat
AIRTEL_MONEY_BASE_URL_UAT=https://openapiuat.airtel.cg
AIRTEL_MONEY_BASE_URL_PROD=https://openapi.airtel.cg
AIRTEL_MONEY_CLIENT_ID=your-client-id
AIRTEL_MONEY_CLIENT_SECRET=your-client-secret
AIRTEL_MONEY_GRANT_TYPE=client_credentials
AIRTEL_MONEY_COUNTRY_CODE=CG
AIRTEL_MONEY_CURRENCY_CODE=XAF
AIRTEL_MONEY_WALLET_TYPE=COLL
AIRTEL_MONEY_RSA_PUBLIC_KEY=
AIRTEL_MONEY_RSA_PUBLIC_KEY_V1=
AIRTEL_MONEY_PIN_ENCRYPTION_MODE=oaep-sha256
AIRTEL_MONEY_MESSAGE_SIGNING_ENABLED=false
AIRTEL_MONEY_CALLBACK_HMAC_SECRET=
AIRTEL_MONEY_CALLBACK_AUTH_ENABLED=false
AIRTEL_MONEY_DISBURSEMENT_API_VERSION=v3Supported environment variables
AIRTEL_MONEY_ENVAIRTEL_MONEY_BASE_URL_UATAIRTEL_MONEY_BASE_URL_PRODAIRTEL_MONEY_CLIENT_IDAIRTEL_MONEY_CLIENT_SECRETAIRTEL_MONEY_GRANT_TYPEAIRTEL_MONEY_COUNTRY_CODEAIRTEL_MONEY_CURRENCY_CODEAIRTEL_MONEY_WALLET_TYPEAIRTEL_MONEY_RSA_PUBLIC_KEYAIRTEL_MONEY_RSA_PUBLIC_KEY_V1AIRTEL_MONEY_PIN_ENCRYPTION_MODEAIRTEL_MONEY_MESSAGE_SIGNING_ENABLEDAIRTEL_MONEY_CALLBACK_HMAC_SECRETAIRTEL_MONEY_CALLBACK_AUTH_ENABLEDAIRTEL_MONEY_DISBURSEMENT_API_VERSION
Manual configuration
import { AirtelMoneyClient } from 'airtelmoneycg'
const airtel = new AirtelMoneyClient({
env: 'uat',
baseUrl: 'https://openapiuat.airtel.cg',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
country: 'CG',
currency: 'XAF',
walletType: 'COLL',
pinEncryptionMode: 'oaep-sha256',
messageSigningEnabled: false,
callbackAuthEnabled: false,
disbursementApiVersion: 'v3',
})API Overview
Collection
Create a USSD push payment:
const result = await airtel.collection.collect({
msisdn: '055123456',
amount: 1000,
reference: 'ORDER1001',
transactionId: 'tx-1001',
})Check collection status:
const status = await airtel.collection.collectStatus('tx-1001')Refund a payment:
const refund = await airtel.collection.refund({
airtelMoneyId: '123456789',
})Account
Get wallet balance:
const balance = await airtel.account.balance()Run KYC lookup:
const user = await airtel.account.kyc('055123456')Disbursement
Create a disbursement:
const payout = await airtel.disbursement.disburse({
payeeMsisdn: '055123456',
payeeWalletType: 'COLL',
reference: 'PAYOUT1001',
pin: '1234',
amount: 2500,
transactionId: 'payout-1001',
transactionType: 'B2C',
})Check disbursement status:
const payoutStatus = await airtel.disbursement.disburseStatus('payout-1001')For Airtel disbursement API v2, transactionType is required on enquiry:
const payoutStatusV2 = await airtel.disbursement.disburseStatus('payout-1001', {
versionTag: 'v2',
transactionType: 'B2C',
})Bank-to-Wallet
Create a bank-to-wallet payment:
const payment = await airtel.banking.bankToWallet({
transactionId: 'btw-1001',
amount: 5000,
payeeMsisdn: '055123456',
note: 'Bank transfer to wallet',
additionalInfo: {
source: 'bank',
},
})Check bank-to-wallet status:
const paymentStatus = await airtel.banking.bankToWalletStatus('btw-1001')Operations
Reconcile
reconcile() is a helper around collection status that maps Airtel response values to a simpler internal status.
const result = await airtel.operations.reconcile('tx-1001')
if (result.status === 'succeeded') {
// update your local transaction
}Health Check
healthCheck() uses the balance endpoint and returns a success/failure summary.
const health = await airtel.operations.healthCheck()Webhook Normalization
Normalize a raw Airtel webhook payload:
const event = airtel.operations.webhook(payload)
console.log(event.status, event.verified)Express Webhook Handler
import express from 'express'
import { AirtelMoneyClient, createAirtelConfigFromEnv } from 'airtelmoneycg'
const app = express()
app.use(express.json())
const airtel = new AirtelMoneyClient(createAirtelConfigFromEnv(process.env))
app.post(
'/webhooks/airtel',
airtel.operations.createExpressWebhookHandler({
onEvent: async (event) => {
console.log('Airtel event', event)
},
onError: async (error) => {
console.error('Webhook error', error)
},
})
)Function Reference
new AirtelMoneyClient(config, options?)
Creates a client instance.
Parameters:
config: AirtelConfigenv?: 'uat' | 'prod'baseUrl: stringclientId: stringclientSecret: stringgrantType?: stringcountry?: stringcurrency?: stringwalletType?: stringrsaPublicKey?: string | nullrsaPublicKeyV1?: string | nullpinEncryptionMode?: 'oaep-sha256' | 'pkcs1'messageSigningEnabled?: booleancallbackHmacSecret?: string | nullcallbackAuthEnabled?: booleandisbursementApiVersion?: 'v2' | 'v3'
options?: AirtelMoneyClientOptionsfetch?: typeof fetch
Returns:
AirtelMoneyClient
createAirtelConfigFromEnv(env?)
Builds an AirtelConfig object from environment variables.
Parameters:
env?: NodeJS.ProcessEnv | Record<string, string | undefined>
Returns:
AirtelConfig
client.getAccessToken()
Gets and caches an OAuth2 access token.
Parameters:
- none
Returns:
Promise<string>
client.encryptPin(pin)
Encrypts a 4-digit Airtel PIN using the configured RSA mode.
Parameters:
pin: string- Must be a 4-digit numeric string such as
1234
- Must be a 4-digit numeric string such as
Returns:
string
client.collection.collect(payload)
Creates a collection payment request.
Parameters:
payload: AirtelUssdPaymentPayloadreference: stringmsisdn: stringamount: numbertransactionId: string
Returns:
Promise<AirtelUssdPaymentResponse>
client.collection.collectStatus(transactionId)
Fetches the status of a collection transaction.
Parameters:
transactionId: string
Returns:
Promise<AirtelTransactionEnquiryResponse>
client.collection.refund(payload)
Creates a refund request from an Airtel Money transaction id.
Parameters:
payload: AirtelRefundPayloadairtelMoneyId: string
Returns:
Promise<AirtelRefundResponse>
client.account.balance()
Fetches the wallet balance.
Parameters:
- none
Returns:
Promise<AirtelBalanceResponse>
client.account.kyc(msisdn)
Runs a KYC lookup for a subscriber.
Parameters:
msisdn: string
Returns:
Promise<AirtelUserEnquiryResponse>
client.disbursement.disburse(payload)
Creates a disbursement request.
Parameters:
payload: AirtelDisbursementPayloadpayeeMsisdn: stringpayeeWalletType?: stringpayeeCurrency?: stringpayeeName?: stringreference: stringpin: stringpinEncrypted?: stringamount: numbertransactionId: stringtransactionType: stringversionTag?: 'v2' | 'v3'
Returns:
Promise<AirtelDisbursementResponse>
client.disbursement.disburseStatus(transactionId, options?)
Fetches the status of a disbursement.
Parameters:
transactionId: stringoptions?: AirtelDisbursementEnquiryOptionsversionTag?: 'v2' | 'v3'transactionType?: string- Required when
versionTagresolves tov2
Returns:
Promise<AirtelDisbursementEnquiryResponse>
client.disbursement.disbursementStatus(transactionId, options?)
Alias of client.disbursement.disburseStatus().
Parameters:
transactionId: stringoptions?: AirtelDisbursementEnquiryOptions
Returns:
Promise<AirtelDisbursementEnquiryResponse>
client.banking.bankToWallet(payload)
Creates a bank-to-wallet payment.
Parameters:
payload: AirtelBankToWalletPayloadtransactionId: stringamount: number | stringpayeeMsisdn: stringnote: stringadditionalInfo?: Record<string, unknown>
Returns:
Promise<AirtelBankToWalletResponse>
client.banking.bankToWalletStatus(transactionId)
Fetches the status of a bank-to-wallet payment.
Parameters:
transactionId: string
Returns:
Promise<AirtelBankToWalletEnquiryResponse>
client.operations.webhook(payload)
Normalizes a raw Airtel webhook payload.
Parameters:
payload: Record<string, unknown>
Returns:
AirtelWebhookEvent
client.operations.reconcile(reference)
Maps a collection status response into a simpler reconciliation structure.
Parameters:
reference: string
Returns:
Promise<AirtelReconcileResult>
client.operations.healthCheck()
Runs a lightweight provider health check using the balance endpoint.
Parameters:
- none
Returns:
Promise<AirtelHealthCheckResult>
client.operations.checkBalance()
Alias of client.account.balance().
Parameters:
- none
Returns:
Promise<AirtelBalanceResponse>
client.operations.createExpressWebhookHandler(options?)
Builds an Express-compatible webhook handler.
Parameters:
options?: ExpressWebhookHandlerOptionsonEvent?: (event: AirtelWebhookEvent) => Promise<void> | voidonError?: (error: unknown) => Promise<void> | void
Returns:
(req: any, res: any) => Promise<void>
normalizeMsisdn(phone)
Utility function that removes the 242 or +242 prefix.
Parameters:
phone: string
Returns:
string
encryptPin(config, pin)
Utility function to encrypt a 4-digit PIN without creating a client instance.
Parameters:
config: AirtelConfigpin: string
Returns:
string
normalizeWebhookEvent(config, payload)
Utility function to normalize a webhook payload without creating an Express handler.
Parameters:
config: AirtelConfigpayload: Record<string, unknown>
Returns:
AirtelWebhookEvent
verifyWebhookHash(config, payload)
Utility function to verify the Airtel callback hash.
Parameters:
config: AirtelConfigpayload: Record<string, unknown>
Returns:
boolean
mapCollectionResponseCode(code)
Maps Airtel collection response_code values to a simplified status bucket.
Parameters:
code?: string | null
Returns:
'pending' | 'succeeded' | 'failed' | null
mapCollectionResponseReason(code)
Maps Airtel collection response_code values to a reason label.
Parameters:
code?: string | null
Returns:
string | null
mapTransactionStatus(status)
Maps Airtel transaction status values such as TS, TA, TIP, TF, TE.
Parameters:
status?: string | null
Returns:
'pending' | 'succeeded' | 'failed' | null
Error Handling
HTTP-level and Airtel business failures raise AirtelApiError.
import { AirtelApiError } from 'airtelmoneycg'
try {
await airtel.collection.collectStatus('tx-1001')
} catch (error) {
if (error instanceof AirtelApiError) {
console.error(error.message)
console.error(error.httpStatus)
console.error(error.providerPayload)
}
}Helpers
The package also exports utility helpers:
encryptPinnormalizeMsisdnnormalizeWebhookEventverifyWebhookHashmapCollectionResponseCodemapCollectionResponseReasonmapTransactionStatus
Example:
import { normalizeMsisdn, encryptPin } from 'airtelmoneycg'
const msisdn = normalizeMsisdn('+242055123456')
const encryptedPin = encryptPin(
{
baseUrl: 'https://openapiuat.airtel.cg',
clientId: 'id',
clientSecret: 'secret',
rsaPublicKey: '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----',
},
'1234'
)Notes and Limitations
- MSISDN normalization removes the
+242or242prefix. - Reference normalization keeps alphanumeric characters only and falls back to a safe generated value when needed.
- OAuth2 access tokens are cached and refreshed with a 30-second safety margin.
messageSigningEnabled=trueis currently not implemented and will throw an explicit runtime error.- This package is server-side only and should not be used in browsers.
Development
npm install
npm run typecheck
npm run build