Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 143 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ A comprehensive Elixir client library for the **MTN Mobile Money (MoMo) API**. E

MTN Mobile Money is a digital financial service that allows users to store, send, and receive money using their mobile phones. This library provides a simple interface to:

- 💳 **Collections** - Request payments from customers
- 💸 **Disbursements** - Send money to recipients
- 💰 **Account Management** - Check balances and transaction status
- 💳 **Collections** - Request payments and withdrawals from customers
- 💸 **Disbursements** - Send money and make deposits to recipients
- 👤 **Account Management** - Get user info, validate accounts, check balances
- 🔍 **Transaction Tracking** - Monitor transaction status and history
- 🔒 **Secure** - Production-ready with proper authentication
- 🌍 **Multi-environment** - Supports both sandbox and production

### ✨ New Features

This library now supports the **complete MTN Mobile Money API** including:

- **Withdrawal Requests** - Request money from customer accounts via Collections API
- **Direct Deposits** - Make instant deposits via Disbursements API
- **User Information** - Get basic user details for both Collections and Disbursements
- **Account Validation** - Check if accounts are active before transactions
- **Default Parameters** - MSISDN defaults for easier phone number handling
- **Enhanced Error Handling** - Proper Elixir error tuples instead of exceptions

## Quick Start

### 1. Installation
Expand Down Expand Up @@ -94,7 +106,7 @@ end

### Collections API

Request payments from customers.
Request payments from customers, withdraw money, and manage account information.

#### Request Payment

Expand All @@ -114,6 +126,26 @@ payment = %{
{:ok, reference_id} = MomoapiElixir.Collections.request_to_pay(config, payment)
```

#### Request Withdrawal

Request withdrawal from a consumer's account (requires user authorization):

```elixir
withdrawal = %{
amount: "1000",
currency: "UGX",
externalId: "withdrawal_#{System.system_time()}",
payer: %{
partyIdType: "MSISDN",
partyId: "256784123456"
},
payerMessage: "Cash withdrawal",
payeeNote: "ATM withdrawal"
}

{:ok, reference_id} = MomoapiElixir.Collections.request_to_withdraw(config, withdrawal)
```

#### Check Transaction Status

```elixir
Expand All @@ -132,6 +164,40 @@ case MomoapiElixir.Collections.get_transaction_status(config, reference_id) do
end
```

#### Get User Information

Get basic user information for account validation:

```elixir
# Using default MSISDN type
case MomoapiElixir.Collections.get_basic_user_info(config, "256784123456") do
{:ok, %{"given_name" => first_name, "family_name" => last_name}} ->
IO.puts("User: #{first_name} #{last_name}")
{:error, %{status_code: 404}} ->
IO.puts("User not found")
{:error, reason} ->
IO.puts("Error: #{inspect(reason)}")
end

# Explicitly specify account type
{:ok, user_info} = MomoapiElixir.Collections.get_basic_user_info(config, "EMAIL", "user@example.com")
```

#### Validate Account Status

Check if an account holder is active before initiating transactions:

```elixir
case MomoapiElixir.Collections.validate_account_holder_status(config, "256784123456") do
{:ok, %{"result" => true}} ->
IO.puts("Account is active and ready for transactions")
{:ok, %{"result" => false}} ->
IO.puts("Account is inactive")
{:error, reason} ->
IO.puts("Validation failed: #{inspect(reason)}")
end
```

#### Get Account Balance

```elixir
Expand All @@ -145,9 +211,11 @@ end

### Disbursements API

Send money to recipients.
Send money to recipients, make deposits, and manage account information.

#### Transfer Money

#### Send Money
Send money to recipients (requires authorization from the recipient):

```elixir
transfer = %{
Expand All @@ -170,18 +238,82 @@ case MomoapiElixir.Disbursements.transfer(config, transfer) do
end
```

#### Check Transfer Status
#### Deposit Money

Direct deposit into a recipient's account (no authorization required):

```elixir
deposit = %{
amount: "1500",
currency: "UGX",
externalId: "deposit_#{System.system_time()}",
payee: %{
partyIdType: "MSISDN",
partyId: "256784987654"
},
payerMessage: "Bonus payment",
payeeNote: "Performance bonus"
}

case MomoapiElixir.Disbursements.deposit(config, deposit) do
{:ok, reference_id} ->
IO.puts("Deposit completed! Reference ID: #{reference_id}")
{:error, reason} ->
IO.puts("Deposit failed: #{inspect(reason)}")
end
```

#### Check Transaction Status

```elixir
{:ok, status} = MomoapiElixir.Disbursements.get_transaction_status(config, reference_id)
IO.inspect(status)
case MomoapiElixir.Disbursements.get_transaction_status(config, reference_id) do
{:ok, %{"status" => "SUCCESSFUL", "amount" => amount}} ->
IO.puts("Transaction of #{amount} completed successfully!")
{:ok, %{"status" => "PENDING"}} ->
IO.puts("Transaction is still processing...")
{:error, reason} ->
IO.puts("Error checking status: #{inspect(reason)}")
end
```

#### Get User Information (Disbursements)

Get basic user information via the Disbursements API:

```elixir
# Using default MSISDN type
case MomoapiElixir.Disbursements.get_basic_user_info(config, "256784987654") do
{:ok, %{"given_name" => first_name, "family_name" => last_name}} ->
IO.puts("Recipient: #{first_name} #{last_name}")
{:error, reason} ->
IO.puts("Error: #{inspect(reason)}")
end
```

#### Validate Account Status (Disbursements)

Check if a recipient account is active before making transfers:

```elixir
case MomoapiElixir.Disbursements.validate_account_holder_status(config, "256784987654") do
{:ok, %{"result" => true}} ->
IO.puts("Recipient account is active")
{:ok, %{"result" => false}} ->
IO.puts("Recipient account is inactive")
{:error, reason} ->
IO.puts("Validation failed: #{inspect(reason)}")
end
```

#### Get Disbursements Balance

```elixir
{:ok, balance} = MomoapiElixir.Disbursements.get_balance(config)
IO.inspect(balance)
case MomoapiElixir.Disbursements.get_balance(config) do
{:ok, %{"availableBalance" => balance, "currency" => currency}} ->
IO.puts("Available balance: #{balance} #{currency}")
{:error, reason} ->
IO.puts("Error getting balance: #{inspect(reason)}")
end
```

## Configuration
Expand Down
167 changes: 167 additions & 0 deletions lib/momoapi_elixir.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ defmodule MomoapiElixir do

### Disbursements (Transfers to payees)
- `transfer/2` - Transfer money to a payee
- `deposit/2` - Deposit money into an account
- `get_transfer_status/2` - Check transfer transaction status
- `get_disbursements_balance/1` - Get Disbursements account balance
- `get_disbursements_user_info/2` - Get basic user information for an account holder (defaults to MSISDN)
- `validate_disbursements_account_status/2` - Validate if account holder is active (defaults to MSISDN)

## Core Modules

Expand Down Expand Up @@ -506,4 +509,168 @@ defmodule MomoapiElixir do
def validate_account_holder_status(config, account_holder_id_type \\ "MSISDN", account_holder_id) do
Collections.validate_account_holder_status(config, account_holder_id_type, account_holder_id)
end

@doc """
Deposit money into an account (Disbursements API).

This function allows you to deposit money directly into a payee's account
without requiring authorization from the payee. Returns a reference ID
that can be used to check the transaction status.

## Parameters

- `config` - Configuration map with subscription_key, user_id, api_key, and target_environment
- `body` - Deposit request map with the following required fields:
- `amount` - Deposit amount as string (e.g., "100")
- `currency` - ISO 4217 currency code (e.g., "UGX")
- `externalId` - Your unique transaction identifier
- `payee` - Map with `partyIdType` ("MSISDN" or "EMAIL") and `partyId`
- `payerMessage` - Message for the deposit (optional)
- `payeeNote` - Note for the payee (optional)

## Examples

config = %{
subscription_key: "your_key",
user_id: "your_user_id",
api_key: "your_api_key",
target_environment: "sandbox"
}

deposit = %{
amount: "1500",
currency: "UGX",
externalId: "deposit_789",
payee: %{
partyIdType: "MSISDN",
partyId: "256784123456"
},
payerMessage: "Bonus payment",
payeeNote: "Performance bonus"
}

case MomoapiElixir.deposit(config, deposit) do
{:ok, reference_id} ->
IO.puts("Deposit initiated: \#{reference_id}")
{:error, validation_errors} when is_list(validation_errors) ->
IO.puts("Validation failed: \#{inspect(validation_errors)}")
{:error, %{status_code: status, body: body}} ->
IO.puts("API error \#{status}: \#{inspect(body)}")
{:error, reason} ->
IO.puts("Deposit failed: \#{inspect(reason)}")
end

This is a convenience function that delegates to `MomoapiElixir.Disbursements.deposit/2`.
"""
@spec deposit(config(), map()) :: {:ok, String.t()} | {:error, term()}
def deposit(config, body) do
Disbursements.deposit(config, body)
end

@doc """
Get basic user information for an account holder (Disbursements API).

Retrieve basic user information such as names for a specific account holder
using their party ID type and party ID via the Disbursements API.

## Parameters

- `config` - Configuration map with subscription_key, user_id, api_key, and target_environment
- `account_holder_id_type` - Type of account identifier (defaults to "MSISDN")
- "MSISDN" - Mobile phone number
- "EMAIL" - Email address
- `account_holder_id` - The account identifier (phone number or email)

## Examples

config = %{
subscription_key: "your_key",
user_id: "your_user_id",
api_key: "your_api_key",
target_environment: "sandbox"
}

# Using default MSISDN type (most common)
case MomoapiElixir.get_disbursements_user_info(config, "256784123456") do
{:ok, %{"given_name" => first_name, "family_name" => last_name}} ->
IO.puts("User: \#{first_name} \#{last_name}")
{:ok, user_info} ->
IO.puts("User info: \#{inspect(user_info)}")
{:error, %{status_code: 404}} ->
IO.puts("User not found")
{:error, %{status_code: status, body: body}} ->
IO.puts("Failed to get user info: \#{status} - \#{inspect(body)}")
{:error, reason} ->
IO.puts("Request failed: \#{inspect(reason)}")
end

# Explicitly specifying EMAIL type
case MomoapiElixir.get_disbursements_user_info(config, "EMAIL", "user@example.com") do
{:ok, user_info} ->
IO.puts("Email user info: \#{inspect(user_info)}")
{:error, reason} ->
IO.puts("Failed: \#{inspect(reason)}")
end

This is a convenience function that delegates to `MomoapiElixir.Disbursements.get_basic_user_info/2` or `/3`.
"""
@spec get_disbursements_user_info(config(), String.t(), String.t()) :: {:ok, map()} | {:error, term()}
@spec get_disbursements_user_info(config(), String.t()) :: {:ok, map()} | {:error, term()}
def get_disbursements_user_info(config, account_holder_id_type \\ "MSISDN", account_holder_id) do
Disbursements.get_basic_user_info(config, account_holder_id_type, account_holder_id)
end

@doc """
Validate account holder status (Disbursements API).

Check if an account holder is active and able to receive transactions
via the Disbursements API. This is useful for validating account details
before initiating transfers or deposits.

## Parameters

- `config` - Configuration map with subscription_key, user_id, api_key, and target_environment
- `account_holder_id_type` - Type of account identifier (defaults to "MSISDN")
- "MSISDN" - Mobile phone number
- "EMAIL" - Email address
- `account_holder_id` - The account identifier (phone number or email)

## Examples

config = %{
subscription_key: "your_key",
user_id: "your_user_id",
api_key: "your_api_key",
target_environment: "sandbox"
}

# Using default MSISDN type (most common)
case MomoapiElixir.validate_disbursements_account_status(config, "256784123456") do
{:ok, %{"result" => true}} ->
IO.puts("Account is active and valid for disbursements")
{:ok, %{"result" => false}} ->
IO.puts("Account is inactive or invalid")
{:error, %{status_code: 404}} ->
IO.puts("Account not found")
{:error, %{status_code: status, body: body}} ->
IO.puts("Failed to validate account: \#{status} - \#{inspect(body)}")
{:error, reason} ->
IO.puts("Request failed: \#{inspect(reason)}")
end

# Explicitly specifying EMAIL type
case MomoapiElixir.validate_disbursements_account_status(config, "EMAIL", "user@example.com") do
{:ok, %{"result" => status}} ->
IO.puts("Email account status: \#{status}")
{:error, reason} ->
IO.puts("Failed: \#{inspect(reason)}")
end

This is a convenience function that delegates to `MomoapiElixir.Disbursements.validate_account_holder_status/2` or `/3`.
"""
@spec validate_disbursements_account_status(config(), String.t(), String.t()) :: {:ok, map()} | {:error, term()}
@spec validate_disbursements_account_status(config(), String.t()) :: {:ok, map()} | {:error, term()}
def validate_disbursements_account_status(config, account_holder_id_type \\ "MSISDN", account_holder_id) do
Disbursements.validate_account_holder_status(config, account_holder_id_type, account_holder_id)
end
end
Loading
Loading