mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 08:58:48 +00:00
trabajando en la tabla de transferencias
This commit is contained in:
@ -8,6 +8,7 @@ import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -17,7 +18,9 @@ import com.imprimelibros.erp.datatables.DataTable;
|
||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||
import com.imprimelibros.erp.i18n.TranslationService;
|
||||
import com.imprimelibros.erp.payments.model.Payment;
|
||||
import com.imprimelibros.erp.payments.model.PaymentStatus;
|
||||
import com.imprimelibros.erp.payments.model.PaymentTransaction;
|
||||
import com.imprimelibros.erp.payments.model.PaymentTransactionStatus;
|
||||
import com.imprimelibros.erp.payments.model.PaymentTransactionType;
|
||||
@ -32,20 +35,34 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
@PreAuthorize("hasRole('SUPERADMIN')")
|
||||
public class PaymentController {
|
||||
|
||||
private final MessageSource messageSource;
|
||||
|
||||
protected final MessageSource messageSource;
|
||||
protected final TranslationService translationService;
|
||||
protected final PaymentTransactionRepository repoPaymentTransaction;
|
||||
protected final UserDao repoUser;
|
||||
|
||||
public PaymentController(PaymentTransactionRepository repoPaymentTransaction, UserDao repoUser,
|
||||
MessageSource messageSource) {
|
||||
MessageSource messageSource, TranslationService translationService) {
|
||||
this.repoPaymentTransaction = repoPaymentTransaction;
|
||||
this.repoUser = repoUser;
|
||||
this.messageSource = messageSource;
|
||||
this.translationService = translationService;
|
||||
}
|
||||
|
||||
@GetMapping()
|
||||
public String index() {
|
||||
public String index(Model model, Locale locale) {
|
||||
|
||||
List<String> keys = List.of(
|
||||
"app.cancelar",
|
||||
"app.aceptar",
|
||||
"pagos.refund.title",
|
||||
"pagos.refund.text",
|
||||
"pagos.refund.success",
|
||||
"pagos.refund.error.general",
|
||||
"pagos.refund.error.invalid-number");
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
|
||||
return "imprimelibros/pagos/gestion-pagos";
|
||||
}
|
||||
|
||||
@ -131,6 +148,7 @@ public class PaymentController {
|
||||
return "<span class=\'badge bg-secondary btn-refund-payment \' data-dsOrderId=\'"
|
||||
+ p.getGatewayOrderId()
|
||||
+ "\' data-transactionId=\'" + pago.getPayment().getId()
|
||||
+ "\' data-amount=\'" + (pago.getAmountCents() - p.getAmountRefundedCents())
|
||||
+ "\' style=\'cursor: pointer;\'>"
|
||||
+ messageSource.getMessage("pagos.table.devuelto", null, locale) + "</span>";
|
||||
}
|
||||
@ -144,4 +162,123 @@ public class PaymentController {
|
||||
|
||||
}
|
||||
|
||||
@GetMapping(value = "datatable/transferencias", produces = "application/json")
|
||||
@ResponseBody
|
||||
public DataTablesResponse<Map<String, Object>> getDatatableTransferencias(HttpServletRequest request,
|
||||
Locale locale) {
|
||||
|
||||
DataTablesRequest dt = DataTablesParser.from(request);
|
||||
|
||||
List<String> searchable = List.of(
|
||||
// "client" no, porque lo calculas a posteriori
|
||||
);
|
||||
|
||||
// Campos ordenables
|
||||
List<String> orderable = List.of(
|
||||
"transferId",
|
||||
"status",
|
||||
"amountCents",
|
||||
"payment.amountRefundedCents",
|
||||
"createdAt", "updatedAt");
|
||||
|
||||
Specification<PaymentTransaction> base = Specification.allOf(
|
||||
(root, query, cb) -> cb.equal(root.get("status"), PaymentTransactionStatus.pending));
|
||||
base = base.and((root, query, cb) -> cb.equal(root.get("type"), PaymentTransactionType.CAPTURE));
|
||||
base = base.and((root, query, cb) -> cb.equal(root.get("payment").get("gateway"), "bank_transfer"));
|
||||
|
||||
String clientSearch = dt.getColumnSearch("client");
|
||||
|
||||
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
|
||||
if (clientSearch != null) {
|
||||
List<Long> userIds = repoUser.findIdsByFullNameLike(clientSearch.trim());
|
||||
|
||||
if (userIds.isEmpty()) {
|
||||
// Ningún usuario coincide → forzamos 0 resultados
|
||||
base = base.and((root, query, cb) -> cb.disjunction());
|
||||
} else {
|
||||
base = base.and((root, query, cb) -> root.join("payment").get("userId").in(userIds));
|
||||
}
|
||||
}
|
||||
Long total = repoPaymentTransaction.count(base);
|
||||
|
||||
return DataTable
|
||||
.of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable)
|
||||
.orderable(orderable)
|
||||
.add("created_at", pago -> Utils.formatDateTime(pago.getCreatedAt(), locale))
|
||||
.add("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale))
|
||||
.add("client", pago -> {
|
||||
if (pago.getPayment() != null && pago.getPayment().getUserId() != null) {
|
||||
Payment payment = pago.getPayment();
|
||||
if (payment.getUserId() != null) {
|
||||
Optional<User> user = repoUser.findById(payment.getUserId().longValue());
|
||||
return user.map(User::getFullName).orElse("");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.add("transfer_id", pago -> {
|
||||
if (pago.getPayment() != null) {
|
||||
return "TRANSF-" + pago.getPayment().getGatewayOrderId();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
})
|
||||
.add("order_id", pago -> {
|
||||
if (pago.getStatus() != PaymentTransactionStatus.pending) {
|
||||
if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) {
|
||||
return pago.getPayment().getOrderId().toString();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return messageSource.getMessage("pagos.transferencia.no-pedido", null, "Pendiente", locale);
|
||||
|
||||
}).add("amount_cents", pago -> Utils.formatCurrency(pago.getAmountCents() / 100.0, locale))
|
||||
.add("amount_cents_refund", pago ->
|
||||
|
||||
{
|
||||
Payment payment = pago.getPayment();
|
||||
if (payment != null) {
|
||||
return Utils.formatCurrency(payment.getAmountRefundedCents() / 100.0, locale);
|
||||
}
|
||||
return "";
|
||||
}).add("status", pago -> {
|
||||
switch (pago.getStatus()) {
|
||||
case PaymentTransactionStatus.pending:
|
||||
return messageSource.getMessage("pagos.table.estado.pending", null, "Pendiente", locale);
|
||||
case PaymentTransactionStatus.succeeded:
|
||||
return messageSource.getMessage("pagos.table.estado.succeeded", null, "Completada", locale);
|
||||
case PaymentTransactionStatus.failed:
|
||||
return messageSource.getMessage("pagos.table.estado.failed", null, "Fallido", locale);
|
||||
default:
|
||||
return pago.getStatus().name();
|
||||
}
|
||||
}).add("actions", pago -> {
|
||||
Payment p = pago.getPayment();
|
||||
if (p != null) {
|
||||
String actions = "";
|
||||
if (pago.getStatus() != PaymentTransactionStatus.succeeded) {
|
||||
actions += "<span class=\'badge bg-secondary btn-mark-as-completed \' data-dsOrderId=\'"
|
||||
+ p.getGatewayOrderId()
|
||||
+ "\' data-transactionId=\'" + pago.getPayment().getId()
|
||||
+ "\' style=\'cursor: pointer;\'>"
|
||||
+ messageSource.getMessage("pagos.table.finalizar", null, locale) + "</span> ";
|
||||
|
||||
}
|
||||
if (pago.getAmountCents() - p.getAmountRefundedCents() > 0) {
|
||||
actions += "<span class=\'badge bg-secondary btn-refund-payment \' data-dsOrderId=\'"
|
||||
+ p.getGatewayOrderId()
|
||||
+ "\' data-transactionId=\'" + pago.getPayment().getId()
|
||||
+ "\' data-amount=\'" + (pago.getAmountCents() - p.getAmountRefundedCents())
|
||||
+ "\' style=\'cursor: pointer;\'>"
|
||||
+ messageSource.getMessage("pagos.table.devuelto", null, locale) + "</span>";
|
||||
}
|
||||
return actions;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}).where(base).toJson(total);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -198,18 +198,7 @@ public class PaymentService {
|
||||
}
|
||||
|
||||
if (authorized) {
|
||||
// GENERAR PEDIDO A PARTIR DEL CARRITO
|
||||
Cart cart = this.cartService.findById(notif.cartId);
|
||||
if (cart != null) {
|
||||
// Bloqueamos el carrito
|
||||
this.cartService.lockCartById(cart.getId());
|
||||
// order ID es generado dentro de createOrderFromCart donde se marcan los
|
||||
// presupuestos como no editables
|
||||
// Long orderId =
|
||||
// this.cartService.pedidoService.createOrderFromCart(cart.getId(), p.getId());
|
||||
// p.setOrderId(orderId);
|
||||
|
||||
}
|
||||
processOrder(notif.cartId);
|
||||
}
|
||||
|
||||
payRepo.save(p);
|
||||
@ -230,7 +219,7 @@ public class PaymentService {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- refundViaRedsys
|
||||
// ---- refundViaRedsys
|
||||
// ----
|
||||
@Transactional
|
||||
public void refundViaRedsys(Long paymentId, long amountCents, String idempotencyKey) {
|
||||
@ -295,7 +284,6 @@ public class PaymentService {
|
||||
payRepo.save(p);
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public Payment createBankTransferPayment(Long cartId, long amountCents, String currency) {
|
||||
Payment p = new Payment();
|
||||
@ -304,6 +292,10 @@ public class PaymentService {
|
||||
Cart cart = this.cartService.findById(cartId);
|
||||
if (cart != null && cart.getUserId() != null) {
|
||||
p.setUserId(cart.getUserId());
|
||||
// En el orderId de la transferencia pendiente guardamos el ID del carrito
|
||||
p.setOrderId(cartId);
|
||||
// Se bloquea el carrito para evitar modificaciones mientras se procesa el pago
|
||||
this.cartService.lockCartById(cartId);
|
||||
}
|
||||
|
||||
p.setCurrency(currency);
|
||||
@ -321,7 +313,7 @@ public class PaymentService {
|
||||
tx.setCurrency(currency);
|
||||
// tx.setProcessedAt(null); // la dejas nula hasta que se confirme
|
||||
txRepo.save(tx);
|
||||
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -360,6 +352,11 @@ public class PaymentService {
|
||||
p.setCapturedAt(LocalDateTime.now());
|
||||
p.setStatus(PaymentStatus.captured);
|
||||
payRepo.save(p);
|
||||
|
||||
// 4) Procesar el pedido asociado al carrito (si existe)
|
||||
if (p.getOrderId() != null) {
|
||||
processOrder(p.getOrderId());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRedsysAuthorized(RedsysService.RedsysNotification notif) {
|
||||
@ -376,4 +373,20 @@ public class PaymentService {
|
||||
return code >= 0 && code <= 99;
|
||||
}
|
||||
|
||||
private Boolean processOrder(Long cartId) {
|
||||
// GENERAR PEDIDO A PARTIR DEL CARRITO
|
||||
Cart cart = this.cartService.findById(cartId);
|
||||
if (cart != null) {
|
||||
// Bloqueamos el carrito
|
||||
this.cartService.lockCartById(cart.getId());
|
||||
// order ID es generado dentro de createOrderFromCart donde se marcan los
|
||||
// presupuestos como no editables
|
||||
// Long orderId =
|
||||
// this.cartService.pedidoService.createOrderFromCart(cart.getId(), p.getId());
|
||||
// p.setOrderId(orderId);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -163,9 +163,9 @@ public class RedsysController {
|
||||
try {
|
||||
String idem = "refund-" + paymentId + "-" + amountCents + "-" + UUID.randomUUID();
|
||||
paymentService.refundViaRedsys(paymentId, amountCents, idem);
|
||||
return ResponseEntity.ok("Refund solicitado");
|
||||
return ResponseEntity.ok("{success:true}");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body("Error refund: " + e.getMessage());
|
||||
return ResponseEntity.badRequest().body("{success:false, error: '" + e.getMessage() + "'}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,10 +287,6 @@ public class RedsysService {
|
||||
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
System.out.println("### Redsys refund REST request:\n" + json);
|
||||
System.out.println("### HTTP " + response.statusCode());
|
||||
System.out.println("### Redsys refund REST response:\n" + response.body());
|
||||
|
||||
if (response.statusCode() / 100 != 2)
|
||||
throw new IllegalStateException("HTTP error Redsys refund: " + response.statusCode());
|
||||
|
||||
@ -313,8 +309,7 @@ public class RedsysService {
|
||||
|
||||
// Decodificar MerchantParameters de la respuesta
|
||||
Map<String, Object> decoded = decodeMerchantParametersToMap(dsMerchantParametersResp);
|
||||
System.out.println("### Redsys refund decoded response:\n" + decoded);
|
||||
|
||||
|
||||
String dsResponse = String.valueOf(decoded.get("Ds_Response"));
|
||||
if (!"0900".equals(dsResponse)) {
|
||||
throw new IllegalStateException("Devolución rechazada, Ds_Response=" + dsResponse);
|
||||
|
||||
Reference in New Issue
Block a user