Android Integrating PayTM Payment Gateway

PayTM is the largest mobile commerce platform and digital wallet company. It has large user base of around 300 million and average transactions of 5 million per day. All digital payments can be made through paytm like mobile recharges, electricity bills, credit cards etc. Almost everything under the earth is covered in paytm ? Paytm is fast, reliable and more accessible payment solution.

PayTM also provides payment gateway solution that can be integrated into web and mobile apps with UPI, Debit/ Credit Card, NetBanking and PayTM Wallet payment options.


Today, in this article we are going to see how to integrate the PayTM gateway in a simple (bit complex actually ? ) e-commerce app. As writing an e-commerce app needs a bit of coding and architecture, everything can’t be covered in a single article. So, I have prepared a sample project (both web and mobile) and provided the code on Github. I also tried making it simple as much as possible so that every beginner can understand it.

1. Mart9 e-Commerce App

The example app I have created is very minimal with very limited screens. It has database (Realm), network layer (Retrofit) and payment options.

  • Login, Register screens to login or create a new user.
  • Home – To list down the available products along with name, thumbnail and price.
  • Cart – A BottomSheet view to maintain the cart items.
  • Payment screen – To make the necessary calls to backend server before redirecting user to payment gateway.
  • Transactions – To show the list of transactions made by a user.

Here are the screenshots from the app.

2. Overview of PayTM Payment Lifecycle

Completing the checkout involves number of calls between app, backend and PayTM server. Below is lifecycle of a transaction from initiation to payment completion.

1. Preparing Order: Once customer selects the items, the cart items will be sent to backend server. This will insert a new order row or update existing row in db and generate unique Order ID. This order ID has to be unique each time user redirected to PayTM payment screen, otherwise PayTM throws duplicate order id error.

2. Order Id & Checksum: The next step is to generate checksum considering the order ID. The checksum has to be generated considering the mandatory fields and Merchant ID.

3. Redirecting to PayTM: Once the checksum is generated, user will be redirected to PayTM payment screen showing multiple payment options like Debit / Credit card, Wallet, Net Banking etc., In this step, if the generated checksum is wrong, user will redirected back to app with error status.

4. Bank Transaction: User completes the payment by choosing the options provided. Here PayTM takes care of communicating with bank and completing the payment.

5. Verifying transaction: Once the transaction is completed, it has to be verified on backend also to avoid fraudulent transactions. Here the backend server makes CURL request to PayTM server to verify the transaction status.

6. Order status: The transaction status is received from backend and appropriate order status is shown to user.

The below diagram illustrates the communication flow between each party. Here you can see the transaction lifecycle in detailed manner.

paytm android integration php laravel flow

3. Integrating PayTM gateway

In real life scenario, building an e-commerce app needs proper architecture and taking security precautions. This article aims to explain the flow as simple as possible. So I have built what is necessary to complete the payment gateway integration. Integrating PayTM (or any other gateway) involves the below steps.

3.1. Registering with PayTM and obtain necessary API keys.
3.2 Building backend app with necessary database schema and REST API. Integrating the PayTM server SDK.
3.3 Integrating PayTM mobile SDK in android / iOS app.

3.1 PayTM SandBox – Test API Details

To get started with PayTM, you can register a new account and get a test account to tryout the payment gateway. Follow the below steps to get your sandbox credentials.

1. Goto PayTM developer website and proceed with necessary steps to create a new account / login into existing account.

2. Once registered, navigate to API Keys in left navigation panel.

3. You can notice API details for Test and Production. Once the app is tested in sandbox, you can approach PayTM team to make your app live.


3.2 Building the backend – REST API

I have chosen Laravel (PHP) framework to build the backend module. In this, I have written necessary REST API required for this app. The admin panel is not built in this project.

This backend app / REST is already live and publicly available to tryout.

Base Url:

Postman dump:

3.3 PayTM Android SDK Integration

Unlike my earlier tutorials, I am focusing only on important things in this article. The code of complete app can be found on Github page. Here, I am particularly interested in as it has the core components of PayTM module.

The user will redirected to PayTM activity once the cart items are selected and clicked on Pay. This activity communicates with backend server multiple times to fulfil the order. I have written detailed comments under each method used.

Step 1: The list of cart items sent to server to create a new order. Here we call /prepareOrder endpoint to generate the unique order ID.

Step 2: Once the order ID is received, next we call /getChecksum by passing the Order ID along with other params to generate the checksum hash.

Step 3: Once the checksum is received, we call pgService.startPaymentTransaction() to redirect user to PayTM payment screen.

Step 4: Once the payment is completed, /transactionStatus is called to verify the transaction on server. Here we pass the same Order ID to verify the status.

Step 5: Once the transaction status is received, user will be shown success or failed screen by calling showOrderStatus() method.

package info.androidhive.paytmgateway.ui.paytm;

import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.paytm.pgsdk.PaytmOrder;
import com.paytm.pgsdk.PaytmPGService;
import com.paytm.pgsdk.PaytmPaymentTransactionCallback;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import info.androidhive.paytmgateway.BuildConfig;
import info.androidhive.paytmgateway.R;
import info.androidhive.paytmgateway.db.AppDatabase;
import info.androidhive.paytmgateway.db.model.CartItem;
import info.androidhive.paytmgateway.db.model.User;
import info.androidhive.paytmgateway.networking.model.AppConfig;
import info.androidhive.paytmgateway.networking.model.ChecksumResponse;
import info.androidhive.paytmgateway.networking.model.Order;
import info.androidhive.paytmgateway.networking.model.OrderItem;
import info.androidhive.paytmgateway.networking.model.PrepareOrderRequest;
import info.androidhive.paytmgateway.networking.model.PrepareOrderResponse;
import info.androidhive.paytmgateway.ui.base.BaseActivity;
import info.androidhive.paytmgateway.ui.transactions.TransactionsActivity;
import io.realm.Realm;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;

public class PayTMActivity extends BaseActivity {
    TextView lblStatus;

    AVLoadingIndicatorView loader;

    ImageView iconStatus;

    TextView statusMessage;

    TextView responseTitle;

    TextView btnCheckOrders;

    LinearLayout layoutOrderPlaced;

    private Realm realm;
    private AppConfig appConfig;
    private User user;

     * Steps to process order:
     * 1. Make server call to prepare the order. Which will create a new order in the db
     * and returns the unique Order ID
     * <p>
     * 2. Once the order ID is received, send the PayTM params to server to calculate the
     * Checksum Hash
     * <p>
     * 3. Send the PayTM params along with checksum hash to PayTM gateway
     * <p>
     * 4. Once the payment is done, send the Order Id back to server to verify the
     * transaction status

    protected void onCreate(Bundle savedInstanceState) {

    public int getLayoutId() {
        return R.layout.activity_pay_tm;

    private void init() {
        realm = Realm.getDefaultInstance();
                .addChangeListener(cartItems -> {


        user = AppDatabase.getUser();
        appConfig = realm.where(AppConfig.class).findFirst();


    private void setStatus(int message) {

     * STEP 1: Sending all the cart items to server and receiving the
     * unique order id that needs to be sent to PayTM
    private void prepareOrder() {

        List<CartItem> cartItems = realm.where(CartItem.class).findAll();
        PrepareOrderRequest request = new PrepareOrderRequest();
        List<OrderItem> orderItems = new ArrayList<>();
        for (CartItem cartItem : cartItems) {
            OrderItem orderItem = new OrderItem();
            orderItem.productId =;
            orderItem.quantity = cartItem.quantity;

        request.orderItems = orderItems;

        getApi().prepareOrder(request).enqueue(new Callback<PrepareOrderResponse>() {
            public void onResponse(Call<PrepareOrderResponse> call, Response<PrepareOrderResponse> response) {
                if (!response.isSuccessful()) {


            public void onFailure(Call<PrepareOrderResponse> call, Throwable t) {

     * STEP 2:
     * Sending the params to server to generate the Checksum
     * that needs to be sent to PayTM
    void getChecksum(PrepareOrderResponse response) {

        if (appConfig == null) {
            Timber.e("App config is null! Can't place the order. This usually shouldn't happen");
            // navigating user to login screen

        Map<String, String> paramMap = preparePayTmParams(response);
        Timber.d("PayTm Params: %s", paramMap);

        getApi().getCheckSum(paramMap).enqueue(new Callback<ChecksumResponse>() {
            public void onResponse(Call<ChecksumResponse> call, Response<ChecksumResponse> response) {
                if (!response.isSuccessful()) {
                    Timber.e("Network call failed");

                Timber.d("Checksum Received: " + response.body().checksum);

                // Add the checksum to existing params list and send them to PayTM
                paramMap.put("CHECKSUMHASH", response.body().checksum);

            public void onFailure(Call<ChecksumResponse> call, Throwable t) {

    public Map<String, String> preparePayTmParams(PrepareOrderResponse response) {
        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("CALLBACK_URL", String.format(BuildConfig.PAYTM_CALLBACK_URL, response.orderGatewayId));
        paramMap.put("CHANNEL_ID", appConfig.getChannel());
        paramMap.put("CUST_ID", "CUSTOMER_" +;
        paramMap.put("INDUSTRY_TYPE_ID", appConfig.getIndustryType());
        paramMap.put("MID", appConfig.getMerchantId());
        paramMap.put("WEBSITE", appConfig.getWebsite());
        paramMap.put("ORDER_ID", response.orderGatewayId);
        paramMap.put("TXN_AMOUNT", response.amount);
        return paramMap;

     * STEP 3: Redirecting to PayTM gateway with necessary params along with checksum
     * This will redirect to PayTM gateway and gives us the PayTM transaction response
    public void placeOrder(Map<String, String> params) {

        // choosing between PayTM staging and production
        PaytmPGService pgService = BuildConfig.IS_PATM_STAGIN ? PaytmPGService.getStagingService() : PaytmPGService.getProductionService();

        PaytmOrder Order = new PaytmOrder(params);

        pgService.initialize(Order, null);

        pgService.startPaymentTransaction(this, true, true,
                new PaytmPaymentTransactionCallback() {
                    public void someUIErrorOccurred(String inErrorMessage) {
                        Timber.e("someUIErrorOccurred: %s", inErrorMessage);
                        // Some UI Error Occurred in Payment Gateway Activity.
                        // // This may be due to initialization of views in
                        // Payment Gateway Activity or may be due to //
                        // initialization of webview. // Error Message details
                        // the error occurred.

                    public void onTransactionResponse(Bundle inResponse) {
                        Timber.d("PayTM Transaction Response: %s", inResponse.toString());
                        String orderId = inResponse.getString("ORDERID");

                    public void networkNotAvailable() { // If network is not
                        // available, then this
                        // method gets called.

                    public void clientAuthenticationFailed(String inErrorMessage) {
                        Timber.e("clientAuthenticationFailed: %s", inErrorMessage);
                        // This method gets called if client authentication
                        // failed. // Failure may be due to following reasons //
                        // 1. Server error or downtime. // 2. Server unable to
                        // generate checksum or checksum response is not in
                        // proper format. // 3. Server failed to authenticate
                        // that client. That is value of payt_STATUS is 2. //
                        // Error Message describes the reason for failure.

                    public void onErrorLoadingWebPage(int iniErrorCode,
                                                      String inErrorMessage, String inFailingUrl) {
                        Timber.e("onErrorLoadingWebPage: %d | %s | %s", iniErrorCode, inErrorMessage, inFailingUrl);

                    public void onBackPressedCancelTransaction() {
                        Toast.makeText(PayTMActivity.this, "Back pressed. Transaction cancelled", Toast.LENGTH_LONG).show();

                    public void onTransactionCancel(String inErrorMessage, Bundle inResponse) {
                        Timber.e("onTransactionCancel: %s | %s", inErrorMessage, inResponse);

     * STEP 4: Verifying the transaction status once PayTM transaction is over
     * This makes server(own) -> server(PayTM) call to verify the transaction status
    private void verifyTransactionStatus(String orderId) {
        getApi().checkTransactionStatus(orderId).enqueue(new Callback<Order>() {
            public void onResponse(Call<Order> call, Response<Order> response) {
                if (!response.isSuccessful()) {
                    Timber.e("Network call failed");


            public void onFailure(Call<Order> call, Throwable t) {

     * Displaying Order Status on UI. This toggles UI between success and failed cases
     * */
    private void showOrderStatus(boolean isSuccess) {
        if (isSuccess) {
            iconStatus.setColorFilter(ContextCompat.getColor(this, R.color.colorGreen));

            // as the order placed successfully, clear the cart
        } else {
            iconStatus.setColorFilter(ContextCompat.getColor(this, R.color.btn_remove_item));


    void onOrdersClick() {
        startActivity(new Intent(PayTMActivity.this, TransactionsActivity.class));

    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == {
            return true;
        return super.onOptionsItemSelected(item);

    protected void onDestroy() {

        if (realm != null) {

4. Testing the App

Once you have completed the PayTM integration, you can test your app (or the app provided in this article), using the test mode credentials provided.

5. References

Below are useful links you can keep handy while working on this project.
1. PayTM Android SDK documentation
2. PayTM REST API documentation
3. PayTM test mode credentials

I hope this article simplified the PayTM integration process. If you have any queries / suggestion, pls do post in the comment section below.

Happy Coding ?

Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: [email protected]

Leave a Reply

Your email address will not be published. Required fields are marked *