Africa's Talking provides a wide range of products for developers to use in their business solutions. These products are SMS, USSD, Voice, Payments, IoT and Airtime. You can read more about Africa's Talking from their website https://africastalking.com.

The Africa's Talking payment API is divided into mobile(Checkout, C2b,B2c,B2b), bank and card. For this article, we will use the mobile checkout API for android apps using Kotlin.

Requirements

  1. Android Studio
  2. Internet Connection
  3. Testing Device
  4. Africa's Talking Account

Step 1: Creating a Sandbox App

Login to your Africa's talking account and click Go to Sandbox App. If you don't have one, please create one using this link.

You will be directed to a dashboard. Select Payments on the left sidebar then select Create Product . Enter the product name, for this tutorial we will use Android Payment.

One final step is adding a callback URL to the payment product. The callback URL will be used to send payment notifications from Africa's Talking APIs.

On the popup that appears put your callback URL and leave the B2C validation empty, click Save.

Hooray! You are done creating a payment product in Africa's Talking. Please take note of the Product name for our case Android Payment

The next step is getting an API key. It helps Africa's Talking authenticate requests that you make to their APIs. On the dashboard click Settings, you will be prompted to enter your account password. An API key will be generated for you. Save the API key as we are going to use it later.

Step 2: Let's get coding now

Open Android Studio and Create a new project with an empty activity. Note: The language should be Kotlin.

Open the AndroidManifest.xml file and add the Internet permission.

 <uses-permission android:name="android.permission.INTERNET" />

We need to add Africa's Talking library to our project. We are going to use Gradle for our case here.

  1. Open build.gradle(Project:project_name) file and add the following under repositories. This is because we will get the library from maven
        maven {
            url  "http://dl.bintray.com/africastalking/java"
        }

The final code should look like this

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url  "http://dl.bintray.com/africastalking/java"
        }
    }
}

2. Open the build.gradle(Module:app) file. We are going to add the library name and version. Add the following line of code under dependencies

implementation 'com.africastalking:core:3.4.2'

Sync the project so that the IDE works properly. Note: This is done after doing any modifications to the build.gradle files.

3. We are going to design the User Interface. We will use the ConstraintLayout, CardView, TextView, Button and an EditText. The final UI should look like this. (The code is below this image)

 Final UI screenshot
Final UI screenshot
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="13dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Africa's Talking"
        android:textAlignment="center"
        android:textSize="30sp"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="190dp" />

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        app:cardBackgroundColor="#ffffff"
        app:cardCornerRadius="3dp"
        app:cardElevation="3dp"
        app:contentPadding="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/phoneInput"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Phone number eg.+25471xxxxxxx" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/amountInput"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Amount"
                    android:inputType="number" />
            </com.google.android.material.textfield.TextInputLayout>

            <Button
                android:id="@+id/buttonPay"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Pay" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

4. Let's get to Kotlin code now.

4.1 Create a Kotlin class called Secrets. We are going to put the API Key, username and product name as variables and put them under companion object. The API key should not be shared so if you are using git, add the Secrets file in .gitignore file. Below is the code

class Secrets {
    companion object {
        var USERNAME = "sandbox"
        var API_KEY = "your_api_key"
        val productName = "Android Payment"
    }
}

The username will be sandbox because we are in the testing mode. In production mode, it will be changed to your Africa's Talking username.

4.2 Let's work on the MainActivity class now. We are going to set up the views using ids, initialize the Africa's Talking library, get response from the API and lastly, we will set up an alert dialog to convey messages.

4.2.1 Declare and initialize the phone number(EditText), amount(EditText) and buttonPay(Button) variables as global variables.

4.2.2 Next, we will setup StrictMode to allow us to use the main UI thread to run our network access. This will prevent our application from crashing. Read more on StrictMode here.

4.2.3 Set a setOnClickListener to the buttonPay and create a function called sendRequest which will be called when the button is clicked.

class MainActivity : AppCompatActivity() {

    private lateinit var phoneInput: EditText
    private lateinit var amountInput: EditText
    private lateinit var buttonPay: Button
    lateinit var alert: AlertDialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val policy =
            StrictMode.ThreadPolicy.Builder().permitAll().build()
        StrictMode.setThreadPolicy(policy)
        phoneInput = findViewById(R.id.phoneInput)
        amountInput = findViewById(R.id.amountInput)
        buttonPay = findViewById(R.id.buttonPay)

        buttonPay.setOnClickListener { sendRequest() }

    }

    private fun sendRequest() {}
    
    }

Let's get the values from the EditTexts when the button is clicked. We should validate the data by checking if the EditText is not empty and the data is correct.

4.2.4 To verify the phone number, we will create a function verifyPhone and pass the phone number as a parameter. The function should return a Boolean value after checking the conditions. The conditions are: The phone number must be from a Kenyan telco company and in international format. That means the total length should be 13 and must contain a + sign.

private fun verifyPhone(phone: String): Boolean {
        return phone.contains("+") && phone.length == 13
    }

4.2.5 To check if the EditTexts are empty we will use the TextUtils.isEmpty(value) function that comes with the Android SDK. If the EditText is empty, we will display an error message.

Here is the complete code

private fun sendRequest() {
        val phone = phoneInput.text.toString()
        val amount = amountInput.text.toString()

        if (TextUtils.isEmpty(phone)) {
            phoneInput.requestFocus()
            phoneInput.error = "Required"
        } else if (TextUtils.isEmpty(amount)) {
            amountInput.requestFocus()
            amountInput.error = "Required"
        } else if (!verifyPhone(phone)) {
            Snackbar.make(
                findViewById(android.R.id.content),
                "Invalid phone number. Required format +25471xxxxxxx",
                Snackbar.LENGTH_LONG
            ).show()
        } else {
        //send the request
        }
        
        }

Let's initialize the library and send a request to the API.

4.2.6 To initialize we need the username and the API Key. We had set this up in the Secrets class.

AfricasTalking.initialize(USERNAME, API_KEY)

4.2.7 Get an instance to a given service by name or by class and assign it to a variable:

val payment = AfricasTalking.getService(AfricasTalking.SERVICE_PAYMENT) as PaymentService

4.2.8 You can add some metadata to the request. Metadata can be a unique identification of the transaction.

val metadata = HashMap<String, String>()
metadata["someKey"] = "someValue"

4.2.9 Now let's send a request to Africa's Talking using the mobileCheckout function. All parameters should be string except amount which should be a float. The currency code should be three (3) characters for our case KES. The phone number should be in international format.

payment.mobileCheckout(productName, phone, "KES", amount.toFloat(), metadata)

You will get a JSON response like this if successful.

{
   "description":"Waiting for user input",
   "status":"PendingConfirmation",
   "transactionId":"ATPid_140c27bfede0f60ac30f44867a2c231e"
}

Here is the code for sending the request

AfricasTalking.initialize(USERNAME, API_KEY)

val payment =AfricasTalking.getService(AfricasTalking.SERVICE_PAYMENT) as PaymentService

val currencyCode = "KES"

val metadata = HashMap<String, String>()
metadata["someKey"] = "someValue"

try {
               
     val response = payment.mobileCheckout(productName, phone,       currencyCode, amount.toFloat(), metadata)

     Log.d("RESPONSE",response.toString())
              
} catch (ex: Exception) {

    Log.e("ERROR",ex.message)

}

So we are done sending a payment request to Africa's Talking. Next we will set up Alert Dialog to notify us of the progress.

4.2.10 Create a global variable call alert of type AlertDialog

lateinit var alert: AlertDialog

4.2.11 Create a function messageAlert and pass 3 parameters (title and message). Set up an alert dialog inside this function.

private fun messageAlert(msg: String, title: String) {
        val alertBuilder = AlertDialog.Builder(this)
        alertBuilder.setCancelable(false)
        alertBuilder.setTitle(title)
        alertBuilder.setMessage(msg)
        alertBuilder.setPositiveButton("OK") { dialog, _ ->
            dialog.dismiss()
        }
        alert = alertBuilder.create()
        alert.show()
    }

4.2.12 Call the messageAlert function in our sendRequest function and pass the params. We will use JSONObject to read the response status and display the alert dialog according to it.

val response = payment.mobileCheckout(productName, phone, currencyCode, amount.toFloat(), metadata)

val jsonObject = JSONObject(response.toString())
    if (jsonObject.getString("status") == "PendingConfirmation") {
        messageAlert("Request Successful", "Success")
     } else {
        messageAlert("Request failed", "Error")
     }
Success screenshot
Success screenshot
Error screenshot
Error screenshot

The code for the whole MainActivity class

class MainActivity : AppCompatActivity() {

    private lateinit var phoneInput: EditText
    private lateinit var amountInput: EditText
    private lateinit var buttonPay: Button
    lateinit var alert: AlertDialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val policy =
            StrictMode.ThreadPolicy.Builder().permitAll().build()
        StrictMode.setThreadPolicy(policy)
        phoneInput = findViewById(R.id.phoneInput)
        amountInput = findViewById(R.id.amountInput)
        buttonPay = findViewById(R.id.buttonPay)

        buttonPay.setOnClickListener { sendRequest() }

    }

    private fun sendRequest() {
        val phone = phoneInput.text.toString()
        val amount = amountInput.text.toString()

        if (TextUtils.isEmpty(phone)) {
            phoneInput.requestFocus()
            phoneInput.error = "Required"
        } else if (TextUtils.isEmpty(amount)) {
            amountInput.requestFocus()
            amountInput.error = "Required"
        } else if (!verifyPhone(phone)) {
            Snackbar.make(
                findViewById(android.R.id.content),
                "Invalid phone number. Required format +25471xxxxxxx",
                Snackbar.LENGTH_LONG
            ).show()
        } else {
            
            AfricasTalking.initialize(USERNAME, API_KEY)
            val payment =       AfricasTalking.getService(AfricasTalking.SERVICE_PAYMENT) as PaymentService

            val productName = productName

            val currencyCode = "KES"

            val metadata = HashMap<String, String>()
            metadata["someKey"] = "someValue"
            try {
                alert.cancel()
                val response = payment.mobileCheckout(
                    productName, phone, currencyCode, amount.toFloat(), metadata)
                val jsonObject = JSONObject(response.toString())
                if (jsonObject.getString("status") == "PendingConfirmation") {
                    messageAlert("Request Successful", "Success")
                } else {
                    messageAlert("Request failed", "Error")
                }
                println(response.toString())
            } catch (ex: Exception) {
                ex.printStackTrace()
            }
        }
    }

    private fun messageAlert(msg: String, title: String) {
        val alertBuilder = AlertDialog.Builder(this)
        alertBuilder.setCancelable(false)
        alertBuilder.setTitle(title)
        alertBuilder.setMessage(msg)
        alertBuilder.setPositiveButton("OK") { dialog, _ ->
            dialog.dismiss()
        }
        alert = alertBuilder.create()
        alert.show()
    }

    private fun verifyPhone(phone: String): Boolean {
        return phone.contains("+") && phone.length == 13
    }

}

We are done with our Africa's Talking mobile checkout payment API tutorial in test mode.

Find the project on GitHub repo

Happy Coding!!

You've successfully subscribed to Decoded For Devs
Welcome back! You've successfully signed in.
Great! You've successfully signed up.
Your link has expired
Success! Your account is fully activated, you now have access to all content.