From ebd6125e16413d1c0332340f3e862cb3d70178e1 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:23:20 -0300 Subject: [PATCH 01/17] feat: add TransaccionCompletaController for full transaction handling --- .../TransaccionCompletaController.java | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java new file mode 100644 index 0000000..bd3e34f --- /dev/null +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -0,0 +1,208 @@ +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.transaccioncompleta.FullTransaction; +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; +import java.util.Random; + +@Log4j2 +@Controller +@RequestMapping("/transaccion-completa") +public class TransaccionCompletaController extends BaseController { + private static final String TEMPLATE_FOLDER = "transaccion_completa"; + private static final String BASE_URL = "/transaccion-completa"; + private static final String PRODUCT = "Webpay Transacción Completa"; + + private static final String VIEW_INDEX = TEMPLATE_FOLDER + "/index"; + private static final String VIEW_CREATE = TEMPLATE_FOLDER + "/create"; + private static final String VIEW_INSTALLMENTS = TEMPLATE_FOLDER + "/installments"; + 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_INDEX; + private static final Map NAV_CREATE; + private static final Map NAV_INSTALLMENTS; + private static final Map NAV_COMMIT; + private static final Map NAV_STATUS; + private static final Map NAV_REFUND; + + static { + NAV_INDEX = new LinkedHashMap<>(); + NAV_INDEX.put("form", "Formulario"); + + NAV_CREATE = new LinkedHashMap<>(); + NAV_CREATE.put("request", "Petición"); + NAV_CREATE.put("response", "Respuesta"); + NAV_CREATE.put("form", "Formulario"); + + NAV_INSTALLMENTS = new LinkedHashMap<>(); + NAV_INSTALLMENTS.put("request", "Petición"); + NAV_INSTALLMENTS.put("response", "Respuesta"); + NAV_INSTALLMENTS.put("form", "Formulario"); + + NAV_COMMIT = new LinkedHashMap<>(); + NAV_COMMIT.put("request", "Petición"); + NAV_COMMIT.put("response", "Respuesta"); + NAV_COMMIT.put("form", "Formulario"); + + NAV_STATUS = new LinkedHashMap<>(); + NAV_STATUS.put("request", "Petición"); + NAV_STATUS.put("response", "Respuesta"); + + NAV_REFUND = NAV_STATUS; + } + + private final FullTransaction tx; + + public TransaccionCompletaController() { + this.tx = new FullTransaction( + new WebpayOptions( + IntegrationCommerceCodes.TRANSACCION_COMPLETA, + 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); + if (label != null) { + breadcrumbs.put(label, url); + } + model.addAttribute("product", PRODUCT); + model.addAttribute("breadcrumbs", breadcrumbs); + } + + @GetMapping("") + public String index(Model model) { + model.addAttribute("navigation", NAV_INDEX); + addProductAndBreadcrumbs(model, null, null); + return VIEW_INDEX; + } + + @PostMapping("/create") + public String create( + HttpServletRequest req, + @RequestParam("number") String number, + @RequestParam("expiry") String expiry, + @RequestParam("cvc") String cvc, + Model model + ) throws TransactionCreateException, IOException { + model.addAttribute("navigation", NAV_CREATE); + addProductAndBreadcrumbs(model, "Crear Transacción", BASE_URL + "/create"); + + String cardNumber = number.replaceAll("\\s+", ""); + String[] expiryParts = expiry.split("/"); + String month = expiryParts.length > 0 ? expiryParts[0] : ""; + String year = expiryParts.length > 1 ? expiryParts[1] : ""; + String cardExpiry = year + "/" + month; + + String buyOrder = "O-" + getRandomNumber(); + String sessionId = "S-" + getRandomNumber(); + double amount = 1000 + new Random().nextInt(1001); + + var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); + req.getSession().setAttribute("transaccion_completa_amount", amount); + + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_CREATE; + } + + @PostMapping("/installments") + public String installments( + @RequestParam("token") String token, + @RequestParam("installments_number") byte installmentsNumber, + Model model + ) throws TransactionInstallmentException, IOException { + model.addAttribute("navigation", NAV_INSTALLMENTS); + addProductAndBreadcrumbs(model, "Consulta de cuotas", BASE_URL + "/installments"); + + var resp = tx.installments(token, installmentsNumber); + model.addAttribute("request_token", token); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_INSTALLMENTS; + } + + @GetMapping("/commit") + public String commit( + HttpServletRequest req, + @RequestParam("token") String token, + @RequestParam(value = "idQueryInstallments", required = false) Long idQueryInstallments, + Model model + ) throws TransactionCommitException, IOException { + model.addAttribute("navigation", NAV_COMMIT); + addProductAndBreadcrumbs(model, "Confirmar", BASE_URL + "/commit"); + + Byte deferredPeriodIndex = null; + Boolean gracePeriod = Boolean.FALSE; + + var resp = tx.commit(token, idQueryInstallments, deferredPeriodIndex, gracePeriod); + Object amount = req.getSession().getAttribute("transaccion_completa_amount"); + req.getSession().removeAttribute("transaccion_completa_amount"); + + model.addAttribute("amount", amount); + model.addAttribute("request_token", token); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_COMMIT; + } + + @GetMapping("/status") + public String status( + @RequestParam("token") String token, + Model model + ) throws TransactionStatusException, IOException { + model.addAttribute("navigation", NAV_STATUS); + addProductAndBreadcrumbs(model, "Estado de transacción", BASE_URL + "/status"); + + var resp = tx.status(token); + model.addAttribute("response_data", resp); + model.addAttribute("response_data_json", toJson(resp)); + + return VIEW_STATUS; + } + + @GetMapping("/refund") + public String refund( + @RequestParam("token") String token, + @RequestParam("amount") double amount, + Model model + ) throws TransactionRefundException, IOException { + model.addAttribute("navigation", NAV_REFUND); + addProductAndBreadcrumbs(model, "Reversa", BASE_URL + "/refund"); + + var resp = tx.refund(token, amount); + model.addAttribute("request_token", token); + model.addAttribute("response_data", resp); + 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 8b03e388e944c28ad5a6f85ef82d8914741d41ec Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:23:29 -0300 Subject: [PATCH 02/17] feat: update styles for card components and improve CSS formatting --- src/main/resources/static/css/styles.css | 63 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index 001d2e2..dbccbe3 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -52,9 +52,9 @@ --tbk-red: #f6f7f9; --tbk-table-header: rgb(35 39 47/0.95); --shadow-elevation: inset 0 0 0 1px hsla(0, 0%, 100%, 0.08); - --white-elevation: 0 0 0 1px hsla(0, 0%, 100%, 0.15), - 0px 0.8px 2px rgba(0, 0, 0, 0.032), 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), - 0px 12px 30px rgba(0, 0, 0, 0.08); + --white-elevation: + 0 0 0 1px hsla(0, 0%, 100%, 0.15), 0px 0.8px 2px rgba(0, 0, 0, 0.032), + 0px 2.7px 6.7px rgba(0, 0, 0, 0.048), 0px 12px 30px rgba(0, 0, 0, 0.08); --tbk-white-red: #ffb9b93b; } @@ -269,6 +269,10 @@ code { margin-bottom: 16px; } +.mr-16 { + margin-right: 16px; +} + .tbk-link { color: var(--tbk-red); text-decoration: none; @@ -651,8 +655,10 @@ code { display: flex; flex-direction: column; border-radius: 4px; - box-shadow: 0px 1px 2px 0px rgba(14, 21, 32, 0.18), - 0px 0px 2px 0px rgba(14, 21, 32, 0.12), 0px 0px 2px 0px rgba(0, 0, 0, 0.04); + box-shadow: + 0px 1px 2px 0px rgba(14, 21, 32, 0.18), + 0px 0px 2px 0px rgba(14, 21, 32, 0.12), + 0px 0px 2px 0px rgba(0, 0, 0, 0.04); color: var(--tbk-black); top: 104px; .dark & { @@ -807,7 +813,9 @@ code { line-height: 1.75rem; font-weight: 700; color: var(--tbk-grey); - transition: opacity 0.5s ease, transform 0.5s ease; + transition: + opacity 0.5s ease, + transform 0.5s ease; padding-left: 24px; } @@ -1112,6 +1120,49 @@ code { } } +/* tx completa */ + +.card-inputs-container { + display: flex; + flex-direction: column; + gap: 20px; + margin-bottom: 50px; + margin-top: 70px; +} + +.card-split-inputs { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 31px; +} + +.card-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + padding-top: 1.5rem; +} + +.card-border { + border: 1px solid #c9d0e4; + border-radius: 1px; + margin-bottom: 30px; + width: 100%; +} + +.card-footer { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.card-wrapper { + z-index: 0; +} + @media (max-width: 1439.98px) { .body-container, .body-container.no-nav { From 3a77a73e24dd678081c2ee247ee0340cfd4d33a6 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:23:59 -0300 Subject: [PATCH 03/17] feat: add card js --- src/main/resources/templates/layout.html | 136 +++++++++++------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html index c14cf99..faf2338 100644 --- a/src/main/resources/templates/layout.html +++ b/src/main/resources/templates/layout.html @@ -1,76 +1,76 @@ - + - - Transbank Sdk Java Example - - - + + Transbank Sdk Java Example + + + - - + + - - - + + + + + - - - + - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
+ gtag("config", "G-6CW8MF50ZX"); + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- - - - - +
+ +
+
+ + + + + From ed0670b1fac24f857bd29558845f79e3a60c16d5 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:24:10 -0300 Subject: [PATCH 04/17] feat: add create transaction page with request and response handling --- .../transaccion_completa/create.html | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/create.html diff --git a/src/main/resources/templates/transaccion_completa/create.html b/src/main/resources/templates/transaccion_completa/create.html new file mode 100644 index 0000000..557cdbd --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/create.html @@ -0,0 +1,65 @@ +
+
+

Transacción Completa - Crear transacción

+

+ En este paso sucede la creación de la transacción con el objetivo de obtener un identificador único para la misma. +

+ +

Paso 1: Petición

+

+ Comienza importando la librería TransaccionCompleta, y a continuación, crea la transacción necesaria. +

+ +

+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.transaccioncompleta.FullTransaction;
+
+FullTransaction tx = new FullTransaction(
+  new WebpayOptions(
+    IntegrationCommerceCodes.TRANSACCION_COMPLETA,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+  )
+);
+
+var resp = tx.create(buyOrder, sessionId, amount, cvv, cardNumber, cardExpiry);
+    
+ +

Paso 2: Respuesta

+

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

+ +
+ +

¡Transacción creada!

+

Ahora que hemos creado la transacción, se abren dos opciones para continuar:

+
    +
  • + Consultar Cuotas (opcional): Alternativamente puedes realizar consultas de cuotas + para ofrecer opciones de pago a plazos. +
  • +
  • + Confirmar Transacción: Debes confirmar directamente la transacción para + finalizar con el proceso de pago. +
  • +
+ +
+
+ Formulario de redirección + +
+ + + +
+ +
+
+
+
From 52ae8366124a422ccbf98b04f7dfa0b8e175a372 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:24:30 -0300 Subject: [PATCH 05/17] feat: add transaction complete form with card details input --- .../templates/transaccion_completa/index.html | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/index.html diff --git a/src/main/resources/templates/transaccion_completa/index.html b/src/main/resources/templates/transaccion_completa/index.html new file mode 100644 index 0000000..5da04d3 --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/index.html @@ -0,0 +1,119 @@ +
+
+

Transacción Completa - Formulario

+

+ En esta primera etapa necesitas obtener los datos esenciales de la tarjeta + de crédito, débito o prepago del titular. Utiliza el formulario para + recolectar esta información de manera segura. +

+ +
+
+
+
+
+
+ + +
+ +
+
+ + +
+
+ + + +
+
+
+ + +
+
+
+ + +
+
From 96d6ba1f4a379101abf55910d6a20321bb7f0b76 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:24:41 -0300 Subject: [PATCH 06/17] feat: add transaction confirmation page with request and response details --- .../transaccion_completa/commit.html | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/commit.html diff --git a/src/main/resources/templates/transaccion_completa/commit.html b/src/main/resources/templates/transaccion_completa/commit.html new file mode 100644 index 0000000..7cfa871 --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/commit.html @@ -0,0 +1,73 @@ +
+
+

Transacción Completa - Confirmar transacción

+

+ En este paso crucial, procederemos a confirmar la transacción con el objetivo de notificar a + Transbank que hemos recibido la transacción de manera exitosa. Es fundamental destacar que si no se confirma la + transacción, esta será caducada. +

+ +

Paso 1: Petición

+

+ Para confirmar la transacción, debes enviar el token correspondiente. En el caso de pagos a + plazos, también debes incluir el ID de la consulta de cuotas. En algunos casos, será necesario proporcionar el + índice del periodo diferido y un valor boolean indicando si se tomará el periodo de gracia. +

+ +

+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.transaccioncompleta.FullTransaction;
+
+FullTransaction tx = new FullTransaction(
+  new WebpayOptions(
+    IntegrationCommerceCodes.TRANSACCION_COMPLETA,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+  )
+);
+var resp = tx.commit(token, idQueryInstallments, deferredPeriodIndex, gracePeriod);
+    
+ +

Paso 2: 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 "response_code" tenga un valor de cero y que el campo "status" + sea "AUTHORIZED". +

+ +
+ +

¡Listo!

+

+ 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. +

+ +

Después de confirmar la transacción, podrás realizar otras operaciones útiles:

+
    +
  • 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.
  • +
+ +
+
+
+ + + +
+ + +
+
+ + CONSULTAR ESTADO +
+
From 054aaf29423b6c0d3291d666432a77f6bae1e928 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:24:48 -0300 Subject: [PATCH 07/17] feat: add installments consultation page with request and response handling --- .../transaccion_completa/installments.html | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/installments.html diff --git a/src/main/resources/templates/transaccion_completa/installments.html b/src/main/resources/templates/transaccion_completa/installments.html new file mode 100644 index 0000000..5f576e0 --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/installments.html @@ -0,0 +1,81 @@ +
+
+

Transacción Completa - Consulta de cuotas

+

+ En esta etapa, realizaremos una consulta de cuotas para conocer sus condiciones. Es importante + destacar que este paso es opcional y se utiliza únicamente si deseas ofrecer opciones de pago a plazos. +

+ +

Paso 1: Petición

+

Para llevar a cabo la consulta de cuotas, debemos enviar los siguientes datos relevantes.

+ +

+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.transaccioncompleta.FullTransaction;
+
+FullTransaction tx = new FullTransaction(
+  new WebpayOptions(
+    IntegrationCommerceCodes.TRANSACCION_COMPLETA,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+  )
+);
+
+var resp = tx.installments(token, installments);
+    
+ +

Paso 2: Respuesta

+

Una vez realizada la consulta de cuotas, recibirás los siguientes datos de respuesta:

+ +
+ +

Confirmar Transacción

+

+ Si decides utilizar cuotas y estás satisfecho con las condiciones obtenidas en la consulta, el + siguiente paso sería confirmar la transacción. +

+ +
+
+ + +
+
+ + +
+ + +
+ + +
+
From 000c13b40e0b3bb1469dc6730af318960eb967ba Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:24:54 -0300 Subject: [PATCH 08/17] feat: add refund transaction page with request and response handling --- .../transaccion_completa/refund.html | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/refund.html diff --git a/src/main/resources/templates/transaccion_completa/refund.html b/src/main/resources/templates/transaccion_completa/refund.html new file mode 100644 index 0000000..7e46aef --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/refund.html @@ -0,0 +1,51 @@ +
+
+

Transacción Completa - Reembolsar

+

+ En esta etapa, tendrás la posibilidad de solicitar el reembolso del dinero al tarjeta habiente. El + tipo de reembolso (Reversa, Anulación o Anulación parcial) dependerá del monto y el tiempo transcurrido desde la + transacción. +

+ +

Paso 1: Petición

+

+ Para efectuar la solicitud de reembolso, necesitarás el token de la transacción y el monto que + deseas reversar. Si decides anular el monto total, puede resultar en una Reversa o Anulación, según ciertas + condiciones. En caso de un monto menor al total, se realizará una Anulación parcial. Las anulaciones parciales + para tarjetas débito y prepago no están soportadas. +

+

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

+ +

+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.transaccioncompleta.FullTransaction;
+
+FullTransaction tx = new FullTransaction(
+  new WebpayOptions(
+    IntegrationCommerceCodes.TRANSACCION_COMPLETA,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+  )
+);
+var resp = tx.refund(token, amount);
+    
+ +

Paso 2: Respuesta

+

+ Transbank responderá con el resultado de la reversa o anulación. Evalúa cuidadosamente esta respuesta para + confirmar que el reembolso se haya procesado de manera efectiva. +

+ +
+ + CONSULTAR ESTADO +
+
From 61189dcfec7d1fbdfebb10d534a61e6ff2f33ccf Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 16:25:01 -0300 Subject: [PATCH 09/17] feat: add transaction status page with request and response handling --- .../transaccion_completa/status.html | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/resources/templates/transaccion_completa/status.html diff --git a/src/main/resources/templates/transaccion_completa/status.html b/src/main/resources/templates/transaccion_completa/status.html new file mode 100644 index 0000000..4e4b236 --- /dev/null +++ b/src/main/resources/templates/transaccion_completa/status.html @@ -0,0 +1,44 @@ +
+
+

Transacción Completa - Estado de transacción

+

+ En esta fase, tendrás la capacidad de solicitar el estado actual de una transacción hasta 7 días + después de su realización. Es importante destacar que no hay límite en la cantidad de solicitudes de este tipo + durante este período. Sin embargo, una vez transcurridos los 7 días, ya no podrás revisar el estado de la + transacción. +

+ +

Paso 1: Petición

+

+ Para llevar a cabo la solicitud de estado, necesitarás el token correspondiente a la transacción + de la cual deseas obtener información. Utiliza este token para realizar una llamada a + Transaction.status(). +

+ +

+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.transaccioncompleta.FullTransaction;
+
+FullTransaction tx = new FullTransaction(
+  new WebpayOptions(
+    IntegrationCommerceCodes.TRANSACCION_COMPLETA,
+    IntegrationApiKeys.WEBPAY,
+    IntegrationType.TEST
+  )
+);
+
+var resp = tx.status(token);
+    
+ +

Paso 2: Respuesta

+

+ Transbank responderá con la siguiente información. Asegúrate de guardar estos detalles; lo único que necesitas + validar es que el campo "response_code" sea igual a cero. +

+ +
+
+
From 4daaf6b3a79ded3420f2aa02e8fcc925d286d381 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Wed, 18 Feb 2026 18:14:57 -0300 Subject: [PATCH 10/17] feat: update navigation labels for transaction confirmation and refund --- .../example/controllers/TransaccionCompletaController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index bd3e34f..e0fc812 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -104,7 +104,7 @@ public String create( Model model ) throws TransactionCreateException, IOException { model.addAttribute("navigation", NAV_CREATE); - addProductAndBreadcrumbs(model, "Crear Transacción", BASE_URL + "/create"); + addProductAndBreadcrumbs(model, "Crear transacción", BASE_URL + "/create"); String cardNumber = number.replaceAll("\\s+", ""); String[] expiryParts = expiry.split("/"); @@ -150,7 +150,7 @@ public String commit( Model model ) throws TransactionCommitException, IOException { model.addAttribute("navigation", NAV_COMMIT); - addProductAndBreadcrumbs(model, "Confirmar", BASE_URL + "/commit"); + addProductAndBreadcrumbs(model, "Confirmar transacción", BASE_URL + "/commit"); Byte deferredPeriodIndex = null; Boolean gracePeriod = Boolean.FALSE; @@ -189,7 +189,7 @@ public String refund( Model model ) throws TransactionRefundException, IOException { model.addAttribute("navigation", NAV_REFUND); - addProductAndBreadcrumbs(model, "Reversa", BASE_URL + "/refund"); + addProductAndBreadcrumbs(model, "Reembolsar", BASE_URL + "/refund"); var resp = tx.refund(token, amount); model.addAttribute("request_token", token); From 7460e2bcfbd2c4e568e84b2bd0785ffe9cfebd76 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 11:52:50 -0300 Subject: [PATCH 11/17] feat: replace Random with ThreadLocalRandom for amount generation --- .../example/controllers/TransaccionCompletaController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index e0fc812..1caa756 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; @Log4j2 @Controller @@ -114,7 +114,7 @@ public String create( String buyOrder = "O-" + getRandomNumber(); String sessionId = "S-" + getRandomNumber(); - double amount = 1000 + new Random().nextInt(1001); + double amount = 1000 + ThreadLocalRandom.current().nextInt(1001); var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); req.getSession().setAttribute("transaccion_completa_amount", amount); From d4e7a6ef58b1a6341e46ceae510b6a76493fed71 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 12:57:01 -0300 Subject: [PATCH 12/17] feat: replace ThreadLocalRandom with SecureRandom for amount generation --- .../example/controllers/TransaccionCompletaController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 1caa756..3850e29 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; +import java.security.SecureRandom; @Log4j2 @Controller @@ -32,6 +32,8 @@ public class TransaccionCompletaController extends BaseController { private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + private static final Map NAV_INDEX; private static final Map NAV_CREATE; private static final Map NAV_INSTALLMENTS; @@ -114,7 +116,7 @@ public String create( String buyOrder = "O-" + getRandomNumber(); String sessionId = "S-" + getRandomNumber(); - double amount = 1000 + ThreadLocalRandom.current().nextInt(1001); + double amount = 1000 + SECURE_RANDOM.nextInt(1001); var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); req.getSession().setAttribute("transaccion_completa_amount", amount); From 8026794b959e468248a343abc648eb7e50b5ab12 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 14:57:25 -0300 Subject: [PATCH 13/17] feat: ensure amount is a double for transaction creation --- .../example/controllers/TransaccionCompletaController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 3850e29..9b65f7e 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -116,7 +116,7 @@ public String create( String buyOrder = "O-" + getRandomNumber(); String sessionId = "S-" + getRandomNumber(); - double amount = 1000 + SECURE_RANDOM.nextInt(1001); + double amount = 1000.0 + SECURE_RANDOM.nextInt(1001); var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); req.getSession().setAttribute("transaccion_completa_amount", amount); From f6dc683b5260ca1075c98fd82b6a2580cbdc508b Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 15:25:17 -0300 Subject: [PATCH 14/17] feat: define a constant instead of duplicating string --- .../TransaccionCompletaController.java | 83 +++++++++++-------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 9b65f7e..3b0dfcf 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -32,6 +32,19 @@ public class TransaccionCompletaController extends BaseController { private static final String VIEW_STATUS = TEMPLATE_FOLDER + "/status"; private static final String VIEW_REFUND = TEMPLATE_FOLDER + "/refund"; + private static final String NAV_LABEL_FORM = "Formulario"; + private static final String NAV_LABEL_REQUEST = "Petición"; + private static final String NAV_LABEL_RESPONSE = "Respuesta"; + + private static final String ATTR_NAVIGATION = "navigation"; + private static final String ATTR_PRODUCT = "product"; + private static final String ATTR_BREADCRUMBS = "breadcrumbs"; + private static final String ATTR_RESPONSE_DATA = "response_data"; + private static final String ATTR_RESPONSE_DATA_JSON = "response_data_json"; + private static final String ATTR_REQUEST_TOKEN = "request_token"; + private static final String ATTR_AMOUNT = "amount"; + private static final String ATTR_ERROR = "error"; + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); private static final Map NAV_INDEX; @@ -43,26 +56,26 @@ public class TransaccionCompletaController extends BaseController { static { NAV_INDEX = new LinkedHashMap<>(); - NAV_INDEX.put("form", "Formulario"); + NAV_INDEX.put("form", NAV_LABEL_FORM); NAV_CREATE = new LinkedHashMap<>(); - NAV_CREATE.put("request", "Petición"); - NAV_CREATE.put("response", "Respuesta"); - NAV_CREATE.put("form", "Formulario"); + NAV_CREATE.put("request", NAV_LABEL_REQUEST); + NAV_CREATE.put("response", NAV_LABEL_RESPONSE); + NAV_CREATE.put("form", NAV_LABEL_FORM); NAV_INSTALLMENTS = new LinkedHashMap<>(); - NAV_INSTALLMENTS.put("request", "Petición"); - NAV_INSTALLMENTS.put("response", "Respuesta"); - NAV_INSTALLMENTS.put("form", "Formulario"); + NAV_INSTALLMENTS.put("request", NAV_LABEL_REQUEST); + NAV_INSTALLMENTS.put("response", NAV_LABEL_RESPONSE); + NAV_INSTALLMENTS.put("form", NAV_LABEL_FORM); NAV_COMMIT = new LinkedHashMap<>(); - NAV_COMMIT.put("request", "Petición"); - NAV_COMMIT.put("response", "Respuesta"); - NAV_COMMIT.put("form", "Formulario"); + NAV_COMMIT.put("request", NAV_LABEL_REQUEST); + NAV_COMMIT.put("response", NAV_LABEL_RESPONSE); + NAV_COMMIT.put("form", NAV_LABEL_FORM); NAV_STATUS = new LinkedHashMap<>(); - NAV_STATUS.put("request", "Petición"); - NAV_STATUS.put("response", "Respuesta"); + NAV_STATUS.put("request", NAV_LABEL_REQUEST); + NAV_STATUS.put("response", NAV_LABEL_RESPONSE); NAV_REFUND = NAV_STATUS; } @@ -86,13 +99,13 @@ private void addProductAndBreadcrumbs(Model model, String label, String url) { if (label != null) { breadcrumbs.put(label, url); } - model.addAttribute("product", PRODUCT); - model.addAttribute("breadcrumbs", breadcrumbs); + model.addAttribute(ATTR_PRODUCT, PRODUCT); + model.addAttribute(ATTR_BREADCRUMBS, breadcrumbs); } @GetMapping("") public String index(Model model) { - model.addAttribute("navigation", NAV_INDEX); + model.addAttribute(ATTR_NAVIGATION, NAV_INDEX); addProductAndBreadcrumbs(model, null, null); return VIEW_INDEX; } @@ -105,7 +118,7 @@ public String create( @RequestParam("cvc") String cvc, Model model ) throws TransactionCreateException, IOException { - model.addAttribute("navigation", NAV_CREATE); + model.addAttribute(ATTR_NAVIGATION, NAV_CREATE); addProductAndBreadcrumbs(model, "Crear transacción", BASE_URL + "/create"); String cardNumber = number.replaceAll("\\s+", ""); @@ -121,8 +134,8 @@ public String create( var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); req.getSession().setAttribute("transaccion_completa_amount", amount); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(ATTR_RESPONSE_DATA, resp); + model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); return VIEW_CREATE; } @@ -133,13 +146,13 @@ public String installments( @RequestParam("installments_number") byte installmentsNumber, Model model ) throws TransactionInstallmentException, IOException { - model.addAttribute("navigation", NAV_INSTALLMENTS); + model.addAttribute(ATTR_NAVIGATION, NAV_INSTALLMENTS); addProductAndBreadcrumbs(model, "Consulta de cuotas", BASE_URL + "/installments"); var resp = tx.installments(token, installmentsNumber); - model.addAttribute("request_token", token); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(ATTR_REQUEST_TOKEN, token); + model.addAttribute(ATTR_RESPONSE_DATA, resp); + model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); return VIEW_INSTALLMENTS; } @@ -151,7 +164,7 @@ public String commit( @RequestParam(value = "idQueryInstallments", required = false) Long idQueryInstallments, Model model ) throws TransactionCommitException, IOException { - model.addAttribute("navigation", NAV_COMMIT); + model.addAttribute(ATTR_NAVIGATION, NAV_COMMIT); addProductAndBreadcrumbs(model, "Confirmar transacción", BASE_URL + "/commit"); Byte deferredPeriodIndex = null; @@ -161,10 +174,10 @@ public String commit( Object amount = req.getSession().getAttribute("transaccion_completa_amount"); req.getSession().removeAttribute("transaccion_completa_amount"); - model.addAttribute("amount", amount); - model.addAttribute("request_token", token); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(ATTR_AMOUNT, amount); + model.addAttribute(ATTR_REQUEST_TOKEN, token); + model.addAttribute(ATTR_RESPONSE_DATA, resp); + model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); return VIEW_COMMIT; } @@ -174,12 +187,12 @@ public String status( @RequestParam("token") String token, Model model ) throws TransactionStatusException, IOException { - model.addAttribute("navigation", NAV_STATUS); + model.addAttribute(ATTR_NAVIGATION, NAV_STATUS); addProductAndBreadcrumbs(model, "Estado de transacción", BASE_URL + "/status"); var resp = tx.status(token); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(ATTR_RESPONSE_DATA, resp); + model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); return VIEW_STATUS; } @@ -190,13 +203,13 @@ public String refund( @RequestParam("amount") double amount, Model model ) throws TransactionRefundException, IOException { - model.addAttribute("navigation", NAV_REFUND); + model.addAttribute(ATTR_NAVIGATION, NAV_REFUND); addProductAndBreadcrumbs(model, "Reembolsar", BASE_URL + "/refund"); var resp = tx.refund(token, amount); - model.addAttribute("request_token", token); - model.addAttribute("response_data", resp); - model.addAttribute("response_data_json", toJson(resp)); + model.addAttribute(ATTR_REQUEST_TOKEN, token); + model.addAttribute(ATTR_RESPONSE_DATA, resp); + model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); return VIEW_REFUND; } @@ -204,7 +217,7 @@ public String refund( @ExceptionHandler(Exception.class) public String handleException(Exception e, Model model) { log.error("Error inesperado", e); - model.addAttribute("error", e.getMessage()); + model.addAttribute(ATTR_ERROR, e.getMessage()); return VIEW_ERROR; } } From a2b26dceef47bba34e5037d13ea7d73b62af7c6f Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 17:21:35 -0300 Subject: [PATCH 15/17] feat: refactor navigation maps and session attribute handling in TransaccionCompletaController --- .../TransaccionCompletaController.java | 59 ++++++++----------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 3b0dfcf..7bdbd30 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -45,39 +45,30 @@ public class TransaccionCompletaController extends BaseController { private static final String ATTR_AMOUNT = "amount"; private static final String ATTR_ERROR = "error"; + private static final String SESSION_AMOUNT = "transaccion_completa_amount"; + private static final String NAV_KEY_REQUEST = "request"; + private static final String NAV_KEY_RESPONSE = "response"; + private static final String NAV_KEY_FORM = "form"; + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); - private static final Map NAV_INDEX; - private static final Map NAV_CREATE; - private static final Map NAV_INSTALLMENTS; - private static final Map NAV_COMMIT; - private static final Map NAV_STATUS; - private static final Map NAV_REFUND; - - static { - NAV_INDEX = new LinkedHashMap<>(); - NAV_INDEX.put("form", NAV_LABEL_FORM); - - NAV_CREATE = new LinkedHashMap<>(); - NAV_CREATE.put("request", NAV_LABEL_REQUEST); - NAV_CREATE.put("response", NAV_LABEL_RESPONSE); - NAV_CREATE.put("form", NAV_LABEL_FORM); - - NAV_INSTALLMENTS = new LinkedHashMap<>(); - NAV_INSTALLMENTS.put("request", NAV_LABEL_REQUEST); - NAV_INSTALLMENTS.put("response", NAV_LABEL_RESPONSE); - NAV_INSTALLMENTS.put("form", NAV_LABEL_FORM); - - NAV_COMMIT = new LinkedHashMap<>(); - NAV_COMMIT.put("request", NAV_LABEL_REQUEST); - NAV_COMMIT.put("response", NAV_LABEL_RESPONSE); - NAV_COMMIT.put("form", NAV_LABEL_FORM); - - NAV_STATUS = new LinkedHashMap<>(); - NAV_STATUS.put("request", NAV_LABEL_REQUEST); - NAV_STATUS.put("response", NAV_LABEL_RESPONSE); - - NAV_REFUND = NAV_STATUS; + private static final Map NAV_INDEX = createNav(NAV_KEY_FORM); + private static final Map NAV_CREATE = createNav(NAV_KEY_REQUEST, NAV_KEY_RESPONSE, NAV_KEY_FORM); + private static final Map NAV_INSTALLMENTS = createNav(NAV_KEY_REQUEST, NAV_KEY_RESPONSE, NAV_KEY_FORM); + private static final Map NAV_COMMIT = createNav(NAV_KEY_REQUEST, NAV_KEY_RESPONSE, NAV_KEY_FORM); + private static final Map NAV_STATUS = createNav(NAV_KEY_REQUEST, NAV_KEY_RESPONSE); + private static final Map NAV_REFUND = NAV_STATUS; + + private static Map createNav(String... keys) { + Map nav = new LinkedHashMap<>(); + for (String key : keys) { + switch (key) { + case NAV_KEY_REQUEST -> nav.put(key, NAV_LABEL_REQUEST); + case NAV_KEY_RESPONSE -> nav.put(key, NAV_LABEL_RESPONSE); + case NAV_KEY_FORM -> nav.put(key, NAV_LABEL_FORM); + } + } + return nav; } private final FullTransaction tx; @@ -132,7 +123,7 @@ public String create( double amount = 1000.0 + SECURE_RANDOM.nextInt(1001); var resp = tx.create(buyOrder, sessionId, amount, Short.parseShort(cvc), cardNumber, cardExpiry); - req.getSession().setAttribute("transaccion_completa_amount", amount); + req.getSession().setAttribute(SESSION_AMOUNT, amount); model.addAttribute(ATTR_RESPONSE_DATA, resp); model.addAttribute(ATTR_RESPONSE_DATA_JSON, toJson(resp)); @@ -171,8 +162,8 @@ public String commit( Boolean gracePeriod = Boolean.FALSE; var resp = tx.commit(token, idQueryInstallments, deferredPeriodIndex, gracePeriod); - Object amount = req.getSession().getAttribute("transaccion_completa_amount"); - req.getSession().removeAttribute("transaccion_completa_amount"); + Object amount = req.getSession().getAttribute(SESSION_AMOUNT); + req.getSession().removeAttribute(SESSION_AMOUNT); model.addAttribute(ATTR_AMOUNT, amount); model.addAttribute(ATTR_REQUEST_TOKEN, token); From ff2cf2afd24908d94c5df622c174f7649b6c3382 Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 17:28:40 -0300 Subject: [PATCH 16/17] feat: add default case to navigation map switch statement in TransaccionCompletaController --- .../example/controllers/TransaccionCompletaController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 7bdbd30..3044682 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -66,6 +66,7 @@ private static Map createNav(String... keys) { case NAV_KEY_REQUEST -> nav.put(key, NAV_LABEL_REQUEST); case NAV_KEY_RESPONSE -> nav.put(key, NAV_LABEL_RESPONSE); case NAV_KEY_FORM -> nav.put(key, NAV_LABEL_FORM); + default -> ""; } } return nav; From 6ee86a6263ec1dcbc7786119008443a8a13f197f Mon Sep 17 00:00:00 2001 From: victor mendoza Date: Thu, 19 Feb 2026 17:30:50 -0300 Subject: [PATCH 17/17] feat: update default case in navigation map switch statement to an empty block in TransaccionCompletaController --- .../example/controllers/TransaccionCompletaController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java index 3044682..312b717 100644 --- a/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java +++ b/src/main/java/cl/transbank/webpay/example/controllers/TransaccionCompletaController.java @@ -66,7 +66,7 @@ private static Map createNav(String... keys) { case NAV_KEY_REQUEST -> nav.put(key, NAV_LABEL_REQUEST); case NAV_KEY_RESPONSE -> nav.put(key, NAV_LABEL_RESPONSE); case NAV_KEY_FORM -> nav.put(key, NAV_LABEL_FORM); - default -> ""; + default -> { } } } return nav;