From 42c5aecc999452fff618380a25ac2f1c0fa3c45f Mon Sep 17 00:00:00 2001 From: mvarlic Date: Fri, 24 Oct 2025 14:53:04 -0500 Subject: [PATCH 01/39] feat: add oneclick deferred --- .../OneclickMallDeferredController.java | 272 ++++++++++++++++++ src/main/resources/templates/index.html | 179 +++++++----- .../oneclick_mall_deferred/authorize.html | 104 +++++++ .../oneclick_mall_deferred/capture.html | 82 ++++++ .../oneclick_mall_deferred/delete.html | 56 ++++ .../oneclick_mall_deferred/finish.html | 109 +++++++ .../oneclick_mall_deferred/refund.html | 68 +++++ .../oneclick_mall_deferred/start.html | 95 ++++++ .../oneclick_mall_deferred/status.html | 39 +++ .../resources/templates/partials/sidebar.html | 67 +++-- 10 files changed, 977 insertions(+), 94 deletions(-) create mode 100644 src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java create mode 100644 src/main/resources/templates/oneclick_mall_deferred/authorize.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/capture.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/delete.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/finish.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/refund.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/start.html create mode 100644 src/main/resources/templates/oneclick_mall_deferred/status.html diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java new file mode 100644 index 0000000..a9fc849 --- /dev/null +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -0,0 +1,272 @@ +package cl.transbank.webpay.example.controllers; + +import cl.transbank.common.IntegrationApiKeys; +import cl.transbank.common.IntegrationCommerceCodes; +import cl.transbank.common.IntegrationType; +import cl.transbank.webpay.common.WebpayOptions; +import cl.transbank.webpay.exception.*; +import cl.transbank.webpay.oneclick.Oneclick; +import cl.transbank.webpay.oneclick.model.MallTransactionCreateDetails; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Log4j2 +@Controller +@RequestMapping("/oneclick-mall-diferido") +public class OneclickMallDeferredController extends BaseController { + + private static final String TEMPLATE_FOLDER = "oneclick_mall_deferred"; + private static final String BASE_URL = "/oneclick-mall-deferred"; + private static final String PRODUCT = "Oneclick Mall Diferido"; + + private static final String VIEW_START = TEMPLATE_FOLDER + "/start"; + private static final String VIEW_FINISH = TEMPLATE_FOLDER + "/finish"; + private static final String VIEW_AUTHORIZE = TEMPLATE_FOLDER + "/authorize"; + private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; + private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + private static final String VIEW_CAPTURE = TEMPLATE_FOLDER + "/capture"; + private static final String VIEW_DELETE = TEMPLATE_FOLDER + "/delete"; + + private final Oneclick.MallInscription inscription; + private final Oneclick.MallTransaction transaction; + + + /* + private static final Map NAV_START; + private static final Map NAV_FINISH; + private static final Map NAV_DELETE; + private static final Map NAV_AUTHORIZE; + private static final Map NAV_STATUS; + private static final Map NAV_CAPTURE; + private static final Map NAV_REFUND; + + static { + NAV_START = new LinkedHashMap<>(); + NAV_START.put("request", "Petición"); + NAV_START.put("response", "Respuesta"); + NAV_START.put("form", "Formulario"); + + NAV_FINISH = new LinkedHashMap<>(); + NAV_FINISH.put("data", "Datos recibidos"); + NAV_FINISH.put("request", "Petición"); + NAV_FINISH.put("response", "Respuesta"); + NAV_FINISH.put("operations", "¡Listo!"); + + NAV_COMMIT = new LinkedHashMap<>(); + NAV_COMMIT.put("data", "Datos recibidos"); + NAV_COMMIT.put("request", "Petición"); + NAV_COMMIT.put("response", "Respuesta"); + NAV_COMMIT.put("operations", "¡Listo!"); + + NAV_STATUS = new LinkedHashMap<>(); + NAV_STATUS.put("request", "Petición"); + NAV_STATUS.put("response", "Respuesta"); + + NAV_REFUND = NAV_STATUS; + }*/ + + public OneclickMallDeferredController() { + var options = new WebpayOptions( + IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED, + IntegrationApiKeys.WEBPAY, + IntegrationType.TEST + ); + inscription = new Oneclick.MallInscription(options); + transaction = new Oneclick.MallTransaction(options); + } + + private void addBreadcrumbs(Model model, String label, String url) { + Map breadcrumbs = new LinkedHashMap<>(); + breadcrumbs.put("Inicio", "/"); + breadcrumbs.put(PRODUCT, BASE_URL + "/start"); + if (label != null) breadcrumbs.put(label, url); + model.addAttribute("product", PRODUCT); + model.addAttribute("breadcrumbs", breadcrumbs); + } + + @GetMapping("/start") + public String start(HttpServletRequest req, Model model) + throws IOException, InscriptionStartException { + + addBreadcrumbs(model, "Iniciar inscripción", "#"); + + String username = "user_" + getRandomNumber(); + String email = "user." + getRandomNumber() + "@example.com"; + String returnUrl = req.getRequestURL().toString().replace("start", "finish"); + + var resp = inscription.start(username, email, returnUrl); + + model.addAttribute("request_data", Map.of( + "username", username, + "email", email, + "returnUrl", returnUrl + )); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute("url", resp.getUrlWebpay()); + model.addAttribute("token", resp.getToken()); + + // Guardar en sesión + req.getSession().setAttribute("username", username); + req.getSession().setAttribute("email", email); + + return VIEW_START; + } + + @GetMapping("/finish") + public String finish(HttpServletRequest req, + @RequestParam(name = "TBK_TOKEN", required = false) String token, + Model model) + throws IOException, InscriptionFinishException { + + addBreadcrumbs(model, "Finalizar inscripción", "#"); + + String username = (String) req.getSession().getAttribute("username"); + String email = (String) req.getSession().getAttribute("email"); + + var resp = inscription.finish(token); + + req.getSession().setAttribute("tbkUser", resp.getTbkUser()); + + model.addAttribute("request_data", Map.of( + "username", username, + "tbkUser", resp.getTbkUser() + )); + + model.addAttribute("token", token); + model.addAttribute("username", username); + model.addAttribute("tbk_user", resp.getTbkUser()); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + model.addAttribute("child_commerce_code1", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD1); + model.addAttribute("child_commerce_code2", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD2); + + return VIEW_FINISH; + } + + @GetMapping("/delete") + public String delete(@RequestParam String username, + @RequestParam("tbk_user") String tbkUser, + Model model) + throws IOException, InscriptionDeleteException { + + addBreadcrumbs(model, "Eliminar inscripción", "#"); + + inscription.delete(tbkUser, username); + model.addAttribute("message", "Inscripción eliminada exitosamente."); + + return VIEW_DELETE; + } + + @GetMapping("/authorize") + public String authorize( + @RequestParam String username, + @RequestParam("tbk_user") String tbkUser, + @RequestParam("child_commerce_code1") String childCode1, + @RequestParam("child_commerce_code2") String childCode2, + @RequestParam("child_commerce_amount1") double amount1, + @RequestParam("child_commerce_amount2") double amount2, + @RequestParam("child_commerce_installments1") int installments1, + @RequestParam("child_commerce_installments2") int installments2, + Model model) + throws IOException, TransactionAuthorizeException { + + addBreadcrumbs(model, "Autorizar transacción", "#"); + + String buyOrder = "buyOrder_" + getRandomNumber(); + String childBuyOrder1 = "childOrder1_" + getRandomNumber(); + String childBuyOrder2 = "childOrder2_" + getRandomNumber(); + + var details = MallTransactionCreateDetails + .build() + .add( + amount1, + childCode1, + childBuyOrder1, + (byte) installments1 + ) + .add( + amount2, + childCode2, + childBuyOrder2, + (byte) installments2 + ); + + var resp = transaction.authorize(username, tbkUser, buyOrder, details); + + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_AUTHORIZE; + } + + @GetMapping("/status") + public String status(@RequestParam("buy_order") String buyOrder, Model model) + throws IOException, TransactionStatusException { + + addBreadcrumbs(model, "Consultar estado", "#"); + + var resp = transaction.status(buyOrder); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_STATUS; + } + + @GetMapping("/refund") + public String refund(@RequestParam("buy_order") String buyOrder, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam double amount, + Model model) + throws IOException, TransactionRefundException { + + addBreadcrumbs(model, "Reembolso", "#"); + + model.addAttribute("buy_order", buyOrder); + var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_REFUND; + } + + @GetMapping("/capture") + public String capture( + @RequestParam("buy_order") String buyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("authorization_code") String authorizationCode, + @RequestParam double amount, + Model model) + throws IOException, TransactionCaptureException { + + addBreadcrumbs(model, "Capturar", "#"); + + model.addAttribute("buy_order", buyOrder); + model.addAttribute("child_buy_order", childBuyOrder); + model.addAttribute("child_commerce_code", childCommerceCode); + + var resp = transaction.capture(childCommerceCode, childBuyOrder, authorizationCode, amount); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_CAPTURE; + } + + @ExceptionHandler(Exception.class) + public String handleException(Exception e, Model model) { + log.error("Error inesperado", e); + model.addAttribute("error", e.getMessage()); + return VIEW_ERROR; + } +} diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index dedf0e3..0e2c7f8 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -1,100 +1,123 @@ - - Transbank Sdk Java Example - - - + + Transbank Sdk Java Example + + + - - + + /> - + - + - - - + - - -
-
-
-
-

- Proyectos de Ejemplo del SDK para Java -

-
+ gtag("config", "G-6CW8MF50ZX"); + + + +
+
+
+
+

+ Proyectos de Ejemplo del SDK para Java +

+
+ JavaLogo +

+ Este proyecto te brinda la oportunidad de + experimentar con las diversas modalidades de + productos que Transbank ofrece a través de su SDK + compatible con Java. Conoce de manera práctica las + soluciones y servicios que Transbank pone a tu + disposición, permitiéndote comprender cómo integrar + estas herramientas tecnológicas en tus proyectos y + aplicaciones. ¡Explora las opciones disponibles y + descubre cómo aprovechar al máximo estas + capacidades! +

+
+ +
+ +
+
+
JavaLogo -

- Este proyecto te brinda la oportunidad de - experimentar con las diversas modalidades de - productos que Transbank ofrece a través de su SDK - compatible con Java. Conoce de manera práctica las - soluciones y servicios que Transbank pone a tu - disposición, permitiéndote comprender cómo integrar - estas herramientas tecnológicas en tus proyectos y - aplicaciones. ¡Explora las opciones disponibles y - descubre cómo aprovechar al máximo estas - capacidades! +

+ El producto más usado para realizar un pago + online. Se genera un único cobro para todos + los productos o servicios adquiridos por el + tarjetahabiente (carro de compras).

+ Ver ejemplos y modalidades +
-
- -
-
-
- webpay -

- El producto más usado para realizar un pago - online. Se genera un único cobro para todos - los productos o servicios adquiridos por el - tarjetahabiente (carro de compras). -

-
- Ver ejemplos y modalidades -
+
+
+ oneclick +

+ El producto más usado para realizar un pago + online. Se genera un único cobro para todos + los productos o servicios adquiridos por el + tarjetahabiente (carro de compras). +

- -
- -
+ Ver ejemplos y modalidades
-
+ +
+ +
- +
+
+
+ diff --git a/src/main/resources/templates/oneclick_mall_deferred/authorize.html b/src/main/resources/templates/oneclick_mall_deferred/authorize.html new file mode 100644 index 0000000..d1cb4bc --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/authorize.html @@ -0,0 +1,104 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Autorizar pago

+ +

+ En este primer paso, procederemos a autorizar una transacción en la tarjeta que ha sido previamente + inscrita. +

+ +

Paso 1: Petición

+ +

+ Ahora que contamos con el username y el tbkUser obtenidos durante la inscripción, + estamos listos para autorizar transacciones en la tarjeta inscrita. +

+ +

+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+var details = MallTransactionCreateDetails
+    .build()
+    .add(
+            amount1,
+            childCode1,
+            childBuyOrder1,
+            (byte) installments1
+    )
+    .add(
+            amount2,
+            childCode2,
+            childBuyOrder2,
+            (byte) installments2
+    );
+var resp = transaction.authorize(username, tbkUser, buyOrder, details);
+    
+ +

Paso 2: Respuesta

+ +

+ Una vez que la transacción ha sido autorizada, Transbank proporcionará la siguiente información. + Es fundamental conservar esta respuesta y verificar que el campo responseCode tenga un valor de + 0 y que el campo status sea AUTHORIZED. +

+ +
+ +

¡Casi listo!

+ +

+ Ya puedes mostrar al usuario una página de éxito de la transacción. + Debes tener en cuenta que la transacción aún no ha sido capturada, solo se ha retenido el saldo en la + tarjeta del Tarjetahabiente. +

+ +
+
+ + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+
+ + + CONSULTAR ESTADO + + +
+
diff --git a/src/main/resources/templates/oneclick_mall_deferred/capture.html b/src/main/resources/templates/oneclick_mall_deferred/capture.html new file mode 100644 index 0000000..88f2818 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/capture.html @@ -0,0 +1,82 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Capturar transacción diferida

+ +

+ En este paso debemos capturar la transacción para realmente capturar el dinero que había sido + previamente reservado al hacer la transacción. +

+ +

Paso 1: Petición

+ +

+ Para capturar una transacción necesitaremos el código de comercio de la tienda hija, la orden de compra + de la tienda hija, el código de autorización y el monto a capturar. Se hace de la siguiente manera: +

+ +

+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+var response = transaction.capture(childCommerceCode, childBuyOrder, authorizationCode, amount);
+    
+ +

Paso 2: Respuesta

+ +

+ Una vez creada la transacción, recibirás los siguientes datos de respuesta: +

+ +
+ +

¡Transacción Capturada!

+

+ Con la transacción capturada, puedes mostrar al usuario una página de éxito de la transacción, + proporcionándole la confirmación de que el proceso se ha completado con éxito. +

+

+ Otras Utilidades: Después de confirmar la transacción, considera las siguientes utilidades + adicionales: +

+

+ Reembolso: Evalúa la posibilidad de reversar o anular el pago según ciertas condiciones + comerciales. +

+

+ Consulta de Estado: Hasta 7 días después de la transacción, puedes consultar su estado para + obtener más detalles. +

+ +
+ + + + +
+
+ + +
+ +
+ + + CONSULTAR ESTADO + +
+
+
+ +
+
diff --git a/src/main/resources/templates/oneclick_mall_deferred/delete.html b/src/main/resources/templates/oneclick_mall_deferred/delete.html new file mode 100644 index 0000000..ade7195 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/delete.html @@ -0,0 +1,56 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Borrar usuario

+ +

+ En este paso fundamental, procederemos a eliminar la inscripción del usuario y su medio de pago. +

+ +

Paso 1: Petición

+

+ Para llevar a cabo la eliminación, necesitas el "username" (Nombre de Usuario) y el "tbkUser". + Realiza la llamada a MallInscription utilizando el siguiente código: +

+ +
+      
+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+      inscription.delete(tbkUser, username);
+      
+    
+ +

Paso 2: Respuesta

+

+ En caso de éxito, Transbank responderá con un status code 204 (No Content), y el SDK no retornará ningún + valor. + La eliminación de la inscripción se ha realizado de manera exitosa. +

+ +

+ En el caso de que no se encuentre el "username" o el "tbkUser", Transbank responderá con un status code 404 + (Not + Found), + y el SDK lanzará una excepción del tipo TransbankException. +

+ +

+ Este proceso garantiza una eliminación segura y eficiente de la inscripción del usuario y su medio de pago + asociado. + ¡Gracias por confiar en Transbank para tus operaciones seguras! Si tienes alguna pregunta, estamos aquí para + ayudarte. +

+ +
+
\ No newline at end of file diff --git a/src/main/resources/templates/oneclick_mall_deferred/finish.html b/src/main/resources/templates/oneclick_mall_deferred/finish.html new file mode 100644 index 0000000..025aaf8 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/finish.html @@ -0,0 +1,109 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Finalizar inscripción

+

+ En esta fase, completaremos el proceso de inscripción, permitiéndonos posteriormente realizar cargos a la + tarjeta que el Tarjetahabiente haya inscrito. +

+ +

Paso 1: Datos recibidos

+

+ Después de finalizar el flujo en el formulario de inscripción, recibirás un GET con la siguiente + información: +

+ +

+{
+  "TBK_TOKEN": ""
+}
+    
+ +

Paso 2: Petición de autorización

+

+ Utiliza el token recibido para finalizar la inscripción mediante una nueva llamada a Oneclick. +

+ +

+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+var response = inscription.finish(token);
+    
+ +

Paso 3: Respuesta

+

+ Transbank responderá con información crucial. Guarda estos detalles, ya que serán necesarios para autorizar + transacciones futuras. +

+ +
+ +

¡La tarjeta ya está inscrita!

+

Con la inscripción exitosa se pueden autorizar transacciones.

+ +

Autorizar una transacción

+

+ Asegúrate de guardar los datos de la respuesta obtenidos durante la inscripción. Estos serán esenciales para + llevar a cabo transacciones de manera efectiva. +

+ +
+ +

+ Después de una inscripción exitosa, tienes dos opciones: autorizar un pago o borrar al usuario que se + acaba de inscribir. +

+ +
+ + + + + +
+

Tienda 1

+
+
+ + +
+
+ + +
+
+ +

Tienda 2

+
+
+ + +
+
+ + +
+
+ + +
+
+ + + BORRAR USUARIO + +
+
diff --git a/src/main/resources/templates/oneclick_mall_deferred/refund.html b/src/main/resources/templates/oneclick_mall_deferred/refund.html new file mode 100644 index 0000000..69a0724 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/refund.html @@ -0,0 +1,68 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Reembolsar

+ +

+ En esta etapa, tienes la opción de solicitar el reembolso del monto al titular de la tarjeta. + Dependiendo del monto y el tiempo transcurrido desde la autorización, este proceso podría resultar + en una Reversa o Anulación, dependiendo de ciertas condiciones + (Reversa en las primeras 3 horas de la autorización, Anulación posterior a eso), + o una Anulación parcial si el monto es menor al total. + Las anulaciones parciales para tarjetas débito y prepago no están soportadas. +

+ +

Paso 1 - Petición

+ +

+ Para llevar a cabo el reembolso, necesitas proporcionar la orden de compra de la transacción, + el código de comercio de la tienda, la orden de compra del detalle de la transacción y el monto que + deseas reversar. + Si anulas el monto total, podría ser una Reversa o Anulación, dependiendo de ciertas + condiciones + (Reversa en las primeras 3 horas de la autorización, Anulación posterior a eso), + o una Anulación Parcial si el monto es menor al total. +

+ +

+ Algunas consideraciones a tener en cuenta: +

+
    +
  • No es posible realizar Anulaciones Parciales en pagos con cuotas.
  • +
+ +

+ En + + este link + + podrás ver mayor información sobre las condiciones y casos para anular o reversar transacciones. +

+ +

+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount);
+    
+ +

Paso 2: Respuesta

+ +

+ Transbank responderá con el resultado del proceso de reembolso, indicando si se ha realizado + una Reversa, Anulación o Anulación Parcial. +

+ +
+ + + CONSULTAR ESTADO + + +
+
diff --git a/src/main/resources/templates/oneclick_mall_deferred/start.html b/src/main/resources/templates/oneclick_mall_deferred/start.html new file mode 100644 index 0000000..7f2705f --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/start.html @@ -0,0 +1,95 @@ +
+
+ +

Webpay Oneclick Mall Diferido - Creación de Inscripción

+

+ En esta etapa comienza el proceso de inscripción del medio de pago. Este paso inicial es fundamental + para dirigir al Tarjetahabiente al formulario de inscripción. +

+ +

+ Todas las transacciones en este proyecto de ejemplo son realizadas en + ambiente de integración. +

+ +

Paso 1: Petición

+
    +
  1. Comienza por importar la librería Oneclick en tu proyecto.
  2. +
  3. Luego, inicia una inscripción utilizando las funciones proporcionadas mediante el SDK.
  4. +
+ +

+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+var resp = inscription.start(username, email, responseUrl);
+    
+ +

Paso 2: Respuesta

+

+ Una vez que hayas iniciado la inscripción, aquí encontrarás los datos de respuesta generados por el proceso. +

+ +
+ +

Paso 3: Creación del formulario

+

+ Utiliza estos datos de respuesta para redireccionar al usuario al formulario de inscripción del + Tarjetahabiente. + Este formulario será la interfaz a través de la cual el usuario completará su inscripción. +

+ +

+
+ +

Ejemplo

+

+ Para llevar a cabo una inscripción en nuestro sistema, primero debemos crearla. Utilizaremos + los siguientes datos para configurar la inscripción: +

+ +
+ +

+ Por último, con la respuesta del servicio que confirma la creación de la inscripción, procedemos a crear el + formulario de redirección. Para fines de este ejemplo, haremos visible el campo "TBK_TOKEN", el cual es + esencial + para completar el proceso de inscripción de manera exitosa. +

+ + + Antes de continuar al formulario de Webpay, asegúrate de contar con los datos de las tarjetas de prueba que están en la + documentación + + +
+
+ Formulario de redirección + + + +
+
+
+
diff --git a/src/main/resources/templates/oneclick_mall_deferred/status.html b/src/main/resources/templates/oneclick_mall_deferred/status.html new file mode 100644 index 0000000..c86c00b --- /dev/null +++ b/src/main/resources/templates/oneclick_mall_deferred/status.html @@ -0,0 +1,39 @@ +
+
+

Webpay Oneclick Mall Diferido - Consultar estado de transacción

+ +

+ Puedes solicitar el estado de una transacción hasta 7 días después de su realización. + No hay límite de solicitudes durante ese período. + Sin embargo, una vez pasados los 7 días, ya no podrás revisar su estado. +

+ +

Paso 1 - Petición:

+

+ Para realizar la consulta, necesitarás el buyOrder de la transacción de interés. + Utiliza este identificador para efectuar una llamada a MallTransaction. +

+ +
+  
+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+  var response = transaction.status(buyOrder);
+  
+
+ +

Paso 2: Respuesta

+

+ Transbank responderá con la siguiente información. + Asegúrate de guardar esta respuesta, y la única validación necesaria es que el campo + responseCode sea igual a 0. +

+ +
+ +
+
\ No newline at end of file diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index 96b2a1e..55d7aca 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -11,18 +11,18 @@ + +
+
+

Webpay Oneclick

+ +
From 2f686710f10c8f4c2eaaddaac5288fa0b6ba8b63 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:09:42 -0300 Subject: [PATCH 02/39] feat: add controller for onclick mall --- .../controllers/OneclickMallController.java | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java new file mode 100644 index 0000000..6eeefe3 --- /dev/null +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -0,0 +1,253 @@ +package cl.transbank.webpay.example.controllers; + +import cl.transbank.common.IntegrationApiKeys; +import cl.transbank.common.IntegrationCommerceCodes; +import cl.transbank.common.IntegrationType; +import cl.transbank.webpay.common.WebpayOptions; +import cl.transbank.webpay.exception.*; +import cl.transbank.webpay.oneclick.Oneclick; +import cl.transbank.webpay.oneclick.model.MallTransactionCreateDetails; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Log4j2 +@Controller +@RequestMapping("/oneclick-mall") +public class OneclickMallController extends BaseController { + private static final String TEMPLATE_FOLDER = "oneclick_mall"; + private static final String BASE_URL = "/oneclick-mall"; + private static final String PRODUCT = "Oneclick Mall"; + + private static final String VIEW_START = TEMPLATE_FOLDER + "/start"; + private static final String VIEW_FINISH = TEMPLATE_FOLDER + "/finish"; + private static final String VIEW_AUTHORIZE = TEMPLATE_FOLDER + "/authorize"; + private static final String VIEW_DELETE = TEMPLATE_FOLDER + "/delete"; + private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; + private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + private static final String VIEW_ERROR = "error/error_page"; + + private static final Map NAV_START; + private static final Map NAV_FINISH; + private static final Map NAV_AUTHORIZE; + private static final Map NAV_DELETE; + private static final Map NAV_STATUS; + private static final Map NAV_REFUND; + + + + static { + NAV_START = new LinkedHashMap<>(); + NAV_START.put("request", "Petición"); + NAV_START.put("response", "Respuesta"); + NAV_START.put("form", "Creación del formulario"); + NAV_START.put("ejemplo", "Ejemplo"); + + NAV_FINISH = new LinkedHashMap<>(); + NAV_FINISH.put("peticion", "Petición"); + NAV_FINISH.put("respuesta", "Respuesta"); + NAV_FINISH.put("form", "Creación del formulario"); + NAV_FINISH.put("ejemplo", "Ejemplo"); + + NAV_AUTHORIZE = new LinkedHashMap<>(); + NAV_AUTHORIZE.put("peticion", "Petición"); + NAV_AUTHORIZE.put("respuesta", "Respuesta"); + NAV_AUTHORIZE.put("consultas", "Consultas"); + + NAV_STATUS = new LinkedHashMap<>(); + NAV_STATUS.put("peticion", "Petición"); + NAV_STATUS.put("respuesta", "Respuesta"); + + NAV_DELETE= new LinkedHashMap<>(); + NAV_DELETE.put("ejemplo", "Ejemplo"); + + NAV_REFUND = NAV_STATUS; + } + + private final Oneclick.MallInscription inscription; + private final Oneclick.MallTransaction transaction; + + public OneclickMallController() { + WebpayOptions options = new WebpayOptions( + IntegrationCommerceCodes.ONECLICK_MALL, + IntegrationApiKeys.WEBPAY, + IntegrationType.TEST + ); + this.inscription = new Oneclick.MallInscription(options); + this.transaction = new Oneclick.MallTransaction(options); + } + + private void addBreadcrumbs(Model model, String label, String url) { + Map breadcrumbs = new LinkedHashMap<>(); + breadcrumbs.put("Inicio", "/"); + breadcrumbs.put(PRODUCT, BASE_URL + "/start"); + if (label != null) breadcrumbs.put(label, url); + model.addAttribute("product", PRODUCT); + model.addAttribute("breadcrumbs", breadcrumbs); + } + + @GetMapping("/start") + public String start(HttpServletRequest req, Model model) + throws IOException, InscriptionStartException { + + addBreadcrumbs(model, "Iniciar inscripción", "#"); + + String username = "user_" + getRandomNumber(); + String email = "user." + getRandomNumber() + "@example.com"; + String returnUrl = req.getRequestURL().toString().replace("start", "finish"); + + var resp = inscription.start(username, email, returnUrl); + + model.addAttribute("request_data", Map.of( + "username", username, + "email", email, + "returnUrl", returnUrl + )); + model.addAttribute("request_data_json", toJson(Map.of( + "username", username, + "email", email, + "returnUrl", returnUrl + ))); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute("url", resp.getUrlWebpay()); + model.addAttribute("token", resp.getToken()); + + req.getSession().setAttribute("username", username); + req.getSession().setAttribute("email", email); + + return VIEW_START; + } + + @GetMapping("/finish") + public String finish(HttpServletRequest req, + @RequestParam(name = "TBK_TOKEN", required = false) String token, + Model model) + throws IOException, InscriptionFinishException { + + addBreadcrumbs(model, "Finalizar inscripción", "#"); + + String username = (String) req.getSession().getAttribute("username"); + + var resp = inscription.finish(token); + + req.getSession().setAttribute("tbkUser", resp.getTbkUser()); + + model.addAttribute("request_data", Map.of( + "username", username, + "tbkUser", resp.getTbkUser() + )); + + model.addAttribute("token", token); + model.addAttribute("username", username); + model.addAttribute("tbk_user", resp.getTbkUser()); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + model.addAttribute("child_commerce_code1", IntegrationCommerceCodes.ONECLICK_MALL_CHILD1); + model.addAttribute("child_commerce_code2", IntegrationCommerceCodes.ONECLICK_MALL_CHILD2); + + return VIEW_FINISH; + } + + @GetMapping("/delete") + public String delete(@RequestParam String username, + @RequestParam("tbk_user") String tbkUser, + Model model) + throws IOException, InscriptionDeleteException { + + addBreadcrumbs(model, "Eliminar inscripción", "#"); + + inscription.delete(tbkUser, username); + model.addAttribute("message", "Inscripción eliminada exitosamente."); + + return VIEW_DELETE; + } + + @GetMapping("/authorize") + public String authorize( + @RequestParam String username, + @RequestParam("tbk_user") String tbkUser, + @RequestParam("child_commerce_code1") String childCode1, + @RequestParam("child_commerce_code2") String childCode2, + @RequestParam("child_commerce_amount1") double amount1, + @RequestParam("child_commerce_amount2") double amount2, + @RequestParam("child_commerce_installments1") int installments1, + @RequestParam("child_commerce_installments2") int installments2, + Model model) + throws IOException, TransactionAuthorizeException { + + addBreadcrumbs(model, "Autorizar transacción", "#"); + + String buyOrder = "Order" + getRandomNumber(); + String childBuyOrder1 = "Order1_" + getRandomNumber(); + String childBuyOrder2 = "Order2_" + getRandomNumber(); + + var details = MallTransactionCreateDetails + .build() + .add( + amount1, + childCode1, + childBuyOrder1, + (byte) installments1 + ) + .add( + amount2, + childCode2, + childBuyOrder2, + (byte) installments2 + ); + + var resp = transaction.authorize(username, tbkUser, buyOrder, details); + + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_AUTHORIZE; + } + + @GetMapping("/status") + public String status(@RequestParam("buy_order") String buyOrder, Model model) + throws IOException, TransactionStatusException { + + addBreadcrumbs(model, "Consultar estado", "#"); + + var resp = transaction.status(buyOrder); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_STATUS; + } + + @GetMapping("/refund") + public String refund(@RequestParam("buy_order") String buyOrder, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam double amount, + Model model) + throws IOException, TransactionRefundException { + + addBreadcrumbs(model, "Reembolso", "#"); + log.info("Iniciando reembolso para buy_order={}, child_buy_order={}, child_commerce_code={}, amount={}", + buyOrder, childBuyOrder, childCommerceCode, amount); + log.error("Error en reembolso para buy_order={}, child_buy_order={}, child_commerce_code={}, amount={}", + buyOrder, childBuyOrder, childCommerceCode, amount); + model.addAttribute("buy_order", buyOrder); + var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_REFUND; + } + + @ExceptionHandler(Exception.class) + public String handleException(Exception e, Model model) { + log.error("Error inesperado", e); + model.addAttribute("error", e.getMessage()); + return VIEW_ERROR; + } +} From 394e251ffb027c94d2bea21054b082873182bded Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:09:57 -0300 Subject: [PATCH 03/39] feat: add onclick in home page --- src/main/resources/templates/index.html | 183 +++++++++++++----------- 1 file changed, 96 insertions(+), 87 deletions(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index dedf0e3..3f271e9 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -1,100 +1,109 @@ - - Transbank Sdk Java Example - - - + + Transbank Sdk Java Example + + + - - + + - + - + - - - + - - -
-
-
-
-

- Proyectos de Ejemplo del SDK para Java -

-
- JavaLogo -

- Este proyecto te brinda la oportunidad de - experimentar con las diversas modalidades de - productos que Transbank ofrece a través de su SDK - compatible con Java. Conoce de manera práctica las - soluciones y servicios que Transbank pone a tu - disposición, permitiéndote comprender cómo integrar - estas herramientas tecnológicas en tus proyectos y - aplicaciones. ¡Explora las opciones disponibles y - descubre cómo aprovechar al máximo estas - capacidades! -

-
+ gtag("config", "G-6CW8MF50ZX"); + + + +
+
+
+
+

Proyectos de Ejemplo del SDK para Java

+
+ JavaLogo +

+ Este proyecto te brinda la oportunidad de experimentar con las + diversas modalidades de productos que Transbank ofrece a través de + su SDK compatible con Java. Conoce de manera práctica las + soluciones y servicios que Transbank pone a tu disposición, + permitiéndote comprender cómo integrar estas herramientas + tecnológicas en tus proyectos y aplicaciones. ¡Explora las + opciones disponibles y descubre cómo aprovechar al máximo estas + capacidades! +

+
-
+
-
-
-
- webpay -

- El producto más usado para realizar un pago - online. Se genera un único cobro para todos - los productos o servicios adquiridos por el - tarjetahabiente (carro de compras). -

-
- Ver ejemplos y modalidades -
-
- -
+
+
+
+ webpay +

+ El producto más usado para realizar un pago online. Se genera + un único cobro para todos los productos o servicios adquiridos + por el tarjetahabiente (carro de compras). +

+
+ Ver ejemplos y modalidades +
-
-
+
+
+ oneclick +

+ El producto más usado para realizar un pago online. Se genera + un único cobro para todos los productos o servicios adquiridos + por el tarjetahabiente (carro de compras). +

+
+ Ver ejemplos y modalidades
-
+
+ +
+ +
- +
+
+
+ From 1ca346ab42c4406e1a8f5f778b6ad15e9b7e671e Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:10:08 -0300 Subject: [PATCH 04/39] feat: add onclick in sidebar --- .../resources/templates/partials/sidebar.html | 137 ++++++++++-------- 1 file changed, 80 insertions(+), 57 deletions(-) diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index 96b2a1e..dba7996 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -1,60 +1,83 @@ From b27e3e6fb9d20cef7d97ca858fbd74cecdabc8d3 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:10:25 -0300 Subject: [PATCH 05/39] feat: create start page for onclik mall --- .../templates/oneclick_mall/start.html | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/start.html diff --git a/src/main/resources/templates/oneclick_mall/start.html b/src/main/resources/templates/oneclick_mall/start.html new file mode 100644 index 0000000..40160ee --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/start.html @@ -0,0 +1,104 @@ +
+
+

Webpay Oneclick Mall - Creación de Inscripción

+

+ En esta etapa comienza el proceso de inscripción del medio de pago. Este + paso inicial es fundamental para dirigir al Tarjetahabiente al formulario + de inscripción. +

+ +

+ Todas las transacciones en este proyecto de ejemplo son realizadas en + ambiente de integración. +

+ +

Paso 1: Petición

+
    +
  1. Comienza por importar la librería Oneclick en tu proyecto.
  2. +
  3. + Luego, inicia una inscripción utilizando las funciones proporcionadas + mediante el SDK. +
  4. +
+ +

+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+var resp = inscription.start(username, email, responseUrl);
+    
+ +

Paso 2: Respuesta

+

+ Una vez que hayas iniciado la inscripción, aquí encontrarás los datos de + respuesta generados por el proceso. +

+ +
+ +

Paso 3: Creación del formulario

+

+ Utiliza estos datos de respuesta para redireccionar al usuario al + formulario de inscripción del Tarjetahabiente. Este formulario será la + interfaz a través de la cual el usuario completará su inscripción. +

+ +

+
+ +

Ejemplo

+

+ Para llevar a cabo una inscripción en nuestro sistema, primero debemos + crearla. Utilizaremos los siguientes datos para configurar la inscripción: +

+ +
+ +

+ Por último, con la respuesta del servicio que confirma la creación de la + inscripción, procedemos a crear el formulario de redirección. Para fines + de este ejemplo, haremos visible el campo "TBK_TOKEN", el cual es esencial + para completar el proceso de inscripción de manera exitosa. +

+ + + Antes de continuar al formulario de Webpay, asegúrate de contar con los + datos de las tarjetas de prueba que están en la + documentación + + +
+
+ Formulario de redirección + + + +
+
+
+
From bfe4d33b78537d72b3f990d63524b311cc751f95 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:10:35 -0300 Subject: [PATCH 06/39] feat: create finish page for onclik mall --- .../templates/oneclick_mall/finish.html | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/finish.html diff --git a/src/main/resources/templates/oneclick_mall/finish.html b/src/main/resources/templates/oneclick_mall/finish.html new file mode 100644 index 0000000..0934eb5 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/finish.html @@ -0,0 +1,147 @@ +
+
+

Webpay Oneclick Mall - Finalizar inscripción

+

+ En esta fase, completaremos el proceso de inscripción, permitiéndonos + posteriormente realizar cargos a la tarjeta que el Tarjetahabiente haya + inscrito. +

+ +

Paso 1: Datos recibidos

+

+ Después de finalizar el flujo en el formulario de inscripción, recibirás + un GET con la siguiente información: +

+ +

+{
+  "TBK_TOKEN": ""
+}
+    
+ +

Paso 2: Petición de autorización

+

+ Utiliza el token recibido para finalizar la inscripción mediante una nueva + llamada a Oneclick. +

+ +

+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+var response = inscription.finish(token);
+    
+ +

Paso 3: Respuesta

+

+ Transbank responderá con información crucial. Guarda estos detalles, ya + que serán necesarios para autorizar transacciones futuras. +

+ +
+ +

¡La tarjeta ya está inscrita!

+

+ Con la inscripción exitosa se pueden autorizar transacciones. +

+ +

Autorizar una transacción

+

+ Asegúrate de guardar los datos de la respuesta obtenidos durante la + inscripción. Estos serán esenciales para llevar a cabo transacciones de + manera efectiva. +

+ +
+ +

+ Después de una inscripción exitosa, tienes dos opciones: autorizar un pago + o borrar al usuario que se acaba de inscribir. +

+ +
+ + + + + +
+

Tienda 1

+
+
+ + +
+
+ + +
+
+ +

Tienda 2

+
+
+ + +
+
+ + +
+
+ + +
+
+ + + BORRAR USUARIO + +
+
From 9143317293169c0bc821a4086e2cb4a43c4e803a Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:10:44 -0300 Subject: [PATCH 07/39] feat: create delete page for onclik mall --- .../templates/oneclick_mall/delete.html | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/delete.html diff --git a/src/main/resources/templates/oneclick_mall/delete.html b/src/main/resources/templates/oneclick_mall/delete.html new file mode 100644 index 0000000..fbcfab1 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/delete.html @@ -0,0 +1,55 @@ +
+
+

Webpay Oneclick Mall - Borrar usuario

+ +

+ En este paso fundamental, procederemos a eliminar la inscripción del + usuario y su medio de pago. +

+ +

Paso 1: Petición

+

+ Para llevar a cabo la eliminación, necesitas el "username" (Nombre de + Usuario) y el "tbkUser". Realiza la llamada a MallInscription utilizando + el siguiente código: +

+ +
+      
+import cl.transbank.common.IntegrationApiKeys;
+import cl.transbank.common.IntegrationCommerceCodes;
+import cl.transbank.common.IntegrationType;
+import cl.transbank.webpay.common.WebpayOptions;
+import cl.transbank.webpay.oneclick.Oneclick;
+
+var options = new WebpayOptions(
+                IntegrationCommerceCodes.ONECLICK_MALL,
+                IntegrationApiKeys.WEBPAY,
+                IntegrationType.TEST
+        );
+var inscription = new Oneclick.MallInscription(options);
+      inscription.delete(tbkUser, username);
+      
+    
+ +

Paso 2: Respuesta

+

+ En caso de éxito, Transbank responderá con un status code 204 (No + Content), y el SDK no retornará ningún valor. La eliminación de la + inscripción se ha realizado de manera exitosa. +

+ +

+ En el caso de que no se encuentre el "username" o el "tbkUser", Transbank + responderá con un status code 404 (Not Found), y el SDK lanzará una + excepción del tipo TransbankException. +

+ +

+ Este proceso garantiza una eliminación segura y eficiente de la + inscripción del usuario y su medio de pago asociado. ¡Gracias por confiar + en Transbank para tus operaciones seguras! Si tienes alguna pregunta, + estamos aquí para ayudarte. +

+
+
From eb0fb59bbac736d6540681f746c01830ac0eef37 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:10:59 -0300 Subject: [PATCH 08/39] feat: create authorize page for onclik mall --- .../templates/oneclick_mall/authorize.html | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/authorize.html diff --git a/src/main/resources/templates/oneclick_mall/authorize.html b/src/main/resources/templates/oneclick_mall/authorize.html new file mode 100644 index 0000000..34bbf59 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/authorize.html @@ -0,0 +1,124 @@ +
+
+

Webpay Oneclick Mall - Autorizar pago

+ +

+ En este primer paso, procederemos a autorizar una transacción en la + tarjeta que ha sido previamente inscrita. +

+ +

Paso 1: Petición

+ +

+ Ahora que contamos con el username y el tbkUser obtenidos + durante la inscripción, estamos listos para autorizar transacciones en la + tarjeta inscrita. +

+ +

+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+var details = MallTransactionCreateDetails
+    .build()
+    .add(
+            amount1,
+            childCode1,
+            childBuyOrder1,
+            (byte) installments1
+    )
+    .add(
+            amount2,
+            childCode2,
+            childBuyOrder2,
+            (byte) installments2
+    );
+var resp = transaction.authorize(username, tbkUser, buyOrder, details);
+    
+ +

Paso 2: Respuesta

+ +

+ Una vez que la transacción ha sido autorizada, Transbank proporcionará la + siguiente información. Es fundamental conservar esta respuesta y verificar + que el campo responseCode tenga un valor de 0 y que el campo + status sea AUTHORIZED. +

+ +
+ +

¡Casi listo!

+ +

+ Ya puedes mostrar al usuario una página de éxito de la transacción. Debes + tener en cuenta que la transacción aún no ha sido capturada, solo se ha + retenido el saldo en la tarjeta del Tarjetahabiente. +

+ +
+
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+
+ + + CONSULTAR ESTADO + +
+
From d27b6d774565c5c9ebac44ed1badd46e4c3c4d71 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:11:07 -0300 Subject: [PATCH 09/39] feat: create refund page for onclik mall --- .../templates/oneclick_mall/refund.html | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/refund.html diff --git a/src/main/resources/templates/oneclick_mall/refund.html b/src/main/resources/templates/oneclick_mall/refund.html new file mode 100644 index 0000000..36da17f --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/refund.html @@ -0,0 +1,77 @@ +
+
+

Webpay Oneclick Mall - Reembolsar

+ +

+ En esta etapa, tienes la opción de solicitar el reembolso del monto al + titular de la tarjeta. Dependiendo del monto y el tiempo transcurrido + desde la autorización, este proceso podría resultar en una + Reversa o Anulación, dependiendo de ciertas condiciones (Reversa + en las primeras 3 horas de la autorización, Anulación posterior a + eso), o una Anulación parcial si el monto es menor al total. Las + anulaciones parciales para tarjetas débito y prepago no están soportadas. +

+ +

Paso 1 - Petición

+ +

+ Para llevar a cabo el reembolso, necesitas proporcionar la orden de compra + de la transacción, el código de comercio de la tienda, la orden de compra + del detalle de la transacción y el monto que deseas reversar. Si anulas el + monto total, podría ser una Reversa o Anulación, dependiendo + de ciertas condiciones (Reversa en las primeras 3 horas de la + autorización, Anulación posterior a eso), o una + Anulación Parcial si el monto es menor al total. +

+ +

Algunas consideraciones a tener en cuenta:

+
    +
  • No es posible realizar Anulaciones Parciales en pagos con cuotas.
  • +
+ +

+ En + + este link + + podrás ver mayor información sobre las condiciones y casos para anular o + reversar transacciones. +

+ +

+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount);
+    
+ +

Paso 2: Respuesta

+ +

+ Transbank responderá con el resultado del proceso de reembolso, indicando + si se ha realizado una Reversa, Anulación o + Anulación Parcial. +

+ +
+ + + CONSULTAR ESTADO + +
+
From 1214d4a750ad7c07a9babbbd97eac2379545074b Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Mon, 27 Oct 2025 12:11:22 -0300 Subject: [PATCH 10/39] feat: create status page for onclik mall --- .../templates/oneclick_mall/status.html | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/resources/templates/oneclick_mall/status.html diff --git a/src/main/resources/templates/oneclick_mall/status.html b/src/main/resources/templates/oneclick_mall/status.html new file mode 100644 index 0000000..d7a9f69 --- /dev/null +++ b/src/main/resources/templates/oneclick_mall/status.html @@ -0,0 +1,39 @@ +
+
+

Webpay Oneclick Mall - Consultar estado de transacción

+ +

+ Puedes solicitar el estado de una transacción hasta 7 días después de su + realización. No hay límite de solicitudes durante ese período. Sin + embargo, una vez pasados los 7 días, ya no podrás revisar su estado. +

+ +

Paso 1 - Petición:

+

+ Para realizar la consulta, necesitarás el buyOrder de la + transacción de interés. Utiliza este identificador para efectuar una + llamada a MallTransaction. +

+ +
+  
+var options = new WebpayOptions(
+    IntegrationCommerceCodes.ONECLICK_MALL,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+);
+var transaction = new Oneclick.MallTransaction(options);
+  var response = transaction.status(buyOrder);
+  
+
+ +

Paso 2: Respuesta

+

+ Transbank responderá con la siguiente información. Asegúrate de guardar + esta respuesta, y la única validación necesaria es que el campo + responseCode sea igual a 0. +

+ +
+
+
From 81c9d93beba9a72de51c3155f6e65267c4f45dd1 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 27 Oct 2025 14:34:15 -0500 Subject: [PATCH 11/39] feat: add content menu --- .../OneclickMallDeferredController.java | 34 +++++++++++++------ .../oneclick_mall_deferred/authorize.html | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index a9fc849..b46dda5 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -41,7 +41,7 @@ public class OneclickMallDeferredController extends BaseController { private final Oneclick.MallTransaction transaction; - /* + private static final Map NAV_START; private static final Map NAV_FINISH; private static final Map NAV_DELETE; @@ -55,25 +55,30 @@ public class OneclickMallDeferredController extends BaseController { NAV_START.put("request", "Petición"); NAV_START.put("response", "Respuesta"); NAV_START.put("form", "Formulario"); + NAV_START.put("example", "Ejemplo"); NAV_FINISH = new LinkedHashMap<>(); - NAV_FINISH.put("data", "Datos recibidos"); - NAV_FINISH.put("request", "Petición"); + NAV_FINISH.put("data", "Datos"); + NAV_FINISH.put("response", "Petición"); NAV_FINISH.put("response", "Respuesta"); - NAV_FINISH.put("operations", "¡Listo!"); + NAV_FINISH.put("authorize", "Autorizar una transacción"); - NAV_COMMIT = new LinkedHashMap<>(); - NAV_COMMIT.put("data", "Datos recibidos"); - NAV_COMMIT.put("request", "Petición"); - NAV_COMMIT.put("response", "Respuesta"); - NAV_COMMIT.put("operations", "¡Listo!"); + NAV_DELETE = new LinkedHashMap<>(); + NAV_DELETE.put("data", "Borrar usuario"); + + NAV_AUTHORIZE = new LinkedHashMap<>(); + NAV_AUTHORIZE.put("request", "Petición"); + NAV_AUTHORIZE.put("response", "Respuesta"); + NAV_AUTHORIZE.put("other", "Otras utilidades"); + + NAV_CAPTURE = NAV_AUTHORIZE; NAV_STATUS = new LinkedHashMap<>(); NAV_STATUS.put("request", "Petición"); NAV_STATUS.put("response", "Respuesta"); NAV_REFUND = NAV_STATUS; - }*/ + } public OneclickMallDeferredController() { var options = new WebpayOptions( @@ -98,6 +103,7 @@ private void addBreadcrumbs(Model model, String label, String url) { public String start(HttpServletRequest req, Model model) throws IOException, InscriptionStartException { + model.addAttribute("navigation", NAV_START); addBreadcrumbs(model, "Iniciar inscripción", "#"); String username = "user_" + getRandomNumber(); @@ -129,6 +135,7 @@ public String finish(HttpServletRequest req, Model model) throws IOException, InscriptionFinishException { + model.addAttribute("navigation", NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); String username = (String) req.getSession().getAttribute("username"); @@ -161,6 +168,7 @@ public String delete(@RequestParam String username, Model model) throws IOException, InscriptionDeleteException { + model.addAttribute("navigation", NAV_DELETE); addBreadcrumbs(model, "Eliminar inscripción", "#"); inscription.delete(tbkUser, username); @@ -182,6 +190,7 @@ public String authorize( Model model) throws IOException, TransactionAuthorizeException { + model.addAttribute("navigation", NAV_AUTHORIZE); addBreadcrumbs(model, "Autorizar transacción", "#"); String buyOrder = "buyOrder_" + getRandomNumber(); @@ -214,7 +223,8 @@ public String authorize( @GetMapping("/status") public String status(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionStatusException { - + + model.addAttribute("navigation", NAV_STATUS); addBreadcrumbs(model, "Consultar estado", "#"); var resp = transaction.status(buyOrder); @@ -231,6 +241,7 @@ public String refund(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionRefundException { + model.addAttribute("navigation", NAV_REFUND); addBreadcrumbs(model, "Reembolso", "#"); model.addAttribute("buy_order", buyOrder); @@ -250,6 +261,7 @@ public String capture( Model model) throws IOException, TransactionCaptureException { + model.addAttribute("navigation", NAV_CAPTURE); addBreadcrumbs(model, "Capturar", "#"); model.addAttribute("buy_order", buyOrder); diff --git a/src/main/resources/templates/oneclick_mall_deferred/authorize.html b/src/main/resources/templates/oneclick_mall_deferred/authorize.html index d1cb4bc..e0432ca 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/authorize.html +++ b/src/main/resources/templates/oneclick_mall_deferred/authorize.html @@ -49,7 +49,7 @@

Paso 2: Respuesta

-

¡Casi listo!

+

¡Casi listo!

Ya puedes mostrar al usuario una página de éxito de la transacción. From 46a26590513ee32f6309285b1aa58f6cc6466b4e Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 27 Oct 2025 15:46:54 -0500 Subject: [PATCH 12/39] feat: remove code tag --- .../templates/oneclick_mall_deferred/authorize.html | 2 +- .../resources/templates/oneclick_mall_deferred/status.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/templates/oneclick_mall_deferred/authorize.html b/src/main/resources/templates/oneclick_mall_deferred/authorize.html index e0432ca..c8cb33b 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/authorize.html +++ b/src/main/resources/templates/oneclick_mall_deferred/authorize.html @@ -44,7 +44,7 @@

Paso 2: Respuesta

Una vez que la transacción ha sido autorizada, Transbank proporcionará la siguiente información. Es fundamental conservar esta respuesta y verificar que el campo responseCode tenga un valor de - 0 y que el campo status sea AUTHORIZED. + 0 y que el campo status sea AUTHORIZED.

diff --git a/src/main/resources/templates/oneclick_mall_deferred/status.html b/src/main/resources/templates/oneclick_mall_deferred/status.html index c86c00b..5718a29 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/status.html +++ b/src/main/resources/templates/oneclick_mall_deferred/status.html @@ -11,7 +11,7 @@

Webpay Oneclick Mall Diferido - Consultar estado de transacción

Paso 1 - Petición:

Para realizar la consulta, necesitarás el buyOrder de la transacción de interés. - Utiliza este identificador para efectuar una llamada a MallTransaction. + Utiliza este identificador para efectuar una llamada a MallTransaction.

@@ -30,10 +30,10 @@ 

Paso 2: Respuesta

Transbank responderá con la siguiente información. Asegúrate de guardar esta respuesta, y la única validación necesaria es que el campo - responseCode sea igual a 0. + responseCode sea igual a 0.

-
\ No newline at end of file +
From 1f35786962c8ecdb6ab2d8f607d5bb9f295add4e Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 27 Oct 2025 15:47:25 -0500 Subject: [PATCH 13/39] feat: remove unused variable --- .../example/controllers/OneclickMallDeferredController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index b46dda5..3f3a4b4 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -139,7 +139,6 @@ public String finish(HttpServletRequest req, addBreadcrumbs(model, "Finalizar inscripción", "#"); String username = (String) req.getSession().getAttribute("username"); - String email = (String) req.getSession().getAttribute("email"); var resp = inscription.finish(token); From b70d239c96b5175f8ffa9abb64d1ac6a6180da49 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 27 Oct 2025 15:51:20 -0500 Subject: [PATCH 14/39] style: remove comment --- .../example/controllers/OneclickMallDeferredController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 3f3a4b4..8b74ea2 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -122,7 +122,6 @@ public String start(HttpServletRequest req, Model model) model.addAttribute("url", resp.getUrlWebpay()); model.addAttribute("token", resp.getToken()); - // Guardar en sesión req.getSession().setAttribute("username", username); req.getSession().setAttribute("email", email); From 202f275e98351add7309cbf63ce1a722b164dfa9 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 27 Oct 2025 16:02:11 -0500 Subject: [PATCH 15/39] feat: replace by constants --- .../OneclickMallDeferredController.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 8b74ea2..a676908 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -28,6 +28,9 @@ public class OneclickMallDeferredController extends BaseController { private static final String TEMPLATE_FOLDER = "oneclick_mall_deferred"; private static final String BASE_URL = "/oneclick-mall-deferred"; private static final String PRODUCT = "Oneclick Mall Diferido"; + private static final String MODEL_NAVIGATION = "navigation"; + private static final String MODEL_RESPONSE = "response_data"; + private static final String MODEL_RESPONSE_JSON = "response_data_json"; private static final String VIEW_START = TEMPLATE_FOLDER + "/start"; private static final String VIEW_FINISH = TEMPLATE_FOLDER + "/finish"; @@ -40,8 +43,6 @@ public class OneclickMallDeferredController extends BaseController { private final Oneclick.MallInscription inscription; private final Oneclick.MallTransaction transaction; - - private static final Map NAV_START; private static final Map NAV_FINISH; private static final Map NAV_DELETE; @@ -103,7 +104,7 @@ private void addBreadcrumbs(Model model, String label, String url) { public String start(HttpServletRequest req, Model model) throws IOException, InscriptionStartException { - model.addAttribute("navigation", NAV_START); + model.addAttribute(MODEL_NAVIGATION, NAV_START); addBreadcrumbs(model, "Iniciar inscripción", "#"); String username = "user_" + getRandomNumber(); @@ -117,8 +118,8 @@ public String start(HttpServletRequest req, Model model) "email", email, "returnUrl", returnUrl )); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); model.addAttribute("url", resp.getUrlWebpay()); model.addAttribute("token", resp.getToken()); @@ -134,7 +135,7 @@ public String finish(HttpServletRequest req, Model model) throws IOException, InscriptionFinishException { - model.addAttribute("navigation", NAV_FINISH); + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); String username = (String) req.getSession().getAttribute("username"); @@ -151,8 +152,8 @@ public String finish(HttpServletRequest req, model.addAttribute("token", token); model.addAttribute("username", username); model.addAttribute("tbk_user", resp.getTbkUser()); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); model.addAttribute("child_commerce_code1", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD1); model.addAttribute("child_commerce_code2", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD2); @@ -166,7 +167,7 @@ public String delete(@RequestParam String username, Model model) throws IOException, InscriptionDeleteException { - model.addAttribute("navigation", NAV_DELETE); + model.addAttribute(MODEL_NAVIGATION, NAV_DELETE); addBreadcrumbs(model, "Eliminar inscripción", "#"); inscription.delete(tbkUser, username); @@ -188,7 +189,7 @@ public String authorize( Model model) throws IOException, TransactionAuthorizeException { - model.addAttribute("navigation", NAV_AUTHORIZE); + model.addAttribute(MODEL_NAVIGATION, NAV_AUTHORIZE); addBreadcrumbs(model, "Autorizar transacción", "#"); String buyOrder = "buyOrder_" + getRandomNumber(); @@ -212,8 +213,8 @@ public String authorize( var resp = transaction.authorize(username, tbkUser, buyOrder, details); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_AUTHORIZE; } @@ -222,11 +223,11 @@ public String authorize( public String status(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionStatusException { - model.addAttribute("navigation", NAV_STATUS); + model.addAttribute(MODEL_NAVIGATION, NAV_STATUS); addBreadcrumbs(model, "Consultar estado", "#"); var resp = transaction.status(buyOrder); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_STATUS; } @@ -239,12 +240,12 @@ public String refund(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionRefundException { - model.addAttribute("navigation", NAV_REFUND); + model.addAttribute(MODEL_NAVIGATION, NAV_REFUND); addBreadcrumbs(model, "Reembolso", "#"); model.addAttribute("buy_order", buyOrder); var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_REFUND; } @@ -259,7 +260,7 @@ public String capture( Model model) throws IOException, TransactionCaptureException { - model.addAttribute("navigation", NAV_CAPTURE); + model.addAttribute(MODEL_NAVIGATION, NAV_CAPTURE); addBreadcrumbs(model, "Capturar", "#"); model.addAttribute("buy_order", buyOrder); @@ -267,8 +268,8 @@ public String capture( model.addAttribute("child_commerce_code", childCommerceCode); var resp = transaction.capture(childCommerceCode, childBuyOrder, authorizationCode, amount); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_CAPTURE; } From c8ab5005f8b14f10f532b351002aafc548ae8d02 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 29 Oct 2025 09:04:52 -0500 Subject: [PATCH 16/39] style: add line --- src/main/resources/templates/oneclick_mall_deferred/delete.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/oneclick_mall_deferred/delete.html b/src/main/resources/templates/oneclick_mall_deferred/delete.html index ade7195..9ce8072 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/delete.html +++ b/src/main/resources/templates/oneclick_mall_deferred/delete.html @@ -53,4 +53,4 @@

Paso 2: Respuesta

-
\ No newline at end of file +
From 6926063391008b0468b29d5782d5d16c22356b60 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 29 Oct 2025 09:05:17 -0500 Subject: [PATCH 17/39] feat: update option menu id --- .../example/controllers/OneclickMallDeferredController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index a676908..2796a04 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -60,7 +60,7 @@ public class OneclickMallDeferredController extends BaseController { NAV_FINISH = new LinkedHashMap<>(); NAV_FINISH.put("data", "Datos"); - NAV_FINISH.put("response", "Petición"); + NAV_FINISH.put("request", "Petición"); NAV_FINISH.put("response", "Respuesta"); NAV_FINISH.put("authorize", "Autorizar una transacción"); From bdcb26d653757b6835bbb38b802193e046bb5473 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 29 Oct 2025 13:05:25 -0300 Subject: [PATCH 18/39] fix: scroll over header --- src/main/resources/static/css/styles.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index 6a827fb..001d2e2 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -86,12 +86,18 @@ --tbk-table-header: hsl(329.58deg 100% 41.76% / 5%); --bg-hover: #343a46; --tbk-white-red: #f9f6f6; + --header-h: 80px; } * { box-sizing: border-box; } +html { + scroll-padding-top: var(--header-h); + scroll-behavior: smooth; +} + body { background-color: var(--background); font-family: "Open Sans", sans-serif; @@ -511,7 +517,7 @@ code { padding-left: 160px; padding-right: 160px; width: 100%; - height: 80px; + height: var(--header-h); box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.08); background-color: var(--elevation); position: sticky; From 582e0cbca2360398dafdd0fae639ea799b986829 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 29 Oct 2025 13:06:24 -0300 Subject: [PATCH 19/39] fix: use correct id for rigth sidebar --- .../controllers/OneclickMallController.java | 34 +++++++++++-------- .../templates/oneclick_mall/authorize.html | 8 ++--- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index 6eeefe3..97b1e14 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -40,6 +40,7 @@ public class OneclickMallController extends BaseController { private static final Map NAV_STATUS; private static final Map NAV_REFUND; + static { @@ -47,27 +48,28 @@ public class OneclickMallController extends BaseController { NAV_START.put("request", "Petición"); NAV_START.put("response", "Respuesta"); NAV_START.put("form", "Creación del formulario"); - NAV_START.put("ejemplo", "Ejemplo"); + NAV_START.put("example", "Ejemplo"); NAV_FINISH = new LinkedHashMap<>(); - NAV_FINISH.put("peticion", "Petición"); - NAV_FINISH.put("respuesta", "Respuesta"); - NAV_FINISH.put("form", "Creación del formulario"); - NAV_FINISH.put("ejemplo", "Ejemplo"); + NAV_FINISH.put("data", "Datos"); + NAV_FINISH.put("request", "Petición"); + NAV_FINISH.put("response", "Respuesta"); + NAV_FINISH.put("authorize", "Autorizar una transacción"); NAV_AUTHORIZE = new LinkedHashMap<>(); - NAV_AUTHORIZE.put("peticion", "Petición"); - NAV_AUTHORIZE.put("respuesta", "Respuesta"); - NAV_AUTHORIZE.put("consultas", "Consultas"); + NAV_AUTHORIZE.put("request", "Petición"); + NAV_AUTHORIZE.put("response", "Respuesta"); + NAV_AUTHORIZE.put("done", "listo"); NAV_STATUS = new LinkedHashMap<>(); - NAV_STATUS.put("peticion", "Petición"); - NAV_STATUS.put("respuesta", "Respuesta"); + NAV_STATUS.put("request", "Petición"); + NAV_STATUS.put("response", "Respuesta"); - NAV_DELETE= new LinkedHashMap<>(); - NAV_DELETE.put("ejemplo", "Ejemplo"); + NAV_DELETE = new LinkedHashMap<>(); + NAV_DELETE.put("data", "Borrar usuario"); NAV_REFUND = NAV_STATUS; + } private final Oneclick.MallInscription inscription; @@ -95,7 +97,7 @@ private void addBreadcrumbs(Model model, String label, String url) { @GetMapping("/start") public String start(HttpServletRequest req, Model model) throws IOException, InscriptionStartException { - + model.addAttribute("navigation", NAV_START); addBreadcrumbs(model, "Iniciar inscripción", "#"); String username = "user_" + getRandomNumber(); @@ -131,6 +133,7 @@ public String finish(HttpServletRequest req, Model model) throws IOException, InscriptionFinishException { + model.addAttribute("navigation", NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); String username = (String) req.getSession().getAttribute("username"); @@ -162,6 +165,7 @@ public String delete(@RequestParam String username, Model model) throws IOException, InscriptionDeleteException { + model.addAttribute("navigation", NAV_DELETE); addBreadcrumbs(model, "Eliminar inscripción", "#"); inscription.delete(tbkUser, username); @@ -183,6 +187,7 @@ public String authorize( Model model) throws IOException, TransactionAuthorizeException { + model.addAttribute("navigation", NAV_AUTHORIZE); addBreadcrumbs(model, "Autorizar transacción", "#"); String buyOrder = "Order" + getRandomNumber(); @@ -216,6 +221,7 @@ public String authorize( public String status(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionStatusException { + model.addAttribute("navigation", NAV_STATUS); addBreadcrumbs(model, "Consultar estado", "#"); var resp = transaction.status(buyOrder); @@ -231,7 +237,7 @@ public String refund(@RequestParam("buy_order") String buyOrder, @RequestParam double amount, Model model) throws IOException, TransactionRefundException { - + model.addAttribute("navigation", NAV_REFUND); addBreadcrumbs(model, "Reembolso", "#"); log.info("Iniciando reembolso para buy_order={}, child_buy_order={}, child_commerce_code={}, amount={}", buyOrder, childBuyOrder, childCommerceCode, amount); diff --git a/src/main/resources/templates/oneclick_mall/authorize.html b/src/main/resources/templates/oneclick_mall/authorize.html index 34bbf59..04c9b7a 100644 --- a/src/main/resources/templates/oneclick_mall/authorize.html +++ b/src/main/resources/templates/oneclick_mall/authorize.html @@ -50,12 +50,12 @@

Paso 2: Respuesta

-

¡Casi listo!

+

¡listo!

- Ya puedes mostrar al usuario una página de éxito de la transacción. Debes - tener en cuenta que la transacción aún no ha sido capturada, solo se ha - retenido el saldo en la tarjeta del Tarjetahabiente. + Con la autorización exitosa, puedes mostrar al usuario una página de éxito + de la transacción, proporcionándole la confirmación de que el proceso se + ha completado con éxito.

From eef84ecd0608120358f4656090a9134621a8918b Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 29 Oct 2025 13:28:13 -0300 Subject: [PATCH 20/39] fix: typo --- .../templates/oneclick_mall/authorize.html | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/resources/templates/oneclick_mall/authorize.html b/src/main/resources/templates/oneclick_mall/authorize.html index 04c9b7a..eebac5e 100644 --- a/src/main/resources/templates/oneclick_mall/authorize.html +++ b/src/main/resources/templates/oneclick_mall/authorize.html @@ -58,6 +58,24 @@

¡listo!

ha completado con éxito.

+
+

Otras utilidades

+

+ Después de autorizar la transacción, considera las siguientes utilidades + adicionales: +

+
    +
  • + Reembolsar: Puedes reversar o anular el pago según ciertas + condiciones comerciales. +
  • +
  • + Consultar Estado: Hasta 7 días después de realizada la + transacción, podrás consultar el estado de la transacción. +
  • +
+
+
From cc4d6249331dde407323b768403e508dbeaf3cc2 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 29 Oct 2025 17:25:29 -0300 Subject: [PATCH 21/39] fix: remove logs --- .../webpay/example/controllers/OneclickMallController.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index 97b1e14..143bdad 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -239,10 +239,7 @@ public String refund(@RequestParam("buy_order") String buyOrder, throws IOException, TransactionRefundException { model.addAttribute("navigation", NAV_REFUND); addBreadcrumbs(model, "Reembolso", "#"); - log.info("Iniciando reembolso para buy_order={}, child_buy_order={}, child_commerce_code={}, amount={}", - buyOrder, childBuyOrder, childCommerceCode, amount); - log.error("Error en reembolso para buy_order={}, child_buy_order={}, child_commerce_code={}, amount={}", - buyOrder, childBuyOrder, childCommerceCode, amount); + model.addAttribute("buy_order", buyOrder); var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount); model.addAttribute("response_data_json", toJson(resp)); From 19e513ed830b7d1c7ebe644bed42734e21575479 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 30 Oct 2025 10:39:56 -0300 Subject: [PATCH 22/39] fix: typo --- .../webpay/example/controllers/OneclickMallController.java | 2 +- src/main/resources/templates/oneclick_mall/authorize.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index 143bdad..8e935b3 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -23,7 +23,7 @@ public class OneclickMallController extends BaseController { private static final String TEMPLATE_FOLDER = "oneclick_mall"; private static final String BASE_URL = "/oneclick-mall"; - private static final String PRODUCT = "Oneclick Mall"; + private static final String PRODUCT = "Webpay Oneclick Mall"; private static final String VIEW_START = TEMPLATE_FOLDER + "/start"; private static final String VIEW_FINISH = TEMPLATE_FOLDER + "/finish"; diff --git a/src/main/resources/templates/oneclick_mall/authorize.html b/src/main/resources/templates/oneclick_mall/authorize.html index eebac5e..9a0f752 100644 --- a/src/main/resources/templates/oneclick_mall/authorize.html +++ b/src/main/resources/templates/oneclick_mall/authorize.html @@ -50,7 +50,7 @@

Paso 2: Respuesta

-

¡listo!

+

¡Listo!

Con la autorización exitosa, puedes mostrar al usuario una página de éxito From 03bf94736f26d6d0503fa2ee2002e5bd6e060428 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Thu, 30 Oct 2025 09:29:49 -0500 Subject: [PATCH 23/39] feat: replace diferido to deferred --- .../controllers/OneclickMallDeferredController.java | 8 ++++---- .../templates/oneclick_mall_deferred/authorize.html | 4 ++-- .../templates/oneclick_mall_deferred/capture.html | 4 ++-- .../templates/oneclick_mall_deferred/finish.html | 4 ++-- .../templates/oneclick_mall_deferred/refund.html | 2 +- src/main/resources/templates/partials/sidebar.html | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 2796a04..8bf6953 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -22,7 +22,7 @@ @Log4j2 @Controller -@RequestMapping("/oneclick-mall-diferido") +@RequestMapping("/oneclick-mall-deferred") public class OneclickMallDeferredController extends BaseController { private static final String TEMPLATE_FOLDER = "oneclick_mall_deferred"; @@ -94,13 +94,13 @@ public OneclickMallDeferredController() { private void addBreadcrumbs(Model model, String label, String url) { Map breadcrumbs = new LinkedHashMap<>(); breadcrumbs.put("Inicio", "/"); - breadcrumbs.put(PRODUCT, BASE_URL + "/start"); + breadcrumbs.put(PRODUCT, BASE_URL + "/"); if (label != null) breadcrumbs.put(label, url); model.addAttribute("product", PRODUCT); model.addAttribute("breadcrumbs", breadcrumbs); } - @GetMapping("/start") + @GetMapping("/") public String start(HttpServletRequest req, Model model) throws IOException, InscriptionStartException { @@ -109,7 +109,7 @@ public String start(HttpServletRequest req, Model model) String username = "user_" + getRandomNumber(); String email = "user." + getRandomNumber() + "@example.com"; - String returnUrl = req.getRequestURL().toString().replace("start", "finish"); + String returnUrl = req.getRequestURL().toString() + "finish"; var resp = inscription.start(username, email, returnUrl); diff --git a/src/main/resources/templates/oneclick_mall_deferred/authorize.html b/src/main/resources/templates/oneclick_mall_deferred/authorize.html index c8cb33b..e39a89c 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/authorize.html +++ b/src/main/resources/templates/oneclick_mall_deferred/authorize.html @@ -58,7 +58,7 @@

¡Casi listo!

- +
@@ -96,7 +96,7 @@

¡Casi listo!

+ th:href="@{/oneclick-mall-deferred/status(buy_order=${response_data.buyOrder})}"> CONSULTAR ESTADO diff --git a/src/main/resources/templates/oneclick_mall_deferred/capture.html b/src/main/resources/templates/oneclick_mall_deferred/capture.html index 88f2818..7e35e19 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/capture.html +++ b/src/main/resources/templates/oneclick_mall_deferred/capture.html @@ -51,7 +51,7 @@

¡Transacción Capturada!

obtener más detalles.

- + @@ -71,7 +71,7 @@

¡Transacción Capturada!

diff --git a/src/main/resources/templates/oneclick_mall_deferred/finish.html b/src/main/resources/templates/oneclick_mall_deferred/finish.html index 025aaf8..c30afa1 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/finish.html +++ b/src/main/resources/templates/oneclick_mall_deferred/finish.html @@ -64,7 +64,7 @@

Autorizar una transacción

acaba de inscribir.

- + @@ -101,7 +101,7 @@

Autorizar una transacción

- BORRAR USUARIO diff --git a/src/main/resources/templates/oneclick_mall_deferred/refund.html b/src/main/resources/templates/oneclick_mall_deferred/refund.html index 69a0724..a405376 100644 --- a/src/main/resources/templates/oneclick_mall_deferred/refund.html +++ b/src/main/resources/templates/oneclick_mall_deferred/refund.html @@ -60,7 +60,7 @@

Paso 2: Respuesta

+ th:href="@{/oneclick-mall-deferred/status(buy_order=${buy_order})}"> CONSULTAR ESTADO diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index c126e6d..f197ba7 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -92,7 +92,7 @@
  • - + Flujo Completo
  • From 7f111bd9d9e7dcef26b1760eabef97a3eb572349 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Sat, 1 Nov 2025 12:33:40 -0500 Subject: [PATCH 24/39] feat: add oneclick error pages --- .../example/controllers/BaseController.java | 8 ++-- .../OneclickMallDeferredController.java | 31 +++++++++++-- .../templates/error/oneclick/recover.html | 19 ++++++++ .../templates/error/oneclick/rejected.html | 46 +++++++++++++++++++ .../templates/error/{ => webpay}/aborted.html | 0 .../error/{ => webpay}/form_error.html | 0 .../templates/error/{ => webpay}/timeout.html | 0 7 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/templates/error/oneclick/recover.html create mode 100644 src/main/resources/templates/error/oneclick/rejected.html rename src/main/resources/templates/error/{ => webpay}/aborted.html (100%) rename src/main/resources/templates/error/{ => webpay}/form_error.html (100%) rename src/main/resources/templates/error/{ => webpay}/timeout.html (100%) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/BaseController.java b/src/main/java/cl/transbank/webpay/example/controllers/BaseController.java index b1f87ef..3bae43e 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/BaseController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/BaseController.java @@ -7,9 +7,11 @@ public abstract class BaseController { protected static final String VIEW_ERROR = "error/error_page"; - protected static final String VIEW_ABORTED_ERROR = "error/aborted"; - protected static final String VIEW_FORM_ERROR = "error/form_error"; - protected static final String VIEW_TIMEOUT_ERROR = "error/timeout"; + protected static final String VIEW_ABORTED_ERROR = "error/webpay/aborted"; + protected static final String VIEW_FORM_ERROR = "error/webpay/form_error"; + protected static final String VIEW_TIMEOUT_ERROR = "error/webpay/timeout"; + protected static final String VIEW_RECOVER_ERROR = "error/oneclick/recover"; + protected static final String VIEW_REJECTED_ERROR = "error/oneclick/rejected"; public String toJson(Object obj) { return (new GsonBuilder().setPrettyPrinting().create()).toJson(obj); diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 8bf6953..39dce37 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -25,6 +25,7 @@ @RequestMapping("/oneclick-mall-deferred") public class OneclickMallDeferredController extends BaseController { + private static final int AUTHORIZED = 0; private static final String TEMPLATE_FOLDER = "oneclick_mall_deferred"; private static final String BASE_URL = "/oneclick-mall-deferred"; private static final String PRODUCT = "Oneclick Mall Diferido"; @@ -45,6 +46,8 @@ public class OneclickMallDeferredController extends BaseController { private static final Map NAV_START; private static final Map NAV_FINISH; + private static final Map NAV_FINISH_RECOVER; + private static final Map NAV_FINISH_REJECTED; private static final Map NAV_DELETE; private static final Map NAV_AUTHORIZE; private static final Map NAV_STATUS; @@ -64,6 +67,14 @@ public class OneclickMallDeferredController extends BaseController { NAV_FINISH.put("response", "Respuesta"); NAV_FINISH.put("authorize", "Autorizar una transacción"); + NAV_FINISH_RECOVER = new LinkedHashMap<>(); + NAV_FINISH_RECOVER.put("data", "Datos"); + + NAV_FINISH_REJECTED = new LinkedHashMap<>(); + NAV_FINISH_REJECTED.put("data", "Datos"); + NAV_FINISH_REJECTED.put("request", "Petición"); + NAV_FINISH_REJECTED.put("response", "Respuesta"); + NAV_DELETE = new LinkedHashMap<>(); NAV_DELETE.put("data", "Borrar usuario"); @@ -131,30 +142,40 @@ public String start(HttpServletRequest req, Model model) @GetMapping("/finish") public String finish(HttpServletRequest req, + @RequestParam Map params, @RequestParam(name = "TBK_TOKEN", required = false) String token, + @RequestParam(name = "TBK_ORDEN_COMPRA", required = false) String ordenCompra, Model model) throws IOException, InscriptionFinishException { model.addAttribute(MODEL_NAVIGATION, NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); + if (ordenCompra != null) { + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_RECOVER); + model.addAttribute("request_data_json", toJson(params)); + return VIEW_RECOVER_ERROR; + } + String username = (String) req.getSession().getAttribute("username"); var resp = inscription.finish(token); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); - req.getSession().setAttribute("tbkUser", resp.getTbkUser()); + if (resp.getResponseCode() != AUTHORIZED) { + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_REJECTED); + return VIEW_REJECTED_ERROR; + } + req.getSession().setAttribute("tbkUser", resp.getTbkUser()); model.addAttribute("request_data", Map.of( "username", username, "tbkUser", resp.getTbkUser() )); - model.addAttribute("token", token); model.addAttribute("username", username); model.addAttribute("tbk_user", resp.getTbkUser()); - model.addAttribute(MODEL_RESPONSE, resp); - model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); - model.addAttribute("child_commerce_code1", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD1); model.addAttribute("child_commerce_code2", IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED_CHILD2); diff --git a/src/main/resources/templates/error/oneclick/recover.html b/src/main/resources/templates/error/oneclick/recover.html new file mode 100644 index 0000000..7ea527a --- /dev/null +++ b/src/main/resources/templates/error/oneclick/recover.html @@ -0,0 +1,19 @@ +
    +
    +

    +

    + La inscripción ha sido anulada por el usuario. En esta instancia, la inscripción fue abandonada al seleccionar la opción 'Abandonar y volver al comercio'. +

    + +

    Datos Recibidos:

    +

    + Después de que el usuario anule la inscripción en el formulario de pago, recibirás un GET con la siguiente información: +

    + +
    + +

    ¡Se abandonó la inscripción!

    +

    Este mensaje indica que la inscripción ha sido cancelada por decisión del usuario. Si tienes alguna pregunta o necesitas asistencia, no dudes en contactarnos. Lamentamos cualquier inconveniente y agradecemos tu comprensión. +

    +
    +
    diff --git a/src/main/resources/templates/error/oneclick/rejected.html b/src/main/resources/templates/error/oneclick/rejected.html new file mode 100644 index 0000000..d8bb68d --- /dev/null +++ b/src/main/resources/templates/error/oneclick/rejected.html @@ -0,0 +1,46 @@ +
    +
    +

    +

    + En esta fase, pueden surgir inconvenientes, ya sea con el titular de la tarjeta o a nivel bancario, lo que + resulta en una inscripción fallida. +

    + +

    Paso 1: Datos recibidos

    +

    + Después de finalizar el flujo en el formulario de inscripción, recibirás un GET con la siguiente información: +

    + + +

    Paso 2: Petición de autorización

    +

    + Utiliza el token recibido para finalizar la Inscripción mediante una nueva llamada a Oneclick. +

    + +
    
    +import cl.transbank.common.IntegrationApiKeys;
    +import cl.transbank.common.IntegrationCommerceCodes;
    +import cl.transbank.common.IntegrationType;
    +import cl.transbank.webpay.common.WebpayOptions;
    +import cl.transbank.webpay.oneclick.Oneclick;
    +
    +var options = new WebpayOptions(
    +                IntegrationCommerceCodes.ONECLICK_MALL_DEFERRED,
    +                IntegrationApiKeys.WEBPAY,
    +                IntegrationType.TEST
    +        );
    +var inscription = new Oneclick.MallInscription(options);
    +var response = inscription.finish(token);
    +    
    + +

    Paso 3: Respuesta

    +

    + Transbank responderá con la siguiente información. +

    + +
    + + +

    En este caso la inscripción fue rechazada, tendrás que repetir el proceso

    +
    +
    diff --git a/src/main/resources/templates/error/aborted.html b/src/main/resources/templates/error/webpay/aborted.html similarity index 100% rename from src/main/resources/templates/error/aborted.html rename to src/main/resources/templates/error/webpay/aborted.html diff --git a/src/main/resources/templates/error/form_error.html b/src/main/resources/templates/error/webpay/form_error.html similarity index 100% rename from src/main/resources/templates/error/form_error.html rename to src/main/resources/templates/error/webpay/form_error.html diff --git a/src/main/resources/templates/error/timeout.html b/src/main/resources/templates/error/webpay/timeout.html similarity index 100% rename from src/main/resources/templates/error/timeout.html rename to src/main/resources/templates/error/webpay/timeout.html From 29e8b25ad8449c0800ba887fa2a7edcfacfd1460 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Sat, 1 Nov 2025 12:38:44 -0500 Subject: [PATCH 25/39] feat: update text case from 'listo' to 'Listo' --- .../webpay/example/controllers/OneclickMallController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index 8e935b3..c198b92 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -59,7 +59,7 @@ public class OneclickMallController extends BaseController { NAV_AUTHORIZE = new LinkedHashMap<>(); NAV_AUTHORIZE.put("request", "Petición"); NAV_AUTHORIZE.put("response", "Respuesta"); - NAV_AUTHORIZE.put("done", "listo"); + NAV_AUTHORIZE.put("done", "Listo"); NAV_STATUS = new LinkedHashMap<>(); NAV_STATUS.put("request", "Petición"); From 8f9284814b12789a29a117dae7840f471e008794 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Sat, 1 Nov 2025 12:40:04 -0500 Subject: [PATCH 26/39] feat: add 'Webpay' to 'Oneclick Mall Diferido' --- .../example/controllers/OneclickMallDeferredController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 39dce37..9bdab26 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -28,7 +28,7 @@ public class OneclickMallDeferredController extends BaseController { private static final int AUTHORIZED = 0; private static final String TEMPLATE_FOLDER = "oneclick_mall_deferred"; private static final String BASE_URL = "/oneclick-mall-deferred"; - private static final String PRODUCT = "Oneclick Mall Diferido"; + private static final String PRODUCT = "Webpay Oneclick Mall Diferido"; private static final String MODEL_NAVIGATION = "navigation"; private static final String MODEL_RESPONSE = "response_data"; private static final String MODEL_RESPONSE_JSON = "response_data_json"; From 2f82e729988bae6dcb3af4f11ac4f069413dea61 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 3 Nov 2025 07:56:57 -0500 Subject: [PATCH 27/39] feat: add error page to Oneclick Mall --- .../controllers/OneclickMallController.java | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index c198b92..d4cdca6 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -21,9 +21,14 @@ @Controller @RequestMapping("/oneclick-mall") public class OneclickMallController extends BaseController { + + private static final int AUTHORIZED = 0; private static final String TEMPLATE_FOLDER = "oneclick_mall"; private static final String BASE_URL = "/oneclick-mall"; private static final String PRODUCT = "Webpay Oneclick Mall"; + private static final String MODEL_NAVIGATION = "navigation"; + private static final String MODEL_RESPONSE = "response_data"; + private static final String MODEL_RESPONSE_JSON = "response_data_json"; private static final String VIEW_START = TEMPLATE_FOLDER + "/start"; private static final String VIEW_FINISH = TEMPLATE_FOLDER + "/finish"; @@ -35,6 +40,8 @@ public class OneclickMallController extends BaseController { private static final Map NAV_START; private static final Map NAV_FINISH; + private static final Map NAV_FINISH_RECOVER; + private static final Map NAV_FINISH_REJECTED; private static final Map NAV_AUTHORIZE; private static final Map NAV_DELETE; private static final Map NAV_STATUS; @@ -56,6 +63,14 @@ public class OneclickMallController extends BaseController { NAV_FINISH.put("response", "Respuesta"); NAV_FINISH.put("authorize", "Autorizar una transacción"); + NAV_FINISH_RECOVER = new LinkedHashMap<>(); + NAV_FINISH_RECOVER.put("data", "Datos"); + + NAV_FINISH_REJECTED = new LinkedHashMap<>(); + NAV_FINISH_REJECTED.put("data", "Datos"); + NAV_FINISH_REJECTED.put("request", "Petición"); + NAV_FINISH_REJECTED.put("response", "Respuesta"); + NAV_AUTHORIZE = new LinkedHashMap<>(); NAV_AUTHORIZE.put("request", "Petición"); NAV_AUTHORIZE.put("response", "Respuesta"); @@ -116,8 +131,8 @@ public String start(HttpServletRequest req, Model model) "email", email, "returnUrl", returnUrl ))); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); model.addAttribute("url", resp.getUrlWebpay()); model.addAttribute("token", resp.getToken()); @@ -129,16 +144,31 @@ public String start(HttpServletRequest req, Model model) @GetMapping("/finish") public String finish(HttpServletRequest req, + @RequestParam Map params, @RequestParam(name = "TBK_TOKEN", required = false) String token, + @RequestParam(name = "TBK_ORDEN_COMPRA", required = false) String ordenCompra, Model model) throws IOException, InscriptionFinishException { model.addAttribute("navigation", NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); + if (ordenCompra != null) { + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_RECOVER); + model.addAttribute("request_data_json", toJson(params)); + return VIEW_RECOVER_ERROR; + } + String username = (String) req.getSession().getAttribute("username"); var resp = inscription.finish(token); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); + + if (resp.getResponseCode() != AUTHORIZED) { + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_REJECTED); + return VIEW_REJECTED_ERROR; + } req.getSession().setAttribute("tbkUser", resp.getTbkUser()); @@ -150,8 +180,6 @@ public String finish(HttpServletRequest req, model.addAttribute("token", token); model.addAttribute("username", username); model.addAttribute("tbk_user", resp.getTbkUser()); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); model.addAttribute("child_commerce_code1", IntegrationCommerceCodes.ONECLICK_MALL_CHILD1); model.addAttribute("child_commerce_code2", IntegrationCommerceCodes.ONECLICK_MALL_CHILD2); @@ -211,8 +239,8 @@ public String authorize( var resp = transaction.authorize(username, tbkUser, buyOrder, details); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE, resp); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_AUTHORIZE; } @@ -225,7 +253,7 @@ public String status(@RequestParam("buy_order") String buyOrder, Model model) addBreadcrumbs(model, "Consultar estado", "#"); var resp = transaction.status(buyOrder); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_STATUS; } @@ -242,7 +270,7 @@ public String refund(@RequestParam("buy_order") String buyOrder, model.addAttribute("buy_order", buyOrder); var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp)); return VIEW_REFUND; } From b694494570c7cfd00c5dafe5e98e78ff46a17baf Mon Sep 17 00:00:00 2001 From: mvarlic Date: Mon, 3 Nov 2025 12:10:21 -0500 Subject: [PATCH 28/39] feat: use constant --- .../example/controllers/OneclickMallController.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index d4cdca6..a4eb530 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -112,7 +112,7 @@ private void addBreadcrumbs(Model model, String label, String url) { @GetMapping("/start") public String start(HttpServletRequest req, Model model) throws IOException, InscriptionStartException { - model.addAttribute("navigation", NAV_START); + model.addAttribute(MODEL_NAVIGATION, NAV_START); addBreadcrumbs(model, "Iniciar inscripción", "#"); String username = "user_" + getRandomNumber(); @@ -150,7 +150,7 @@ public String finish(HttpServletRequest req, Model model) throws IOException, InscriptionFinishException { - model.addAttribute("navigation", NAV_FINISH); + model.addAttribute(MODEL_NAVIGATION, NAV_FINISH); addBreadcrumbs(model, "Finalizar inscripción", "#"); if (ordenCompra != null) { @@ -193,7 +193,7 @@ public String delete(@RequestParam String username, Model model) throws IOException, InscriptionDeleteException { - model.addAttribute("navigation", NAV_DELETE); + model.addAttribute(MODEL_NAVIGATION, NAV_DELETE); addBreadcrumbs(model, "Eliminar inscripción", "#"); inscription.delete(tbkUser, username); @@ -215,7 +215,7 @@ public String authorize( Model model) throws IOException, TransactionAuthorizeException { - model.addAttribute("navigation", NAV_AUTHORIZE); + model.addAttribute(MODEL_NAVIGATION, NAV_AUTHORIZE); addBreadcrumbs(model, "Autorizar transacción", "#"); String buyOrder = "Order" + getRandomNumber(); @@ -249,7 +249,7 @@ public String authorize( public String status(@RequestParam("buy_order") String buyOrder, Model model) throws IOException, TransactionStatusException { - model.addAttribute("navigation", NAV_STATUS); + model.addAttribute(MODEL_NAVIGATION, NAV_STATUS); addBreadcrumbs(model, "Consultar estado", "#"); var resp = transaction.status(buyOrder); @@ -265,7 +265,7 @@ public String refund(@RequestParam("buy_order") String buyOrder, @RequestParam double amount, Model model) throws IOException, TransactionRefundException { - model.addAttribute("navigation", NAV_REFUND); + model.addAttribute(MODEL_NAVIGATION, NAV_REFUND); addBreadcrumbs(model, "Reembolso", "#"); model.addAttribute("buy_order", buyOrder); From 7564be4ed204512d3c0583e02d211de8b45863ae Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 5 Nov 2025 10:37:05 -0500 Subject: [PATCH 29/39] feat: update page error with request data --- .../webpay/example/controllers/OneclickMallController.java | 1 + .../example/controllers/OneclickMallDeferredController.java | 1 + src/main/resources/templates/error/oneclick/rejected.html | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java index a4eb530..66922c4 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallController.java @@ -167,6 +167,7 @@ public String finish(HttpServletRequest req, if (resp.getResponseCode() != AUTHORIZED) { model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_REJECTED); + model.addAttribute("request_data_json", toJson(params)); return VIEW_REJECTED_ERROR; } diff --git a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java index 9bdab26..49d10a6 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/OneclickMallDeferredController.java @@ -165,6 +165,7 @@ public String finish(HttpServletRequest req, if (resp.getResponseCode() != AUTHORIZED) { model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_REJECTED); + model.addAttribute("request_data_json", toJson(params)); return VIEW_REJECTED_ERROR; } diff --git a/src/main/resources/templates/error/oneclick/rejected.html b/src/main/resources/templates/error/oneclick/rejected.html index d8bb68d..623f1fc 100644 --- a/src/main/resources/templates/error/oneclick/rejected.html +++ b/src/main/resources/templates/error/oneclick/rejected.html @@ -11,8 +11,9 @@

    Paso 1: Datos recibidos

    Después de finalizar el flujo en el formulario de inscripción, recibirás un GET con la siguiente información:

    +
    -

    Paso 2: Petición de autorización

    +

    Paso 2: Petición de autorización

    Utiliza el token recibido para finalizar la Inscripción mediante una nueva llamada a Oneclick.

    From 0854815636a44516f24e602ac2f62e3757746931 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 5 Nov 2025 14:30:19 -0500 Subject: [PATCH 30/39] feat: update message in page error --- src/main/resources/templates/error/oneclick/rejected.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/error/oneclick/rejected.html b/src/main/resources/templates/error/oneclick/rejected.html index 623f1fc..d009262 100644 --- a/src/main/resources/templates/error/oneclick/rejected.html +++ b/src/main/resources/templates/error/oneclick/rejected.html @@ -42,6 +42,6 @@

    Paso 3: Respuesta

    -

    En este caso la inscripción fue rechazada, tendrás que repetir el proceso

    +

    Eso significa que la inscripción fue rechazada, por lo que se tiene que repetir el proceso.

From 569a9f076bb04316a6f8eed61584ce2138c0d3f8 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Tue, 11 Nov 2025 17:39:46 -0500 Subject: [PATCH 31/39] feat: update table component --- src/main/resources/templates/partials/table.html | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/main/resources/templates/partials/table.html b/src/main/resources/templates/partials/table.html index 5b2997c..768c5bf 100644 --- a/src/main/resources/templates/partials/table.html +++ b/src/main/resources/templates/partials/table.html @@ -5,19 +5,7 @@
-
-
- -
-
-
- : - -
-
-
-
-
+
From 98e53198a3a0867d4af80566a18eb6c0a2fd2469 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Tue, 11 Nov 2025 17:40:24 -0500 Subject: [PATCH 32/39] feat: add webpay mall component --- .../controllers/WebpayPlusMallController.java | 199 ++++++++++++++++++ .../resources/templates/partials/sidebar.html | 20 ++ .../templates/webpay_plus_mall/commit.html | 89 ++++++++ .../templates/webpay_plus_mall/create.html | 103 +++++++++ .../templates/webpay_plus_mall/refund.html | 64 ++++++ .../templates/webpay_plus_mall/status.html | 28 +++ 6 files changed, 503 insertions(+) create mode 100644 src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java create mode 100644 src/main/resources/templates/webpay_plus_mall/commit.html create mode 100644 src/main/resources/templates/webpay_plus_mall/create.html create mode 100644 src/main/resources/templates/webpay_plus_mall/refund.html create mode 100644 src/main/resources/templates/webpay_plus_mall/status.html diff --git a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java new file mode 100644 index 0000000..54dadad --- /dev/null +++ b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java @@ -0,0 +1,199 @@ +package cl.transbank.webpay.example.controllers; + +import cl.transbank.common.IntegrationApiKeys; +import cl.transbank.common.IntegrationCommerceCodes; +import cl.transbank.common.IntegrationType; +import cl.transbank.webpay.common.WebpayOptions; +import cl.transbank.model.MallTransactionCreateDetails; +import cl.transbank.webpay.exception.TransactionCommitException; +import cl.transbank.webpay.exception.TransactionCreateException; +import cl.transbank.webpay.exception.TransactionRefundException; +import cl.transbank.webpay.exception.TransactionStatusException; +import cl.transbank.webpay.webpayplus.WebpayPlus; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Log4j2 +@Controller +@RequestMapping("/webpay-mall") +public class WebpayPlusMallController extends BaseController { + private static final String TEMPLATE_FOLDER = "webpay_plus_mall"; + private static final String BASE_URL = "/webpay-mall"; + private static final String PRODUCT = "Webpay Mall"; + private static final String MODEL_NAVIGATION = "navigation"; + + private static final String VIEW_CREATE = TEMPLATE_FOLDER + "/create"; + private static final String VIEW_COMMIT = TEMPLATE_FOLDER + "/commit"; + private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; + private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + + private static final Map NAV_CREATE; + private static final Map NAV_COMMIT; + private static final Map NAV_STATUS; + private static final Map NAV_REFUND; + + static { + NAV_CREATE = new LinkedHashMap<>(); + NAV_CREATE.put("request", "Petición"); + NAV_CREATE.put("response", "Respuesta"); + NAV_CREATE.put("form", "Formulario"); + + NAV_COMMIT = new LinkedHashMap<>(); + NAV_COMMIT.put("data", "Datos recibidos"); + NAV_COMMIT.put("request", "Petición"); + NAV_COMMIT.put("response", "Respuesta"); + NAV_COMMIT.put("operations", "¡Listo!"); + + NAV_STATUS = new LinkedHashMap<>(); + NAV_STATUS.put("request", "Petición"); + NAV_STATUS.put("response", "Respuesta"); + + NAV_REFUND = NAV_STATUS; + } + + private final WebpayPlus.MallTransaction tx; + + public WebpayPlusMallController() { + this.tx = new WebpayPlus.MallTransaction( + new WebpayOptions( + IntegrationCommerceCodes.WEBPAY_PLUS_MALL, + IntegrationApiKeys.WEBPAY, + IntegrationType.TEST + ) + ); + } + + private void addProductAndBreadcrumbs(Model model, String label, String url) { + var breadcrumbs = new LinkedHashMap(); + breadcrumbs.put("Inicio", "/"); + breadcrumbs.put(PRODUCT, BASE_URL + "/create"); + if (label != null) + breadcrumbs.put(label, url); + model.addAttribute("product", PRODUCT); + model.addAttribute("breadcrumbs", breadcrumbs); + } + + @GetMapping("/create") + public String create(HttpServletRequest req, Model model) throws TransactionCreateException, IOException { + model.addAttribute(MODEL_NAVIGATION, NAV_CREATE); + addProductAndBreadcrumbs(model, null, null); + + String buyOrder = "buyOrder_" + getRandomNumber(); + String sessionId = "sessionId_" + getRandomNumber(); + String returnUrl = req.getRequestURL().toString().replace("create", "commit"); + + var childCommerceCode1 = IntegrationCommerceCodes.WEBPAY_PLUS_MALL_CHILD1; + var childBuyOrder1 = "childBuyOrder-" + getRandomNumber(); + var amount1 = 1000; + + var childCommerceCode2 = IntegrationCommerceCodes.WEBPAY_PLUS_MALL_CHILD2; + var childBuyOrder2 = "childBuyOrder-" + getRandomNumber(); + var amount2 = 1000; + + var details = MallTransactionCreateDetails.build() + .add(amount1, childCommerceCode1, childBuyOrder1) + .add(amount2, childCommerceCode2, childBuyOrder2); + + Map request = new LinkedHashMap<>(Map.ofEntries( + Map.entry("buyOrder", buyOrder), + Map.entry("sessionId", sessionId), + Map.entry("returnUrl", returnUrl), + Map.entry("details", toJson(details.getDetails())) + )); + + model.addAttribute("request", request); + + var resp = tx.create(buyOrder, sessionId, returnUrl, details); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_CREATE; + } + + @PostMapping(value = "/commit") + public String commitPost( + HttpServletRequest req, + @RequestParam Map params, + Model model) { + model.addAttribute("request_data_json", toJson(params)); + model.addAttribute(MODEL_NAVIGATION, NAV_COMMIT); + addProductAndBreadcrumbs(model, "Confirmar transacción", "#"); + return VIEW_FORM_ERROR; + } + + @GetMapping(value = "/commit") + public String commit( + HttpServletRequest req, + @RequestParam Map params, + @RequestParam(name = "token_ws", required = false) String tokenWs, + @RequestParam(name = "TBK_TOKEN", required = false) String tbkToken, + Model model) throws TransactionCommitException, IOException, TransactionStatusException { + + String viewTemplate = VIEW_COMMIT; + model.addAttribute("request_data_json", toJson(params)); + model.addAttribute(MODEL_NAVIGATION, NAV_COMMIT); + addProductAndBreadcrumbs(model, "Confirmar transacción", "#"); + + if (tbkToken != null && tokenWs != null) { + viewTemplate = VIEW_FORM_ERROR; + } else if (tbkToken != null) { + viewTemplate = VIEW_ABORTED_ERROR; + var resp = tx.status(tbkToken); + model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute("response_data", resp); + } else if (tokenWs != null) { + var resp = tx.commit(tokenWs); + model.addAttribute("token", tokenWs); + model.addAttribute("returnUrl", req.getRequestURL().toString()); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + } else { + viewTemplate = VIEW_TIMEOUT_ERROR; + } + return viewTemplate; + } + + @GetMapping("/status") + public String status(@RequestParam("token_ws") String token, Model model) + throws IOException, TransactionStatusException { + model.addAttribute(MODEL_NAVIGATION, NAV_STATUS); + addProductAndBreadcrumbs(model, "Consultar estado de transacción", "#"); + + final var resp = tx.status(token); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_STATUS; + } + + @GetMapping("/refund") + public String refund(@RequestParam("token") String token, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam double amount, + Model model) throws TransactionRefundException, IOException { + + model.addAttribute(MODEL_NAVIGATION, NAV_REFUND); + addProductAndBreadcrumbs(model, "Reembolsar", "#"); + model.addAttribute("token", token); + + final var resp = tx.refund(token, childBuyOrder, childCommerceCode, amount); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_REFUND; + } + + @ExceptionHandler(Exception.class) + public String handleException(Exception e, Model model) { + log.error("Error inesperado", e); + model.addAttribute("error", e.getMessage()); + return VIEW_ERROR; + } + +} diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index f197ba7..edc9ecc 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -26,6 +26,26 @@ +
  • + + +
  • +
  • + + + + + + + + + + diff --git a/src/main/resources/templates/webpay_plus_mall/create.html b/src/main/resources/templates/webpay_plus_mall/create.html new file mode 100644 index 0000000..a2978cd --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall/create.html @@ -0,0 +1,103 @@ +
    +
    +

    Webpay Mall - Creación de transacción Mall

    +

    + En esta etapa, se procederá a la creación de una transacción con el + fin de obtener un identificador único. Esto nos permitirá redirigir + al Tarjetahabiente hacia el formulario de pago en el siguiente paso. +

    + +

    + Todas las transacciones en este proyecto de ejemplo son realizadas en ambiente de integración. +

    + +

    Paso 1: Petición

    +
      +
    • + Comienza por importar la librería WebpayPlus en tu proyecto. +
    • +
    • + Luego, crea una transacción utilizando las funciones + proporcionadas mediante el SDK. +
    • +
    + +
    
    +import cl.transbank.common.IntegrationType;
    +import cl.transbank.webpay.common.WebpayOptions;
    +import cl.transbank.webpay.webpayplus.WebpayPlus;
    +
    +var details = MallTransactionCreateDetails.build()
    +                .add(amount1, childCommerceCode1, childBuyOrder1)
    +                .add(amount2, childCommerceCode2, childBuyOrder2);
    +
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));
    +
    +var resp = tx.create(buyOrder, sessionId, returnUrl, details);
    +
    + +

    Paso 2: Respuesta

    +

    + Una vez que hayas creado la transacción, aquí encontrarás los datos + de respuesta generados por el proceso. +

    + +
    + +

    Paso 3: Creación del formulario

    +

    + Utiliza estos datos de respuesta para redireccionar al usuario al + formulario de pago al Tarjetahabiente. Este formulario será la + interfaz a través de la cual el usuario realizará su transacción. +

    + +
    
    +
    + + +

    Ejemplo

    +

    + Para llevar a cabo una transacción de compra en nuestro sistema, + primero debemos crear la transacción. Utilizaremos los siguientes + datos para configurar la transacción: +

    + +
    + +

    + Por último, con la respuesta del servicio que confirma la creación + de la transacción, procedemos a crear el formulario de pago. Para + fines de este ejemplo, haremos visible el campo "token_ws", el cual + es esencial para completar el proceso de pago de manera exitosa. +

    + Antes de continuar al formulario de Webpay, asegúrate de contar con + los datos de las tarjetas de prueba que están en la + documentación. + +
    +
    + Formulario de redirección + + +
    +
    +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall/refund.html b/src/main/resources/templates/webpay_plus_mall/refund.html new file mode 100644 index 0000000..6b7dc01 --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall/refund.html @@ -0,0 +1,64 @@ +
    +
    +

    Webpay Mall - Reembolsar

    +

    + En esta etapa, tienes la opción de solicitar el reembolso del monto + al titular de la tarjeta. Dependiendo del monto y el tiempo + transcurrido desde la transacción, este proceso podría resultar en + una Reversa o Anulación, dependiendo de ciertas condiciones (Reversa + en las primeras 3 horas de la autorización, anulación posterior a + eso), o una Anulación parcial si el monto es menor al total. Las + anulaciones parciales para tarjetas débito y prepago no están + soportadas. +

    + +

    Paso 1 - Petición:

    +

    + Para llevar a cabo el reembolso, necesitas proporcionar el token de la transacción y el monto que deseas reversar. + Si anulas el monto total, podría ser una Reversa o Anulación, dependiendo de ciertas condiciones + (Reversa en las primeras 3 horas de la autorización, anulación posterior a eso), + o una Anulación Parcial si el monto es menor al total. +

    +

    + Algunas consideraciones a tener en cuenta: +

    +
      +
    • + No es posible realizar Anulaciones Parciales en pagos con cuotas. +
    • +
    + +

    + En + este link + + podrás ver mayor información sobre las condiciones y casos para + anular o reversar transacciones. +

    + +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    +final var response = tx.refund(token, amount);
    +        
    + +

    Paso 2 - Respuesta

    +

    + Transbank responderá con el resultado del proceso de reembolso, + indicando si se ha realizado una Reversa, Anulación o Anulación + Parcial. +

    +
    +
    +
    + + CONSULTAR ESTADO +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall/status.html b/src/main/resources/templates/webpay_plus_mall/status.html new file mode 100644 index 0000000..cc710df --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall/status.html @@ -0,0 +1,28 @@ +
    +
    +

    Webpay Mall - Consultar estado de transacción

    +

    + Puedes solicitar el estado de una transacción hasta 7 días después + de su realización. No hay límite de solicitudes de este tipo durante + ese período. Sin embargo, una vez pasados los 7 días, ya no podrás + revisar su estado. +

    + +

    Paso 1 - Petición:

    +

    + Para realizar la consulta, necesitas el token de la transacción de la cual deseas obtener el estado. Utiliza este token para realizar una llamada al SDK. +

    +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    +final var response = tx.status(token);
    +        
    + +

    Paso 2 - Respuesta

    +

    + Una vez que hayas creado la transacción, aquí encontrarás los datos de respuesta generados por el proceso. +

    +
    +
    +
    +
    +
    From 8011e4b3d62fca8ff1e66cf61070882d06e4fe86 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Tue, 11 Nov 2025 17:40:57 -0500 Subject: [PATCH 33/39] feat: replace POST to GET --- src/main/resources/templates/webpay_plus/commit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/webpay_plus/commit.html b/src/main/resources/templates/webpay_plus/commit.html index fe931a0..377eccd 100644 --- a/src/main/resources/templates/webpay_plus/commit.html +++ b/src/main/resources/templates/webpay_plus/commit.html @@ -14,7 +14,7 @@

    Webpay Plus - Confirmar transacción

    Paso 1 - Datos recibidos:

    Después de completar el flujo en el formulario de pago, recibirás un - POST con la siguiente información: + GET con la siguiente información:

    From 0257e9b4fff6fb9f243d115cb9ee04e678c55c8c Mon Sep 17 00:00:00 2001 From: mvarlic Date: Tue, 11 Nov 2025 17:42:48 -0500 Subject: [PATCH 34/39] feat: update snippet --- src/main/resources/templates/webpay_plus/create.html | 2 +- src/main/resources/templates/webpay_plus_deferred/create.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/webpay_plus/create.html b/src/main/resources/templates/webpay_plus/create.html index f43ba0b..70c1e79 100644 --- a/src/main/resources/templates/webpay_plus/create.html +++ b/src/main/resources/templates/webpay_plus/create.html @@ -23,7 +23,7 @@

    Paso 1: Petición

    import cl.transbank.webpay.common.WebpayOptions; import cl.transbank.webpay.webpayplus.WebpayPlus; -tx = new WebpayPlus.Transaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST)); +var tx = new WebpayPlus.Transaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST)); var resp = tx.create(buyOrder, sessionId, amount, returnUrl); diff --git a/src/main/resources/templates/webpay_plus_deferred/create.html b/src/main/resources/templates/webpay_plus_deferred/create.html index 61f1de8..389b69b 100644 --- a/src/main/resources/templates/webpay_plus_deferred/create.html +++ b/src/main/resources/templates/webpay_plus_deferred/create.html @@ -23,7 +23,7 @@

    Paso 1: Petición

    import cl.transbank.webpay.common.WebpayOptions; import cl.transbank.webpay.webpayplus.WebpayPlus; -tx = new WebpayPlus.Transaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST)); +var tx = new WebpayPlus.Transaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST)); var resp = tx.create(buyOrder, sessionId, amount, returnUrl); From f57710a225a58ad16e2023b84c6af8570e5d85f3 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Tue, 11 Nov 2025 18:32:31 -0500 Subject: [PATCH 35/39] feat: update table --- .../controllers/WebpayPlusMallController.java | 15 +++++++++++-- .../resources/templates/partials/table.html | 21 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java index 54dadad..501cee3 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java @@ -100,12 +100,23 @@ public String create(HttpServletRequest req, Model model) throws TransactionCrea var details = MallTransactionCreateDetails.build() .add(amount1, childCommerceCode1, childBuyOrder1) .add(amount2, childCommerceCode2, childBuyOrder2); - + Map request = new LinkedHashMap<>(Map.ofEntries( Map.entry("buyOrder", buyOrder), Map.entry("sessionId", sessionId), Map.entry("returnUrl", returnUrl), - Map.entry("details", toJson(details.getDetails())) + Map.entry("details", new Object[]{ + Map.of( + "amount", amount1, + "commerceCode", childCommerceCode1, + "buyOrder", childBuyOrder1 + ), + Map.of( + "amount", amount2, + "commerceCode", childCommerceCode2, + "buyOrder", childBuyOrder2 + ) + }) )); model.addAttribute("request", request); diff --git a/src/main/resources/templates/partials/table.html b/src/main/resources/templates/partials/table.html index 768c5bf..2161366 100644 --- a/src/main/resources/templates/partials/table.html +++ b/src/main/resources/templates/partials/table.html @@ -4,8 +4,25 @@
    Valor
    -
    -
    +
    +
    + + +
    +
    + +
    +
    +
    + : + +
    +
    +
    +
    +
    +
    + From db6e24e88bd1d6ad5684027f9fc03106551c8e38 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 12 Nov 2025 12:40:30 -0500 Subject: [PATCH 36/39] feat: update table component --- .../controllers/WebpayPlusMallController.java | 17 +++++++---------- .../resources/templates/partials/table.html | 14 +++++--------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java index 501cee3..2440b51 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallController.java @@ -101,22 +101,19 @@ public String create(HttpServletRequest req, Model model) throws TransactionCrea .add(amount1, childCommerceCode1, childBuyOrder1) .add(amount2, childCommerceCode2, childBuyOrder2); - Map request = new LinkedHashMap<>(Map.ofEntries( - Map.entry("buyOrder", buyOrder), - Map.entry("sessionId", sessionId), - Map.entry("returnUrl", returnUrl), - Map.entry("details", new Object[]{ - Map.of( + var request = new LinkedHashMap(); + request.put("buyOrder", buyOrder); + request.put("sessionId", sessionId); + request.put("returnUrl", returnUrl); + request.put("details[0]", Map.of( "amount", amount1, "commerceCode", childCommerceCode1, "buyOrder", childBuyOrder1 - ), - Map.of( + )); + request.put("details[1]", Map.of( "amount", amount2, "commerceCode", childCommerceCode2, "buyOrder", childBuyOrder2 - ) - }) )); model.addAttribute("request", request); diff --git a/src/main/resources/templates/partials/table.html b/src/main/resources/templates/partials/table.html index 2161366..cb5099c 100644 --- a/src/main/resources/templates/partials/table.html +++ b/src/main/resources/templates/partials/table.html @@ -4,18 +4,14 @@
    Valor
    -
    -
    -
    - - -
    -
    +
    +
    +
    -
    +
    - : + :
    From 0bf89566f1696a5fc50600d24bfc6db67e61e1ee Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 12 Nov 2025 15:50:09 -0500 Subject: [PATCH 37/39] feat: add webpay mall deferred --- .../WebpayPlusMallDeferredController.java | 240 ++++++++++++++++++ .../resources/templates/partials/sidebar.html | 24 ++ .../webpay_plus_mall_deferred/capture.html | 78 ++++++ .../webpay_plus_mall_deferred/commit.html | 86 +++++++ .../webpay_plus_mall_deferred/create.html | 103 ++++++++ .../webpay_plus_mall_deferred/refund.html | 64 +++++ .../webpay_plus_mall_deferred/status.html | 28 ++ 7 files changed, 623 insertions(+) create mode 100644 src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallDeferredController.java create mode 100644 src/main/resources/templates/webpay_plus_mall_deferred/capture.html create mode 100644 src/main/resources/templates/webpay_plus_mall_deferred/commit.html create mode 100644 src/main/resources/templates/webpay_plus_mall_deferred/create.html create mode 100644 src/main/resources/templates/webpay_plus_mall_deferred/refund.html create mode 100644 src/main/resources/templates/webpay_plus_mall_deferred/status.html diff --git a/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallDeferredController.java b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallDeferredController.java new file mode 100644 index 0000000..bc81e94 --- /dev/null +++ b/src/main/java/cl/transbank/webpay/example/controllers/WebpayPlusMallDeferredController.java @@ -0,0 +1,240 @@ +package cl.transbank.webpay.example.controllers; + +import cl.transbank.common.IntegrationApiKeys; +import cl.transbank.common.IntegrationCommerceCodes; +import cl.transbank.common.IntegrationType; +import cl.transbank.webpay.common.WebpayOptions; +import cl.transbank.model.MallTransactionCreateDetails; +import cl.transbank.webpay.exception.TransactionCaptureException; +import cl.transbank.webpay.exception.TransactionCommitException; +import cl.transbank.webpay.exception.TransactionCreateException; +import cl.transbank.webpay.exception.TransactionRefundException; +import cl.transbank.webpay.exception.TransactionStatusException; +import cl.transbank.webpay.webpayplus.WebpayPlus; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Log4j2 +@Controller +@RequestMapping("/webpay-mall-diferido") +public class WebpayPlusMallDeferredController extends BaseController { + private static final String TEMPLATE_FOLDER = "webpay_plus_mall_deferred"; + private static final String BASE_URL = "/webpay-mall"; + private static final String PRODUCT = "Webpay Mall Diferido"; + private static final String MODEL_NAVIGATION = "navigation"; + + private static final String VIEW_CREATE = TEMPLATE_FOLDER + "/create"; + private static final String VIEW_COMMIT = TEMPLATE_FOLDER + "/commit"; + private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; + private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + private static final String VIEW_CAPTURE = TEMPLATE_FOLDER + "/capture"; + + private static final Map NAV_CREATE; + private static final Map NAV_COMMIT; + private static final Map NAV_STATUS; + private static final Map NAV_REFUND; + private static final Map NAV_CAPTURE; + + static { + NAV_CREATE = new LinkedHashMap<>(); + NAV_CREATE.put("request", "Petición"); + NAV_CREATE.put("response", "Respuesta"); + NAV_CREATE.put("form", "Formulario"); + + NAV_COMMIT = new LinkedHashMap<>(); + NAV_COMMIT.put("data", "Datos recibidos"); + NAV_COMMIT.put("request", "Petición"); + NAV_COMMIT.put("response", "Respuesta"); + NAV_COMMIT.put("operations", "¡Listo!"); + + NAV_STATUS = new LinkedHashMap<>(); + NAV_STATUS.put("request", "Petición"); + NAV_STATUS.put("response", "Respuesta"); + + NAV_REFUND = NAV_STATUS; + + NAV_CAPTURE = new LinkedHashMap<>(); + NAV_CAPTURE.put("request", "Petición"); + NAV_CAPTURE.put("response", "Respuesta"); + NAV_CAPTURE.put("other", "Otras utilidades"); + } + + private final WebpayPlus.MallTransaction tx; + + public WebpayPlusMallDeferredController() { + this.tx = new WebpayPlus.MallTransaction( + new WebpayOptions( + IntegrationCommerceCodes.WEBPAY_PLUS_MALL_DEFERRED, + IntegrationApiKeys.WEBPAY, + IntegrationType.TEST + ) + ); + } + + private void addProductAndBreadcrumbs(Model model, String label, String url) { + var breadcrumbs = new LinkedHashMap(); + breadcrumbs.put("Inicio", "/"); + breadcrumbs.put(PRODUCT, BASE_URL + "/create"); + if (label != null) + breadcrumbs.put(label, url); + model.addAttribute("product", PRODUCT); + model.addAttribute("breadcrumbs", breadcrumbs); + } + + @GetMapping("/create") + public String create(HttpServletRequest req, Model model) throws TransactionCreateException, IOException { + model.addAttribute(MODEL_NAVIGATION, NAV_CREATE); + addProductAndBreadcrumbs(model, null, null); + + String buyOrder = "buyOrder_" + getRandomNumber(); + String sessionId = "sessionId_" + getRandomNumber(); + String returnUrl = req.getRequestURL().toString().replace("create", "commit"); + + var childCommerceCode1 = IntegrationCommerceCodes.WEBPAY_PLUS_MALL_DEFERRED_CHILD1; + var childBuyOrder1 = "childBuyOrder-" + getRandomNumber(); + var amount1 = 1000; + + var childCommerceCode2 = IntegrationCommerceCodes.WEBPAY_PLUS_MALL_DEFERRED_CHILD2; + var childBuyOrder2 = "childBuyOrder-" + getRandomNumber(); + var amount2 = 1000; + + var details = MallTransactionCreateDetails.build() + .add(amount1, childCommerceCode1, childBuyOrder1) + .add(amount2, childCommerceCode2, childBuyOrder2); + + var request = new LinkedHashMap(); + request.put("buyOrder", buyOrder); + request.put("sessionId", sessionId); + request.put("returnUrl", returnUrl); + request.put("details[0]", Map.of( + "amount", amount1, + "commerceCode", childCommerceCode1, + "buyOrder", childBuyOrder1 + )); + request.put("details[1]", Map.of( + "amount", amount2, + "commerceCode", childCommerceCode2, + "buyOrder", childBuyOrder2 + )); + + model.addAttribute("request", request); + + var resp = tx.create(buyOrder, sessionId, returnUrl, details); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_CREATE; + } + + @PostMapping(value = "/commit") + public String commitPost( + HttpServletRequest req, + @RequestParam Map params, + Model model) { + model.addAttribute("request_data_json", toJson(params)); + model.addAttribute(MODEL_NAVIGATION, NAV_COMMIT); + addProductAndBreadcrumbs(model, "Confirmar transacción", "#"); + return VIEW_FORM_ERROR; + } + + @GetMapping(value = "/commit") + public String commit( + HttpServletRequest req, + @RequestParam Map params, + @RequestParam(name = "token_ws", required = false) String tokenWs, + @RequestParam(name = "TBK_TOKEN", required = false) String tbkToken, + Model model) throws TransactionCommitException, IOException, TransactionStatusException { + + String viewTemplate = VIEW_COMMIT; + model.addAttribute("request_data_json", toJson(params)); + model.addAttribute(MODEL_NAVIGATION, NAV_COMMIT); + addProductAndBreadcrumbs(model, "Confirmar transacción", "#"); + + if (tbkToken != null && tokenWs != null) { + viewTemplate = VIEW_FORM_ERROR; + } else if (tbkToken != null) { + viewTemplate = VIEW_ABORTED_ERROR; + var resp = tx.status(tbkToken); + model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute("response_data", resp); + } else if (tokenWs != null) { + var resp = tx.commit(tokenWs); + model.addAttribute("token", tokenWs); + model.addAttribute("returnUrl", req.getRequestURL().toString()); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + } else { + viewTemplate = VIEW_TIMEOUT_ERROR; + } + return viewTemplate; + } + + @GetMapping("/status") + public String status(@RequestParam("token_ws") String token, Model model) + throws IOException, TransactionStatusException { + model.addAttribute(MODEL_NAVIGATION, NAV_STATUS); + addProductAndBreadcrumbs(model, "Consultar estado de transacción", "#"); + + final var resp = tx.status(token); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_STATUS; + } + + @GetMapping("/refund") + public String refund(@RequestParam("token") String token, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam double amount, + Model model) throws TransactionRefundException, IOException { + + model.addAttribute(MODEL_NAVIGATION, NAV_REFUND); + addProductAndBreadcrumbs(model, "Reembolsar", "#"); + model.addAttribute("token", token); + + final var resp = tx.refund(token, childBuyOrder, childCommerceCode, amount); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_REFUND; + } + + @GetMapping("/capture") + public String capture( + @RequestParam("token") String token, + @RequestParam("child_buy_order") String childBuyOrder, + @RequestParam("child_commerce_code") String childCommerceCode, + @RequestParam("authorization_code") String authorizationCode, + @RequestParam double amount, + Model model) + throws IOException, TransactionCaptureException { + + model.addAttribute(MODEL_NAVIGATION, NAV_CAPTURE); + addProductAndBreadcrumbs(model, "Capturar", "#"); + + model.addAttribute("token", token); + model.addAttribute("child_buy_order", childBuyOrder); + model.addAttribute("child_commerce_code", childCommerceCode); + + var resp = tx.capture(childCommerceCode, token, childBuyOrder, authorizationCode, amount); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_CAPTURE; + } + + + @ExceptionHandler(Exception.class) + public String handleException(Exception e, Model model) { + log.error("Error inesperado", e); + model.addAttribute("error", e.getMessage()); + return VIEW_ERROR; + } + +} diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index edc9ecc..a6257de 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -68,6 +68,30 @@
  • + +
  • + + +
  • + diff --git a/src/main/resources/templates/webpay_plus_mall_deferred/capture.html b/src/main/resources/templates/webpay_plus_mall_deferred/capture.html new file mode 100644 index 0000000..ddc5094 --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall_deferred/capture.html @@ -0,0 +1,78 @@ +
    +
    +

    Webpay Mall Diferido - Captura de transacción

    +

    + En este paso debemos capturar la transacción para realmente capturar el dinero que habia sido previamente + reservado al hacer la transacción +

    + +

    Paso 1: Petición

    +

    + Para capturar una transacción necesitaremos el Token, Orden de compra, Código de autorización y monto a + capturar. Se hace de la siguiente manera. +

    + +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));    
    +var resp = tx.capture(childCommerceCode, token, childBuyOrder, authorizationCode, amount);
    +        
    + +

    Paso 2: Respuesta

    +

    + Una vez creada la transacción, recibirás los siguientes datos de respuesta: +

    + +
    + +

    ¡Transacción Capturada!

    + +

    + Con la transacción confirmada, puedes mostrar al usuario una página de éxito de la transacción, proporcionándole la confirmación de que el proceso se ha completado con éxito. +

    + +

    Otras Utilidades

    +

    + Después de confirmar la transacción, considera las siguientes utilidades adicionales: +

    +
      +
    • Reembolso: Evalúa la posibilidad de reversar o anular el pago según ciertas condiciones comerciales.
    • +
    • Consultar Estado: Hasta 7 días después de la transacción, puedes consultar su estado para obtener más detalles.
    • +
    + +
    +
    +
    + + + + + +
    +
    + + CONSULTAR ESTADO +
    +
    +
    +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall_deferred/commit.html b/src/main/resources/templates/webpay_plus_mall_deferred/commit.html new file mode 100644 index 0000000..aa023c8 --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall_deferred/commit.html @@ -0,0 +1,86 @@ +
    +
    +

    Webpay Mall Diferido - Confirmar transacción

    +

    + En este paso es importante confirmar la transacción para notificar a + Transbank que hemos recibido exitosamente los detalles de la + transacción. + Es importante destacar que si la confirmación no se realiza, la + transacción será caducada. +

    + +

    Paso 1 - Datos recibidos:

    +

    + Después de completar el flujo en el formulario de pago, recibirás un + GET con la siguiente información: +

    + +
    + +

    Paso 2 - Petición:

    +

    + Utilizarás el token recibido para confirmar la transacción mediante + el SDK. +

    + +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    +var response = tx.commit(tokenWs);
    +        
    + +

    Paso 3 - Respuesta:

    +

    + Una vez que la transacción ha sido confirmada Transbank + proporcionará la siguiente información. Es fundamental conservar + esta respuesta y verificar que el campo "responseCode" tenga un + valor de cero y que el campo "status" sea "AUTHORIZED". +

    + +
    + +

    ¡Transaccion Confirmada!

    +

    + Ahora que se ha confirmado la transacción, puedes capturar el monto previamente autorizado. + El monto a capturar puede ser igual o menor al monto autorizado. + La captura debe efectuarse dentro de un plazo máximo de 7 días calendario. +

    + +
      +
    • + Capturar: Captura un monto igual o menor al previamente autorizado. +
    • +
    • + Consultar Estado: Hasta 7 días + después de la transacción, podrás consultar el estado de la + transacción. +
    • +
    + +
    +
    +
    +
    + + + + + + +
    + +
    +
    +
    + + + + +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall_deferred/create.html b/src/main/resources/templates/webpay_plus_mall_deferred/create.html new file mode 100644 index 0000000..39414a7 --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall_deferred/create.html @@ -0,0 +1,103 @@ +
    +
    +

    Webpay Mall Diferido - Creación de transacción Mall

    +

    + En esta etapa, se procederá a la creación de una transacción con el + fin de obtener un identificador único. Esto nos permitirá redirigir + al Tarjetahabiente hacia el formulario de pago en el siguiente paso. +

    + +

    + Todas las transacciones en este proyecto de ejemplo son realizadas en ambiente de integración. +

    + +

    Paso 1: Petición

    +
      +
    • + Comienza por importar la librería WebpayPlus en tu proyecto. +
    • +
    • + Luego, crea una transacción utilizando las funciones + proporcionadas mediante el SDK. +
    • +
    + +
    
    +import cl.transbank.common.IntegrationType;
    +import cl.transbank.webpay.common.WebpayOptions;
    +import cl.transbank.webpay.webpayplus.WebpayPlus;
    +
    +var details = MallTransactionCreateDetails.build()
    +                .add(amount1, childCommerceCode1, childBuyOrder1)
    +                .add(amount2, childCommerceCode2, childBuyOrder2);
    +
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));
    +
    +var resp = tx.create(buyOrder, sessionId, returnUrl, details);
    +
    + +

    Paso 2: Respuesta

    +

    + Una vez que hayas creado la transacción, aquí encontrarás los datos + de respuesta generados por el proceso. +

    + +
    + +

    Paso 3: Creación del formulario

    +

    + Utiliza estos datos de respuesta para redireccionar al usuario al + formulario de pago al Tarjetahabiente. Este formulario será la + interfaz a través de la cual el usuario realizará su transacción. +

    + +
    
    +
    + + +

    Ejemplo

    +

    + Para llevar a cabo una transacción de compra en nuestro sistema, + primero debemos crear la transacción. Utilizaremos los siguientes + datos para configurar la transacción: +

    + +
    + +

    + Por último, con la respuesta del servicio que confirma la creación + de la transacción, procedemos a crear el formulario de pago. Para + fines de este ejemplo, haremos visible el campo "token_ws", el cual + es esencial para completar el proceso de pago de manera exitosa. +

    + Antes de continuar al formulario de Webpay, asegúrate de contar con + los datos de las tarjetas de prueba que están en la + documentación. + +
    +
    + Formulario de redirección + + +
    +
    +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall_deferred/refund.html b/src/main/resources/templates/webpay_plus_mall_deferred/refund.html new file mode 100644 index 0000000..ba0e2ce --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall_deferred/refund.html @@ -0,0 +1,64 @@ +
    +
    +

    Webpay Mall Diferido - Reembolsar

    +

    + En esta etapa, tienes la opción de solicitar el reembolso del monto + al titular de la tarjeta. Dependiendo del monto y el tiempo + transcurrido desde la transacción, este proceso podría resultar en + una Reversa o Anulación, dependiendo de ciertas condiciones (Reversa + en las primeras 3 horas de la autorización, anulación posterior a + eso), o una Anulación parcial si el monto es menor al total. Las + anulaciones parciales para tarjetas débito y prepago no están + soportadas. +

    + +

    Paso 1 - Petición:

    +

    + Para llevar a cabo el reembolso, necesitas proporcionar el token de la transacción y el monto que deseas reversar. + Si anulas el monto total, podría ser una Reversa o Anulación, dependiendo de ciertas condiciones + (Reversa en las primeras 3 horas de la autorización, anulación posterior a eso), + o una Anulación Parcial si el monto es menor al total. +

    +

    + Algunas consideraciones a tener en cuenta: +

    +
      +
    • + No es posible realizar Anulaciones Parciales en pagos con cuotas. +
    • +
    + +

    + En + este link + + podrás ver mayor información sobre las condiciones y casos para + anular o reversar transacciones. +

    + +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    +var resp = tx.refund(token, childBuyOrder, childCommerceCode, amount);
    +        
    + +

    Paso 2 - Respuesta

    +

    + Transbank responderá con el resultado del proceso de reembolso, + indicando si se ha realizado una Reversa, Anulación o Anulación + Parcial. +

    +
    +
    +
    + + CONSULTAR ESTADO +
    +
    diff --git a/src/main/resources/templates/webpay_plus_mall_deferred/status.html b/src/main/resources/templates/webpay_plus_mall_deferred/status.html new file mode 100644 index 0000000..13b072f --- /dev/null +++ b/src/main/resources/templates/webpay_plus_mall_deferred/status.html @@ -0,0 +1,28 @@ +
    +
    +

    Webpay Mall Diferido - Consultar estado de transacción

    +

    + Puedes solicitar el estado de una transacción hasta 7 días después + de su realización. No hay límite de solicitudes de este tipo durante + ese período. Sin embargo, una vez pasados los 7 días, ya no podrás + revisar su estado. +

    + +

    Paso 1 - Petición:

    +

    + Para realizar la consulta, necesitas el token de la transacción de la cual deseas obtener el estado. Utiliza este token para realizar una llamada al SDK. +

    +
    
    +var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    +final var response = tx.status(token);
    +        
    + +

    Paso 2 - Respuesta

    +

    + Una vez que hayas creado la transacción, aquí encontrarás los datos de respuesta generados por el proceso. +

    +
    +
    +
    +
    +
    From d8bd10d9f87fd251c4eddf114c32686124b8ec55 Mon Sep 17 00:00:00 2001 From: mvarlic Date: Wed, 12 Nov 2025 15:50:31 -0500 Subject: [PATCH 38/39] feat: update snippet --- src/main/resources/templates/webpay_plus_mall/refund.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/webpay_plus_mall/refund.html b/src/main/resources/templates/webpay_plus_mall/refund.html index 6b7dc01..ae65198 100644 --- a/src/main/resources/templates/webpay_plus_mall/refund.html +++ b/src/main/resources/templates/webpay_plus_mall/refund.html @@ -42,7 +42,7 @@

    Paso 1 - Petición:

    
     var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));            
    -final var response = tx.refund(token, amount);
    +var resp = tx.refund(token, childBuyOrder, childCommerceCode, amount);
             

    Paso 2 - Respuesta

    From 5e2c8193a32874abfc8348f81983c2c3633b11f1 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Tue, 18 Nov 2025 14:09:10 -0300 Subject: [PATCH 39/39] feat: hide onclick and webpay mall options --- src/main/resources/templates/index.html | 20 ---- .../resources/templates/partials/sidebar.html | 95 +------------------ 2 files changed, 1 insertion(+), 114 deletions(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 3f271e9..4bdc054 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -76,26 +76,6 @@

    Proyectos de Ejemplo del SDK para Java

    >Ver ejemplos y modalidades - -
    -
    - oneclick -

    - El producto más usado para realizar un pago online. Se genera - un único cobro para todos los productos o servicios adquiridos - por el tarjetahabiente (carro de compras). -

    -
    - Ver ejemplos y modalidades -

    diff --git a/src/main/resources/templates/partials/sidebar.html b/src/main/resources/templates/partials/sidebar.html index a6257de..3596791 100644 --- a/src/main/resources/templates/partials/sidebar.html +++ b/src/main/resources/templates/partials/sidebar.html @@ -26,26 +26,7 @@ -
  • - - -
  • - +
  • - -
  • - - -
  • - - - - -
    -

    Webpay Oneclick

    -