banner



How To Set Up Ussd Gateway

DUB PyPI Build Status

Setting Up a USSD Service for MicroFinance Institutions

A pace-by-step guide

  • Setting up the logic for USSD is easy with the Africa'southward Talking API. This is a guide to how to use the code provided on this repository to create a USSD that allows users to become registered then access a carte of the post-obit services:
USSD APP Features
Request to become a call from back up
Deposit Money to user's account
Withdraw money from users business relationship
Ship money from users account to some other
Repay loan
Buy Airtime

INSTALLATION AND GUIDE

  1. clone/download the projection into the directory of your choice

  2. Create a .env file on your root directory

Be sure to substitute the example variables with your credentials

Docker

  • To install using docker, run

                                          $ docker-compose up -b 8080:8000                                  

    This will get-go your awarding on port 8080

Using a virtual environs

  1. Create a virtual surround

                                          $ python3 -m venv venv    $ . venv/bin/activate                                  
  2. Install the project'south dependancies

                                          $ pip install requirements.txt                                  
  3. Configure your flask path

                                          $ consign FLASK_APP=manage.py                                  
  4. Initialise your database

  5. Launch application

  6. Head to https://localhost:5000

  • You need to gear up up on the sandbox and create a USSD channel that you will utilise to test past dialing into it via our simulator.

  • Assuming that you are doing your development on a localhost, yous take to expose your application living in the webroot of your localhost to the internet via a tunneling application like Ngrok. Otherwise, if your server has a public IP, you are good to go! Your URL callback for this demo volition get: http:///MfUSSD/microfinanceUSSD.php

  • This application has been adult on an Ubuntu xvi.04LTS and lives in the web root at /var/www/html/MfUSSD. Courtesy of Ngrok, the publicly accessible url is: https://49af2317.ngrok.io (instead of http://localhost) which is referenced in the lawmaking as well. (Create your own which volition be unlike.)

  • The webhook or callback to this application therefore becomes: https://49af2317.ngrok.io/api/v1.i/ussd/callback. To allow the application to talk to the Africa's Talking USSD gateway, this callback URL is placed in the dashboard, under ussd callbacks here.

  • Finally, this application works with a connexion to an sqlite database. This is the default database shipped with python, however its recomended switching to a proper database when deploying the application. As well create a session_levels tabular array and a users tabular array. These details are configured in the models.py and this is required in the master awarding script app/apiv2/views.py

Field Blazon Aught Primal Default Extra
id int(6) Aye NULL
name varchar(thirty) Yes NULL
phonenumber varchar(xx) YES NULL
city varchar(30) Yep NULL
validation varchar(thirty) YES Nix
reg_date timestamp NO CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
  • The awarding uses redis for session management. User sessions are stored equally fundamental value pairs in redis.

Features on the Services Listing

This USSD awarding has the post-obit user journey.

  • The user dials the ussd lawmaking - something similar *384*303#

  • The application checks if the user is registered or not. If the user is registered, the services menu is served which allows the user to: receive SMS, receive a call with an IVR menu.

  • In example the user is not registered, the awarding prompts the user for their proper noun and city (with validations), earlier successfully serving the services menu.

Code walkthrough

This documentation is for the USSD application that lives in https://49af2317.ngrok.io/api/v1.ane/ussd/callback.

  • The applications entrypoint is at app/ussd/views.py
              #one. This code but runs after a post request from AT              @ussd.road('/ussd/callback',                methods                =['POST'])              def              ussd_callback():              """                              Handles postal service telephone call back from AT                                            :return:                              """            

Import all the necessary scripts to run this application

              # ii. Import all neccesary modules              from              flask              import              1000,              make_response              from              app.models              import              AnonymousUser              from              .              import              ussd              from              .airtime              import              Airtime              from              .deposit              import              Deposit              from              .dwelling house              import              LowerLevelMenu              from              .register              import              RegistrationMenu              from              .withdraw              import              WithDrawal            

Receive the HTTP POST from AT. app/ussd/decorators.py

Nosotros will utilize a decorator that hooks on to the awarding request, to query and initialize session metadata stored in redis.

              # 3. go data from ATs post payload              session_id              =              asking.values.get("sessionId",              None)              phone_number              =              request.values.get("phoneNumber",              None)              text              =              request.values.get("text",              "default")

The AT USSD gateway keeps chaining the user response. We desire to catch the latest input from a string like onei2

              text_array              =              text.split("*")              user_response              =              text_array[len(text_array)              -              1]

Interactions with the user can be managed using the received sessionId and a level management procedure that your application implements every bit follows.

  • The USSD session has a set time limit(xx-180 secs based on provider) under which the sessionId does not change. Using this sessionId, it is easy to navigate your user across the USSD menus by graduating their level(menu step) so that y'all dont serve them the aforementioned bill of fare or lose track of where the user is.
  • Query redis for the user's session level using the sessionID as the key. If this exists, the user is returning and they therefore have a stored level. Take hold of that level and serve that user the correct menu. Otherwise, serve the user the home menu.
  • The session metadata is stored in flask'southward thou global variable to allow for access within the current request context.
              # 4. Query session metadata from redis or initialize a new session for this user if the session does non exist              # get session              session              =              redis.become(session_id)              if              session              is              None:              session              =              {"level":              0,              "session_id":              session_id}              redis.gear up(session_id,              json.dumps(session))              else:              session              =              json.loads(session.decode())              # add user, response and session to the request variable g              g.user_response              =              text_array[len(text_array)              -              1]              g.session              =              session              g.current_user              =              user              thou.phone_number              =              phone_number              chiliad.session_id              =              session_id              return              func(*              args,              **              kwargs)

Before serving the menu, check if the incoming phone number request belongs to a registered user(sort of a login). If they are registered, they can access the bill of fare, otherwise, they should first register.

app/ussd/views.py

              # 5. Check if the user is in the db              session_id              =              g.session_id              user              =              g.current_user              session              =              g.session              user_response              =              grand.user_response              if              isinstance(user,              AnonymousUser):              # register user              menu              =              RegistrationMenu(session_id              =              session_id,              session              =              session,              phone_number              =              1000.phone_number,              user_response              =              user_response,              user              =              user)              return              card.execute()

If the user is available and all their mandatory fields are complete, and so the awarding switches between their responses to figure out which bill of fare to serve. The get-go menu is normally a effect of receiving a blank text -- the user just dialed in.

              # 7. Serve the Services Menu                            if              level              <              2:              menu              =              LowerLevelMenu(session_id              =              session_id,              session              =              session,              phone_number              =              m.phone_number,              user_response              =              user_response,              user              =              user)              render              menu.execute()              if              level              >=              50:              menu              =              Deposit(session_id              =              session_id,              session              =              session,              phone_number              =              g.phone_number,              user_response              =              user_response,              user              =              user,              level              =              level)              render              menu.execute()              if              level              >=              40:              menu              =              WithDrawal(session_id              =              session_id,              session              =              session,              phone_number              =              g.phone_number,              user_response              =              user_response,              user              =              user,              level              =              level)              return              menu.execute()              if              level              >=              10:              menu              =              Airtime(session_id              =              session_id,              session              =              session,              phone_number              =              g.phone_number,              user_response              =              user_response,              user              =              user,              level              =              level)              return              card.execute()            

If the user is not registered, we employ the users level - purely to take the user through the registration process. We also enclose the logic in a condition that prevents the user from sending empty responses.

              if              isinstance(user,              AnonymousUser):              # register user              menu              =              RegistrationMenu(session_id              =              session_id,              session              =              session,              phone_number              =              g.phone_number,              user_response              =              user_response,              user              =              user)              return              menu.execute()            

Complexities of Voice.

  • The phonation service included in this script requires a few juggling acts and probably requires a brusque review of its ain. When the user requests a to go a phone call, the following happens. a) The script at https://49af2317.ngrok.io/api/v1.i/ussd/callback requests the telephone call() method through the Africa'southward Talking Vox Gateway, passing the number to be called and the caller/dialer Id. The call is made and it comes into the users phone. When they respond isActive becomes ane.
              def              please_call(self):              # call the user and bridge to a sales person              menu_text              =              "END Please wait while we place your telephone call.\due north"              # make a telephone call              caller              =              current_app.config["AT_NUMBER"]              to              =              self.user.phone_number              # create a new instance of our awesome gateway              gateway              =              AfricasTalkingGateway(              current_app.config["AT_USERNAME"],              current_app.config["AT_APIKEY"])              try:              gateway.telephone call(caller,              to)              except              AfricasTalkingGateway              as              e:              print              "Encountered an error when calling: {}".format(str(east))              # impress the response on to the page so that our gateway can read it              return              answer(menu_text)              instance              "ii":

b) Every bit a outcome, Africa'southward Talking gateway check the callback for the voice number in this example +254703554404. c) The callback is a route on our views.py file whose URL is: https://49af2317.ngrok.io/api/v1.i/vocalization/callback d) The instructions are to reply with a text to speech message for the user to enter dtmf digits.

              @ussd.route('/phonation/callback',                methods                =['POST'])              def              voice_callback():              """                              voice_callback from AT'southward gateway is handled here                                            """              sessionId              =              request.get('sessionId')              isActive              =              request.get('isActive')              if              isActive              ==              "ane":              callerNumber              =              asking.get('callerNumber')              # Become values from the AT'due south Mail request              session_id              =              asking.values.get("sessionId",              None)              isActive              =              request.values.get('isActive')              serviceCode              =              request.values.get("serviceCode",              None)              text              =              request.values.get("text",              "default")              text_array              =              text.carve up("*")              user_response              =              text_array[len(text_array)              -              1]              # Etch the response              menu_text              =              '<?xml version="1.0" encoding="UTF-8"?>'              menu_text              +=              '<Response>'              menu_text              +=              '<GetDigits timeout="30" finishOnKey="#" callbackUrl="https://49af2317.ngrok.io/api/v1.ane/phonation/callback">'              menu_text              +=              '<Say>"Thank you for calling. Printing 0 to talk to sales, 1 to talk to support or 2 to hear this message again."</Say>'              menu_text              +=              '</GetDigits>'              menu_text              +=              '<Say>"Thank you for calling. Good farewell!"</Say>'              menu_text              +=              '</Response>'              # Impress the response onto the folio so that our gateway can read it              return              answer(menu_text)              else:              # Read in call details (duration, toll). This flag is set once the call is completed.              # Note that the gateway does not expect a response in thie example              duration              =              asking.get('durationInSeconds')              currencyCode              =              request.go('currencyCode')              amount              =              request.go('amount')              # Yous can so store this information in the database for your records                          

eastward) When the user enters the digit - in this case 0, one or 2, this digit is submitted to another route also in our views.py file which lives at https://49af2317.ngrok.io/api/v1.1/voice/menu and which switches betwixt the various dtmf digits to make an outgoing call to the right recipient, who will exist bridged to speak to the person currently listening to music on concord. We specify this music with the ringtone flag as follows: ringbackTone="url_to/static/media/SautiFinaleMoney.mp3"

              @ussd.route('/voice/bill of fare')              def              voice_menu():              """                              When the user enters the digit - in this case 0, 1 or 2, this route                                            switches between the various dtmf digits to                                            make an approachable call to the correct recipient, who volition be                                            bridged to speak to the person currently listening to music on concur.                                            We specify this music with the ringtone flag equally follows:                                            ringbackTone="url_to/static/media/SautiFinaleMoney.mp3"                              """              # one. Receive Postal service from AT              isActive              =              asking.get('isActive')              callerNumber              =              request.get('callerNumber')              dtmfDigits              =              request.get('dtmfDigits')              sessionId              =              request.become('sessionId')              # Bank check if isActive=1 to act on the call or isActive=='0' to store the              # result              if              (isActive              ==              '1'):              # 2a. Switch through the DTMFDigits              if              (dtmfDigits              ==              "0"):              # Etch response - talk to sales-              response              =              '<?xml version="1.0" encoding="UTF-8"?>'              response              +=              '<Response>'              response              +=              '<Say>Please hold while we connect you to Sales.</Say>'              response              +=              '<Dial phoneNumbers="880.welovenerds@ke.sip.africastalking.com" ringbackTone="{}"/>'.format(url_for('media',              path              =              'SautiFinaleMoney.mp3'))              response              +=              '</Response>'              # Print the response onto the folio so that our gateway can read it              render              answer(response)              elif              (dtmfDigits              ==              "one"):              # 2c. Compose response - talk to support-              response              =              '<?xml version="one.0" encoding="UTF-8"?>'              response              +=              '<Response>'              response              +=              '<Say>Please hold while we connect you to Support.</Say>'              response              +=              '<Dial phoneNumbers="880.welovenerds@ke.sip.africastalking.com" ringbackTone="{}"/>'.format(url_for('media',              path              =              'SautiFinaleMoney.mp3'))              response              +=              '</Response>'              # Impress the response onto the page so that our gateway can read information technology              render              reply(response)              elif              (dtmfDigits              ==              "2"):              # 2nd. Redirect to the main IVR-              response              =              '<?xml version="1.0" encoding="UTF-8"?>'              response              +=              '<Response>'              response              +=              '<Redirect>{}</Redirect>'.format(url_for('voice_callback'))              response              +=              '</Response>'              # Print the response onto the page so that our gateway can read information technology              return              respond(response)              else:              # 2e. By default talk to back up              response              =              '<?xml version="1.0" encoding="UTF-eight"?>'              response              +=              '<Response>'              response              +=              '<Say>Please agree while we connect you lot to Support.</Say>'              response              +=              '<Dial phoneNumbers="880.welovenerds@ke.sip.africastalking.com" ringbackTone="{}"/>'.format(url_for('media',              path              =              'SautiFinaleMoney.mp3'))              response              +=              '</Response>'              # Print the response onto the page and then that our gateway can read it              return              respond(response)              else:              # three. Store the data from the Mail              durationInSeconds              =              request.get('durationInSeconds')              direction              =              request.get('management')              amount              =              request.get('amount')              callerNumber              =              request.get('callerNumber')              destinationNumber              =              asking.get('destinationNumber')              sessionId              =              asking.get('sessionId')              callStartTime              =              request.become('callStartTime')              isActive              =              request.get('isActive')              currencyCode              =              request.get('currencyCode')              status              =              request.get('status')              # 3a. Store the data, write your SQL statements here-            

When the agent/person picks upward, the conversation can go along.

  • That is basically our awarding! Happy coding!

How To Set Up Ussd Gateway,

Source: https://github.com/Piusdan/USSD-Python-Demo

Posted by: carneswournig.blogspot.com

0 Response to "How To Set Up Ussd Gateway"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel