AirtelMoney

An Elixir SDK for Airtel Money APIs, providing a clean and idiomatic interface for collections, disbursements, transaction queries, and webhooks.

Features

Installation

Add airtel_money to your list of dependencies in mix.exs:

def deps do
[
{:airtel_money, "~> 0.1.0"}
]
end

Run:

mix deps.get

Configuration

Configure the SDK in your config/config.exs:

config :airtel_money,
client_id: "your_client_id",
client_secret: "your_client_secret",
country: "CD",
currency: "CDF",
environment: :sandbox,
webhook_secret: "your_webhook_secret" # Optional, for webhook verification

Configuration Options

Usage

Start the Application

The SDK uses OTP supervision. Ensure the application is started:

# In your application.ex
children = [
AirtelMoney.Application
]

Collect a Payment

case AirtelMoney.collect(%{
amount: "1000",
msisdn: "2439xxxxxxx",
reference: "INV-001"
}) do
{:ok, result} ->
IO.inspect(result)
{:error, %AirtelMoney.Error{message: message}} ->
IO.puts("Error: #{message}")
end

Disburse a Payment

# For production, you need to encrypt the PIN first
case AirtelMoney.Encryption.encrypt_pin("1234") do
{:ok, encrypted_pin} ->
case AirtelMoney.disburse(%{
amount: "5000",
msisdn: "2439xxxxxxx",
reference: "PAY-001",
pin: encrypted_pin
}) do
{:ok, result} ->
IO.inspect(result)
{:error, %AirtelMoney.Error{message: message}} ->
IO.puts("Error: #{message}")
end
{:error, reason} ->
IO.puts("PIN encryption failed: #{reason}")
end

Query Transaction Status

case AirtelMoney.transaction_status("TXN123") do
{:ok, status} ->
IO.inspect(status)
{:error, %AirtelMoney.Error{message: message}} ->
IO.puts("Error: #{message}")
end

Query Balance

case AirtelMoney.balance() do
{:ok, balance} ->
IO.inspect(balance)
{:error, %AirtelMoney.Error{message: message}} ->
IO.puts("Error: #{message}")
end

Validate MSISDN

case AirtelMoney.Utils.validate_msisdn("243900000000") do
:ok ->
IO.puts("Valid MSISDN")
{:error, reason} ->
IO.puts("Invalid MSISDN: #{reason}")
end

Fetch RSA Public Key

case AirtelMoney.Encryption.fetch_public_key() do
{:ok, public_key} ->
IO.puts("Public key fetched successfully")
# Store this key in your config for future use
{:error, error} ->
IO.puts("Failed to fetch public key: #{error.message}")
end

Webhooks

Verify Webhook Signature

# In your webhook controller
def handle(conn, params) do
signature = get_req_header(conn, "x-airtel-signature")
payload = conn.assigns[:raw_body]
case AirtelMoney.verify_webhook(payload, signature) do
:ok ->
# Signature is valid, process webhook
{:ok, webhook_data} = AirtelMoney.parse_webhook(payload)
# Handle webhook_data
send_resp(conn, 200, "OK")
{:error, :invalid_signature} ->
send_resp(conn, 401, "Invalid signature")
end
end

Using the Plug (Phoenix)

Add the plug to your router:

pipeline :webhooks do
plug AirtelMoney.WebhookPlug
end
scope "/webhooks" do
pipe_through :webhooks
post "/airtel", WebhookController, :handle
end

The plug will:

Telemetry

The SDK emits telemetry events for monitoring:

Attach handlers to monitor events:

:telemetry.attach(
"airtel-money-handler",
[:airtel_money, :success],
&handle_event/4,
nil
)
def handle_event([:airtel_money, event], measurements, metadata, _config) do
IO.puts("Event: #{event}, Duration: #{measurements.duration}ms")
end

Error Handling

All API functions return {:ok, result} or {:error, %AirtelMoney.Error{}}.

%AirtelMoney.Error{
code: "ERR001",
message: "Invalid request",
status: 400
}

Testing

Run tests:

mix test

Run tests with coverage:

mix test.ci

Development

Linting

mix lint

Setup

mix setup

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License.

Documentation

Full documentation is available at HexDocs.