Changelog

View Source

All notable changes to this project are documented here. This project adheres to Semantic Versioning.

v0.2.0

A substantial release. 0.1.0 code keeps working — every old function remains as a deprecated shim, returning the shapes it always did, until 0.3.0.

Security

  • TLS certificate verification is now on. 0.1.0 passed hackney: [:insecure] on every request, so the RSA-encrypted API key and the bearer session token travelled over connections that were never authenticated. Certificates and hostnames are now verified against the system trust store, and a regression test proves a known-bad certificate is rejected.
  • Request timeouts reduced from 500 seconds to 10s connect / 30s receive. The old value meant a hung endpoint blocked the caller for over eight minutes.
  • The library's own config/config.exs no longer carries credential-shaped placeholders, and CI fails the build if anything resembling a real API key reaches the package.

Fixed

  • HTTP 403, 404, 429, 502 and 504 raised FunctionClauseError instead of returning an error tuple. Only ten statuses were handled; everything else crashed the caller. All statuses are handled now.
  • A non-JSON response body raised. Gateway HTML error pages and empty bodies now return {:error, %ElixirMpesa.Error{reason: :invalid_json}}.
  • Base.decode64/1's bare :error escaped through the encryption path and caused a WithClauseError further up. Malformed public keys now return a proper error, and whitespace in a key is tolerated.
  • The direct_* helpers encrypted the wrong value — they called encrypt_api_key/1 where encrypt_session_key/2 was meant, silently discarding the session ID if any option was passed.
  • input_currency and input_country were documented but read by nothing. They are now :currency and :country, are actually applied, and are set together by :market.
  • Missing configuration produced nil, which became a request to https://openapi.m-pesa.com//ipg/v2//getSession/. Configuration is now validated up front with an error naming the missing key.
  • mix compile --warnings-as-errors and mix format --check-formatted both failed on 0.1.0. Both pass.

Added

  • Automatic session management. ElixirMpesa.Session obtains a session key on first use, caches it per market, refreshes at 80% of its one-hour lifetime, collapses concurrent cache misses into a single getSession call, and re-authenticates once if M-Pesa rejects the key mid-flight.
  • ElixirMpesa.Error, a single exception struct for every failure, with a documented atom reason and a category (:config, :crypto, :transport, :http, :api) that distinguishes "M-Pesa declined" from "the outcome is unknown".
  • ElixirMpesa.Response, replacing raw string-keyed maps.
  • Market presetsmarket: :tanzania | :lesotho | :ghana | :drc sets the URL context, country and currency together.

  • reversal/2 is now reachable. It existed in 0.1.0 but was never exposed on the top-level module and was absent from the documentation.
  • ! variants for every operation, raising on failure.
  • conversation_id/0 for generating idempotency keys.
  • @spec and @type on every public function — 0.1.0 had none at all — verified by dialyzer.
  • Eight guides, a real test suite (91 tests, 18 doctests, no network), credo, dialyzer and a CI workflow that matches this project.
  • llms.txt and AGENTS.md for coding assistants.

Changed

  • HTTPoison replaced with Req, which verifies TLS by default and makes the whole suite testable via Req.Test with no network.
  • A payment must now carry "input_ThirdPartyConversationID". The library will not generate one for a payment: a generated ID would give each retry a fresh idempotency key and defeat M-Pesa's duplicate detection. Read-only queries still get one generated.
  • Payments are never retried automatically. Read-only queries are.
  • "input_Country", "input_Currency" and "input_ServiceProviderCode" are filled in from configuration when omitted.
  • Minimum Elixir raised to 1.15, from a claimed 1.14 that was never tested and that Req does not support.
  • Source files moved from lib/ex_mpesa/ to lib/elixir_mpesa/, matching the module names since the 0.1.0 rename.
  • Corrected the repository URL in the package metadata. It pointed at a GitHub user that does not exist, so the "GitHub" link on Hex and every "Source" link in the published docs were dead.

Deprecated

Removal in 0.3.0. See the upgrade guide.

  • c2b_single_stage, b2c_single_stage, b2b_single_stagec2b/2, b2c/2, b2b/2
  • direct_c2b_single_stage, direct_b2c_single_stage, direct_query_transaction_status → the plain function of the same operation
  • Passing a session key to query_transaction_status, query_beneficiary_name, query_direct_debit, direct_debit_creation, direct_debit_payment and direct_debit_cancel → pass options instead
  • ElixirMpesa.GenerateSessionKey and encrypt_api_key/1, generate_session_key/2, encrypt_session_key/2 → handled automatically; ElixirMpesa.session_key/1 if needed
  • ElixirMpesa.HttpRequest → removed, replaced by ElixirMpesa.Client

v0.1.0

Initial release. Session key generation, C2B, B2C, B2B, transaction status queries, direct debit operations and beneficiary name queries.