package com.imprimelibros.erp.redsys; import com.imprimelibros.erp.payments.PaymentService; import com.imprimelibros.erp.payments.model.Payment; import com.imprimelibros.erp.redsys.RedsysService.FormPayload; import org.springframework.context.MessageSource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.UUID; @Controller @RequestMapping("/pagos/redsys") public class RedsysController { private final PaymentService paymentService; private final MessageSource messageSource; public RedsysController(PaymentService paymentService, MessageSource messageSource) { this.paymentService = paymentService; this.messageSource = messageSource; } @PostMapping(value = "/crear", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public ResponseEntity crearPago(@RequestParam("amountCents") Long amountCents, @RequestParam("method") String method, @RequestParam("cartId") Long cartId) throws Exception { if ("bank-transfer".equalsIgnoreCase(method)) { // 1) Creamos el Payment interno SIN orderId (null) Payment p = paymentService.createBankTransferPayment(cartId, amountCents, "EUR"); // 2) Mostramos instrucciones de transferencia String html = """ Pago por transferencia

Pago por transferencia bancaria

Hemos registrado tu intención de pedido.

Importe: %s €

IBAN: ES00 1234 5678 9012 3456 7890

Concepto: TRANSF-%d

En cuanto recibamos la transferencia, procesaremos tu pedido.

Volver al resumen

""".formatted( String.format("%.2f", amountCents / 100.0), p.getId() // usamos el ID del Payment como referencia ); byte[] body = html.getBytes(StandardCharsets.UTF_8); return ResponseEntity.ok() .contentType(MediaType.TEXT_HTML) .body(body); } // Tarjeta o Bizum (Redsys) FormPayload form = paymentService.createRedsysPayment(cartId, amountCents, "EUR", method); String html = """ Redirigiendo a Redsys…
""".formatted( form.action(), form.signatureVersion(), form.merchantParameters(), form.signature(), cartId); byte[] body = html.getBytes(StandardCharsets.UTF_8); return ResponseEntity.ok() .contentType(MediaType.TEXT_HTML) .body(body); } // GET: cuando el usuario cae aquí sin parámetros, o Redsys redirige por GET @GetMapping("/ok") public String okGet(RedirectAttributes redirectAttrs, Model model, Locale locale) { String msg = messageSource.getMessage("checkout.success.payment", null, "Pago realizado con éxito. Gracias por su compra.", locale); model.addAttribute("successPago", msg); redirectAttrs.addFlashAttribute("successPago", msg); return "redirect:/cart"; } // POST: si Redsys envía Ds_Signature y Ds_MerchantParameters (muchas // integraciones ni lo usan) @PostMapping(value = "/ok", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public ResponseEntity okPost(@RequestParam("Ds_Signature") String signature, @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { // opcional: idempotente, si /notify ya ha hecho el trabajo no pasa nada paymentService.handleRedsysNotification(signature, merchantParameters, locale); return ResponseEntity.ok("

Pago realizado correctamente

Volver"); } catch (Exception e) { return ResponseEntity.badRequest() .body("

Error validando pago

" + e.getMessage() + "
"); } } @GetMapping("/ko") public String koGet(RedirectAttributes redirectAttrs, Model model, Locale locale) { String msg = messageSource.getMessage("checkout.error.payment", null, "Error al procesar el pago: el pago ha sido cancelado o rechazado Por favor, inténtelo de nuevo.", locale); model.addAttribute("errorPago", msg); redirectAttrs.addFlashAttribute("errorPago", msg); return "redirect:/cart"; } @PostMapping(value = "/ko", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public ResponseEntity koPost( @RequestParam("Ds_Signature") String signature, @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { // Procesamos la notificación IGUAL que en /ok y /notify paymentService.handleRedsysNotification(signature, merchantParameters, locale); // Mensaje para el usuario (pago cancelado/rechazado) String html = "

Pago cancelado o rechazado

Volver"; return ResponseEntity.ok(html); } catch (Exception e) { // Si algo falla al validar/procesar, lo mostramos (útil en entorno de pruebas) String html = "

Error procesando notificación KO

" + e.getMessage() + "
"; return ResponseEntity.badRequest().body(html); } } @PostMapping(value = "/notify", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public String notifyRedsys(@RequestParam("Ds_Signature") String signature, @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { paymentService.handleRedsysNotification(signature, merchantParameters, locale); return "OK"; } catch (Exception e) { e.printStackTrace(); // 👈 para ver el motivo del 500 en logs return "ERROR"; } } @PostMapping(value = "/refund/{paymentId}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public ResponseEntity refund(@PathVariable Long paymentId, @RequestParam("amountCents") Long amountCents) { try { String idem = "refund-" + paymentId + "-" + amountCents + "-" + UUID.randomUUID(); paymentService.refundViaRedsys(paymentId, amountCents, idem); return ResponseEntity.ok("{success:true}"); } catch (Exception e) { return ResponseEntity.badRequest().body("{success:false, error: '" + e.getMessage() + "'}"); } } }