Seguridad

Integración de Android de la pasarela de pago PayTM

PayTM es la plataforma de comercio móvil más grande y la compañía de billetera digital más grande. Tiene una gran base de usuarios de alrededor de 300 millones y transacciones promedio de 5 millones por día. Todos los pagos digitales se pueden realizar a través de PayTM, como recargas móviles, facturas de servicios públicos, tarjetas de crédito, etc. Casi todo lo que hay bajo tierra está cubierto con Paytm. Paytm es una solución de pago rápida, confiable y más accesible.

PayTM también ofrece una solución de pasarela de pago que se puede integrar con aplicaciones web y móviles. UPI, Débito directo[?]Tarjeta de crédito, NetBanking y PayTM Opciones de pago con billetera.

En este artículo de hoy, aprenderá cómo integrar la pasarela PayTM en un simple (en realidad algo complejo 😛). Comercio electrónico App. Dado que escribir una aplicación de comercio electrónico requiere un poco de codificación y arquitectura, no se puede cubrir todo en un solo artículo. Así que preparé un proyecto de muestra (tanto web como móvil) y publiqué el código en Github. También he tratado de hacerlo lo más simple posible para que cualquier principiante pueda entenderlo.

1. Aplicación de comercio electrónico Mart9

La aplicación de muestra que creé es mínima con pantallas muy limitadas. Tiene una base de datos (reino), capa de red (modernización) y opciones de pago.

  • Iniciar sesión Registrarse Pantallas para iniciar sesión o crear un nuevo usuario.
  • En casa – Enumere los productos disponibles junto con su nombre, miniatura y precio.
  • atrevimiento – Una vista BottomSheet para administrar los artículos del carrito de compras.
  • Pantalla de pago – Realizar las llamadas necesarias al servidor backend antes de que el usuario sea redirigido a la pasarela de pago.
  • Actas – Para ver la lista de transacciones realizadas por un usuario.

Aquí están las capturas de pantalla de la aplicación.

2. Descripción general del ciclo de vida de los pagos PayTM

Completar el pago incluye la cantidad de llamadas entre la aplicación, el backend y el servidor PayTM. A continuación se muestra el ciclo de vida de una transacción desde el inicio hasta la finalización del pago.

1. Preparar orden: Tan pronto como el cliente haya seleccionado el artículo, el Elemento del carrito de compras se envía al servidor backend. Esto insertará una nueva línea de pedido o actualizará y generará una línea existente en db número de pedido único. Este ID de pedido debe ser único cada vez que se redirige al usuario a la pantalla de pago de PayTM. De lo contrario, PayTM emitirá un error de ID de pedido duplicado.

2. Número de pedido y suma de comprobación: El siguiente paso es generar una suma de comprobación teniendo en cuenta el ID del pedido. La suma de comprobación debe generarse teniendo en cuenta los campos obligatorios y ID de distribuidor.

3. Reenvío a PayTM: Una vez que se ha generado la suma de comprobación, el usuario será dirigido a la pantalla de pago de PayTM donde se encuentran varias opciones de pago como Débito directo[?]Tarjeta de crédito, cartera, Banca neta etc. Si en este paso la suma de comprobación generada es incorrecta, el usuario será redirigido a la aplicación con el estado de error.

Cuarto. Transacción bancaria: El usuario completa el pago seleccionando las opciones proporcionadas. Here PayTM se encarga de la comunicación con el banco y la finalización del pago.

5. Verificar transacción: Tan pronto como se complete la transacción, también se debe verificar en el backend Evite ser fraudulento Actas. Aquí es donde lo hace el servidor backend RIZO solicitud de Servidor PayTM para comprobar el estado de la transacción.

Sexto. Estado de la orden: La transacción estado es recibido por el backend y el estado del pedido correspondiente se muestra al usuario.

El siguiente diagrama muestra el flujo de comunicación entre las partes individuales. Aquí puede ver el ciclo de vida de la transacción en detalle.

3. Integración de la pasarela PayTM

En el escenario del mundo real, la creación de una aplicación de comercio electrónico requiere una arquitectura adecuada y precauciones de seguridad. Este artículo tiene como objetivo explicar el proceso de la manera más sencilla posible. Así que construí lo necesario para completar la integración de la pasarela de pago. La integración de PayTM (o cualquier otra puerta de enlace) implica los siguientes pasos.

3.1. Regístrese con PayTM y obtenga el Clave API.
3.2 Cree una aplicación de backend con el esquema de base de datos requerido y API REST. Integración de PayTM Server SDK.
3.3 Integración de PayTM Mobile SDK en la aplicación Android / iOS.

3.1 PayTM SandBox – Detalles sobre la API de prueba

Para comenzar con PayTM, puede registrar una nueva cuenta y crear una cuenta de prueba para probar la pasarela de pago. Siga los pasos a continuación para obtener el suyo Credenciales de sandbox.

1. Vaya al sitio web para desarrolladores de PayTM y continúe con los pasos necesarios para crear una nueva cuenta / iniciar sesión en una cuenta existente.

2. Después de registrarse, navegue hasta Clave API en el área de navegación izquierda.

3. Puede determinar los detalles de la API para examen y producción. Una vez que la aplicación se haya probado en una caja de arena, puede ponerse en contacto con el equipo de PayTM para ponerla en funcionamiento.

3.2 Creación de la API REST de backend

Elegí el marco Laravel (PHP) para crear el módulo de backend. En él escribí la API REST requerida para esta aplicación. El panel de administración no está integrado en este proyecto.

Esta aplicación backend / REST ya está activa y disponible públicamente para realizar pruebas.

URL base: https://demo.androidhive.info/paytm/public/api/

Vertedero del cartero: https://www.getpostman.com/collections/8b2e7763a8b7e0673918

3.3 Integración con el SDK de Android PayTM

A diferencia de mis tutoriales anteriores, en este artículo solo me centraré en cosas importantes. Puede encontrar el código de la aplicación completa en la página de Github. Estoy particularmente interesado en PayTMActivity.java, ya que contiene los componentes centrales del módulo PayTM.

El usuario es redirigido a la actividad de PayTM tan pronto como se hayan seleccionado los artículos del carrito de compras y se haga clic en Pagar. Esta actividad se comunica varias veces con el servidor backend para cumplir con el pedido. He escrito comentarios detallados sobre cada método utilizado.

Paso 1: La lista de artículos del carrito enviados al servidor para crear un nuevo pedido. Aquí llamamos / prepareOrder Punto final para generar el número de pedido único.

Paso 2: Tan pronto como se reciba el número de pedido, lo siguiente que lo llamaremos / getChecksum pasando el ID del trabajo junto con otros parámetros para generar el Hash de suma de comprobación.

Paso 3: Tan pronto como se haya recibido la suma de comprobación, lo llamaremos pgService.startPaymentTransaction () para redirigir al usuario a la pantalla de pago PayTM.

Paso 4: Una vez que se completa el pago, / Estado de la transacción se llama para verificar la transacción en el servidor. Estaremos aquí número de orden para comprobar el estado.

Paso 5: Una vez que se recibe el estado de la transacción, al usuario se le presentará la pantalla Exitosa o Fallida showOrderStatus () Método.

package info.androidhive.paytmgateway.ui.paytm;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
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 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 info.androidhive.paytmgateway.BuildConfig;
import info.androidhive.paytmgateway.R;
import info.androidhive.paytmgateway.app.Constants;
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 {
    @BindView(R.id.lbl_status)
    TextView lblStatus;

    @BindView(R.id.loader)
    AVLoadingIndicatorView loader;

    @BindView(R.id.icon_status)
    ImageView iconStatus;

    @BindView(R.id.status_message)
    TextView statusMessage;

    @BindView(R.id.title_status)
    TextView responseTitle;

    @BindView(R.id.btn_check_orders)
    TextView btnCheckOrders;

    @BindView(R.id.layout_order_placed)
    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
     */

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pay_tm);
        ButterKnife.bind(this);
        setToolbar();
        enableToolbarUpNavigation();
        getSupportActionBar().setTitle(getString(R.string.title_preparing_order));
        changeStatusBarColor();
        init();
    

    @Override
    public int getLayoutId() 
        return R.layout.activity_pay_tm;
    

    private void init() 
        realm = Realm.getDefaultInstance();
        realm.where(CartItem.class).findAllAsync()
                .addChangeListener(cartItems -> 

                );

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

        prepareOrder();
    

    private void setStatus(int message) 
        lblStatus.setText(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() 
        setStatus(R.string.msg_preparing_order);

        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 = cartItem.product.id;
            orderItem.quantity = cartItem.quantity;
            orderItems.add(orderItem);
        

        request.orderItems = orderItems;

        getApi().prepareOrder(request).enqueue(new Callback<PrepareOrderResponse>() 
            @Override
            public void onResponse(Call<PrepareOrderResponse> call, Response<PrepareOrderResponse> response) 
                if (!response.isSuccessful()) 
                    handleUnknownError();
                    showOrderStatus(false);
                    return;
                

                getChecksum(response.body());
            

            @Override
            public void onFailure(Call<PrepareOrderResponse> call, Throwable t) 
                handleError
                showOrderStatus(false);
            
        );
    

    /**
     * STEP 2:
     * Sending the params to server to generate the Checksum
     * that needs to be sent to PayTM
     */
    void getChecksum(PrepareOrderResponse response) 
        setStatus(R.string.msg_fetching_checksum);

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

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

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

                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);
                placeOrder(paramMap);
            

            @Override
            public void onFailure(Call<ChecksumResponse> call, Throwable t) 
                handleError
                showOrderStatus(false);
            
        );
    

    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_" + user.id);
        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) 
        setStatus(R.string.msg_redirecting_to_paytm);

        // 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() 
                    @Override
                    public void someUIErrorOccurred(String inErrorMessage) 
                        Timber.e("someUIErrorOccurred: %s", inErrorMessage);
                        finish();
                        // 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.
                    

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

                    @Override
                    public void networkNotAvailable()  // If network is not
                        Timber.e("networkNotAvailable");
                        finish();
                        // available, then this
                        // method gets called.
                    

                    @Override
                    public void clientAuthenticationFailed(String inErrorMessage) 
                        Timber.e("clientAuthenticationFailed: %s", inErrorMessage);
                        finish();
                        // 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.
                    

                    @Override
                    public void onErrorLoadingWebPage(int iniErrorCode,
                                                      String inErrorMessage, String inFailingUrl) 
                        Timber.e("onErrorLoadingWebPage: %d 

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

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

    /**
     * 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) 
        setStatus(R.string.msg_verifying_status);
        getApi().checkTransactionStatus(orderId).enqueue(new Callback<Order>() 
            @Override
            public void onResponse(Call<Order> call, Response<Order> response) 
                if (!response.isSuccessful()) 
                    Timber.e("Network call failed");
                    handleUnknownError();
                    showOrderStatus(false);
                    return;
                

                showOrderStatus(response.body().status.equalsIgnoreCase(Constants.ORDER_STATUS_COMPLETED));
            

            @Override
            public void onFailure(Call<Order> call, Throwable t) 
                handleError
                showOrderStatus(false);
            
        );
    

    /*
     * Displaying Order Status on UI. This toggles UI between success and failed cases
     * */
    private void showOrderStatus(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.color.colorGreen));
            responseTitle.setText(R.string.thank_you);
            statusMessage.setText(R.string.msg_order_placed_successfully);

            // as the order placed successfully, clear the cart
            AppDatabase.clearCart();
         else 
            iconStatus.setImageResource(R.drawable.baseline_close_black_48);
            iconStatus.setColorFilter(ContextCompat.getColor(this, R.color.btn_remove_item));
            responseTitle.setText(R.string.order_failed);
            statusMessage.setText(R.string.msg_order_placed_failed);
        

        layoutOrderPlaced.setVisibility(View.VISIBLE);
    

    @OnClick(R.id.btn_check_orders)
    void onOrdersClick() 
        startActivity(new Intent(PayTMActivity.this, TransactionsActivity.class));
        finish();
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        if (item.getItemId() == android.R.id.home) 
            finish();
            return true;
        
        return super.onOptionsItemSelected(item);
    

    @Override
    protected void onDestroy() 
        super.onDestroy();

        if (realm != null) 
            realm.removeAllChangeListeners();
            realm.close();
        
    
}

4. Pruebe la aplicación

Una vez que haya completado la integración de PayTM, puede probar su aplicación (o la aplicación proporcionada en este artículo) utilizando las credenciales del modo de prueba proporcionadas.

5. Referencias

A continuación se muestran algunos enlaces útiles que puede tener a mano mientras trabaja en este proyecto.
1. Documentación del SDK de Android PayTM
2. Documentación de la API REST de PayTM
3. Credenciales para el modo de prueba PayTM

Espero que este artículo haya simplificado el proceso de integración de PayTM. Si tiene alguna pregunta o sugerencia, publíquela en la sección de comentarios a continuación.

Diviértete codificando 🙂

¡Hola! Soy el fundador entusiasta de la programación. Mis habilidades incluyen Android, iOS, PHP, Ruby on Rails y mucho más. Si tienes alguna idea que debo desarrollar? Hablemos: [email protected]

LEER  Apple acaba de aclarar los métodos de pago alternativos en iOS. Spoiler: Apple todavía cobra una comisión.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba