Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 airtelmoneycg

Quick 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=v3

Supported environment variables

  • AIRTEL_MONEY_ENV
  • AIRTEL_MONEY_BASE_URL_UAT
  • AIRTEL_MONEY_BASE_URL_PROD
  • AIRTEL_MONEY_CLIENT_ID
  • AIRTEL_MONEY_CLIENT_SECRET
  • AIRTEL_MONEY_GRANT_TYPE
  • AIRTEL_MONEY_COUNTRY_CODE
  • AIRTEL_MONEY_CURRENCY_CODE
  • AIRTEL_MONEY_WALLET_TYPE
  • AIRTEL_MONEY_RSA_PUBLIC_KEY
  • AIRTEL_MONEY_RSA_PUBLIC_KEY_V1
  • AIRTEL_MONEY_PIN_ENCRYPTION_MODE
  • AIRTEL_MONEY_MESSAGE_SIGNING_ENABLED
  • AIRTEL_MONEY_CALLBACK_HMAC_SECRET
  • AIRTEL_MONEY_CALLBACK_AUTH_ENABLED
  • AIRTEL_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: AirtelConfig
    • env?: 'uat' | 'prod'
    • baseUrl: string
    • clientId: string
    • clientSecret: string
    • grantType?: string
    • country?: string
    • currency?: string
    • walletType?: string
    • rsaPublicKey?: string | null
    • rsaPublicKeyV1?: string | null
    • pinEncryptionMode?: 'oaep-sha256' | 'pkcs1'
    • messageSigningEnabled?: boolean
    • callbackHmacSecret?: string | null
    • callbackAuthEnabled?: boolean
    • disbursementApiVersion?: 'v2' | 'v3'
  • options?: AirtelMoneyClientOptions
    • fetch?: 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

Returns:

  • string

client.collection.collect(payload)

Creates a collection payment request.

Parameters:

  • payload: AirtelUssdPaymentPayload
    • reference: string
    • msisdn: string
    • amount: number
    • transactionId: 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: AirtelRefundPayload
    • airtelMoneyId: 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: AirtelDisbursementPayload
    • payeeMsisdn: string
    • payeeWalletType?: string
    • payeeCurrency?: string
    • payeeName?: string
    • reference: string
    • pin: string
    • pinEncrypted?: string
    • amount: number
    • transactionId: string
    • transactionType: string
    • versionTag?: 'v2' | 'v3'

Returns:

  • Promise<AirtelDisbursementResponse>

client.disbursement.disburseStatus(transactionId, options?)

Fetches the status of a disbursement.

Parameters:

  • transactionId: string
  • options?: AirtelDisbursementEnquiryOptions
    • versionTag?: 'v2' | 'v3'
    • transactionType?: string
    • Required when versionTag resolves to v2

Returns:

  • Promise<AirtelDisbursementEnquiryResponse>

client.disbursement.disbursementStatus(transactionId, options?)

Alias of client.disbursement.disburseStatus().

Parameters:

  • transactionId: string
  • options?: AirtelDisbursementEnquiryOptions

Returns:

  • Promise<AirtelDisbursementEnquiryResponse>

client.banking.bankToWallet(payload)

Creates a bank-to-wallet payment.

Parameters:

  • payload: AirtelBankToWalletPayload
    • transactionId: string
    • amount: number | string
    • payeeMsisdn: string
    • note: string
    • additionalInfo?: 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?: ExpressWebhookHandlerOptions
    • onEvent?: (event: AirtelWebhookEvent) => Promise<void> | void
    • onError?: (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: AirtelConfig
  • pin: string

Returns:

  • string

normalizeWebhookEvent(config, payload)

Utility function to normalize a webhook payload without creating an Express handler.

Parameters:

  • config: AirtelConfig
  • payload: Record<string, unknown>

Returns:

  • AirtelWebhookEvent

verifyWebhookHash(config, payload)

Utility function to verify the Airtel callback hash.

Parameters:

  • config: AirtelConfig
  • payload: 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:

  • encryptPin
  • normalizeMsisdn
  • normalizeWebhookEvent
  • verifyWebhookHash
  • mapCollectionResponseCode
  • mapCollectionResponseReason
  • mapTransactionStatus

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 +242 or 242 prefix.
  • 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=true is 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

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages