Skip to content

Commit

Permalink
#286 initial steps for supporting mollie: create a payment + redirect
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Jun 5, 2017
1 parent d7d3b69 commit ada20a5
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/main/java/alfio/config/SpringBootInitializer.java
Expand Up @@ -88,7 +88,7 @@ public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() {
mimeMappings.put("svg", "image/svg+xml");
container.setSessionTimeout(2, TimeUnit.HOURS);
container.setMimeMappings(new MimeMappings(mimeMappings));
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404-not-found"), new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500-internal-server-error"), new ErrorPage("/session-expired"));
//container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404-not-found"), new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500-internal-server-error"), new ErrorPage("/session-expired"));

Optional.ofNullable(System.getProperty("alfio.worker.name")).ifPresent(workerName -> {
((JettyEmbeddedServletContainerFactory)container).addServerCustomizers(server -> {
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/alfio/controller/ReservationController.java
Expand Up @@ -420,8 +420,22 @@ public String handleReservation(@PathVariable("eventName") String eventName,
return redirectReservation(ticketReservation, eventName, reservationId);
}
}
//

//handle mollie redirect
if(paymentForm.getPaymentMethod() == PaymentProxy.MOLLIE) {
OrderSummary orderSummary = ticketReservationManager.orderSummaryForReservationId(reservationId, event, locale);
try {
String checkoutUrl = paymentManager.createMollieCheckoutRequest(event, reservationId, orderSummary, customerName,
paymentForm.getEmail(), paymentForm.getBillingAddress(), locale, paymentForm.isPostponeAssignment());
assignTickets(eventName, reservationId, paymentForm, bindingResult, request, true);
return "redirect:" + checkoutUrl;
} catch (Exception e) {
bindingResult.reject(ErrorsCode.STEP_2_PAYMENT_REQUEST_CREATION);
SessionUtil.addToFlash(bindingResult, redirectAttributes);
return redirectReservation(ticketReservation, eventName, reservationId);
}
}
//

final PaymentResult status = ticketReservationManager.confirm(paymentForm.getToken(), paymentForm.getPaypalPayerID(), event, reservationId, paymentForm.getEmail(),
customerName, locale, paymentForm.getBillingAddress(), reservationCost, SessionUtil.retrieveSpecialPriceSessionId(request),
Expand Down
76 changes: 76 additions & 0 deletions src/main/java/alfio/manager/MollieManager.java
Expand Up @@ -16,10 +16,86 @@
*/
package alfio.manager;

import alfio.manager.system.ConfigurationManager;
import alfio.model.CustomerName;
import alfio.model.Event;
import alfio.model.OrderSummary;
import alfio.model.TicketReservation;
import alfio.model.system.Configuration;
import alfio.model.system.ConfigurationKeys;
import alfio.repository.TicketReservationRepository;
import alfio.util.Json;
import com.google.gson.reflect.TypeToken;
import com.squareup.okhttp.*;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

@Component
@Log4j2
@AllArgsConstructor
public class MollieManager {

private final OkHttpClient client = new OkHttpClient();
private final ConfigurationManager configurationManager;
private final MessageSource messageSource;
private final TicketReservationRepository ticketReservationRepository;

public String createCheckoutRequest(Event event, String reservationId, OrderSummary orderSummary, CustomerName customerName, String email, String billingAddress, Locale locale, boolean postponeAssignment) throws Exception {

String mollieAPIKey = configurationManager.getRequiredValue(Configuration.from(event.getOrganizationId(), ConfigurationKeys.MOLLIE_API_KEY));

String eventName = event.getShortName();

String baseUrl = StringUtils.removeEnd(configurationManager.getRequiredValue(Configuration.from(event.getOrganizationId(), event.getId(), ConfigurationKeys.BASE_URL)), "/");

String bookUrl = baseUrl+"/event/" + eventName + "/reservation/" + reservationId + "/book";


Map<String, Object> payload = new HashMap<>();
payload.put("amount", orderSummary.getTotalPrice()); // quite ugly, but the mollie api require json floating point...

//
String description = messageSource.getMessage("reservation-email-subject", new Object[] {configurationManager.getShortReservationID(event, reservationId), event.getDisplayName()}, locale);
payload.put("description", description);
payload.put("redirectUrl", bookUrl);
payload.put("webhookUrl", baseUrl+"/api/webhook/mollie"); //for testing: "https://test.alf.io/api/webhook/mollie"
payload.put("metadata", Collections.singletonMap("reservationId", reservationId));


RequestBody body = RequestBody.create(MediaType.parse("application/json"), Json.GSON.toJson(payload));
Request request = new Request.Builder().url("https://api.mollie.nl/v1/payments")
.header("Authorization", "Bearer " + mollieAPIKey)
.post(body)
.build();




Response resp = client.newCall(request).execute();
try(ResponseBody responseBody = resp.body()) {
String respBody = responseBody.string();
if (!resp.isSuccessful()) {
String msg = "was not able to create a payment for reservation id " + reservationId + ": " + respBody;
log.warn(msg);
throw new Exception(msg);
} else {

TicketReservation reservation = ticketReservationRepository.findReservationById(reservationId);
//TODO: switch the reservation in a status "PROCESSING" -> so we can then handle them in the webhook
//TODO: see statuses: https://www.mollie.com/en/docs/status
// basically, we need to handle the cases "expired", "cancelled" and "paid"
Map<String, Object> res = Json.GSON.fromJson(respBody, (new TypeToken<Map<String, Object>>() {}).getType());
return ((Map<String, String> )res.get("links")).get("paymentUrl");
}
}

}
}
4 changes: 4 additions & 0 deletions src/main/java/alfio/manager/PaymentManager.java
Expand Up @@ -134,6 +134,10 @@ public String createPaypalCheckoutRequest(Event event, String reservationId, Ord
return paypalManager.createCheckoutRequest(event, reservationId, orderSummary, customerName, email, billingAddress, locale, postponeAssignment);
}

public String createMollieCheckoutRequest(Event event, String reservationId, OrderSummary orderSummary, CustomerName customerName, String email, String billingAddress, Locale locale, boolean postponeAssignment) throws Exception {
return mollieManager.createCheckoutRequest(event, reservationId, orderSummary, customerName, email, billingAddress, locale, postponeAssignment);
}

public boolean refund(TicketReservation reservation, Event event, Optional<Integer> amount, String username) {
Transaction transaction = transactionRepository.loadByReservationId(reservation.getId());
boolean res = false;
Expand Down
4 changes: 3 additions & 1 deletion src/main/resources/alfio/i18n/public.properties
Expand Up @@ -339,4 +339,6 @@ reservation-page.country.outside-eu=Outside the EU
reservation-page.vat-validation-error=The VAT Nr. is not valid for the selected country. Please check the input parameter.
reservation-page.vat-validation-required=Please validate the VAT number.
reservation-page.vat-error=Cannot validate the VAT. Please try again.
invoice.vat-voided=VAT not added as per EU Directives
invoice.vat-voided=VAT not added as per EU Directives
reservation-page.mollie.description=TODO: mollie description
reservation-page.mollie=Mollie
2 changes: 2 additions & 0 deletions src/main/resources/alfio/i18n/public_de.properties
Expand Up @@ -332,3 +332,5 @@ reservation-page.time-for-completion.labels.and=und
reservation-page.vat-validation-required=Bitte die MwSt-Nummer valideren.
reservation-page.vat-validation-error=Die MwSt-Nummer ist nicht g\u00FCltig f\u00FCr das ausgew\u00E4hlte Land. Bitte pr\u00FCfen.
reservation-page.receipt-address=Rechnungsadresse
reservation-page.mollie=Mollie
reservation-page.mollie.description=TODO: mollie description
4 changes: 3 additions & 1 deletion src/main/resources/alfio/i18n/public_it.properties
Expand Up @@ -325,4 +325,6 @@ reservation-page.time-for-completion.labels.plural=| secondi| minuti| ore| giorn
reservation-page.time-for-completion.labels.and=e
reservation-page.vat-validation-required=\u00C8 necessario validare il num. IVA prima di continuare.
reservation-page.vat-validation-error=Il numero IVA immesso non risulta valido per il paese selezionato.
reservation-page.receipt-address=Indirizzo per la ricevuta
reservation-page.receipt-address=Indirizzo per la ricevuta
reservation-page.mollie=Mollie
reservation-page.mollie.description=TODO: mollie description
2 changes: 2 additions & 0 deletions src/main/resources/alfio/i18n/public_nl.properties
Expand Up @@ -336,3 +336,5 @@ reservation-page.time-for-completion.labels.and=en
reservation-page.vat-validation-required=Verifieer uw BTW nummer.
reservation-page.vat-validation-error=Uw BTW nummer is niet geldig voor het geselecteerde land. Check nogmaals of uw BTW nummer klopt.
reservation-page.receipt-address=Factuuradres
reservation-page.mollie=Mollie
reservation-page.mollie.description=TODO: mollie description
1 change: 1 addition & 0 deletions src/main/webapp/WEB-INF/templates/event/payment/mollie.ms
@@ -0,0 +1 @@
<div class="wMarginBottom wMarginTop text-muted">{{#i18n}}reservation-page.mollie.description{{/i18n}}</div>
3 changes: 3 additions & 0 deletions src/main/webapp/WEB-INF/templates/event/reservation-page.ms
Expand Up @@ -207,6 +207,7 @@
<input type="radio" required name="paymentMethod" id="option-{{.}}" data-payment-method="{{.}}" autocomplete="off" value="{{.}}">
{{#is-payment-method}}[STRIPE,{{.}}]<i class="fa fa-cc-stripe"></i> {{#i18n}}reservation-page.credit-card{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[PAYPAL,{{.}}]<i class="fa fa-paypal"></i> {{#i18n}}reservation-page.paypal{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[MOLLIE,{{.}}] {{#i18n}}reservation-page.mollie{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[ON_SITE,{{.}}]<i class="fa fa-money"></i> {{#i18n}}reservation-page.on-site{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[OFFLINE,{{.}}]<i class="fa fa-exchange"></i> {{#i18n}}reservation-page.offline{{/i18n}}{{/is-payment-method}}
</label>
Expand All @@ -218,6 +219,7 @@
<h4 class="wMarginTop">
{{#is-payment-method}}[STRIPE,{{.}}]<i class="fa fa-credit-card"></i> {{#i18n}}reservation-page.credit-card{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[PAYPAL,{{.}}]<i class="fa fa-paypal"></i> {{#i18n}}reservation-page.paypal{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[MOLLIE,{{.}}] {{#i18n}}reservation-page.mollie{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[ON_SITE,{{.}}]<i class="fa fa-money"></i> {{#i18n}}reservation-page.on-site{{/i18n}}{{/is-payment-method}}
{{#is-payment-method}}[OFFLINE,{{.}}]<i class="fa fa-exchange"></i> {{#i18n}}reservation-page.offline{{/i18n}}{{/is-payment-method}}
</h4>
Expand All @@ -228,6 +230,7 @@
<div class="payment-method-detail" id="payment-method-{{.}}">
{{#is-payment-method}}[STRIPE,{{.}}]{{> /event/payment/stripe }}{{/is-payment-method}}
{{#is-payment-method}}[PAYPAL,{{.}}]{{> /event/payment/paypal }}{{/is-payment-method}}
{{#is-payment-method}}[MOLLIE,{{.}}]{{> /event/payment/mollie }}{{/is-payment-method}}
{{#is-payment-method}}[ON_SITE,{{.}}]{{> /event/payment/on-site }}{{/is-payment-method}}
{{#is-payment-method}}[OFFLINE,{{.}}]{{> /event/payment/offline }}{{/is-payment-method}}
</div>
Expand Down

0 comments on commit ada20a5

Please sign in to comment.