Categories
android

Android Integrating PayTM Payment Gateway – ECommerce App

PayTM is the most important cellular trade platform and virtual pockets corporate. It has massive consumer base of round 300 million and reasonable transactions of 5 million in step with day. All virtual bills can also be made via paytm like cellular recharges, electrical energy expenses, bank cards and many others. Almost the whole lot below the earth is roofed in paytm 🙂 Paytm is rapid, dependable and extra out there fee resolution.

PayTM additionally supplies fee gateway resolution that may be built-in into internet and cellular apps with UPI, Debit/ Credit Card, NetBanking and PayTM Wallet fee choices.

Today, on this article we’re going to see methods to combine the PayTM gateway in a easy (bit complicated in truth 😛 ) e-commerce app. As writing an e-commerce app wishes slightly of coding and structure, the whole lot can’t be coated in one article. So, I’ve ready a pattern undertaking (each internet and cellular) and equipped the code on Github. I additionally attempted making it easy up to conceivable in order that each newbie can realize it.

1. Mart9 e-Commerce App

The instance app I’ve created could be very minimum with very restricted monitors. It has database (Realm), community layer (Retrofit) and fee choices.

  • Login, Register monitors to login or create a brand new consumer.
  • Home – To checklist down the to be had merchandise along side title, thumbnail and worth.
  • Cart – A BottomSheet view to take care of the cart pieces.
  • Payment display screen – To make the important calls to backend server earlier than redirecting consumer to fee gateway.
  • Transactions – To display the checklist of transactions made by way of a consumer.

Here are the screenshots from the app.

2. Overview of PayTM Payment Lifecycle

Completing the checkout comes to selection of calls between app, backend and PayTM server. Below is lifecycle of a transaction from initiation to fee of completion.

1. Preparing Order: Once buyer selects the pieces, the cart pieces can be despatched to backend server. This will insert a brand new order row or replace current row in db and generate distinctive Order ID. This order ID must be distinctive each and every time consumer redirected to PayTM fee display screen, in a different way PayTM throws replica order identity error.

2. Order Id & Checksum: The subsequent step is to generate checksum making an allowance for the order ID. The checksum must be generated making an allowance for the required fields and Merchant ID.

3. Redirecting to PayTM: Once the checksum is generated, consumer can be redirected to PayTM fee display screen appearing more than one fee choices like Debit / Credit card, Wallet, Net Banking and many others., In this step, if the generated checksum is mistaken, consumer will redirected again to app with error standing.

4. Bank Transaction: User completes the fee by way of opting for the choices equipped. Here PayTM looks after speaking with financial institution and finishing the fee.

5. Verifying transaction: Once the transaction is done, it must be verified on backend additionally to steer clear of fraudulent transactions. Here the backend server makes CURL request to PayTM server to ensure the transaction standing.

6. Order standing: The transaction standing is gained from backend and suitable order standing is proven to consumer.

The beneath diagram illustrates the verbal exchange float between each and every birthday celebration. Here you’ll see the transaction lifecycle in detailed way.

paytm android integration php laravel flow

3. Integrating PayTM gateway

In actual existence situation, construction an e-commerce app wishes correct structure and taking safety precautions. This article objectives to give an explanation for the float so simple as conceivable. So I’ve constructed what’s important to finish the fee gateway integration. Integrating PayTM (or some other gateway) comes to the beneath steps.

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

3.1 PayTM SandBox – Test API Details

To get began with PayTM, you’ll sign up a brand new account and get a check account to tryout the fee gateway. Follow the beneath steps to get your sandbox credentials.

1. Goto PayTM developer web site and continue with important steps to create a brand new account / login into current account.

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

3. You can realize API main points for Test and Production. Once the app is examined in sandbox, you’ll manner PayTM staff to make your app are living.

paytm-developer-test-api-keys

3.2 Building the backend – REST API

I’ve selected Laravel (PHP) framework to construct the backend module. In this, I’ve written important REST API required for this app. The admin panel isn’t constructed on this undertaking.

This backend app / REST is already are living and publicly to be had to tryout.

Base Url: https://demo.iandroid.eu.information/paytm/public/api/

Postman sell off: https://www.getpostman.com/collections/8b2e7763a8b7e0673918

3.3 PayTM Android SDK Integration

Unlike my previous tutorials, I’m focusing simplest on essential issues on this article. The code of entire app can also be discovered on Github web page. Here, I’m specifically all in favour of PayTMActivity.java because it has the core parts of PayTM module.

The consumer will redirected to PayTM process as soon as the cart pieces are decided on and clicked on Pay. This process communicates with backend server more than one occasions to fulfil the order. I’ve written detailed feedback below each and every manner used.

Step 1: The checklist of cart pieces despatched to server to create a brand new order. Here we name /get readyOrder endpoint to generate the distinctive order ID.

Step 2: Once the order ID is gained, subsequent we name /getChecksum by way of passing the Order ID along side different params to generate the checksum hash.

Step 3: Once the checksum is gained, we name pgService.get startedPaymentTransaction() to redirect consumer to PayTM fee display screen.

Step 4: Once the fee is done, /transactionStatus is named to ensure the transaction on server. Here we move the similar Order ID to ensure the standing.

Step 5: Once the transaction standing is gained, consumer can be proven luck or failed display screen by way of calling displayOrderStatus() manner.

bundle information.iandroid.eu.paytmgateway.ui.paytm; import android.content material.Intent;
import android.os.Bundle;
import android.make stronger.v4.content material.ContextCompat;
import android.view.MenuItem;
import android.view.View;
import android.widget.SymbolView;
import android.widget.LinearLayout;
import android.widget.Textual contentView;
import android.widget.Toast; import com.paytm.pgsdk.PaytmOrder;
import com.paytm.pgsdk.PaytmPGService;
import com.paytm.pgsdk.PaytmPaymentTransactionCallback;
import com.wang.avi.AVLoadingIndicatorView; 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 information.iandroid.eu.paytmgateway.BuildConfig;
import information.iandroid.eu.paytmgateway.R;
import information.iandroid.eu.paytmgateway.app.Constants;
import information.iandroid.eu.paytmgateway.db.AppDatabase;
import information.iandroid.eu.paytmgateway.db.type.CartItem;
import information.iandroid.eu.paytmgateway.db.type.User;
import information.iandroid.eu.paytmgateway.networking.type.AppConfig;
import information.iandroid.eu.paytmgateway.networking.type.ChecksumResponse;
import information.iandroid.eu.paytmgateway.networking.type.Order;
import information.iandroid.eu.paytmgateway.networking.type.OrderItem;
import information.iandroid.eu.paytmgateway.networking.type.Get readyOrderRequest;
import information.iandroid.eu.paytmgateway.networking.type.Get readyOrderResponse;
import information.iandroid.eu.paytmgateway.ui.base.BaseActivity;
import information.iandroid.eu.paytmgateway.ui.transactions.TransactionsActivity;
import io.realm.Realm;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import trees.log.Timber; public magnificence PayTMActivity extends BaseActivity { @BindView(R.identity.lbl_status) Textual contentView lblStatus; @BindView(R.identity.loader) AVLoadingIndicatorView loader; @BindView(R.identity.icon_status) SymbolView iconStatus; @BindView(R.identity.status_message) Textual contentView standingMessage; @BindView(R.identity.title_status) Textual contentView responseTitle; @BindView(R.identity.btn_check_orders) Textual contentView btnCheckOrders; @BindView(R.identity.layout_order_placed) LinearLayout structureOrderPositioned; non-public Realm realm; non-public AppConfig appConfig; non-public User consumer; /** * Steps to procedure order: * 1. Make server name to arrange the order. Which will create a brand new order within the db * and returns the original Order ID * <p> * 2. Once the order ID is gained, ship the PayTM params to server to calculate the * Checksum Hash * <p> * 3. Send the PayTM params along side checksum hash to PayTM gateway * <p> * 4. Once the fee is completed, ship the Order Id again to server to ensure the * transaction standing */ @Override safe void onCreate(Bundle storedInstanceState) { tremendous.onCreate(storedInstanceState); setContentView(R.structure.activity_pay_tm); ButterKnife.bind(this); setToolbar(); allowToolbarUpNavigation(); getSupportActionBar().setTitle(getString(R.string.title_preparing_order)); alternateStatusBarColor(); init(); } @Override public int getLayoutId() { go back R.structure.activity_pay_tm; } non-public void init() { realm = Realm.getDefaultInstance(); realm.the place(CartItem.magnificence).to findAllAsync() .addChangeListener(cartItems -> { }); consumer = AppDatabase.getUser(); appConfig = realm.the place(AppConfig.magnificence).findFirst(); get readyOrder(); } non-public void setStatus(int message) { lblStatus.setText(message); } /** * STEP 1: Sending the entire cart pieces to server and receiving the * distinctive order identity that must be despatched to PayTM */ non-public void get readyOrder() { setStatus(R.string.msg_preparing_order); List<CartItem> cartItems = realm.the place(CartItem.magnificence).to findAll(); Get readyOrderRequest request = new Get readyOrderRequest(); List<OrderItem> orderItems = new ArrayList<>(); for (CartItem cartItem : cartItems) { OrderItem orderItem = new OrderItem(); orderItem.productId = cartItem.product.identity; orderItem.amount = cartItem.amount; orderItems.upload(orderItem); } request.orderItems = orderItems; getApi().get readyOrder(request).enqueue(new Callback<Get readyOrderResponse>() { @Override public void onResponse(Call<Get readyOrderResponse> name, Response<Get readyOrderResponse> reaction) { if (!reaction.isSuccessful()) { handleUnknownError(); displayOrderStatus(false); go back; } getChecksum(reaction.frame()); } @Override public void onFailure(Call<Get readyOrderResponse> name, Throwable t) { maintainError(t); displayOrderStatus(false); } }); } /** * STEP 2: * Sending the params to server to generate the Checksum * that must be despatched to PayTM */ void getChecksum(Get readyOrderResponse reaction) { setStatus(R.string.msg_fetching_checksum); if (appConfig == null) { Timber.e("App config is null! Can't position the order. This normally should not occur"); // navigating consumer to login display screen releaseLogin(PayTMActivity.this); end(); go back; } Map<String, String> paramMap = get readyPayTmParams(reaction); Timber.d("PayTm Params: %s", paramMap); getApi().getCheckSum(paramMap).enqueue(new Callback<ChecksumResponse>() { @Override public void onResponse(Call<ChecksumResponse> name, Response<ChecksumResponse> reaction) { if (!reaction.isSuccessful()) { Timber.e("Network name failed"); handleUnknownError(); displayOrderStatus(false); go back; } Timber.d("Checksum Received: " + reaction.frame().checksum); // Add the checksum to current params checklist and ship them to PayTM paramMap.put("CHECKSUMHASH", reaction.frame().checksum); positionOrder(paramMap); } @Override public void onFailure(Call<ChecksumResponse> name, Throwable t) { maintainError(t); displayOrderStatus(false); } }); } public Map<String, String> get readyPayTmParams(Get readyOrderResponse reaction) { Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("CALLBACK_URL", String.structure(BuildConfig.PAYTM_CALLBACK_URL, reaction.orderGatewayId)); paramMap.put("CHANNEL_ID", appConfig.getChannel()); paramMap.put("CUST_ID", "CUSTOMER_" + consumer.identity); paramMap.put("INDUSTRY_TYPE_ID", appConfig.getIndustryType()); paramMap.put("MID", appConfig.getMerchantId()); paramMap.put("WEBSITE", appConfig.getWebsite()); paramMap.put("ORDER_ID", reaction.orderGatewayId); paramMap.put("TXN_AMOUNT", reaction.quantity); go back paramMap; } /** * STEP 3: Redirecting to PayTM gateway with important params along side checksum * This will redirect to PayTM gateway and offers us the PayTM transaction reaction */ public void positionOrder(Map<String, String> params) { setStatus(R.string.msg_redirecting_to_paytm); // opting for between PayTM staging and manufacturing PaytmPGService pgService = BuildConfig.IS_PATM_STAGIN ? PaytmPGService.getStagingService() : PaytmPGService.getProductionCarrier(); PaytmOrder Order = new PaytmOrder(params); pgService.initialize(Order, null); pgService.get startedPaymentTransaction(this, true, true, new PaytmPaymentTransactionCallback() { @Override public void someUIErrorOccurred(String inErrorMessage) { Timber.e("someUIErrorOccurred: %s", inErrorMessage); end(); // Some UI Error Occurred in Payment Gateway Activity. // // This is also because of initialization of perspectives in // Payment Gateway Activity or is also because of // // initialization of webview. // Error Message main points // the mistake took place. } @Override public void onTransactionResponse(Bundle inResponse) { Timber.d("PayTM Transaction Response: %s", inResponse.toString()); String orderId = inResponse.getString("ORDERID"); testTransactionStatus(orderId); } @Override public void networkNotAvailable() { // If community isn't Timber.e("networkNotAvailable"); end(); // to be had, then this // manner will get known as. } @Override public void clientAuthenticationFailed(String inErrorMessage) { Timber.e("clientAuthenticationFailed: %s", inErrorMessage); end(); // This manner will get known as if consumer authentication // failed. // Failure is also because of following causes // // 1. Server error or downtime. // 2. Server not able to // generate checksum or checksum reaction isn't in // correct structure. // 3. Server didn't authenticate // that consumer. That is price of payt_STATUS is 2. // // Error Message describes the cause of failure. } @Override public void onErrorLoadingWebPage(int iniErrorCode, String inErrorMessage, String inFailingUrl)  %s  @Override public void onBackPressedCancelTransaction() { Toast.makeText(PayTMActivity.this, "Back pressed. Transaction cancelled", Toast.LENGTH_LONG).display(); end(); } @Override public void onTransactionCancel(String inErrorMessage, Bundle inResponse)  %s", inErrorMessage, inResponse); end();  }); } /** * STEP 4: Verifying the transaction standing as soon as PayTM transaction is over * This makes server(personal) -> server(PayTM) name to ensure the transaction standing */ non-public void testTransactionStatus(String orderId) { setStatus(R.string.msg_verifying_status); getApi().testTransactionStatus(orderId).enqueue(new Callback<Order>() { @Override public void onResponse(Call<Order> name, Response<Order> reaction) { if (!reaction.isSuccessful()) { Timber.e("Network name failed"); handleUnknownError(); displayOrderStatus(false); go back; } displayOrderStatus(reaction.frame().standing.equalsIgnoreCase(Constants.ORDER_STATUS_COMPLETED)); } @Override public void onFailure(Call<Order> name, Throwable t) { maintainError(t); displayOrderStatus(false); } }); } /* * Displaying Order Status on UI. This toggles UI between luck and failed instances * */ non-public void displayOrderStatus(boolean isSuccess) { loader.setVisibility(View.GONE); lblStatus.setVisibility(View.GONE); if (isSuccess) { iconStatus.setImageResource(R.drawable.baseline_check_black_48); iconStatus.setColorFilter(ContextCompat.getColor(this, R.colour.colorGreen)); responseTitle.setText(R.string.thank_you); standingMessage.setText(R.string.msg_order_placed_successfully); // because the order positioned effectively, transparent the cart AppDatabase.transparentCart(); } else { iconStatus.setImageResource(R.drawable.baseline_close_black_48); iconStatus.setColorFilter(ContextCompat.getColor(this, R.colour.btn_remove_item)); responseTitle.setText(R.string.order_failed); standingMessage.setText(R.string.msg_order_placed_failed); } structureOrderPositioned.setVisibility(View.VISIBLE); } @OnClick(R.identity.btn_check_orders) void onOrdersClick() { get startedActivity(new Intent(PayTMActivity.this, TransactionsActivity.magnificence)); end(); } @Override public boolean onOptionsItemSelected(MenuItem merchandise) { if (merchandise.getItemId() == android.R.identity.house) { end(); go back true; } go back tremendous.onOptionsItemSelected(merchandise); } @Override safe void onDestroy() { tremendous.onDestroy(); if (realm != null) { realm.take awayAllTradeListeners(); realm.shut(); } }
}

4. Testing the App

Once you have got finished the PayTM integration, you’ll check your app (or the app equipped on this article), the use of the check mode credentials equipped.

5. References

Below are helpful hyperlinks you’ll stay at hand whilst operating in this undertaking.
1. PayTM Android SDK documentation
2. PayTM REST API documentation
3. PayTM check mode credentials

I’m hoping this newsletter simplified the PayTM integration procedure. If you have got any queries / advice, pls do put up within the remark segment beneath.

Happy Coding 🙂

Hi there! I’m Founder at iandroid.eu and programming fanatic. My talents comprises Android, iOS, PHP, Ruby on Rails and lot extra. If you have got any concept that you’d need me to expand? Let’s communicate: [email protected]