Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
42c5aec
feat: add oneclick deferred
mvarlic Oct 24, 2025
2f68671
feat: add controller for onclick mall
victormendoza96 Oct 27, 2025
394e251
feat: add onclick in home page
victormendoza96 Oct 27, 2025
1ca346a
feat: add onclick in sidebar
victormendoza96 Oct 27, 2025
b27e3e6
feat: create start page for onclik mall
victormendoza96 Oct 27, 2025
bfe4d33
feat: create finish page for onclik mall
victormendoza96 Oct 27, 2025
9143317
feat: create delete page for onclik mall
victormendoza96 Oct 27, 2025
eb0fb59
feat: create authorize page for onclik mall
victormendoza96 Oct 27, 2025
d27b6d7
feat: create refund page for onclik mall
victormendoza96 Oct 27, 2025
1214d4a
feat: create status page for onclik mall
victormendoza96 Oct 27, 2025
81c9d93
feat: add content menu
mvarlic Oct 27, 2025
46a2659
feat: remove code tag
mvarlic Oct 27, 2025
1f35786
feat: remove unused variable
mvarlic Oct 27, 2025
b70d239
style: remove comment
mvarlic Oct 27, 2025
202f275
feat: replace by constants
mvarlic Oct 27, 2025
c8ab500
style: add line
mvarlic Oct 29, 2025
6926063
feat: update option menu id
mvarlic Oct 29, 2025
3b084f9
Merge branch 'feat/oneclick-mall' into feat/oneclick-mall-deferred
mvarlic Oct 29, 2025
bdcb26d
fix: scroll over header
victormendoza96 Oct 29, 2025
582e0cb
fix: use correct id for rigth sidebar
victormendoza96 Oct 29, 2025
eef84ec
fix: typo
victormendoza96 Oct 29, 2025
49cdfac
Merge branch 'feat/oneclick-mall' into feat/oneclick-mall-deferred
mvarlic Oct 29, 2025
cc4d624
fix: remove logs
victormendoza96 Oct 29, 2025
19e513e
fix: typo
victormendoza96 Oct 30, 2025
03bf947
feat: replace diferido to deferred
mvarlic Oct 30, 2025
7f111bd
feat: add oneclick error pages
mvarlic Nov 1, 2025
cc915e8
Merge branch 'feat/oneclick-mall' into feat/oneclick-mall-deferred
mvarlic Nov 1, 2025
29e8b25
feat: update text case from 'listo' to 'Listo'
mvarlic Nov 1, 2025
8f92848
feat: add 'Webpay' to 'Oneclick Mall Diferido'
mvarlic Nov 1, 2025
2f82e72
feat: add error page to Oneclick Mall
mvarlic Nov 3, 2025
b694494
feat: use constant
mvarlic Nov 3, 2025
7564be4
feat: update page error with request data
mvarlic Nov 5, 2025
0854815
feat: update message in page error
mvarlic Nov 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
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 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";
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<String, String> NAV_START;
private static final Map<String, String> NAV_FINISH;
private static final Map<String, String> NAV_FINISH_RECOVER;
private static final Map<String, String> NAV_FINISH_REJECTED;
private static final Map<String, String> NAV_AUTHORIZE;
private static final Map<String, String> NAV_DELETE;
private static final Map<String, String> NAV_STATUS;
private static final Map<String, String> 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("example", "Ejemplo");

NAV_FINISH = new LinkedHashMap<>();
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_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");
NAV_AUTHORIZE.put("done", "Listo");

NAV_STATUS = new LinkedHashMap<>();
NAV_STATUS.put("request", "Petición");
NAV_STATUS.put("response", "Respuesta");

NAV_DELETE = new LinkedHashMap<>();
NAV_DELETE.put("data", "Borrar usuario");

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<String, String> 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 {
model.addAttribute(MODEL_NAVIGATION, NAV_START);
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(MODEL_RESPONSE, resp);
model.addAttribute(MODEL_RESPONSE_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 Map<String, String> 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));

if (resp.getResponseCode() != AUTHORIZED) {
model.addAttribute(MODEL_NAVIGATION, NAV_FINISH_REJECTED);
model.addAttribute("request_data_json", toJson(params));
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("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 {

model.addAttribute(MODEL_NAVIGATION, NAV_DELETE);
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 {

model.addAttribute(MODEL_NAVIGATION, NAV_AUTHORIZE);
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(MODEL_RESPONSE, resp);
model.addAttribute(MODEL_RESPONSE_JSON, toJson(resp));

return VIEW_AUTHORIZE;
}

@GetMapping("/status")
public String status(@RequestParam("buy_order") String buyOrder, Model model)
throws IOException, TransactionStatusException {

model.addAttribute(MODEL_NAVIGATION, NAV_STATUS);
addBreadcrumbs(model, "Consultar estado", "#");

var resp = transaction.status(buyOrder);
model.addAttribute(MODEL_RESPONSE_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 {
model.addAttribute(MODEL_NAVIGATION, NAV_REFUND);
addBreadcrumbs(model, "Reembolso", "#");

model.addAttribute("buy_order", buyOrder);
var resp = transaction.refund(buyOrder, childCommerceCode, childBuyOrder, amount);
model.addAttribute(MODEL_RESPONSE_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;
}
}
Loading