Upgrading

View Source

0.1.0 → 0.2.0

Your existing code keeps working. Every 0.1.0 function is still present, still returns the same shapes, and is marked @deprecated so the compiler points you at its replacement. The shims will be removed in 0.3.0.

Upgrade first, for the security fix

Version 0.1.0 sent every request with hackney: [:insecure], disabling TLS certificate verification. The encrypted API key and bearer session token travelled over connections that were never authenticated. 0.2.0 verifies certificates and hostnames on every call.

0.1.0 also raised FunctionClauseError instead of returning an error tuple on HTTP 403, 404, 429, 502 and 504, and on any non-JSON response body.

What changed

0.1.00.2.0
Manual encrypt → getSession → encrypt before every callAutomatic, cached, refreshed
Session key passed to every functionNot passed at all
{:ok, %{"output_..." => ...}}{:ok, %ElixirMpesa.Response{}}
{:error, %{"output_..." => ...}}{:error, %ElixirMpesa.Error{}}
c2b_single_stage/3, direct_c2b_single_stage/2c2b/2
Country and currency on every callFrom configuration
reversal unreachable from the top-level moduleElixirMpesa.reversal/2
HTTPoison, TLS verification offReq, TLS verified

Before

{:ok, encrypted_api_key} = ElixirMpesa.encrypt_api_key()
{:ok, session_data} = ElixirMpesa.generate_session_key(encrypted_api_key)
{:ok, session_key} = ElixirMpesa.encrypt_session_key(session_data["output_SessionID"])

{:ok, result} = ElixirMpesa.c2b_single_stage(%{
  "input_Amount" => 10.0,
  "input_Country" => "LES",
  "input_Currency" => "LSL",
  "input_CustomerMSISDN" => "26675000000",
  "input_ServiceProviderCode" => "00000",
  "input_ThirdPartyConversationID" => "asv02e5958774f7ba228d83d0d689761",
  "input_TransactionReference" => "T12344C",
  "input_PurchasedItemsDesc" => "Test purchase"
}, session_key)

result["output_TransactionID"]

After

{:ok, response} = ElixirMpesa.c2b(%{
  "input_Amount" => "10",
  "input_CustomerMSISDN" => "26675000000",
  "input_ThirdPartyConversationID" => ElixirMpesa.conversation_id(),
  "input_TransactionReference" => "T12344C",
  "input_PurchasedItemsDesc" => "Test purchase"
})

response.transaction_id

Configuration changes

  config :elixir_mpesa,
    api_type: "sandbox",
-   input_currency: "LSL",
-   input_country: "LES",
-   url_context: "vodacomLES",
+   market: :lesotho,
+   service_provider_code: "00000",
    api_key: ...,
    public_key: ...

input_currency and input_country were documented in 0.1.0 but read by nothing — you had to pass "input_Currency" and "input_Country" on every call regardless. They are now :currency and :country, they are actually used, and :market sets all three of them together.

url_context, country and currency still work individually if you prefer them.

Function renames

0.1.00.2.0
c2b_single_stage/2,3 · direct_c2b_single_stage/1,2c2b/1,2
b2c_single_stage/2,3 · direct_b2c_single_stage/1,2b2c/1,2
b2b_single_stage/2,3b2b/1,2
query_transaction_status/2,3 · direct_query_transaction_status/1,2query_transaction_status/1,2
direct_debit_creation/2,3direct_debit_creation/1,2
direct_debit_payment/2,3direct_debit_payment/1,2
direct_debit_cancel/2,3direct_debit_cancel/1,2
query_beneficiary_name/2,3query_beneficiary_name/1,2
query_direct_debit/2,3query_direct_debit/1,2
Transactions.query_status/3query_transaction_status/1,2
(unexposed)reversal/1,2
ElixirMpesa.GenerateSessionKey.*Automatic; ElixirMpesa.session_key/1 if needed

Six of these keep their names. Where the name is unchanged, the two forms are told apart by the second argument: a binary is the old session key, a keyword list is the new options. Both work during 0.2.x.

Two things now enforced

A payment must carry "input_ThirdPartyConversationID". It is the idempotency key. The library will not generate one for a payment, because a generated one would give each retry a fresh key and defeat M-Pesa's duplicate detection. Read-only queries still get one generated. Use ElixirMpesa.conversation_id/0.

Payments are never retried automatically. 0.1.0 did not retry either, but it is worth stating: a gateway timeout on a payment often means the payment succeeded and only the response was lost. Query the status rather than resending — or resend with the same conversation ID.

Elixir version

The floor is now 1.15, up from a claimed 1.14. Req requires 1.15, and 1.14 was never actually tested.