Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -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<String, String> NAV_CREATE;
private static final Map<String, String> NAV_COMMIT;
private static final Map<String, String> NAV_STATUS;
private static final Map<String, String> NAV_REFUND;
private static final Map<String, String> 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<String, String>();
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<String, Object>();
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<String, String> 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<String, String> 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;
}

}
24 changes: 24 additions & 0 deletions src/main/resources/templates/partials/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,30 @@
</li>
</ul>
</li>

<li style="margin-bottom: 20px">
<button class="sidebar-collapsible-title">
<span class="general-text">Webpay Plus Mall Diferido</span>
<img
th:src="@{/images/t-arrow.svg}"
class="arrow"
alt="t-arrow"
width="24"
height="24"
/>
</button>
<ul class="collapsible-content" id="collapse-webpay-plus-mall-deferred">
<li class="collapsible-items">
<a
th:href="@{/webpay-mall-diferido/create}"
class="tbk-sidebar-item"
>
Flujo Completo
</a>
</li>
</ul>
</li>

</ul>
<hr class="sidebar-divider" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/webpay_plus_mall/refund.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ <h2 id="request">Paso 1 - Petición:</h2>

<pre><code class="language-java mb-32">
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);
</code></pre>

<h2 id="response">Paso 2 - Respuesta</h2>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<div th:replace="~{layout :: layout(~{::content})}">
<div th:fragment="content">
<h1>Webpay Mall Diferido - Captura de transacción</h1>
<p class="mb-32">
En este paso debemos capturar la transacción para realmente capturar el dinero que habia sido previamente
reservado al hacer la transacción
</p>

<h2 id="request">Paso 1: Petición</h2>
<p class="mb-32">
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.
</p>

<pre><code class="language-java">
var tx = new WebpayPlus.MallTransaction(new WebpayOptions(commerceCode, apiKey, IntegrationType.TEST));
var resp = tx.capture(childCommerceCode, token, childBuyOrder, authorizationCode, amount);
</code></pre>

<h2 class="mt-32" id="response">Paso 2: Respuesta</h2>
<p class="mb-32">
Una vez creada la transacción, recibirás los siguientes datos de respuesta:
</p>

<pre><code class="language-json mb-32" th:text="${response_data_json}"></code></pre>

<h2 id="operations">¡Transacción Capturada!</h2>

<p class="mb-32">
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.
</p>

<h2>Otras Utilidades</h2>
<p>
Después de confirmar la transacción, considera las siguientes utilidades adicionales:
</p>
<ul class="bullet-list mb-32">
<li><span class="fw-700">Reembolso:</span> Evalúa la posibilidad de reversar o anular el pago según ciertas condiciones comerciales.</li>
<li><span class="fw-700">Consultar Estado:</span> Hasta 7 días después de la transacción, puedes consultar su estado para obtener más detalles.</li>
</ul>

<form action="/webpay-mall-diferido/refund" method="GET">
<div class="tbk-refund-card mb-32">
<div class="input-container">
<label for="amount" class="tbk-label"
>Monto a reembolsar:</label
>
<input
type="text"
name="amount"
class="tbk-input-text"
th:value="${response_data.capturedAmount}"
/>
<input
type="hidden"
name="token"
th:value="${token}"
/>
<input
type="hidden"
name="child_buy_order"
th:value="${child_buy_order}"
/>
<input
type="hidden"
name="child_commerce_code"
th:value="${child_commerce_code}"
/>
</div>
<div class="tbk-refund-button-container">
<button class="tbk-button primary">REEMBOLSAR</button>
<a th:href="@{/webpay-mall-diferido/status(token_ws=${token})}"
class="tbk-button primary mb-32">CONSULTAR ESTADO</a>
</div>
</div>
</form>
</div>
</div>
Loading