From caa3a32116399a19215b218b21077aaa17770884 Mon Sep 17 00:00:00 2001 From: Celestino Bellone Date: Fri, 3 Apr 2020 19:11:20 +0200 Subject: [PATCH] do not cancel reservation if refund fails --- .../admin/AdminReservationApiController.java | 3 +-- .../manager/AdminReservationManager.java | 25 ++++++++++++------- .../java/alfio/manager/PaymentManager.java | 12 ++++++--- .../manager/payment/BaseStripeManager.java | 6 +++-- src/main/java/alfio/model/Audit.java | 2 ++ .../reservation-cancel/reservation-cancel.js | 12 ++++++--- 6 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/java/alfio/controller/api/admin/AdminReservationApiController.java b/src/main/java/alfio/controller/api/admin/AdminReservationApiController.java index 36c8690cbe..f5ee3dd83d 100644 --- a/src/main/java/alfio/controller/api/admin/AdminReservationApiController.java +++ b/src/main/java/alfio/controller/api/admin/AdminReservationApiController.java @@ -194,8 +194,7 @@ public Result getPaymentInfo(@PathVariable("eventName public Result removeReservation(@PathVariable("eventName") String eventName, @PathVariable("reservationId") String reservationId, @RequestParam("refund") boolean refund, @RequestParam(value = "notify", defaultValue = "false") boolean notify, Principal principal) { - adminReservationManager.removeReservation(eventName, reservationId, refund, notify, principal.getName()); - return Result.success(true); + return adminReservationManager.removeReservation(eventName, reservationId, refund, notify, principal.getName()); } @PostMapping("/event/{eventName}/{reservationId}/credit") diff --git a/src/main/java/alfio/manager/AdminReservationManager.java b/src/main/java/alfio/manager/AdminReservationManager.java index 4d706da21e..feb0d0553c 100644 --- a/src/main/java/alfio/manager/AdminReservationManager.java +++ b/src/main/java/alfio/manager/AdminReservationManager.java @@ -654,9 +654,12 @@ public Result getPaymentInfo(String eventName, String } @Transactional - public void removeReservation(String eventName, String reservationId, boolean refund, boolean notify, String username) { - removeReservation(eventName, reservationId, refund, notify, username, true) - .ifSuccess(pair -> markAsCancelled(pair.getRight(), username, pair.getLeft().getId())); + public Result removeReservation(String eventName, String reservationId, boolean refund, boolean notify, String username) { + return removeReservation(eventName, reservationId, refund, notify, username, true) + .map(pair -> { + markAsCancelled(pair.getRight(), username, pair.getLeft().getId()); + return true; + }); } @Transactional @@ -666,22 +669,26 @@ public void creditReservation(String eventName, String reservationId, boolean re } private Result> removeReservation(String eventName, String reservationId, boolean refund, boolean notify, String username, boolean removeReservation) { - return loadReservation(eventName, reservationId, username).map(res -> { + return loadReservation(eventName, reservationId, username).flatMap(res -> { Event e = res.getRight(); TicketReservation reservation = res.getLeft(); List tickets = res.getMiddle(); + if(refund && reservation.getPaymentMethod() != null && reservation.getPaymentMethod().isSupportRefund()) { + //fully refund + boolean refundResult = paymentManager.refund(reservation, e, null, username); + if(!refundResult) { + return Result.error(ErrorCode.custom("refund.failed", "Cannot perform refund")); + } + } + specialPriceRepository.resetToFreeAndCleanupForReservation(List.of(reservationId)); removeTicketsFromReservation(reservation, e, tickets.stream().map(Ticket::getId).collect(toList()), notify, username, removeReservation, false); additionalServiceItemRepository.updateItemsStatusWithReservationUUID(reservation.getId(), AdditionalServiceItem.AdditionalServiceItemStatus.CANCELLED); - if(refund && reservation.getPaymentMethod() != null && reservation.getPaymentMethod().isSupportRefund()) { - //fully refund - paymentManager.refund(reservation, e, null, username); - } - return Pair.of(e, reservation); + return Result.success(Pair.of(e, reservation)); }); } diff --git a/src/main/java/alfio/manager/PaymentManager.java b/src/main/java/alfio/manager/PaymentManager.java index 23379b5b4b..4ee578f344 100644 --- a/src/main/java/alfio/manager/PaymentManager.java +++ b/src/main/java/alfio/manager/PaymentManager.java @@ -144,15 +144,21 @@ public boolean refund(TicketReservation reservation, Event event, Integer amount .map(paymentProvider -> ((RefundRequest)paymentProvider).refund(transaction, event, amount)) .orElse(false); + Map changes = Map.of( + "refund", amount != null ? amount.toString() : "full", + "paymentMethod", reservation.getPaymentMethod().toString() + ); if(res) { - Map changes = new HashMap<>(); - changes.put("refund", amount != null ? amount.toString() : "full"); - changes.put("paymentMethod", reservation.getPaymentMethod().toString()); auditingRepository.insert(reservation.getId(), userRepository.findIdByUserName(username).orElse(null), event.getId(), Audit.EventType.REFUND, new Date(), Audit.EntityType.RESERVATION, reservation.getId(), Collections.singletonList(changes)); extensionManager.handleRefund(event, reservation, getInfo(reservation, event)); + } else { + auditingRepository.insert(reservation.getId(), userRepository.findIdByUserName(username).orElse(null), + event.getId(), + Audit.EventType.REFUND_ATTEMPT_FAILED, new Date(), Audit.EntityType.RESERVATION, reservation.getId(), + Collections.singletonList(changes)); } return res; diff --git a/src/main/java/alfio/manager/payment/BaseStripeManager.java b/src/main/java/alfio/manager/payment/BaseStripeManager.java index 3686395c8c..ae99735c25 100644 --- a/src/main/java/alfio/manager/payment/BaseStripeManager.java +++ b/src/main/java/alfio/manager/payment/BaseStripeManager.java @@ -58,6 +58,7 @@ class BaseStripeManager { static final String STRIPE_MANAGER_TYPE_KEY = "stripeManagerType"; static final String SUCCEEDED = "succeeded"; + static final String PENDING = "pending"; private final ConfigurationManager configurationManager; private final ConfigurationRepository configurationRepository; private final TicketRepository ticketRepository; @@ -260,8 +261,9 @@ boolean refund(Transaction transaction, Event event, Integer amountToRefund) { if(requestOptionsOptional.isPresent()) { RequestOptions options = requestOptionsOptional.get(); Refund r = Refund.create(params, options); - if(SUCCEEDED.equals(r.getStatus())) { - log.info("Stripe: refund for payment {} executed with success for amount: {}", chargeId, amountOrFull); + boolean pending = PENDING.equals(r.getStatus()); + if(SUCCEEDED.equals(r.getStatus()) || pending) { + log.info("Stripe: refund for payment {} {} for amount: {}", chargeId, pending ? "registered": "executed with success", amountOrFull); return true; } else { log.warn("Stripe: was not able to refund payment with id {}, returned status is not 'succeded' but {}", chargeId, r.getStatus()); diff --git a/src/main/java/alfio/model/Audit.java b/src/main/java/alfio/model/Audit.java index 83186309db..dbc23d56cc 100644 --- a/src/main/java/alfio/model/Audit.java +++ b/src/main/java/alfio/model/Audit.java @@ -45,6 +45,7 @@ public enum EventType { PAYMENT_CONFIRMED, PAYMENT_ALREADY_CONFIRMED, REFUND, + REFUND_ATTEMPT_FAILED, CHECK_IN, MANUAL_CHECK_IN, REVERT_CHECK_IN, @@ -71,6 +72,7 @@ public enum EventType { AUTOMATIC_PAYMENT_CONFIRMATION, AUTOMATIC_PAYMENT_CONFIRMATION_FAILED, DYNAMIC_DISCOUNT_CODE_CREATED + } private final String reservationId; diff --git a/src/main/webapp/resources/js/admin/feature/reservation-cancel/reservation-cancel.js b/src/main/webapp/resources/js/admin/feature/reservation-cancel/reservation-cancel.js index b7172707a8..59e6027fce 100644 --- a/src/main/webapp/resources/js/admin/feature/reservation-cancel/reservation-cancel.js +++ b/src/main/webapp/resources/js/admin/feature/reservation-cancel/reservation-cancel.js @@ -2,7 +2,7 @@ 'use strict'; angular.module('adminApplication').component('reservationCancel', { - controller: ['AdminReservationService', 'EventService', ReservationCancelCtrl], + controller: ['AdminReservationService', 'EventService', 'NotificationHandler', ReservationCancelCtrl], templateUrl: '../resources/js/admin/feature/reservation-cancel/reservation-cancel.html', bindings: { event: '<', @@ -14,7 +14,7 @@ }); - function ReservationCancelCtrl(AdminReservationService, EventService) { + function ReservationCancelCtrl(AdminReservationService, EventService, NotificationHandler) { var ctrl = this; ctrl.confirmRemove = confirmRemove; @@ -32,8 +32,12 @@ function confirmRemove() { ctrl.submitted = true; - return EventService.cancelReservation(ctrl.event.shortName, ctrl.reservationId, ctrl.refund, ctrl.notify, ctrl.credit).then(function() { - ctrl.onSuccess(); + return EventService.cancelReservation(ctrl.event.shortName, ctrl.reservationId, ctrl.refund, ctrl.notify, ctrl.credit).then(function(response) { + if(response.data.success) { + ctrl.onSuccess(); + } else { + NotificationHandler.showError(response.data.firstErrorOrNull.description); + } }).finally(function() { ctrl.submitted = false; });