API How To Build a Spring Boot USSD Application

How To Build a Spring Boot USSD Application

-

- Advertisment -

Introduction

According to Wikipedia USSD (Unstructured Supplementary Service Data) is a communications protocol used by GSM cellular telephones to communicate with the mobile network operator’s computers. USSD can be used for WAP browsing, prepaid callback service, mobile-money services, location-based content services, menu-based information services, and as part of configuring the phone on the network.

The Basic USSD Flow usually take the approach below;

  • A user dials the provided USSD code. (This is done automatically for services that exist readily within the SIM Tool Kit)
  • The request is forwarded to the MNO(Mobile Network Operator).
  • The MNO routes the request through a gateway to the machine hosting the web application.
  • The application processes the requests it receives and sends back a valid response.
  • The feedback is processed by the MNO and sent back to the mobile phone.

Who is this article for?
This article is aimed towards Java Developers who would like a more robust variation of USSD implementation that readily utilizes the large ecosystem provided by spring boot.

What you’ll need

  • Some knowledge of Java and Spring boot
  • Africa’s talking account
  • Some docker Knowledge (minimal)
  • AWS account(If you decide to host on AWS)

The idea behind this article is to act as an extension of the current Africa’s Talking documentation on USSD java implementation, to talk about handling sessions better as well caching said sessions for faster access to improve performance of the USSD application at scale, and finally deploying this and hooking into Africa’s Talking.

Getting Started

USSD is session driven, What does this mean ?, well whenever you dial *XXX# a session with a unique Id is created and maintained to allow interaction of the end device and the remote API. The session typically lasts 180s for most MNOs in Kenya, it may be different for MNOs in other countries. As the developer, you’ll need to keep track of the session and be cautious of the time limit to ensure that menus, as well as responses, are served faster and better to ensure a seamless user experience.
The MNO also lets you control the session, this is done by attaching CON or END at the start of every response

CON: Means an intermediate menu or that the session is CONtinuing and hence will require user input
END: This means the final menu and will trigger session termination i.e session is ENDing.

The above is properly documented on Africas Talking's dev docs

To get us started head over to Spring Initializr and start your project with the following dependencies

  • Spring Web
  • Lombok (To get rid of boilerplate code by providing encapsulations support)
  • Spring Data Redis

The src folder structure is as below

main
├── java
│   └── com
│       └── decoded
│           └── ussd
│               ├── configs
│               ├── controllers
│               ├── data
│               ├── enums
│               ├── repositories
│               ├── services
│               └── UssdApplication.java

To start us off let’s create a controller .i.e. IndexController.java

Session Handling

This implementation is tightly coupled to Africa’s talking USSD provision, hence session management depends heavily on the sessionId Param provided to the callback registered for the serviceCode. We will need to store the sessionId to enable us to track and destroy the session accordingly from the API’s end. Caching becomes extremely important at this point as it will allow faster storage and retrieval of the active sessions.

The session can be stored in the DB however Queries to the Database are expensive and hence in-memory caching becomes an obvious strategy to implement

The cache layer for this particular tutorial will be implemented on Redis. Let’s start by configuring everything needed by Redis to handle the session from the application’s end.

  1. Create @redis labs account and deploy a free redis instance
  2. Setting Up application configurations
    @Configuration
    @ConfigurationProperties(prefix = "decoded")
    @Data
    public class ApplicationConfiguration {
    
        private CacheConfigurationProperties cache;
    
        @Getter(value = AccessLevel.PUBLIC)
        private class CacheConfigurationProperties {
            private Integer port;
            private String host;
            private String password;
            private String defaultTtl;
        }
    }
    application.yaml for the corresponding configurations will be as below
    decoded:
        cache:
            port: #redis-port
            host: #redis-host
            password: #provided password
            default-ttl: 180
    
  3. Redis configurations
    We’ll use Lettuce which comes readily integrated within spring data redis to configure the connection Factory
    configs/RedisConfiguration.java

     4. Session DtoThis is how we’ll store the session data in the cache layer.

     5. Session DAO

public interface UssdSessionRepository extends CrudRepository<UssdSession, String> { }

    6. Session Management Service

Dynamic Menus

It’s important to have the ability to load menus dynamically at runtime, the reason being that making changes to dynamically served data is much easier than on statically defined menus may need recompilation and redeployment of the app.

Menu Structure

  1. Menu Levels
    For every request to the API, the server responds with a message, sending the user deeper into the implemented sequence hence the term levels. It’s important to keep track of where the user is currently at, to know which Menu or response to serve.
  2. Menu Options
    As the name suggests these are just options provided to the user within the Menu to allow the user to know how to respond on the USSD interface.

To start us off with the Menus we’ll define the structure of the Menu Levels and Options

MenuLevels Definition

Menu Options Definition

Actions will help us know how to route the options selected by the user

Option Actions Definition

public enum MenuOptionAction {

    PROCESS_ACC_BALANCE("PROCESS_ACC_BALANCE"),
    PROCESS_ACC_PHONE_NUMBER("PROCESS_ACC_PHONE_NUMBER"),
    PROCESS_ACC_NUMBER("PROCESS_ACC_NUMBER");

    private String action;

    MenuOptionAction(String action) {
        this.action = action;
    }

    @JsonValue
    private String getAction() {
        return action;
    }
}

The Actual Menus

For this tutorial, we’ll store the menus in a JSON file within the resource folder.

Note that replaceable vars are denoted as ${XXXXXX}

Fetching the Menu from the store.

Since the Menus are stored in the resource folder all we need to do is directly read the data into an InputStream that will be converted to String and Map that to the correct Type.

Create a MenuService within the service folder and add the code below.

Menu Routing

Let’s now put everything together by creating the UssdRoutingService within the services folder

  • Fetching the next Menu level to be displayed

  • Get the Menu Level Properties directly from the Menu Service

  • Processing the provided input, determining the response type, and updating the session accordingly

  • Checking the response action and switching through the available Action types.
    This is where API calls to an external service can be made to trigger certain events or pull data from said API.

  • Replace variables on the response text with the fetched data using the StringSubstitutor class. for this step, you need to first add the org.apache.commons, commons-text, and commons-lang3 dependencies
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.9</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>
    /**
     * 
     * @param variablesMap
     * @param response
     * @return
     */
    public String replaceVariable(Map<String, String> variablesMap, 
    String response) {
        StringSubstitutor sub = new StringSubstitutor(variablesMap);
        return sub.replace(response);
    }
  • Updating Session Data with the newly set level as well as the previous session, will allow for complex functions such as allowing movement back and forth through the available menus
    /**
     * 
     * @param session
     * @param menuLevel
     * @return
     */
    public UssdSession updateSessionMenuLevel(UssdSession session,  
    String menuLevel) {
        session.setPreviousMenuLevel(session.getCurrentMenuLevel());
        session.setCurrentMenuLevel(menuLevel);
        return sessionService.update(session);
    }
  • This is processed on every request to track session updating or creating the session based on the details provided.
    /**
     * Check, Set or update the existing session with the provided 
       Session Id
     * 
     * @param sessionId
     * @param serviceCode
     * @param phoneNumber
     * @param text
     * @return
     */
    public UssdSession checkAndSetSession(String sessionId, String 
    serviceCode, String phoneNumber, String text) {
        UssdSession session = sessionService.get(sessionId);

        if (session != null) {
            session.setText(text);
            return sessionService.update(session);
        }

        session = new UssdSession();
        session.setCurrentMenuLevel("1");
        session.setPreviousMenuLevel("1");
        session.setId(sessionId);
        session.setPhoneNumber(phoneNumber);
        session.setServiceCode(serviceCode);
        session.setText(text);

        return sessionService.createUssdSession(session);
    }

Testing

To test your USSD application locally with ease you may need an emulator. Find this custom emulator hosted on GitHub you may need to do a few modifications to support your implementation.

Deployment of the USSD platform

For this particular implementation, Elastic Beanstalk was chosen for deployment. find more on this refer to @aws beanstalk. For More information on how to dockerize Spring boot applications please refer to this article.

below are a few snippets of the docker file as well as Dockerrun.aws for elastic beanstalk

# Start with a base image containing Java runtime
FROM maven:3.6.3-jdk-8-slim AS build

COPY src /usr/src/app/src

COPY pom.xml /usr/src/app

RUN mvn -f /usr/src/app/pom.xml clean package


FROM openjdk:8-jdk-alpine

# Make port 8080 available to the world outside this container
EXPOSE 8080 

COPY --from=build /usr/src/app/target/ussd-0.0.1-SNAPSHOT.jar /usr/app/target/ussd-0.0.1-SNAPSHOT.jar

# Run the jar file 
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/usr/app/target/ussd-0.0.1-SNAPSHOT.jar"]
{
    "AWSEBDockerrunVersion": "1",
    "Image": {
        "Update": "true"
    },
    "Ports": [
        {
            "ContainerPort": "8080"
        }
    ],
    "Logging": "/var/log/",
    "Volumes": []
}

Once we have deployed the application the next step is to link the particular deployment to a service code and allow phone users to access our application on USSD.

Steps to linking service Code are as below;

  • Head over to Africa’s talking and create an account or login if you already have one.
  • Switch to sandbox
  • Open the USSD menu and create a channel as shown below;
    Building a Spring Boot USSD Application
  • Link Channel callback to the URL of the hosted app
    Building a Spring Boot USSD Application

    Building a Spring Boot USSD Application

  • Test App Using the simulator

Conclusion

You should now have a basic idea of how USSD applications work and how to implement the same on spring-boot. The information and demonstration provided in this guide is not the only way to handle USSD applications in production, There are definitely a multitude of other battle-tested implementations, this is mainly based on implementations I’ve seen in some of the projects I have been involved in.

This @repo has the full implementation of the content discussed within the tutorial, A docker-compose file is readily available to allow quick deployment and testing of the implementation. Also if you need a ussd emulator you can host locally @this works fine, be sure to modify the request type from GET to POST as our project has been implemented with a POST hook in mind.

That’s all. Feel free to raise any issues on Github or start a discussion.

Good Luck & Happy Coding

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Posts

How To Use Gmail as an Email Client for your Django Web App

Introduction The project looks into how you can use Gmail as an Email Client for your website, this can be...

How To Build A Subscription App Using Django, HTML, CSS, Javascript and Stripe API.

What we are building We are going to build a subscription-based app where users have to create an account, pay...

One Time Password (O.T.P.) Implementation Using Africa’s Talking Short Code API in Django

Introduction This article explores the implementation of One Time Passwords (O.T.P.) when users log into a web app. A randomly...

Google Map API Setup & Geo-Coding with R

A step by step process from Google Map API creation, integration with R and visualization of spatial statistical data
- Advertisement -

Building Application using Node.js HTTPS Module and Africa’s Talking SMS API

Introduction For a long time, HTTP (Hypertext Transfer Protocol) was responsible for communication between a client application and a server...

Building an Exam Enrollment and Grade Notification System using Africa’s Talking SMS Shortcode API in Django

Introduction This article explores the application of SMS shortcodes to create transparency and improve the quality of education in learning...
- Advertisement -

You might also likeRELATED
Recommended to you