ExUssd

Actions Status Hex.pm Hex.pm Coverage 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.0"}
]
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

Menu

ExUssd supports Ussd customizations through Menu struct via the render function

ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
end
)
{:ok, "CON Welcome"}
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 1
{:ok, "CON selected product a\n0:BACK"}
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
|> Map.put(:should_close, true)
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
|> Map.put(:should_close, true)
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 1
{:ok, "END selected product a"}
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:default_error_message, "Invalid selection, try again\n")
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 11
{:ok, "CON Invalid selection, try again\nWelcome\n1:Product A\n2:Product B"}
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:display_style, ")")
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1)Product A\n2)Product B"}
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:split, 2)
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end),
Menu.render(
name: "Product C",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product c")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B\n98:MORE"}
# simulate 98
{:ok, "CON Welcome\n3:Product C\n0:BACK"}
iex> ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:title, "Enter Pin Number")
|> Map.put(:handle, true)
|> Map.put(:validation_menu, Menu.render(
name: "",
handler: fn menu, api_parameters, should_handle ->
case should_handle do
true ->
case api_parameters.text == "5342" do
true ->
menu
|> Map.put(:title, "success, thank you.")
|> Map.put(:success, true)
|> Map.put(:should_close, true)
_->
menu |> Map.put(:error, "Wrong pin number\n")
end
false -> menu
end
end)
)
end
)
{:ok, "CON Enter Pin Number"}
## simulate 5342
{:ok, "END success, thank you."}
## simulate 5555
{:ok, "CON Wrong pin number\nEnter Pin Number"}

Render Menu

ExUssd to render Menu struct for different ussd providers. ExUssd provides goto function that starts and manages the ussd sessions. The goto function receives the following parameters.

iex> menu = ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
end
)
iex> 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"}