diff --git a/README.md b/README.md index d3f4c71..066ba90 100644 --- a/README.md +++ b/README.md @@ -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 @@ -94,7 +106,7 @@ end ### Collections API -Request payments from customers. +Request payments from customers, withdraw money, and manage account information. #### Request Payment @@ -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 @@ -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 @@ -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 = %{ @@ -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 diff --git a/lib/momoapi_elixir.ex b/lib/momoapi_elixir.ex index 3fcdbd5..cee0ebd 100644 --- a/lib/momoapi_elixir.ex +++ b/lib/momoapi_elixir.ex @@ -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 @@ -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 diff --git a/lib/momoapi_elixir/disbursements.ex b/lib/momoapi_elixir/disbursements.ex index 4b8050f..3a5608a 100644 --- a/lib/momoapi_elixir/disbursements.ex +++ b/lib/momoapi_elixir/disbursements.ex @@ -2,11 +2,17 @@ defmodule MomoapiElixir.Disbursements do @moduledoc """ Disbursements API for MTN Mobile Money. - This module provides functions to transfer money to payees, - check transaction status, and get account balance. + This module provides functions to: + - Transfer money to payees + - Deposit money into accounts + - Check transaction status and account balance + - Get basic user information for account holders + - Validate account holder status """ - alias MomoapiElixir.{Auth, Client, Validator} + alias MomoapiElixir.{Auth, Validator} + + @client Application.compile_env(:momoapi_elixir, :http_client, MomoapiElixir.Client) @type config :: %{ subscription_key: String.t(), @@ -46,7 +52,7 @@ defmodule MomoapiElixir.Disbursements do {:ok, token} <- Auth.get_token(:disbursements, config), {:ok, reference_id} <- generate_reference_id(), headers <- build_headers(token, config, reference_id), - {:ok, response} <- Client.post("/disbursement/v1_0/transfer", validated_body, headers) do + {:ok, response} <- @client.post("/disbursement/v1_0/transfer", validated_body, headers) do handle_transfer_response(response, reference_id) end end @@ -64,7 +70,7 @@ defmodule MomoapiElixir.Disbursements do def get_balance(config) do with {:ok, token} <- Auth.get_token(:disbursements, config), headers <- build_headers(token, config), - {:ok, response} <- Client.get("/disbursement/v1_0/account/balance", headers) do + {:ok, response} <- @client.get("/disbursement/v1_0/account/balance", headers) do handle_balance_response(response) end end @@ -84,11 +90,123 @@ defmodule MomoapiElixir.Disbursements do def get_transaction_status(config, reference_id) do with {:ok, token} <- Auth.get_token(:disbursements, config), headers <- build_headers(token, config, reference_id), - {:ok, response} <- Client.get("/disbursement/v1_0/transfer/#{reference_id}", headers) do + {:ok, response} <- @client.get("/disbursement/v1_0/transfer/#{reference_id}", headers) do handle_transaction_response(response) end end + @doc """ + Deposit money into a payee account. + + This function allows you to deposit money directly into a payee's account + without requiring authorization from the payee. + + ## 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 + + iex> config = %{subscription_key: "key", user_id: "user", api_key: "api", target_environment: "sandbox"} + iex> deposit = %{amount: "500", currency: "UGX", externalId: "deposit_123", payee: %{partyIdType: "MSISDN", partyId: "256784123456"}} + iex> MomoapiElixir.Disbursements.deposit(config, deposit) + {:ok, "reference-id-uuid"} + """ + @spec deposit(config(), map()) :: {:ok, String.t()} | {:error, term()} + def deposit(config, body) do + with {:ok, validated_body} <- Validator.validate_disbursements(body), + {:ok, token} <- Auth.get_token(:disbursements, config), + {:ok, reference_id} <- generate_reference_id(), + headers <- build_headers(token, config, reference_id), + {:ok, response} <- @client.post("/disbursement/v1_0/deposit", validated_body, headers) do + handle_transfer_response(response, reference_id) + end + end + + @doc """ + Get basic user information for an account holder. + + Retrieve basic user information for a specific account holder using their + party ID type and party ID. + + ## 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 + + iex> config = %{subscription_key: "key", user_id: "user", api_key: "api", target_environment: "sandbox"} + # Using default MSISDN type + iex> MomoapiElixir.Disbursements.get_basic_user_info(config, "256784123456") + {:ok, %{"given_name" => "John", "family_name" => "Doe"}} + + # Explicitly specifying type + iex> MomoapiElixir.Disbursements.get_basic_user_info(config, "MSISDN", "256784123456") + {:ok, %{"given_name" => "John", "family_name" => "Doe"}} + + # Using email + iex> MomoapiElixir.Disbursements.get_basic_user_info(config, "EMAIL", "user@example.com") + {:ok, %{"given_name" => "Jane", "family_name" => "Smith"}} + """ + @spec get_basic_user_info(config(), String.t(), String.t()) :: {:ok, map()} | {:error, term()} + @spec get_basic_user_info(config(), String.t()) :: {:ok, map()} | {:error, term()} + def get_basic_user_info(config, account_holder_id_type \\ "MSISDN", account_holder_id) do + with {:ok, token} <- Auth.get_token(:disbursements, config), + headers <- build_headers(token, config), + {:ok, response} <- @client.get("/disbursement/v1_0/accountholder/#{account_holder_id_type}/#{account_holder_id}/basicuserinfo", headers) do + handle_user_info_response(response) + end + end + + @doc """ + Validate account holder status. + + Check if an account holder is active and able to receive transactions. + + ## 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 + + iex> config = %{subscription_key: "key", user_id: "user", api_key: "api", target_environment: "sandbox"} + # Using default MSISDN type + iex> MomoapiElixir.Disbursements.validate_account_holder_status(config, "256784123456") + {:ok, %{"result" => true}} + + # Explicitly specifying type + iex> MomoapiElixir.Disbursements.validate_account_holder_status(config, "MSISDN", "256784123456") + {:ok, %{"result" => true}} + + # Using email + iex> MomoapiElixir.Disbursements.validate_account_holder_status(config, "EMAIL", "user@example.com") + {:ok, %{"result" => false}} + """ + @spec validate_account_holder_status(config(), String.t(), String.t()) :: {:ok, map()} | {:error, term()} + @spec validate_account_holder_status(config(), String.t()) :: {:ok, map()} | {:error, term()} + def validate_account_holder_status(config, account_holder_id_type \\ "MSISDN", account_holder_id) do + with {:ok, token} <- Auth.get_token(:disbursements, config), + headers <- build_headers(token, config), + {:ok, response} <- @client.get("/disbursement/v1_0/accountholder/#{account_holder_id_type}/#{account_holder_id}/active", headers) do + handle_account_status_response(response) + end + end + # Private functions defp generate_reference_id do @@ -133,6 +251,22 @@ defmodule MomoapiElixir.Disbursements do {:error, %{status_code: status_code, body: decode_body(body)}} end + defp handle_user_info_response(%{status_code: 200, body: body}) do + {:ok, decode_body(body)} + end + + defp handle_user_info_response(%{status_code: status_code, body: body}) do + {:error, %{status_code: status_code, body: decode_body(body)}} + end + + defp handle_account_status_response(%{status_code: 200, body: body}) do + {:ok, decode_body(body)} + end + + defp handle_account_status_response(%{status_code: status_code, body: body}) do + {:error, %{status_code: status_code, body: decode_body(body)}} + end + defp decode_body(""), do: %{} defp decode_body(body) when is_binary(body) do case Poison.decode(body) do diff --git a/lib/momoapi_elixir/validator.ex b/lib/momoapi_elixir/validator.ex index 026442b..46e20ef 100644 --- a/lib/momoapi_elixir/validator.ex +++ b/lib/momoapi_elixir/validator.ex @@ -40,13 +40,13 @@ defmodule MomoapiElixir.Validator do end def validate_collections(body) do - body - |> validate_required_fields(:collections) - |> validate_amount() - |> validate_currency() - |> validate_external_id() - |> validate_payer() - |> validate_messages() + [] + |> validate_required_fields(body, :collections) + |> validate_amount(body) + |> validate_currency(body) + |> validate_external_id(body) + |> validate_payer(body) + |> validate_messages(body) |> return_validation_result(body) end @@ -70,26 +70,26 @@ defmodule MomoapiElixir.Validator do end def validate_disbursements(body) do - body - |> validate_required_fields(:disbursements) - |> validate_amount() - |> validate_currency() - |> validate_external_id() - |> validate_payee() - |> validate_messages() + [] + |> validate_required_fields(body, :disbursements) + |> validate_amount(body) + |> validate_currency(body) + |> validate_external_id(body) + |> validate_payee(body) + |> validate_messages(body) |> return_validation_result(body) end # Private validation functions - defp validate_required_fields(body, :collections) do + defp validate_required_fields(errors, body, :collections) do required_fields = [:amount, :currency, :externalId, :payer] - validate_fields_present(body, required_fields, []) + validate_fields_present(body, required_fields, errors) end - defp validate_required_fields(body, :disbursements) do + defp validate_required_fields(errors, body, :disbursements) do required_fields = [:amount, :currency, :externalId, :payee] - validate_fields_present(body, required_fields, []) + validate_fields_present(body, required_fields, errors) end defp validate_fields_present(_body, [], errors), do: errors @@ -109,10 +109,8 @@ defmodule MomoapiElixir.Validator do end end - defp validate_amount(errors) when is_list(errors), do: errors - - defp validate_amount(body) do - case Map.get(body, :amount) do + defp validate_amount(errors, body) do + new_errors = case Map.get(body, :amount) do nil -> [] "" -> [] amount when is_binary(amount) -> @@ -130,12 +128,11 @@ defmodule MomoapiElixir.Validator do amount -> [%{field: :amount, message: "Amount must be a string", value: amount}] end + new_errors ++ errors end - defp validate_currency(errors) when is_list(errors), do: errors - - defp validate_currency(body) do - case Map.get(body, :currency) do + defp validate_currency(errors, body) do + new_errors = case Map.get(body, :currency) do nil -> [] "" -> [] currency when is_binary(currency) -> @@ -147,41 +144,39 @@ defmodule MomoapiElixir.Validator do currency -> [%{field: :currency, message: "Currency must be a string", value: currency}] end + new_errors ++ errors end - defp validate_external_id(errors) when is_list(errors), do: errors - - defp validate_external_id(body) do - case Map.get(body, :externalId) do + defp validate_external_id(errors, body) do + new_errors = case Map.get(body, :externalId) do nil -> [] "" -> [%{field: :externalId, message: "External ID cannot be empty", value: ""}] external_id when is_binary(external_id) -> [] external_id -> [%{field: :externalId, message: "External ID must be a string", value: external_id}] end + new_errors ++ errors end - defp validate_payer(errors) when is_list(errors), do: errors - - defp validate_payer(body) do - case Map.get(body, :payer) do + defp validate_payer(errors, body) do + new_errors = case Map.get(body, :payer) do nil -> [] payer when is_map(payer) -> validate_party(payer, :payer) payer -> [%{field: :payer, message: "Payer must be a map", value: payer}] end + new_errors ++ errors end - defp validate_payee(errors) when is_list(errors), do: errors - - defp validate_payee(body) do - case Map.get(body, :payee) do + defp validate_payee(errors, body) do + new_errors = case Map.get(body, :payee) do nil -> [] payee when is_map(payee) -> validate_party(payee, :payee) payee -> [%{field: :payee, message: "Payee must be a map", value: payee}] end + new_errors ++ errors end defp validate_party(party, party_type) do @@ -225,24 +220,24 @@ defmodule MomoapiElixir.Validator do errors end - defp validate_messages(errors) when is_list(errors), do: errors - - defp validate_messages(body) do - errors = [] + defp validate_messages(errors, body) do + new_errors = [] - errors = case Map.get(body, :payerMessage) do - nil -> errors - message when is_binary(message) and byte_size(message) <= 160 -> errors - message when is_binary(message) -> [%{field: :payerMessage, message: "Payer message cannot exceed 160 characters", value: message} | errors] - message -> [%{field: :payerMessage, message: "Payer message must be a string", value: message} | errors] + new_errors = case Map.get(body, :payerMessage) do + nil -> new_errors + message when is_binary(message) and byte_size(message) <= 160 -> new_errors + message when is_binary(message) -> [%{field: :payerMessage, message: "Payer message cannot exceed 160 characters", value: message} | new_errors] + message -> [%{field: :payerMessage, message: "Payer message must be a string", value: message} | new_errors] end - case Map.get(body, :payeeNote) do - nil -> errors - note when is_binary(note) and byte_size(note) <= 160 -> errors - note when is_binary(note) -> [%{field: :payeeNote, message: "Payee note cannot exceed 160 characters", value: note} | errors] - note -> [%{field: :payeeNote, message: "Payee note must be a string", value: note} | errors] + new_errors = case Map.get(body, :payeeNote) do + nil -> new_errors + note when is_binary(note) and byte_size(note) <= 160 -> new_errors + note when is_binary(note) -> [%{field: :payeeNote, message: "Payee note cannot exceed 160 characters", value: note} | new_errors] + note -> [%{field: :payeeNote, message: "Payee note must be a string", value: note} | new_errors] end + + new_errors ++ errors end defp return_validation_result([], body), do: {:ok, body}