lib/knet.ex

defmodule Knet do
  @moduledoc """
  Documentation for `Knet`.
  """

  @doc """
  Takes a map of payment details and returns a KNET payment link.
  The `payment_details` map should contain the following required keys:
  - `knet_username`
  - `knet_password`
  - `knet_key`
  - `amount`
  - `response_url`
  - `error_url`
  - `track_id`

  The following keys are optional:
  - `knet_url` (defaults to https://kpay.com.kw/kpg/PaymentHTTP.htm)
  - `currency_code` (defaults to 414)
  - `lang`. Could be ENG or AR (defaults to ENG)
  - `udf1` to `udf5` (user defined fields). These are optional and will be returned back to you in the response.

  """
  @spec get_payment_link(map()) :: String.t()
  def get_payment_link(payment_details) do
    payment_details
    |> get_payment_params()
    |> encrypt_params(payment_details)
    |> get_payment_url(payment_details)
  end

  defp get_payment_params(payment_details) do
    %{
      "id" => Map.get(payment_details, "knet_username"),
      "password" => Map.get(payment_details, "knet_password"),
      "amt" => Map.get(payment_details, "amount"),
      "currencycode" => Map.get(payment_details, "currency_code", "414"),
      "action" => "1",
      "langid" => Map.get(payment_details, "lang", "ENG"),
      "responseURL" => Map.get(payment_details, "response_url"),
      "errorURL" => Map.get(payment_details, "error_url"),
      "trackid" => Map.get(payment_details, "track_id"),
      "udf1" => Map.get(payment_details, "udf1"),
      "udf2" => Map.get(payment_details, "udf2"),
      "udf3" => Map.get(payment_details, "udf3"),
      "udf4" => Map.get(payment_details, "udf4"),
      "udf5" => Map.get(payment_details, "udf5")
    }
  end

  defp encrypt_params(params, payment_details) do
    params =
      params
      |> Enum.map(fn {k, v} -> "#{k}=#{v}" end)
      |> Enum.join("&")

    block_size = 16
    pad = block_size - rem(String.length(params), block_size)
    padded_params = params <> String.duplicate(List.to_string([pad]), pad)

    api_key = Map.get(payment_details, "knet_key")

    :crypto.crypto_one_time(:aes_128_cbc, api_key, api_key, padded_params, true)
    |> Base.encode16(case: :lower)
  end

  defp get_payment_url(encrypted_data, payment_details) do
    knet_url = Map.get(payment_details, "knet_url", "https://kpay.com.kw/kpg/PaymentHTTP.htm")
    knet_username = Map.get(payment_details, "knet_username")
    response_url = Map.get(payment_details, "response_url")
    error_url = Map.get(payment_details, "error_url")

    "#{knet_url}?param=paymentInit&trandata=#{encrypted_data}&tranportalId=#{knet_username}&responseURL=#{response_url}&errorURL=#{error_url}"
  end
end