ExUssd

Actions StatusHex.pmHex.pmCoverage Status

Introduction

ExUssd lets you create simple, flexible, and customizable USSD interface. Under the hood ExUssd uses Elixir Registry to create and route individual USSD session.

Sections

Installation

If available in Hex, the package can be installed by adding ex_ussd to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_ussd, "~> 0.1.2"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_ussd.

Providers

ExUssd currently supports

Africastalking API

Infobip API

Configuration

To Use One of the above gateway providers for your project Create a copy of config/dev.exs or config/prod.exs from config/dev.sample.exs Use the gateway key to set the ussd vendor.

AfricasTalking

Add below config to dev.exs / prod.exs files

config :ex_ussd, :gateway, AfricasTalking

Infobip

Add below config to dev.exs / prod.exs files

config :ex_ussd, :gateway, Infobip

Documentation

ExUssd supports Ussd customizations through Menu struct via the render function

ussd title only

defmodule MyHomeHandler do
   @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
     menu |> Map.put(:title, "Welcome")
   end
end
menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
ExUssd.goto(
  internal_routing: %{text: "", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "" }
  )
{:ok, "CON Welcome"}

ussd menu_list

defmodule ProductAHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
    menu |> Map.put(:title, "selected product a")
  end
end

defmodule ProductBHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
    menu |> Map.put(:title, "selected product b")
  end
end

defmodule ProductCHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
    menu |> Map.put(:title, "selected product c")
  end
end

defmodule MyHomeHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
    menu
    |> Map.put(:title, "Welcome")
    |> Map.put(
      :menu_list,
      [
        ExUssd.Menu.render(name: "Product A", handler: ProductAHandler),
        ExUssd.Menu.render(name: "Product B", handler: ProductBHandler),
        ExUssd.Menu.render(name: "Product C", handler: ProductCHandler)
      ]
    )
  end
end
menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
ExUssd.goto(
  internal_routing: %{text: "", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "" }
)

{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 1
menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
ExUssd.goto(
  internal_routing: %{text: "1", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "1" }
  )
{:ok, "CON selected product a\n0:BACK"}

ussd validation menu

defmodule PinValidateHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, api_parameters) do
    case api_parameters.text == "5555" do
      true ->
        menu
        |> Map.put(:title, "success, thank you.")
        |> Map.put(:should_close, true)

      _ ->
        menu |> Map.put(:error, "Wrong pin number\n")
    end
  end
end

defmodule MyHomeHandler do
  @behaviour ExUssd.Handler
  def handle_menu(menu, _api_parameters) do
    menu
    |> Map.put(:title, "Enter your pin number")
    |> Map.put(:validation_menu, ExUssd.Menu.render(name: "", handler: PinValidateHandler))
  end
end

menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)

ExUssd.goto(
  internal_routing: %{text: "", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "" }
)
{:ok, "CON Enter your pin number"}

# simulate wrong pin
menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
ExUssd.goto(
  internal_routing: %{text: "3339", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "3339" }
)
{:ok, "CON Wrong pin number\nEnter your pin number"}

# simulate correct pin
menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
ExUssd.goto(
  internal_routing: %{text: "5555", session_id: "session_01", service_code: "*544#"},
  menu: menu,
  api_parameters: %{"sessionId" => "session_01", "phoneNumber" => "254722000000", "networkCode" => "Safaricom", "serviceCode" => "*544#", "text" => "5555" }
)
{:ok, "END success, thank you."}

receive data as props

defmodule MyHomeHandler do
  @behaviour ExUssd.Handler
 def handle_menu(menu, api_parameters) do
  %{language: language} = menu.data
  case language do
     "Swahili" -> menu |> Map.put(:title, "Karibu")
     _-> menu |> Map.put(:title, "Welcome")
    end
   end
end

data = %{language: "Swahili"}
ExUssd.Menu.render(name: "Home", data: data, handler: MyHomeHandler)

Testing

To test your USSD menu, ExUssd provides a simulate function that helps you test menu rendering and logic implemented by mimicking USSD gateway callback.

  iex> defmodule MyHomeHandler do
        @behaviour ExUssd.Handler
        def handle_menu(menu, _api_parameters) do
          menu |> Map.put(:title, "Welcome")
        end
      end
  iex> menu = ExUssd.Menu.render(name: "Home", handler: MyHomeHandler)
  iex> ExUssd.simulate(menu: menu, text: "")

  {:ok, %{menu_string: "Welcome", should_close: false}}

Examples

ussd examples using ExUssd can be found here https://github.com/lenileiro/ussd_examples

Contribution

If you'd like to contribute, start by searching through the issues and pull requests to see whether someone else has raised a similar idea or question. If you don't see your idea listed, Open an issue.

Check the Contribution guide on how to contribute.

Contributors

Auto-populated from: contributors-img

<img src="https://contributors-img.firebaseapp.com/image?repo=beamkenya/ex_ussd" />

Licence

ExPesa is released under MIT License

license