Skip to content

Commit

Permalink
#3 handle errors
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Nov 1, 2014
1 parent a449b76 commit f31ca07
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 21 deletions.
28 changes: 18 additions & 10 deletions src/main/java/io/bagarino/controller/ReservationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.bagarino.controller;

import com.stripe.exception.StripeException;
import com.stripe.model.Charge;
import io.bagarino.controller.decorator.SaleableTicketCategory;
import io.bagarino.controller.support.TemplateManager;
import io.bagarino.manager.EventManager;
Expand All @@ -37,6 +38,7 @@
import io.bagarino.repository.TicketRepository;
import io.bagarino.repository.user.OrganizationRepository;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.tuple.Pair;
Expand All @@ -57,12 +59,14 @@
import java.util.*;
import java.util.stream.Collectors;

import static io.bagarino.controller.ErrorsCode.STEP_2_PAYMENT_PROCESSING_ERROR;
import static io.bagarino.util.OptionalWrapper.optionally;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;

@Controller
@Log4j2
public class ReservationController {

private final EventRepository eventRepository;
Expand Down Expand Up @@ -193,7 +197,7 @@ public String showReservationPage(
@RequestMapping(value = "/event/{eventName}/reservation/{reservationId}", method = RequestMethod.POST)
public String handleReservation(@PathVariable("eventName") String eventName,
@PathVariable("reservationId") String reservationId, PaymentForm paymentForm, BindingResult bindingResult,
Model model, HttpServletRequest request) {
Model model, HttpServletRequest request, Locale locale) {

Optional<Event> event = optionally(() -> eventRepository.findByShortName(eventName));
if (!event.isPresent()) {
Expand Down Expand Up @@ -235,17 +239,21 @@ public String handleReservation(@PathVariable("eventName") String eventName,
// transition to IN_PAYMENT, so we can keep track if we have a failure between the stripe payment and the
// completion of the reservation
ticketReservationManager.transitionToInPayment(reservationId, email, fullName, billingAddress);

try {
stripeManager.chargeCreditCard(paymentForm.getStripeToken(), reservationCost.getPriceWithVAT(),
final Optional<Charge> charge = stripeManager.chargeCreditCard(paymentForm.getStripeToken(), reservationCost.getPriceWithVAT(),
event.get(), reservationId, email, fullName, billingAddress);
} catch (StripeException se) {
ticketReservationManager.reTransitionToPending(reservationId);
bindingResult
.reject(ErrorsCode.STEP_2_PAYMENT_PROCESSING_ERROR, new Object[] { se.getMessage() }, null);
model.addAttribute("error", bindingResult).addAttribute("hasErrors", bindingResult.hasErrors())
.addAttribute("paymentForm", paymentForm);//
return showReservationPage(eventName, reservationId, false, false, model);
log.info("transaction {} paid: {}", reservationId, charge.isPresent());
} catch (Exception e) {
ticketReservationManager.reTransitionToPending(reservationId);
String errorMessageCode = "error.STEP2_STRIPE_abort";
if(e instanceof StripeException) {
errorMessageCode = stripeManager.handleException((StripeException)e);
}
bindingResult.reject(STEP_2_PAYMENT_PROCESSING_ERROR, new Object[]{messageSource.getMessage(errorMessageCode, null, locale)}, null);
model.addAttribute("error", bindingResult)
.addAttribute("hasErrors", bindingResult.hasErrors())
.addAttribute("paymentForm", paymentForm);
return showReservationPage(eventName, reservationId, false, false, model);
}
}

Expand Down
92 changes: 89 additions & 3 deletions src/main/java/io/bagarino/manager/StripeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,28 @@
package io.bagarino.manager;


import com.stripe.exception.StripeException;
import com.stripe.exception.*;
import com.stripe.model.Charge;
import io.bagarino.manager.system.ConfigurationManager;
import io.bagarino.model.Event;
import io.bagarino.repository.TicketRepository;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static io.bagarino.model.system.ConfigurationKeys.STRIPE_PUBLIC_KEY;
import static io.bagarino.model.system.ConfigurationKeys.STRIPE_SECRET_KEY;

@Component
@Log4j2
public class StripeManager {

private final Map<Class<? extends StripeException>, StripeExceptionHandler> handlers;
private final ConfigurationManager configurationManager;
private final TicketRepository ticketRepository;

Expand All @@ -43,6 +47,13 @@ public StripeManager(ConfigurationManager configurationManager,
TicketRepository ticketRepository) {
this.configurationManager = configurationManager;
this.ticketRepository = ticketRepository;

handlers = new HashMap<>();
handlers.put(CardException.class, this::handleCardException);
handlers.put(InvalidRequestException.class, this::handleInvalidRequestException);
handlers.put(AuthenticationException.class, this::handleAuthenticationException);
handlers.put(APIConnectionException.class, this::handleApiConnectionException);
handlers.put(StripeException.class, this::handleGenericException);
}

public String getSecretKey() {
Expand All @@ -63,7 +74,7 @@ public String getPublicKey() {
* @return
* @throws StripeException
*/
public Charge chargeCreditCard(String stripeToken, long amountInCent, Event event,
public Optional<Charge> chargeCreditCard(String stripeToken, long amountInCent, Event event,
String reservationId, String email, String fullName, String billingAddress) throws StripeException {

Map<String, Object> chargeParams = new HashMap<>();
Expand All @@ -82,7 +93,82 @@ public Charge chargeCreditCard(String stripeToken, long amountInCent, Event even
initialMetadata.put("billingAddress", billingAddress);
}
chargeParams.put("metadata", initialMetadata);
return Charge.create(chargeParams, getSecretKey());
Charge result = Charge.create(chargeParams, getSecretKey());
if(result.getPaid()) {
return Optional.of(result);
}
return Optional.empty();
}

public String handleException(StripeException exc) {
return findExceptionHandler(exc).handle(exc);
}

private StripeExceptionHandler findExceptionHandler(StripeException exc) {
final Optional<StripeExceptionHandler> eh = Optional.ofNullable(handlers.get(exc.getClass()));
if(!eh.isPresent()) {
log.warn("cannot find an ExceptionHandler for {}. Falling back to the default one.", exc.getClass());
}
return eh.orElseGet(() -> handlers.get(StripeException.class));
}

/* exception handlers... */

/**
* This handler simply returns the message code from stripe.
* There is no need in writing something in the log.
* @param e the exception
* @return the code
*/
private String handleCardException(StripeException e) {
CardException ce = (CardException)e;
return "error.STEP2_STRIPE_" + ce.getCode();
}

/**
* handles invalid request exception using the error.STEP2_STRIPE_invalid_ prefix for the message.
* @param e the exception
* @return message code
*/
private String handleInvalidRequestException(StripeException e) {
InvalidRequestException ire = (InvalidRequestException)e;
return "error.STEP2_STRIPE_invalid_" + ire.getParam();
}

/**
* Logs the failure and report the failure to the admin (to be done)
* @param e the exception
* @return error.STEP2_STRIPE_abort
*/
private String handleAuthenticationException(StripeException e) {
log.error("an AuthenticationException has occurred. Please fix configuration!!", e);
return "error.STEP2_STRIPE_abort";
}

/**
* Logs the failure and report the failure to the admin (to be done)
* @param e
* @return
*/
private String handleApiConnectionException(StripeException e) {
log.error("unable to connect to the Stripe API", e);
return "error.STEP2_STRIPE_abort";
}

/**
* Logs the failure and report the failure to the admin (to be done)
* @param e
* @return
*/
private String handleGenericException(StripeException e) {
log.error("unexpected error during transaction", e);
return "error.STEP2_STRIPE_unexpected";
}


@FunctionalInterface
private static interface StripeExceptionHandler {
String handle(StripeException exc);
}

}
4 changes: 3 additions & 1 deletion src/main/resources/io/bagarino/i18n/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ error.STEP2_STRIPE_incorrect_zip=The card's zip code failed validation.
error.STEP2_STRIPE_card_declined=The card was declined.
error.STEP2_STRIPE_missing=There is no card on a customer that is being charged.
error.STEP2_STRIPE_processing_error=An error occurred while processing the card.
error.STEP2_STRIPE_rate_limit=An error occurred due to requests hitting the API too quickly.
error.STEP2_STRIPE_abort=A network error has occurred, and you have not been charged. Please try again.
error.STEP2_STRIPE_rate_limit=A network error has occurred, and you have not been charged. Please try again.
error.STEP2_STRIPE_unexpected=An unexpected error has occurred. Please contact the event\u00B4s organizers in order to get assistance.

error.STEP_2_PAYMENT_PROCESSING_ERROR=Payment processing error: {0}
error.STEP_2_MISSING_STRIPE_TOKEN=Error with the payment processor: missing token, have you enabled javascript?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,6 @@ email-confirmation.summary=Category: {0}, Amount: {1}, Subtotal: {2} {3} (DE)
email-confirmation.order-information=Order information : {0} (DE)

#ticket-has-changed-owner-txt.ms
ticket-has-changed-owner.notice=Notice: the owner email of the ticket for event {0} has changed from {1} to {2}. Please check at {3} if it's correct and rectify it in case of error. (DE)
ticket-has-changed-owner.notice=Notice: the owner email of the ticket for event {0} has changed from {1} to {2}. Please check at {3} if it's correct and rectify it in case of error. (DE)
error.STEP2_STRIPE_abort=A network error has occurred, and you have not been charged. Please try again. (DE)
error.STEP2_STRIPE_unexpected=An unexpected error has occurred. Please contact the event\u00B4s organizers in order to get assistance. (DE)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error.STEP2_STRIPE_abort=
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,6 @@ email-confirmation.summary=Category: {0}, Amount: {1}, Subtotal: {2} {3} (FR)
email-confirmation.order-information=Order information : {0} (FR)

#ticket-has-changed-owner-txt.ms
ticket-has-changed-owner.notice=Notice: the owner email of the ticket for event {0} has changed from {1} to {2}. Please check at {3} if it's correct and rectify it in case of error. (FR)
ticket-has-changed-owner.notice=Notice: the owner email of the ticket for event {0} has changed from {1} to {2}. Please check at {3} if it's correct and rectify it in case of error. (FR)
error.STEP2_STRIPE_abort=A network error has occurred, and you have not been charged. Please try again.(FR)
error.STEP2_STRIPE_unexpected=An unexpected error has occurred. Please contact the event\u00B4s organizers in order to get assistance.(FR)
12 changes: 7 additions & 5 deletions src/main/resources/io/bagarino/i18n/application_it.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ show-event.sales-end=Acquistabile fino al {0}


#reservation-page.ms
reservation-page.title=Sommario dell\u00B4ordine per
reservation-page.title=Riepilogo dell\u00B4ordine per
reservation-page.category=Categoria
reservation-page.price=Prezzo
reservation-page.amount=Quantit\u00E0
Expand Down Expand Up @@ -85,9 +85,11 @@ error.STEP2_STRIPE_incorrect_zip=La carta immessa non \u00E8 stata accettata dal
error.STEP2_STRIPE_card_declined=Il pagamento \u00E8 stato rifiutato.
error.STEP2_STRIPE_missing=La carta di credito immessa non \u00E8 stata accettata dal sistema di pagamento.
error.STEP2_STRIPE_processing_error=Errore sconosciuto durante l\u00B4elaborazione del pagamento.
error.STEP2_STRIPE_abort=Errore di connessione al sistema di pagamento. Non \u00E8 stata effettuata alcuna transazione. Ti preghiamo di riprovare
error.STEP2_STRIPE_rate_limit=Errore interno del server. Ti preghiamo di riprovare.
error.STEP2_STRIPE_unexpected=Errore inatteso. Ti preghiamo di contattare gli organizzatori dell\u00B4evento per verificare lo stato della transazione.

error.STEP_2_PAYMENT_PROCESSING_ERROR=Payment processing error: {0} (IT)
error.STEP_2_PAYMENT_PROCESSING_ERROR=Errore durante il pagamento: {0}
error.STEP_2_MISSING_STRIPE_TOKEN=Error with the payment processor: missing token, have you enabled javascript? (IT)
error.STEP_2_EMPTY_EMAIL=Email is mandatory (IT)
error.STEP_2_EMAIL_IS_TOO_LONG=Email is too long: maximum allowed is 255 characters (IT)
Expand Down Expand Up @@ -116,7 +118,7 @@ email.ticket-email-sent=Il biglietto ti \u00E8 stato spedito via email.
#email
ticket-email-subject=Il tuo biglietto per l\u00B4evento {0}
reservation-email-subject=La tua prenotazione per l\u00B4evento {0}
ticket-has-changed-owner-subject=Notice: your ticket for event {0} has changed email owner (IT)
ticket-has-changed-owner-subject=Avviso\: l\u00B4email associata al tuo biglietto per l\u00B4evento {0} \u00E8 stata modificata

#ticket-email-txt
email.hello=Ciao {0},
Expand All @@ -126,9 +128,9 @@ email.event-reminder-date=in data\:
email.kind-regards=A presto,
email-ticket.attached=In allegato il biglietto\: {0}
email-confirmation.completed=La tua prenotazione per l\u00B4evento {0} \u00E8 stata completata con successo. Puoi controllarla/modificarla qui\: {1}
email-confirmation.reservation-summary=Sommario della prenotazione
email-confirmation.reservation-summary=Riepilogo della prenotazione
email-confirmation.summary=Categoria\: {0}, Quantit\u00E0\: {1}, Subtotale\: {2} {3}
email-confirmation.order-information=Informazioni sull\u00B4ordine \: {0}

#ticket-has-changed-owner-txt.ms
ticket-has-changed-owner.notice=Notice: the owner email of the ticket for event {0} has changed from {1} to {2}. Please check at {3} if it's correct and rectify it in case of error. (IT)
ticket-has-changed-owner.notice=Avviso\: l\u00B4email associata al tuo biglietto per l\u00B4evento {0} \u00E8 stata modificata da {1} a {2}. Per favore controlla che sia tutto OK usando il seguente link\: {3}.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
data-stripe-message-card_declined="{{#i18n}}error.STEP2_STRIPE_card_declined{{/i18n}}"
data-stripe-message-missing="{{#i18n}}error.STEP2_STRIPE_missing{{/i18n}}"
data-stripe-message-processing_error="{{#i18n}}error.STEP2_STRIPE_processing_error{{/i18n}}"
data-stripe-message-abort="{{#i18n}}error.STEP2_STRIPE_abort{{/i18n}}"
data-stripe-message-rate_limit="{{#i18n}}error.STEP2_STRIPE_rate_limit{{/i18n}}"
></script>
{{/orderSummary.free}}
Expand Down

0 comments on commit f31ca07

Please sign in to comment.