Ariel Rey

Construyendo los pagos del futuro

MercadoPago

Una mirada al presente ¿o a los días que fueron futuro?

Corría diciembre de 2016 cuando ingresé a Mercado Libre 🙌. Sabía que iba a trabajar en Mercado Pago pero no que iba a ser para el equipo de Checkout. ¿Y qué es lo que hacemos? Somos responsables de construir la plataforma de pagos web de Mercado Pago, que llamamos Checkout, usada por importantes marcas y gran variedad de emprendimientos para procesar sus pagos de manera online.

En ese mismo año, en Mercado Libre nos encontrábamos en una época de transición. En ese entonces, todas nuestras aplicaciones eran monolíticas 😟, es decir, tanto la lógica de negocio como las responsabilidades de frontend estaban unificadas en una sola aplicación web. Por ello, parte de la transición tuvo como objetivo separar estas dos partes y migrar cada una a distintos stacks tecnológicos.

alt

Separación de responsabilidades en la transición

Al entrar al equipo ya sabía que íbamos a crear una plataforma nueva. Pero como la plataforma de pagos actual funcionaba, no podía evitar preguntarme: ¿por qué debíamos hacer una plataforma nueva? Uno de los motivos era la transición, y por otro lado, la necesidad de renovar la tecnología que se encontraba en el stack viejo (Groovy / Grails). Pero este no era el principal motivo, la motivación real era mucho más grande y desafiante: sabíamos que podíamos hacerlo mejor, que queríamos superarnos y que éramos capaces de llevar la experiencia de nuestros vendedores/ras con sus compradores/as a otro nivel.

Lo primero que hicimos fue relevar técnicamente la aplicación actual. Aquí fue cuando nos dimos cuenta de que teníamos duplicada la lógica de negocio. ¿Qué quiere decir esto? Teníamos condicionales (ifs) en el backend (.groovy) que se repetían en el frontend (.gsp). Esto nos hacía más propensos a cometer errores e incrementaba el tiempo de resolución al tener que buscarlos tanto en los controladores como en las vistas.

[“…nos dimos cuenta que teníamos duplicada la lógica de negocio”]

Al momento de escribir este articulo, Mercado Pago opera en Argentina 🇦🇷, Brasil 🇧🇷, México 🇲🇽, Colombia 🇨🇴, Chile 🇨🇱, Perú 🇵🇪, Uruguay 🇺🇾 y Venezuela 🇻🇪. Cada país tiene sus normas, bancos, documentos de identidad, medios de pago e idioma. Esto implica particularidades, tanto en el backend como en el frontend. Por ejemplo, a la hora de validar los documentos de identificación, cada uno tiene su propio patrón de validación: algunos pueden resolverse mediante expresiones regulares y otros mediante funciones. Sumado a esto, en el frontend tenemos que mostrar mensajes adecuados para cada país, ya sea por cuestiones de idioma, localizaciones, normas o regulaciones.

===

Si querés conocer más sobre el proceso de transición tecnológico en Mercado Libre, mirá la presentación The road to Node realizada por Guille Paz en la NodeConf Argentina 2017.

[YouTube huH_TyPDBpU]

Nuestra Keystone o Piedra Angular: el backend

alt

Para solventar la problemática que teníamos sobre la lógica duplicada, decidimos que nuestra piedra angular sea centrar la lógica de negocio en el backend. Como seguro han escuchado por ahí, un gran poder conlleva una gran responsabilidad, por lo que si bien esto solucionaba el mayor problema que teníamos, también generaba uno nuevo…

Vamos a suponer que una persona de Argentina realiza una compra online en una tienda de su país 🇦🇷. Primero, el frontend deberá procesar los datos de su tarjeta y, luego, los de su documento. En Argentina hay varios documentos válidos: DNI, pasaporte, cédula, libreta de enrolamiento, etc. Para cada país, las opciones cambian.

Por eso, el frontend debe saber qué información mostrar según diferentes situaciones. Pero, si agregamos esta lógica al frontend, estaríamos nuevamente duplicando código, justo lo que queríamos evitar. Además, todos los datos enviados desde el frontend deben ser validados por el backend por cuestiones de seguridad. Tengan en cuenta que el frontend puede ser manipulado, y si quisiéramos, podríamos modificar el valor para enviar otro.

Entonces, ¿cómo hacer que el frontend sepa qué mostrar si no tiene dentro de sí la lógica de negocio?

En los siguientes párrafos, quiero contarte cómo respondimos esta pregunta.

Punto de partida :  ¿cómo nos integramos?

Antes de pasar a la arquitectura, voy a explicar de manera rápida cómo se realiza una integración con Mercado Pago. Con esto me refiero a cómo es el proceso de agregar una pasarela de pagos provista por Mercado Pago, también llamada Checkout, a una tienda online. Con tienda online nos referimos a un sitio en donde se venda un producto o servicio y con pasarela de pago nos referimos a una interfaz en donde el usuario ingresa todos los datos necesarios para realizar el pago (documento, número de tarjeta, etc…).

Para iniciar el Checkout es necesario crear una preferencia mediante una API pública. Una preferencia contiene toda la información del producto o servicio que se va a pagar. Por ejemplo, el monto, la descripción, los datos del comprador/a y los medios a aceptar

Al crear la preferencia, se obtiene una URL que contiene un ID de preferencia. Con esta URL, los vendedores/ras podrán permitir a sus compradores/as acceder al Checkout y completar un pago.

https://www.mercadopago.com.ar/checkout/v1/redirect?preference-id=XXXXXXX-XXXXXX-XXXXX

La Arquitectura

Aquitectura

Representación Gráfica

Tal como Neo, llegó el momento en el que nos encontramos con el Arquitecto. Es aquí donde tuvimos que tomar una decisión. Siguiendo con la separación de responsabilidades, decidimos dividir la aplicación en dos.

Por un lado tenemos el backend, el encargado de toda la lógica de negocio y de realizar la comunicación con todos los micro servicios de Mercado Pago. Y por el otro, vamos a tener el frontend, que se comunica con el backend y con el comprador/a.

alt

La imagen anterior nos muestra que el primer paso del proceso de integración con Mercado Pago consiste en una comunicación iniciada desde el navegador (por el comprador/a) hacia el frontend, con un solo dato: el ID de preferencia creada. Luego, este ID es propagado hacia backend, donde se realiza la creación del flujo.

Es aquí donde empiezan a jugar las reglas de negocio. Al crear la preferencia utilizando las credenciales del usuario/a integrador (esto es, las claves personales que Mercado Pago provee a cada usuario/a de la plataforma) podemos saber a qué país pertenece. Con todos los datos del producto y el país, el backend es capaz de determinar cuál es el paso inicial al que debe redirigir al usuario/a.

Hay que entender que no todos los flujos son iguales y no todos son lineales. Por ejemplo:

  • Si el vendedor/ra excluyó todos los medios de pago menos tarjeta de crédito, debemos redirigir al usuario/a al paso de carga de tarjeta de crédito, ya que es el único medio de pago disponible.
  • Si el vendedor/ra, por el contrario, decide ofrecer todos los medios de pagos disponibles debemos redirigir al usuario/a al paso en el que el comprador/a selecciona el medio de pago.
  • Si el usuario/a está autenticado/a en Mercado Pago y tiene suficiente dinero en su cuenta podemos ofrecerle pagar con el mismo. De esta manera, solo la/lo llevamos al resumen de su compra para finalizar.

[“Existen muchos flujos y cada uno de ellos dependerá de las reglas de negocio”]

No importa a qué paso se redirija al usuario, el frontend será el encargado de dibujar la pantalla.

Traspaso de responsabilidades

Ahora sí, ¿cómo hacemos para que el frontend pueda dibujarse sin tener la información para hacerlo? 🤔

alt

Representación gráfica del frontend

Cuando tomamos la decisión de que el backend tuviera toda la lógica de negocio, podríamos decir que vendamos al frontend y lo pusimos al mando del volante. Es por esto que el backend será el encargado de decirle al frontend, basado en la lógica de negocio, qué página y qué componentes deberá mostrar. Podríamos ejemplificar esto pensando en que el frontend es un conductor de Rally 🚗 y el backend el copiloto que le da indicaciones.

Para comprender cómo logramos esto, primero tenemos que saber cómo funciona internamente.

Primero, construimos el backend como una máquina de estados. Cuando se hace una primera conexión con el backend, éste crea un nuevo flujo que posiciona un puntero en un estado inicial. Ese puntero luego se irá moviendo hacia otros estados. Cada estado representa un paso del Checkout y es enviado al frontend en conjunto con los componentes a mostrar.

De esta manera, cuando un usuario/a ingresa a un Checkout, el puntero se colocará en la pantalla que represente el primer estado, por ejemplo, en un formulario de pedido de tarjeta.

Una vez que el usuario/a hace click en el botón “Continuar” del formulario, los datos son enviados al backend. Éste procesa los datos, los valida y, si está todo bien, mueve el puntero al siguiente estado. El proceso se repite hasta llegar a un estado final. Así es como se vería un flujo completo:

alt

De este modo, garantizamos que todo el proceso de compra sea orquestado por el backend sin perder el estado del flujo entre los distintos pasos. Y entonces, ¿qué recibe el frontend?

Un frontend basado en Optimus Prime 🤖

Si hay algo que nos quedó claro hasta ahora, es que el frontend es pariente de Sócrates, su existencia está basada en la frase solo sé que no se nada.

Lo primero que hicimos fue pensar de qué manera se comunicarían con él. Las dos principales rutas son:

  1. /
  2. /id-de-flujo/página

La primer ruta ( / ) sería la encargada de realizar la creación del flujo e iniciar la máquina de estados. Este flujo recibirá un ID de preferencia y se lo enviará al backend. Como respuesta, el backend le dirá cual es el ID del Flujo y a qué página debe dirigir al usuario (esto es, la ruta número 2)

Con estos datos, el frontend redireccionará a la segunda ruta (/id-de-flujo/pagina) y, por último, el backend responderá con los componentes que debe dibujar el frontend de cara al usuario. Los componentes son enviados al frontend de la siguiente manera:

alt

Cada uno de los componentes enviados tiene una serie de propiedades. La más importante es el type, que identifica el tipo de componente a dibujarse. Aquí es donde comienza a trabajar el motor Optimus 🤖. Este motor se encarga de transformar 😅 los componentes enviados por el backend en componentes de HTML. Optimus 🤖 contiene un gran diccionario en el que cada type hace referencia a un componente de HTML (React), al cual se le pasaran los atributos mediante propiedades (props): text, label, value, show…

Para los más técnicos 🤓, a continuación se muestra un ejemplo de la declaración de un componente de React del tipo title.

alt

Continuando con el ejemplo, estos dos componentes se dibujaran en el frontend de la siguiente manera:

alt

Componentes convertidos a HTML

El frontend es una gran librería de componentes en la que, mediante el motor transformador de Optimus 🤖, se dibujan pantallas sin conocer el porqué. Siguiendo con las analogías, podemos decir que el frontend tiene todas las piezas de un rompecabezas y el backend sabe cómo construirlo.

Costo y Beneficio

Luego de casi dos años de desarrollo (al momento de escribir la nota) pudimos identificar que este enfoque tiene sus ventajas pero también sus contras.

Menos esfuerzo del frontend

Cuando empezamos a desarrollar la plataforma, los esfuerzos de ambos equipos estaban equiparados. Pero al pasar de los meses, fuimos creando todos los componentes básicos y esto provocó que el esfuerzo de frontend fuera disminuyendo.

A un año de desarrollo ya contábamos con la mayoría de los componentes, teníamos estandarizadas las respuestas del backend y aceitado el proceso de definición. Actualmente, el desarrollo del frontend pasó a modo ad-hoc o a pedido 👷. Es decir, lo hacemos solamente si se necesita crear componentes nuevos o deuda técnica.

alt

Esfuerzo de desarrollo de frontend y backend

No obstante, si bien el esfuerzo disminuye, la complejidad aumenta. Por ejemplo, si un mismo componente debe verse distinto dependiendo de la página. Lograr esto es complicado y más complejo que hacer un nuevo componente, pero a la vez implica una oportunidad para desafiarse a uno mismo y encontrar la mejor manera de hacerlo.

Mayor complejidad de backend

Si bien el esfuerzo del frontend disminuye a medida que pasa el tiempo, el trabajo del lado de backend se mantiene constante 😅. El frontend es el encargado de dibujar las pantallas pero el backend es el encargado de decirle cómo 😆. En resumen, aunque el frontend pueda reutilizar sus componentes y no tenga que hacer trabajo extra, desde el backend siempre debe especificarse cómo dibujar esta pantalla.

Ahora, los equipos que construyen el backend no solo deben tener en cuenta la lógica de negocio, sino también qué debería mostrar el frontend en cada escenario posible. Esto agrega dificultad a la hora de desarrollar y al momento de probar nuestro código, ya que tenemos que realizar tests para entender cuales son los componentes visuales que van a retornar.

Recapitulando

En conclusión, al separar responsabilidades logramos encapsular la lógica de negocio en una sola pieza de la aplicación. Así, pudimos evitar las duplicaciones y minimizar la probabilidad de cometer errores.

Vimos que no había una opción perfecta: la nueva lógica permitió crear pantallas de manera más simple y rápida, aún cuando significó agregar responsabilidad y complejidad al backend.

Antes de terminar, me gustaría agradecer al equipo de Checkout OFF Web (COW 🐮) responsable de la construcción del mismo 💪

Hola 👋 mi nombre es Ari Rey y soy parte del equipo de IT en @MercadoLibre @MercadoPago. En Mercado Libre estamos todos los días innovando, desafiándonos y buscando nuevas maneras de hacer las cosas. Si querés saber más de lo que estamos haciendo podes seguirnos en LinkedIn. En cuanto a mí, podes seguirme en Twitter y en Github.