ElixirMpesa (ElixirMpesa v0.2.0)
View SourceVodacom M-Pesa OpenAPI client for Elixir.
Payments, reversals, direct debit and transaction queries across Tanzania, Lesotho, Ghana and the DRC.
This is the Vodacom OpenAPI, not Safaricom Daraja
If you are integrating M-Pesa in Kenya, you want the Daraja API and a different
library. This one targets openapi.m-pesa.com, the pan-African Vodacom/Vodafone
platform, whose endpoints, authentication and payload shapes are unrelated to Daraja's.
Setup
# config/runtime.exs
config :elixir_mpesa,
api_type: "sandbox",
market: :tanzania,
service_provider_code: System.get_env("MPESA_SERVICE_PROVIDER_CODE"),
api_key: System.get_env("MPESA_API_KEY"),
public_key: System.get_env("MPESA_PUBLIC_KEY")Setting :market fills in the country, currency and URL context together. See
Markets and Authentication.
Taking a payment
attrs = %{
"input_Amount" => "10",
"input_CustomerMSISDN" => "255700000000",
"input_TransactionReference" => "INV-1024",
"input_ThirdPartyConversationID" => ElixirMpesa.conversation_id(),
"input_PurchasedItemsDesc" => "Order 1024"
}
case ElixirMpesa.c2b(attrs) do
{:ok, response} -> {:ok, response.transaction_id}
{:error, %ElixirMpesa.Error{} = error} -> {:error, Exception.message(error)}
endCountry, currency and service provider code are filled in from configuration when you omit them, so most calls carry only what actually varies per transaction.
The session key is handled for you — ElixirMpesa.Session obtains one on first use,
caches it per market, refreshes it before it expires and re-authenticates once if
M-Pesa rejects it. See Getting started.
Return values
Every function returns {:ok, ElixirMpesa.Response.t()} or
{:error, ElixirMpesa.Error.t()}. Match on error.reason, which is a documented atom.
Each function also has a ! variant that returns the response directly and raises on
failure.
Idempotency
"input_ThirdPartyConversationID" is the idempotency key, and it is required for
every payment. Use a fresh one per logical transaction — conversation_id/0 generates
one — and reuse the same one when retrying, so M-Pesa can reject the duplicate rather
than charging twice. Read-only queries get one generated automatically.
Summary
Payments
Pays another business.
Same as b2b/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Pays a customer from your business — disbursements, refunds, salaries.
Same as b2c/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Charges a customer and credits your business.
Same as c2b/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Reverses a completed transaction.
Same as reversal/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Queries
Looks up the registered name behind a phone number.
Same as query_beneficiary_name/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Looks up the status of a direct debit mandate.
Same as query_direct_debit/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Looks up the status of a transaction.
Same as query_transaction_status/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Direct debit
Cancels a direct debit mandate.
Same as direct_debit_cancel/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Creates a direct debit mandate against a customer.
Same as direct_debit_creation/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Collects a payment against an existing mandate.
Same as direct_debit_payment/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Session
Generates a unique conversation ID suitable for "input_ThirdPartyConversationID".
Discards the cached session so the next call re-authenticates.
Returns a valid encrypted session key, obtaining and caching one if needed.
Types
Request attributes, using M-Pesa's own input_* key names.
A unique identifier for one logical transaction. See conversation_id/0.
A market with a built-in preset. See ElixirMpesa.Config.markets/0.
A customer phone number in international format without a +, e.g. "255700000000".
Payments
Pays another business.
Required attributes
"input_Amount""input_PrimaryPartyCode""input_ReceiverPartyCode""input_TransactionReference""input_PurchasedItemsDesc""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.b2b(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.b2b(attrs, market: :ghana)
@spec b2b!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as b2b/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Pays a customer from your business — disbursements, refunds, salaries.
Required attributes
"input_Amount""input_CustomerMSISDN""input_TransactionReference""input_PaymentItemsDesc""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.b2c(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.b2c(attrs, market: :ghana)
@spec b2c!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as b2c/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Charges a customer and credits your business.
Required attributes
"input_Amount""input_CustomerMSISDN""input_TransactionReference""input_PurchasedItemsDesc""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.c2b(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.c2b(attrs, market: :ghana)
@spec c2b!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as c2b/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Reverses a completed transaction.
Required attributes
"input_TransactionID""input_ReversalAmount""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.reversal(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.reversal(attrs, market: :ghana)
@spec reversal!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as reversal/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Queries
Looks up the registered name behind a phone number.
Required attributes
"input_CustomerMSISDN""input_ThirdPartyConversationID"- optional; generated when omitted.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.query_beneficiary_name(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.query_beneficiary_name(attrs, market: :ghana)
@spec query_beneficiary_name!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as query_beneficiary_name/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Looks up the status of a direct debit mandate.
Required attributes
"input_CustomerMSISDN""input_ThirdPartyReference""input_ThirdPartyConversationID"- optional; generated when omitted.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.query_direct_debit(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.query_direct_debit(attrs, market: :ghana)
@spec query_direct_debit!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as query_direct_debit/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Looks up the status of a transaction.
Required attributes
"input_QueryReference""input_ThirdPartyConversationID"- optional; generated when omitted.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.query_transaction_status(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.query_transaction_status(attrs, market: :ghana)
@spec query_transaction_status!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as query_transaction_status/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Direct debit
Cancels a direct debit mandate.
Required attributes
"input_CustomerMSISDN""input_ThirdPartyReference""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.direct_debit_cancel(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.direct_debit_cancel(attrs, market: :ghana)
@spec direct_debit_cancel!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as direct_debit_cancel/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Creates a direct debit mandate against a customer.
Required attributes
"input_AgreedTC""input_CustomerMSISDN""input_ThirdPartyReference""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.direct_debit_creation(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.direct_debit_creation(attrs, market: :ghana)
@spec direct_debit_creation!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as direct_debit_creation/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Collects a payment against an existing mandate.
Required attributes
"input_Amount""input_CustomerMSISDN""input_ThirdPartyReference""input_ThirdPartyConversationID"- unique per transaction; seeconversation_id/0. Required.
"input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in
from configuration when omitted.
Options
:market,:api_type,:url_context,:country,:currency,:service_provider_code,:api_key,:public_key- override configuration for this call.:cache- set tofalseto bypass the session cache.
Examples
{:ok, response} = ElixirMpesa.direct_debit_payment(attrs)
response.transaction_id
# Against a different market, for one call only
ElixirMpesa.direct_debit_payment(attrs, market: :ghana)
@spec direct_debit_payment!( attrs(), keyword() ) :: ElixirMpesa.Response.t()
Same as direct_debit_payment/2 but returns the response directly and raises
ElixirMpesa.Error on failure.
Session
@spec conversation_id() :: conversation_id()
Generates a unique conversation ID suitable for "input_ThirdPartyConversationID".
Use one per logical transaction, and reuse the same value when retrying that transaction so M-Pesa can recognise the duplicate.
Examples
iex> id = ElixirMpesa.conversation_id()
iex> String.length(id)
32
iex> ElixirMpesa.conversation_id() != ElixirMpesa.conversation_id()
true
@spec refresh_session(keyword()) :: :ok | {:error, ElixirMpesa.Error.t()}
Discards the cached session so the next call re-authenticates.
Examples
:ok = ElixirMpesa.refresh_session(market: :tanzania)
@spec session_key(keyword()) :: {:ok, String.t()} | {:error, ElixirMpesa.Error.t()}
Returns a valid encrypted session key, obtaining and caching one if needed.
You rarely need this — every operation calls it for you. It is here for callers doing something the library does not cover, such as an endpoint added after this release.
Examples
{:ok, session_key} = ElixirMpesa.session_key()
{:ok, session_key} = ElixirMpesa.session_key(market: :ghana)
Types
Request attributes, using M-Pesa's own input_* key names.
@type conversation_id() :: String.t()
A unique identifier for one logical transaction. See conversation_id/0.
@type market() :: ElixirMpesa.Config.market()
A market with a built-in preset. See ElixirMpesa.Config.markets/0.
@type msisdn() :: String.t()
A customer phone number in international format without a +, e.g. "255700000000".
@type result() :: {:ok, ElixirMpesa.Response.t()} | {:error, ElixirMpesa.Error.t()}