The Vodacom M-Pesa OpenAPI serves several countries from one host, distinguished by a URL context. Each market has its own credentials, its own country code and its own currency.

Supported markets

:marketCountryURL contextCountry codeCurrency
:tanzaniaTanzaniavodacomTZNTZNTZS
:lesothoLesothovodacomLESLESLSL
:ghanaGhanavodafoneGHAGHAGHS
:drcDR CongovodacomDRCDRCCDF

Note that Ghana's context is vodafoneGHA, not vodacomGHA — a common source of 404s.

Setting :market applies all three values at once:

config :elixir_mpesa, market: :tanzania

which is equivalent to, but harder to get wrong than:

config :elixir_mpesa,
  url_context: "vodacomTZN",
  country: "TZN",
  currency: "TZS"

ElixirMpesa.Config.markets/0 lists them at runtime, and ElixirMpesa.Config.market/1 returns one preset.

Endpoints

Requests go to:

https://openapi.m-pesa.com/{api_type}/ipg/v2/{url_context}/{operation}

where api_type is sandbox or openapi (production). So a Tanzanian production C2B payment lands on:

https://openapi.m-pesa.com/openapi/ipg/v2/vodacomTZN/c2bPayment/singleStage/

Serving more than one market

Configuration can be overridden per call, and sessions are cached per market, so one application can serve several countries at once without them interfering:

ElixirMpesa.c2b(tanzanian_attrs, market: :tanzania)
ElixirMpesa.c2b(ghanaian_attrs, market: :ghana)

Each market needs its own API key and public key, so pass those too — or keep them in a map and merge:

@markets %{
  tanzania: [market: :tanzania, api_key: {:system, "MPESA_TZ_KEY"}, ...],
  ghana: [market: :ghana, api_key: {:system, "MPESA_GH_KEY"}, ...]
}

def pay(market, attrs), do: ElixirMpesa.c2b(attrs, @markets[market])

A market without a preset

Vodacom adds markets from time to time, and Mozambique runs a separate platform altogether. You do not need to wait for a release — set the three values directly:

ElixirMpesa.c2b(attrs,
  url_context: "vodacomMOZ",
  country: "MOZ",
  currency: "MZN"
)

Everything else works unchanged. If you are using a market this library does not list, a pull request adding it to ElixirMpesa.Config is welcome.

Phone number format

"input_CustomerMSISDN" is the full international number without a + or leading zeros:

CountryDialling codeExample
Tanzania255"255700000000"
Lesotho266"26650000000"
Ghana233"233200000000"
DR Congo243"243800000000"

A number in the wrong format is one of the most common causes of a rejected transaction. Normalise before sending.