From 63f77f3fce84f40587e967f71072fd0d621d2ad5 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Tue, 10 Nov 2020 09:35:11 +0100 Subject: [PATCH] OO-5059: implement an alternative Paypal payment with Smart Buttons --- .../method/AccessMethodHandler.java | 6 + .../provider/auto/AutoAccessHandler.java | 5 + .../provider/free/FreeAccessHandler.java | 5 + .../provider/paypal/PaypalAccessHandler.java | 5 + .../paypal/manager/PaypalManagerImpl.java | 10 +- .../PaypalCheckoutAccessHandler.java | 19 ++- .../paypalcheckout/PaypalCheckoutManager.java | 29 +++++ .../paypalcheckout/PaypalCheckoutModule.java | 13 ++ .../manager/CheckoutV2Provider.java | 91 ++++++++++--- .../manager/PaypalCSPDirectiveProvider.java | 84 ++++++++++++ .../manager/PaypalCheckoutManagerImpl.java | 73 ++++++++++- .../manager/PaypalCheckoutTransactionDAO.java | 12 ++ .../model/CreateSmartOrder.java | 46 +++++++ .../model/PaypalCheckoutTransactionImpl.java | 34 +++++ ...heckoutAccountConfigurationController.java | 16 ++- .../ui/PaypalSmartButtonAccessController.java | 105 +++++++++++++++ .../ui/PaypalSmartButtonMapper.java | 123 ++++++++++++++++++ .../ui/_content/paypal_smart_buttons.html | 99 ++++++++++++++ .../ui/_i18n/LocalStrings_de.properties | 10 +- .../ui/_i18n/LocalStrings_en.properties | 8 ++ .../ui/_i18n/LocalStrings_fr.properties | 3 + .../ui/_i18n/LocalStrings_it.properties | 2 + .../provider/token/TokenAccessHandler.java | 5 + .../ui/AccessConfigurationController.java | 36 ++--- .../ui/OrderDetailController.java | 3 +- .../accesscontrol/ui/PriceFormat.java | 11 +- .../ui/_content/configuration_list.html | 4 +- .../ui/_i18n/LocalStrings_de.properties | 1 + .../ui/_i18n/LocalStrings_en.properties | 1 + .../manager/ShibbolethAutoAccessHandler.java | 2 + .../accesscontrol/ACFrontendManagerTest.java | 10 +- .../accesscontrol/ACOrderManagerTest.java | 13 +- .../PaypalCheckoutTransactionDAOTest.java | 34 +++++ 33 files changed, 849 insertions(+), 69 deletions(-) create mode 100644 src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCSPDirectiveProvider.java create mode 100644 src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/CreateSmartOrder.java create mode 100644 src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonAccessController.java create mode 100644 src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonMapper.java create mode 100644 src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_content/paypal_smart_buttons.html diff --git a/src/main/java/org/olat/resource/accesscontrol/method/AccessMethodHandler.java b/src/main/java/org/olat/resource/accesscontrol/method/AccessMethodHandler.java index 24bef22c53e..8a75d1f6ee0 100644 --- a/src/main/java/org/olat/resource/accesscontrol/method/AccessMethodHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/method/AccessMethodHandler.java @@ -50,6 +50,12 @@ public interface AccessMethodHandler { public boolean isPaymentMethod(); + /** + * + * @return true if it is technically possible to have 2 methods overlap + */ + public boolean isOverlapAllowed(AccessMethodHandler handler); + public String getType(); public String getMethodName(Locale locale); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java index 8b03cd15204..202bfb90f21 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java @@ -58,6 +58,11 @@ public abstract class AutoAccessHandler implements AccessMethodHandler { return false; } + @Override + public boolean isOverlapAllowed(AccessMethodHandler handler) { + return true; + } + @Override public AccessMethodSecurityCallback getSecurityCallback(Identity identity, Roles roles) { return SYSTEM_AC_SECURITY_CALLBACK; diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/free/FreeAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/free/FreeAccessHandler.java index 745dbf9e426..98c9e20a507 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/free/FreeAccessHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/free/FreeAccessHandler.java @@ -63,6 +63,11 @@ public class FreeAccessHandler implements AccessMethodHandler { return false; } + @Override + public boolean isOverlapAllowed(AccessMethodHandler handler) { + return true; + } + @Override public String getType() { return METHOD_TYPE; diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalAccessHandler.java index 56374246ea3..d0d63310f7b 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalAccessHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalAccessHandler.java @@ -65,6 +65,11 @@ public class PaypalAccessHandler implements AccessMethodHandler { public boolean isPaymentMethod() { return true; } + + @Override + public boolean isOverlapAllowed(AccessMethodHandler handler) { + return true; + } @Override public String getType() { diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java index 98914efe730..5ef5d6a8d4e 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java @@ -180,7 +180,7 @@ public class PaypalManagerImpl implements PaypalManager { private PaypalAccessMethod getMethodSecure(Long key) { PaypalAccessMethod smethod = null; List<PaypalAccessMethod> methods = getPaypalMethods(); - if(methods.size() > 0) { + if(!methods.isEmpty()) { smethod = methods.get(0); } else { smethod = new PaypalAccessMethod(); @@ -199,11 +199,9 @@ public class PaypalManagerImpl implements PaypalManager { private List<PaypalAccessMethod> getPaypalMethods() { StringBuilder sb = new StringBuilder(); sb.append("select method from ").append(PaypalAccessMethod.class.getName()).append(" method"); - - List<PaypalAccessMethod> methods = dbInstance.getCurrentEntityManager() + return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), PaypalAccessMethod.class) .getResultList(); - return methods; } @Override @@ -284,8 +282,7 @@ public class PaypalManagerImpl implements PaypalManager { query.setParameter("transactionId", transactionId); } - List<PaypalTransaction> transactions = query.getResultList(); - return transactions; + return query.getResultList(); } private boolean appendAnd(StringBuilder sb, boolean where) { @@ -643,6 +640,7 @@ public class PaypalManagerImpl implements PaypalManager { } } + @Override public PayResponse request(Identity delivery, OfferAccess offerAccess, String mapperUri, String sessionId) { StringBuilder url = new StringBuilder(); url.append(Settings.createServerURI()).append(mapperUri); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutAccessHandler.java index 4f78cc27aa9..dea26af850d 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutAccessHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutAccessHandler.java @@ -41,6 +41,7 @@ import org.olat.resource.accesscontrol.model.PSPTransaction; import org.olat.resource.accesscontrol.provider.paypalcheckout.ui.PaypalCheckoutAccessConfigurationController; import org.olat.resource.accesscontrol.provider.paypalcheckout.ui.PaypalCheckoutAccessController; import org.olat.resource.accesscontrol.provider.paypalcheckout.ui.PaypalCheckoutTransactionDetailsController; +import org.olat.resource.accesscontrol.provider.paypalcheckout.ui.PaypalSmartButtonAccessController; import org.olat.resource.accesscontrol.ui.AbstractConfigurationMethodController; import org.olat.resource.accesscontrol.ui.FormController; @@ -60,6 +61,15 @@ public class PaypalCheckoutAccessHandler implements AccessMethodHandler { public boolean isPaymentMethod() { return true; } + + @Override + public boolean isOverlapAllowed(AccessMethodHandler handler) { + if(handler instanceof PaypalCheckoutAccessHandler) { + PaypalCheckoutModule paypalModule = CoreSpringFactory.getImpl(PaypalCheckoutModule.class); + return !paypalModule.isSmartButtons(); + } + return true; + } @Override public String getType() { @@ -79,7 +89,14 @@ public class PaypalCheckoutAccessHandler implements AccessMethodHandler { @Override public FormController createAccessController(UserRequest ureq, WindowControl wControl, OfferAccess link, Form form) { - if(form == null) { + PaypalCheckoutModule paypalModule = CoreSpringFactory.getImpl(PaypalCheckoutModule.class); + if(paypalModule.isSmartButtons()) { + if(form == null) { + return new PaypalSmartButtonAccessController(ureq, wControl, link); + } else { + return new PaypalSmartButtonAccessController(ureq, wControl, link, form); + } + } else if(form == null) { return new PaypalCheckoutAccessController(ureq, wControl, link); } else { return new PaypalCheckoutAccessController(ureq, wControl, link, form); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutManager.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutManager.java index 1ad7059d7b5..b4b551e344b 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutManager.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutManager.java @@ -27,6 +27,7 @@ import org.olat.resource.accesscontrol.Order; import org.olat.resource.accesscontrol.OrderPart; import org.olat.resource.accesscontrol.model.PSPTransaction; import org.olat.resource.accesscontrol.provider.paypalcheckout.model.CheckoutRequest; +import org.olat.resource.accesscontrol.provider.paypalcheckout.model.CreateSmartOrder; /** * @@ -40,6 +41,34 @@ public interface PaypalCheckoutManager { public void updateTransaction(String uuid); + /** + * Create an order in OpenOlat and in Paypal, reserve the access + * if needed. + * + * @param delivery The identity which buy the access + * @param offerAccess The offer + * @return The order id or null if the reservation fails + */ + public CreateSmartOrder createOrder(Identity delivery, OfferAccess offerAccess); + + /** + * + * @param paypalOrderId The order id of the Paypal order. + */ + public void approveTransaction(String paypalOrderId); + + /** + * + * @param paypalOrderId The order id of the Paypal order. + */ + public void cancelTransaction(String paypalOrderId); + + /** + * + * @param paypalOrderId The order id of the Paypal order. + */ + public void errorTransaction(String paypalOrderId); + public PaypalCheckoutTransaction loadTransaction(Order order, OrderPart part); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutModule.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutModule.java index d472aaa430d..6b417248dfa 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutModule.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/PaypalCheckoutModule.java @@ -40,6 +40,7 @@ public class PaypalCheckoutModule extends AbstractSpringModule { private static final String PAYPAL_CLIENT_ID = "paypal.checkout.v2.client.id"; private static final String PAYPAL_CLIENT_SECRET = "paypal.checkout.v2.client.secret"; private static final String PAYPAL_CURRENCY = "paypal.checkout.v2.currency"; + private static final String PAYPAL_SMART_BUTTONS = "paypal.checkout.v2.smart.buttons"; private static final String[] currencies = new String[] { "AUD", @@ -74,6 +75,8 @@ public class PaypalCheckoutModule extends AbstractSpringModule { private boolean sandbox; @Value("${paypal.checkout.v2.currency:CHF}") private String paypalCurrency; + @Value("${paypal.checkout.v2.smart.buttons:true}") + private boolean smartButtons; @Autowired public PaypalCheckoutModule(CoordinatorManager coordinatorManager) { @@ -90,6 +93,7 @@ public class PaypalCheckoutModule extends AbstractSpringModule { clientId = getStringPropertyValue(PAYPAL_CLIENT_ID, clientId); clientSecret = getStringPropertyValue(PAYPAL_CLIENT_SECRET, clientSecret); paypalCurrency = getStringPropertyValue(PAYPAL_CURRENCY, paypalCurrency); + smartButtons = "true".equals(getStringPropertyValue(PAYPAL_SMART_BUTTONS, Boolean.toString(smartButtons))); } public String getClientId() { @@ -122,7 +126,16 @@ public class PaypalCheckoutModule extends AbstractSpringModule { this.paypalCurrency = currency; setStringProperty(PAYPAL_CURRENCY, currency, true); } + + public boolean isSmartButtons() { + return smartButtons; + } + public void setSmartButtons(boolean smartButtons) { + this.smartButtons = smartButtons; + setStringProperty(PAYPAL_SMART_BUTTONS, Boolean.toString(smartButtons), true); + } + public boolean isSandbox() { return sandbox; } diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/CheckoutV2Provider.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/CheckoutV2Provider.java index 10f162e574d..56ae1cb5595 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/CheckoutV2Provider.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/CheckoutV2Provider.java @@ -77,33 +77,54 @@ public class CheckoutV2Provider { @Autowired private PaypalCheckoutTransactionDAO transactionDao; + public PaypalCheckoutTransaction createOrder(org.olat.resource.accesscontrol.Order order, PaypalCheckoutTransaction trx) { + ApplicationContext applicationContext = new ApplicationContext(); + OrderRequest orderRequest = buildOrderRequest(order, "AUTHORIZE", applicationContext); + OrdersCreateRequest request = buildOrdersCreateRequest(orderRequest); + + try { + HttpResponse<Order> orderResponse = client().execute(request); + if (orderResponse.statusCode() == 201) { + Order paypalOrder = orderResponse.result(); + trx.setPaypalOrderId(paypalOrder.id()); + trx.setPaypalOrderStatus(paypalOrder.status()); + trx.setStatus(PaypalCheckoutStatus.INPROCESS); + log.info(Tracing.M_AUDIT, "Create Paypal order: id:{} status:{}", paypalOrder.id(), paypalOrder.status()); + for (LinkDescription link : orderResponse.result().links()) { + log.debug("Create Paypal link: rel:{} href:{}", link.rel(), link.href()); + log.debug("Create Paypal link: rel:{} media:{} for schema:{}", link.rel(), link.mediaType(), + link.schema()); + } + } else { + log.error(Tracing.M_AUDIT, "Create Paypal order status:{}", orderResponse.statusCode()); + } + } catch (HttpException e) { + JSONObject message = new JSONObject(e.getMessage()); + log.error(Tracing.M_AUDIT, prettyPrint(message, "")); + log.error(Tracing.M_AUDIT, "Create Paypal order", e); + trx.setStatus(PaypalCheckoutStatus.ERROR); + trx.setPaypalOrderStatus(PaypalCheckoutStatus.ERROR.name()); + } catch (IOException e) { + log.error(Tracing.M_AUDIT, "Create Paypal order", e); + trx.setStatus(PaypalCheckoutStatus.ERROR); + trx.setPaypalOrderStatus(PaypalCheckoutStatus.ERROR.name()); + } + trx = transactionDao.update(trx); + dbInstance.commit(); + return trx; + } + public CheckoutRequest paymentUrl(String url, org.olat.resource.accesscontrol.Order order, PaypalCheckoutTransaction trx, String sessionId) { String returnURL = url + "/" + trx.getSecureSuccessUUID() + ".html;jsessionid=" + sessionId + "?status=success"; String cancelURL = url + "/" + trx.getSecureCancelUUID() + ".html;jsessionid=" + sessionId + "?status=cancel"; + ApplicationContext applicationContext = new ApplicationContext() + .cancelUrl(cancelURL) + .returnUrl(returnURL); - OrderRequest orderRequest = new OrderRequest(); - orderRequest.checkoutPaymentIntent("AUTHORIZE"); - ApplicationContext applicationContext = new ApplicationContext().cancelUrl(cancelURL).returnUrl(returnURL); - orderRequest.applicationContext(applicationContext); - - String price = PriceFormat.format(order.getTotal()); - String currencyCode = order.getCurrencyCode(); - - AmountWithBreakdown amount = new AmountWithBreakdown() - .currencyCode(currencyCode).value(price) - .amountBreakdown(new AmountBreakdown().itemTotal(new com.paypal.orders.Money().currencyCode(currencyCode).value(price))); - - List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<>(); - PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest() - .amountWithBreakdown(amount); - purchaseUnitRequests.add(purchaseUnitRequest); - orderRequest.purchaseUnits(purchaseUnitRequests); + OrderRequest orderRequest = buildOrderRequest(order, "AUTHORIZE", applicationContext); + OrdersCreateRequest request = buildOrdersCreateRequest(orderRequest); - OrdersCreateRequest request = new OrdersCreateRequest(); - request.header("prefer", "return=representation"); - request.requestBody(orderRequest); - CheckoutRequest checkoutRequest = new CheckoutRequest(); try { HttpResponse<Order> orderResponse = client().execute(request); @@ -144,6 +165,34 @@ public class CheckoutV2Provider { return checkoutRequest; } + private OrdersCreateRequest buildOrdersCreateRequest(OrderRequest orderRequest) { + OrdersCreateRequest request = new OrdersCreateRequest(); + request.header("prefer", "return=representation"); + request.requestBody(orderRequest); + return request; + } + + private OrderRequest buildOrderRequest(org.olat.resource.accesscontrol.Order order, String intent, ApplicationContext applicationContext) { + OrderRequest orderRequest = new OrderRequest(); + orderRequest.checkoutPaymentIntent(intent); + orderRequest.applicationContext(applicationContext); + + String price = PriceFormat.format(order.getTotal()); + String currencyCode = order.getCurrencyCode(); + + AmountWithBreakdown amount = new AmountWithBreakdown() + .currencyCode(currencyCode).value(price) + .amountBreakdown(new AmountBreakdown().itemTotal(new com.paypal.orders.Money().currencyCode(currencyCode).value(price))); + + List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<>(); + PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest() + .amountWithBreakdown(amount); + purchaseUnitRequests.add(purchaseUnitRequest); + orderRequest.purchaseUnits(purchaseUnitRequests); + + return orderRequest; + } + public PaypalCheckoutTransaction authorizeUrl(PaypalCheckoutTransaction trx) { OrdersAuthorizeRequest request = new OrdersAuthorizeRequest(trx.getPaypalOrderId()); request.requestBody(new OrderRequest()); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCSPDirectiveProvider.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCSPDirectiveProvider.java new file mode 100644 index 00000000000..f235048d065 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCSPDirectiveProvider.java @@ -0,0 +1,84 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.resource.accesscontrol.provider.paypalcheckout.manager; + +import java.util.Collection; +import java.util.List; + +import org.olat.core.commons.services.csp.CSPDirectiveProvider; +import org.olat.resource.accesscontrol.AccessControlModule; +import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutModule; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 9 nov. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class PaypalCSPDirectiveProvider implements CSPDirectiveProvider { + + private static final List<String> sandboxedPaypal = List.of("https://www.paypal.com", "https://www.sandbox.paypal.com"); + private static final List<String> productionPaypal = List.of("https://www.paypal.com"); + + @Autowired + private AccessControlModule acModule; + @Autowired + private PaypalCheckoutModule paypalCheckoutModule; + + @Override + public Collection<String> getScriptSrcUrls() { + return urls(); + } + + @Override + public Collection<String> getImgSrcUrls() { + return null; + } + + @Override + public Collection<String> getFontSrcUrls() { + return null; + } + + @Override + public Collection<String> getConnectSrcUrls() { + return urls(); + } + + @Override + public Collection<String> getFrameSrcUrls() { + return urls(); + } + + @Override + public Collection<String> getMediaSrcUrls() { + return null; + } + + private Collection<String> urls() { + if(acModule.isPaypalCheckoutEnabled()) { + return paypalCheckoutModule.isSandbox() ? sandboxedPaypal : productionPaypal; + } + return null; + } +} diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutManagerImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutManagerImpl.java index 741de5781d9..0eb7b5e7f4e 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutManagerImpl.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutManagerImpl.java @@ -48,6 +48,7 @@ import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutMan import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutStatus; import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutTransaction; import org.olat.resource.accesscontrol.provider.paypalcheckout.model.CheckoutRequest; +import org.olat.resource.accesscontrol.provider.paypalcheckout.model.CreateSmartOrder; import org.olat.resource.accesscontrol.provider.paypalcheckout.model.PaypalCheckoutAccessMethod; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -92,6 +93,35 @@ public class PaypalCheckoutManagerImpl implements PaypalCheckoutManager { return checkoutProvider.paymentUrl(url.toString(), order, trx, sessionId); } + @Override + public CreateSmartOrder createOrder(Identity delivery, OfferAccess offerAccess) { + Offer offer = offerAccess.getOffer(); + Price amount = offer.getPrice(); + + if(acService.reserveAccessToResource(delivery, offerAccess)) { + Order order = orderManager.saveOneClick(delivery, offerAccess, OrderStatus.PREPAYMENT); + PaypalCheckoutTransaction trx = transactionDao.createTransaction(amount, order, order.getParts().get(0), offerAccess.getMethod()); + trx = checkoutProvider.createOrder(order, trx); + return new CreateSmartOrder(trx.getPaypalOrderId(), true); + } + log.info(Tracing.M_AUDIT, "Can reserve: {}", delivery); + return new CreateSmartOrder(null, false); + } + + public PaypalCheckoutTransaction approveOrder(PaypalCheckoutTransaction trx) { + try { + trx = checkoutProvider.captureOrder(trx); + if(PaypalCheckoutStatus.COMPLETED.name().equals(trx.getPaypalOrderStatus())) { + completeTransactionSucessfully(trx); + } else { + completeDeniedTransaction(trx); + } + } catch (IOException e) { + log.error("", e); + } + return trx; + } + @Override public void updateTransaction(String uuid) { PaypalCheckoutTransaction trx = loadTransactionByUUID(uuid); @@ -106,6 +136,39 @@ public class PaypalCheckoutManagerImpl implements PaypalCheckoutManager { } } + @Override + public void approveTransaction(String paypalOrderId) { + PaypalCheckoutTransaction trx = transactionDao.loadTransactionByPaypalOrderId(paypalOrderId); + if(trx != null) { + log.info(Tracing.M_AUDIT, "Paypal Checkout transaction approved: {}", trx); + completeTransaction(trx); + } else { + log.error("Paypal Checkout transaction not found for approval: {} (Paypal order id)", paypalOrderId); + } + } + + @Override + public void cancelTransaction(String paypalOrderId) { + PaypalCheckoutTransaction trx = transactionDao.loadTransactionByPaypalOrderId(paypalOrderId); + if(trx != null) { + log.info(Tracing.M_AUDIT, "Paypal Checkout transaction cancelled: {}", trx); + cancelTransaction(trx); + } else { + log.error("Paypal Checkout transaction not found for cancellation: {} (Paypal order id)", paypalOrderId); + } + } + + @Override + public void errorTransaction(String paypalOrderId) { + PaypalCheckoutTransaction trx = transactionDao.loadTransactionByPaypalOrderId(paypalOrderId); + if(trx != null) { + log.info(Tracing.M_AUDIT, "Paypal Checkout transaction error: {}", trx); + completeDeniedTransaction(trx); + } else { + log.error("Paypal Checkout transaction not found for error: {} (Paypal order id)", paypalOrderId); + } + } + private PaypalCheckoutTransaction completeTransaction(PaypalCheckoutTransaction trx) { try { trx = checkoutProvider.authorizeUrl(trx); @@ -150,7 +213,7 @@ public class PaypalCheckoutManagerImpl implements PaypalCheckoutManager { ResourceReservation reservation = acService.getReservation(identity, resource); if(reservation != null) { acService.removeReservation(identity, identity, reservation); - log.info(Tracing.M_AUDIT, "Remove reservation after cancellation for: " + reservation + " to " + identity); + log.info(Tracing.M_AUDIT, "Remove reservation after cancellation for: {} to {}", reservation, identity); } } } @@ -171,12 +234,12 @@ public class PaypalCheckoutManagerImpl implements PaypalCheckoutManager { transactionManager.update(transaction, AccessTransactionStatus.ERROR); for(OrderLine line:part.getOrderLines()) { acService.denyAccesToResource(identity, line.getOffer()); - log.info(Tracing.M_AUDIT, "Paypal payed access revoked for: " + buildLogMessage(line, method) + " to " + identity); + log.info(Tracing.M_AUDIT, "Paypal payed access revoked for: {} to {}", buildLogMessage(line, method), identity); ResourceReservation reservation = reservationDao.loadReservation(identity, line.getOffer().getResource()); if(reservation != null) { acService.removeReservation(identity, identity, reservation); - log.info(Tracing.M_AUDIT, "Remove reservation after cancellation for: " + reservation + " to " + identity); + log.info(Tracing.M_AUDIT, "Remove reservation after cancellation for: {} to {}", reservation, identity); } } } @@ -201,10 +264,10 @@ public class PaypalCheckoutManagerImpl implements PaypalCheckoutManager { transactionManager.save(transaction); for(OrderLine line:part.getOrderLines()) { if(acService.allowAccesToResource(identity, line.getOffer())) { - log.info(Tracing.M_AUDIT, "Paypal Checkout payed access granted for: " + buildLogMessage(line, method) + " to " + identity); + log.info(Tracing.M_AUDIT, "Paypal Checkout payed access granted for: {} to {}", buildLogMessage(line, method), identity); transaction = transactionManager.update(transaction, AccessTransactionStatus.SUCCESS); } else { - log.error("Paypal Checkout payed access refused for: " + buildLogMessage(line, method) + " to " + identity); + log.error("Paypal Checkout payed access refused for: {} to {}", buildLogMessage(line, method), identity); transaction = transactionManager.update(transaction, AccessTransactionStatus.ERROR); } } diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAO.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAO.java index fcf4780add5..2eb970050eb 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAO.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAO.java @@ -95,6 +95,18 @@ public class PaypalCheckoutTransactionDAO { return transactions.isEmpty() ? null : transactions.get(0); } + public PaypalCheckoutTransaction loadTransactionByPaypalOrderId(String paypalOrderId) { + StringBuilder sb = new StringBuilder(128); + sb.append("select trx from paypalcheckouttransaction as trx") + .append(" where trx.paypalOrderId=:paypalOrderId"); + + List<PaypalCheckoutTransaction> transactions = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), PaypalCheckoutTransaction.class) + .setParameter("paypalOrderId", paypalOrderId) + .getResultList(); + return transactions.isEmpty() ? null : transactions.get(0); + } + public List<PSPTransaction> loadTransactionBy(List<Order> orders) { if(orders == null || orders.isEmpty()) return new ArrayList<>(); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/CreateSmartOrder.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/CreateSmartOrder.java new file mode 100644 index 00000000000..20bd9498f23 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/CreateSmartOrder.java @@ -0,0 +1,46 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.resource.accesscontrol.provider.paypalcheckout.model; + +/** + * + * Initial date: 9 nov. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class CreateSmartOrder { + + private final String paypalOrderId; + private final boolean reservationOk; + + public CreateSmartOrder(String paypalOrderId, boolean reservationOk) { + this.paypalOrderId = paypalOrderId; + this.reservationOk = reservationOk; + } + + public boolean isReservationOk() { + return reservationOk; + } + + public String getPaypalOrderId() { + return paypalOrderId; + } + +} diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/PaypalCheckoutTransactionImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/PaypalCheckoutTransactionImpl.java index bf0155ea9ed..b8a1f5adb74 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/PaypalCheckoutTransactionImpl.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/model/PaypalCheckoutTransactionImpl.java @@ -34,6 +34,7 @@ import javax.persistence.TemporalType; import org.hibernate.annotations.Target; import org.olat.core.id.Persistable; +import org.olat.core.util.StringHelper; import org.olat.resource.accesscontrol.Price; import org.olat.resource.accesscontrol.model.PSPTransactionStatus; import org.olat.resource.accesscontrol.model.PriceImpl; @@ -306,4 +307,37 @@ public class PaypalCheckoutTransactionImpl implements Persistable, PaypalCheckou public boolean equalsByPersistableKey(Persistable persistable) { return equals(persistable); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PaypalCheckoutTransaction[key=").append(getKey()).append("]") + .append("[price=").append(securePrice).append("]") + //order + .append("[orderId=").append(orderId).append("]") + .append("[orderPartId=").append(orderPartId).append("]") + .append("[methodId=").append(methodId).append("]"); + if(StringHelper.containsNonWhitespace(paypalOrderId)) { + sb.append("[paypalOrderId=").append(paypalOrderId).append("]"); + } + if(StringHelper.containsNonWhitespace(paypalOrderStatus)) { + sb.append("[paypalOrderStatus=").append(paypalOrderStatus).append("]"); + } + if(StringHelper.containsNonWhitespace(paypalOrderStatusReason)) { + sb.append("[paypalOrderStatusReason=").append(paypalOrderStatusReason).append("]"); + } + if(StringHelper.containsNonWhitespace(paypalAuthorizationId)) { + sb.append("[paypalAuthorizationId=").append(paypalAuthorizationId).append("]"); + } + if(StringHelper.containsNonWhitespace(paypalCaptureId)) { + sb.append("[paypalCaptureId=").append(paypalCaptureId).append("]"); + } + if(StringHelper.containsNonWhitespace(secureSuccessUUID)) { + sb.append("[successUUID=").append(secureSuccessUUID ).append("]"); + } + if(StringHelper.containsNonWhitespace(secureCancelUUID)) { + sb.append("[cancelUUID=").append(secureCancelUUID).append("]"); + } + return sb.toString(); + } } \ No newline at end of file diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalCheckoutAccountConfigurationController.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalCheckoutAccountConfigurationController.java index 7f3ca48fa09..2cf4ce25806 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalCheckoutAccountConfigurationController.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalCheckoutAccountConfigurationController.java @@ -48,10 +48,12 @@ import org.springframework.beans.factory.annotation.Autowired; public class PaypalCheckoutAccountConfigurationController extends FormBasicController { private static final String[] onKeys = new String[] { "on" }; + private static final String[] smartButtonsKeys = new String[] { "smartbuttons", "standard" }; private TextElement clientIdEl; private TextElement clientSecretEl; private SingleSelection currencyEl; + private SingleSelection smartButtonsEl; private MultipleSelectionElement enableEl; private final List<String> paypalCurrencies; @@ -83,6 +85,16 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr enableEl.select(onKeys[0], acModule.isPaypalCheckoutEnabled()); enableEl.addActionListener(FormEvent.ONCHANGE); + KeyValues smartButtons = new KeyValues(); + smartButtons.add(KeyValues.entry(smartButtonsKeys[0], translate("checkout.smart.buttons.enabled"))); + smartButtons.add(KeyValues.entry(smartButtonsKeys[1], translate("checkout.standard"))); + smartButtonsEl = uifactory.addRadiosVertical("checkout.smart.buttons", "checkout.smart.buttons", formLayout, smartButtons.keys(), smartButtons.values()); + if(paypalModule.isSmartButtons()) { + smartButtonsEl.select(smartButtonsKeys[0], true); + } else { + smartButtonsEl.select(smartButtonsKeys[1], true); + } + KeyValues currencies = new KeyValues(); paypalCurrencies.forEach(currency -> currencies.add(KeyValues.entry(currency, currency))); currencyEl = uifactory.addDropdownSingleselect("currency", "currency", formLayout, currencies.keys(), currencies.values(), null); @@ -99,7 +111,6 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr final FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonLayout", getTranslator()); formLayout.add(buttonGroupLayout); - uifactory.addFormSubmitButton("save", buttonGroupLayout); } @@ -108,6 +119,7 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr currencyEl.setVisible(enabled); clientIdEl.setVisible(enabled); clientSecretEl.setVisible(enabled); + smartButtonsEl.setVisible(enabled); } @Override @@ -133,7 +145,6 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr && !StringHelper.containsNonWhitespace(element.getValue())) { element.setErrorKey("form.legende.mandatory", null); allOk &= false; - } return allOk; @@ -157,6 +168,7 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr if(currencyEl.isOneSelected() && paypalCurrencies.contains(currencyEl.getSelectedKey())) { paypalModule.setPaypalCurrency(currencyEl.getSelectedKey()); } + paypalModule.setSmartButtons(smartButtonsEl.isOneSelected() && smartButtonsEl.isSelected(0)); } else { paypalModule.setClientId(null); paypalModule.setClientSecret(null); diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonAccessController.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonAccessController.java new file mode 100644 index 00000000000..ad58d806403 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonAccessController.java @@ -0,0 +1,105 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.resource.accesscontrol.provider.paypalcheckout.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.Form; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.htmlheader.jscss.JSAndCSSFormItem; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.StringHelper; +import org.olat.resource.accesscontrol.OfferAccess; +import org.olat.resource.accesscontrol.Price; +import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutModule; +import org.olat.resource.accesscontrol.ui.FormController; +import org.olat.resource.accesscontrol.ui.PriceFormat; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 6 nov. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class PaypalSmartButtonAccessController extends FormBasicController implements FormController { + + private OfferAccess link; + + @Autowired + private PaypalCheckoutModule paypalModule; + + public PaypalSmartButtonAccessController(UserRequest ureq, WindowControl wControl, OfferAccess link) { + super(ureq, wControl, "paypal_smart_buttons"); + this.link = link; + initForm(ureq); + } + + public PaypalSmartButtonAccessController(UserRequest ureq, WindowControl wControl, OfferAccess link, Form form) { + super(ureq, wControl, LAYOUT_CUSTOM, "paypal_smart_buttons", form); + this.link = link; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + String appId = paypalModule.getClientId(); + layoutCont.contextPut("clientId", appId); + String currency = link.getOffer().getPrice().getCurrencyCode(); + layoutCont.contextPut("currency", currency); + + String description = link.getOffer().getDescription(); + if(StringHelper.containsNonWhitespace(description)) { + description = StringHelper.xssScan(description); + layoutCont.contextPut("description", description); + } + + Price price = link.getOffer().getPrice(); + String priceStr = PriceFormat.fullFormat(price); + layoutCont.contextPut("price", priceStr); + + String mapperUri = registerMapper(ureq, new PaypalSmartButtonMapper(getIdentity(), link, this)); + layoutCont.contextPut("mapperUri", mapperUri); + } + + JSAndCSSFormItem js = new JSAndCSSFormItem("js", new String[] { "https://www.paypal.com/sdk/js?client-id=" + paypalModule.getClientId() + "¤cy=CHF&intent=authorize" }); + formLayout.add(js); + } + + @Override + protected void fireEvent(UserRequest ureq, Event event) { + super.fireEvent(ureq, event); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + // + } +} diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonMapper.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonMapper.java new file mode 100644 index 00000000000..2b4b9090da4 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/PaypalSmartButtonMapper.java @@ -0,0 +1,123 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.resource.accesscontrol.provider.paypalcheckout.ui; + +import javax.servlet.http.HttpServletRequest; + +import org.json.JSONObject; +import org.olat.core.CoreSpringFactory; +import org.olat.core.dispatcher.mapper.Mapper; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.UserRequestImpl; +import org.olat.core.gui.media.JSONMediaResource; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.id.Identity; +import org.olat.core.util.StringHelper; +import org.olat.resource.accesscontrol.OfferAccess; +import org.olat.resource.accesscontrol.provider.paypalcheckout.PaypalCheckoutManager; +import org.olat.resource.accesscontrol.provider.paypalcheckout.model.CreateSmartOrder; +import org.olat.resource.accesscontrol.ui.AccessEvent; + +/** + * + * Initial date: 6 nov. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class PaypalSmartButtonMapper implements Mapper { + + private CreateSmartOrder order; + private final OfferAccess link; + private final Identity identity; + private final PaypalCheckoutManager paypalManager; + private final PaypalSmartButtonAccessController controller; + + public PaypalSmartButtonMapper(Identity identity, OfferAccess link, PaypalSmartButtonAccessController controller) { + this.link = link; + this.identity = identity; + this.controller = controller; + paypalManager = CoreSpringFactory.getImpl(PaypalCheckoutManager.class); + } + + @Override + public MediaResource handle(String relPath, HttpServletRequest request) { + MediaResource resource = null; + if(relPath.contains("create-paypal-transaction")) { + resource = createPaypalTransaction(); + } else if(relPath.contains("approve-paypal-transaction")) { + resource = approvePaypalTransaction(request); + } else if(relPath.contains("cancel-paypal-transaction")) { + resource = cancelPaypalTransaction(); + } else if(relPath.contains("error-paypal-transaction")) { + resource = errorPaypalTransaction(); + } + return resource; + } + + private MediaResource createPaypalTransaction() { + if(order == null) { + order = paypalManager.createOrder(identity, link); + } + if(order != null) { + if(StringHelper.containsNonWhitespace(order.getPaypalOrderId())) { + return buildOrderResource(order.getPaypalOrderId()); + } else if(!order.isReservationOk()) { + JSONObject obj = new JSONObject(); + obj.put("reservation", false); + return new JSONMediaResource(obj, "UTF-8"); + } + } + return null; + } + + private MediaResource approvePaypalTransaction(HttpServletRequest request) { + if(order != null && StringHelper.containsNonWhitespace(order.getPaypalOrderId())) { + paypalManager.approveTransaction(order.getPaypalOrderId()); + + UserRequest ureq = new UserRequestImpl("m", request, null); + controller.fireEvent(ureq, AccessEvent.ACCESS_OK_EVENT); + return buildOrderResource(order.getPaypalOrderId()); + } + return null; + } + + private MediaResource cancelPaypalTransaction() { + if(order != null && StringHelper.containsNonWhitespace(order.getPaypalOrderId())) { + paypalManager.cancelTransaction(order.getPaypalOrderId()); + return buildOrderResource(order.getPaypalOrderId()); + } + return null; + } + + private MediaResource errorPaypalTransaction() { + if(order != null && StringHelper.containsNonWhitespace(order.getPaypalOrderId())) { + paypalManager.errorTransaction(order.getPaypalOrderId()); + return buildOrderResource(order.getPaypalOrderId()); + } + return null; + } + + private JSONMediaResource buildOrderResource(String paypalOrderId) { + JSONObject obj = new JSONObject(); + obj.put("orderID", paypalOrderId); + obj.put("reservation", true); + return new JSONMediaResource(obj, "UTF-8"); + } +} diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_content/paypal_smart_buttons.html b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_content/paypal_smart_buttons.html new file mode 100644 index 00000000000..c04a1244032 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_content/paypal_smart_buttons.html @@ -0,0 +1,99 @@ +<div class="o_paypal"> + <fieldset class=" o_form form-horizontal clearfix"> + <legend><i class="o_icon o_icon-fw o_ac_paypal_icon"> </i> $r.translate("access.paypal.title")</legend> + <div id="paypal_desc_${r.getCId()}" class="o_desc">$r.translate("access.paypal.desc.smart.buttons")</div> + <div id="paypal_processing_${r.getCId()}" class="o_desc" style="display: none;"><i class="o_icon o_icon-lg o_icon_pending o_icon-spin"> </i> $r.translate("paypal.process.transaction")</div> + + <div id="paypal_wrapper_${r.getCId()}"> + + #if($r.isNotEmpty($description)) + <div class="form-group clearfix"> + <label class="control-label col-sm-3" for-id="paypal_desc_${r.getCId()}">$r.translate("offer.description")</label> + <div class="col-sm-9"><p id="paypal_desc_${r.getCId()}" class="form-control-static">$description</p></div> + </div> + #end + <div class="form-group clearfix"> + <label class="control-label col-sm-3" for-id="paypal_price_${r.getCId()}">$r.translate("offer.price")</label> + <div class="col-sm-9"><p id="paypal_price_${r.getCId()}" class="form-control-static">$price</p></div> + </div> + + <div id="paypal_buttons_${r.getCId()}"> + <script defer> + jQuery(function() { + jQuery.ajax({ + url: 'https://www.paypal.com/sdk/js?client-id=$clientId¤cy=$currency&intent=authorize', + cache: true, + dataType: "script", + success: loadButtons + }); + + function loadButtons() { + paypal.Buttons({ + createOrder: function(data, actions) { + return fetch('$mapperUri/create-paypal-transaction', { + method: 'post', + headers: { 'content-type': 'application/json' } + }).then(function(res) { + return res.json(); + }).then(function(resData) { + // Use the same key name for order ID on the client and server + if(resData.reservation) { + return resData.orderID; + } + document.getElementById("paypal_wrapper_${r.getCId()}").style.display = "none"; + document.getElementById("paypal_reservation_${r.getCId()}").style.display = "block"; + return null; + }); + }, + onApprove: function(data, actions) { + document.getElementById("paypal_wrapper_${r.getCId()}").style.display = "none"; + document.getElementById("paypal_processing_${r.getCId()}").style.display = "block"; + return fetch('$mapperUri/approve-paypal-transaction', { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + orderID: data.orderID + }) + }).then(function(res) { + return res.json(); + }).then(function(resData) { + setTimeout(function() { + location.reload(); + }, 100); + }); + }, + onCancel: function (data, actions) { + return fetch('$mapperUri/cancel-paypal-transaction', { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + orderID: data.orderID + }) + }).then(function(res) { + document.getElementById("paypal_wrapper_${r.getCId()}").style.display = "none"; + document.getElementById("paypal_cancel_${r.getCId()}").style.display = "block"; + }); + }, + onError: function (err) { + return fetch('$mapperUri/error-paypal-transaction', { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + orderID: data.orderID + }) + }).then(function(res) { + document.getElementById("paypal_wrapper_${r.getCId()}").style.display = "none"; + document.getElementById("paypal_error_${r.getCId()}").style.display = "block"; + }); + } + }).render('#paypal_buttons_${r.getCId()}'); + } + }); + </script> + </div> + </div> + </fieldset> + <div id="paypal_reservation_${r.getCId()}" class="o_error" style="display: none;" role="alert">$r.translate("paypal.reservation.failed") </div> + <div id="paypal_cancel_${r.getCId()}" class="o_warning" style="display: none;" role="alert">$r.translate("paypal.cancelled.transaction")</div> + <div id="paypal_error_${r.getCId()}" class="o_error" style="display: none;" role="alert">$r.translate("paypal.error.transaction")</div> +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_de.properties index c6818d2064b..3598376e347 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_de.properties @@ -1,11 +1,15 @@ #Mon Aug 26 14:09:25 CEST 2019 access.button=Zur Zahlung access.paypal.desc=Diese Ressource kann mit einem PayPal Konto gekauft werden. Halten Sie Ihre PayPal Kontoinformationen und w\u00E4hlen Sie die Schaltfl\u00E4che "Kaufen" um sich f\u00FCr den Zugang zu registrieren. +access.paypal.desc.smart.buttons=$\:access.paypal.desc access.paypal.title=Bezahlen mit PayPal checkout.client.id=Client ID checkout.client.secret=Client secret checkout.config.description=Konfigurieren Sie die PayPal API-Berechtigung f\u00FCr den PayPal Zugang mit den Elementen Client ID und Client Secret. Diese zwei Sicherheitselemente m\u00FCssen Sie zuerst in Ihrem PayPal Businesskonto erstellen. <strong>OpenOlat unterst\u00FCtzt nicht nachtr\u00E4gliche Anderungen am Bestellungen von Ihrem Paypal Konto.</strong> checkout.config.title=Paypal Checkout konfigurieren +checkout.smart.buttons=Integration +checkout.smart.buttons.enabled=Paypal Smart Buttons +checkout.standard=Standard (Paypal Konto erforderlich) config.disabled.warning=Das PayPal Bezahlungsmodul ist f\u00FCr dieses System nicht freigeschaltet. Um Buchungen \u00FCber PayPal abwickeln zu k\u00F6nnen, nehmen Sie bitte Kontakt auf mit <a href="mailto:{0}">{0}</a>. currency=W\u00E4hrung currency.error=Fehler in der W\u00E4hrungsauswahl @@ -22,15 +26,19 @@ offer.description=$org.olat.resource.accesscontrol.ui\:offer.description offer.price=Preis oo.order.nr=Bestellung ID (in OpenOlat) paypal.before.redirect.error=Unerwarteter Fehler +paypal.cancelled.transaction=Sie haben die Transaktion abgebrochen. Die Buchung ist nicht ausgef\u00FChrt worden und Ihre Kreditkarte wurde nicht belastet. paypal.capture.id=Capture ID paypal.checkout.method=Paypal Checkout v2 paypal.enable=Paypal einschalten +paypal.error.transaction=Ein Fehler ist aufgetreten. paypal.invoice.id=Invoice ID paypal.menu.title=Paypal paypal.menu.title.alt=Paypal Checkout konfigurieren paypal.order.id=Bestellung ID (in Paypal) paypal.order.status=Status paypal.order.status.reason=Status Erkl\u00E4rung +paypal.process.transaction=Transkation ist erfolgreich und wird verarbeitet. +paypal.reservation.failed=Es gibt momentan keinen freien Platz. paypal.segment.account=Konto paypal.segment.transactions=Transactionen paypal.transaction.amount=Preis (in Paypal) @@ -39,7 +47,7 @@ paypal.transactions.empty=Keine Transaktion gefunden price=Preis price.error=Fehler in der Preisangabe show.all=Show all -status.canceled=Abgebrochenn +status.canceled=Abgebrochen status.error=Unerwartete Fehler status.new=Neu, noch nicht abgeschlossen status.success=Erfolgreich abgeschlossen diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_en.properties index df3ff08f467..de1d2d5dadc 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_en.properties @@ -1,11 +1,15 @@ #Mon Aug 26 17:17:13 CEST 2019 access.button=Go to Payment access.paypal.desc=PayPal description +access.paypal.desc.smart.buttons=$\:access.paypal.desc access.paypal.title=Pay with PayPal checkout.client.id=Client ID checkout.client.secret=Client secret checkout.config.description=Configure your authorisation to use your Paypal count with the Client ID and Client Secret. These two security elements must be created in your Paypal Business Account. <strong>OpenOlat doesn't support subsequent changes in order from your Paypal Account (yet).</strong> checkout.config.title=Configure Paypal Checkout +checkout.smart.buttons=Integration +checkout.smart.buttons.enabled=Paypal Smart Buttons +checkout.standard=Standard (Paypal account mandatory) config.disabled.warning=The PayPal payment module is not activated for this system. To handle bookings via PayPal can you please contact <a href="mailto:{0}">{0}</a>. currency=Currency currency.error=Currency error @@ -22,15 +26,19 @@ offer.description=$org.olat.resource.accesscontrol.ui\:offer.description offer.price=Price oo.order.nr=Order ID (in OpenOlat) paypal.before.redirect.error=Unexpected error +paypal.cancelled.transaction=You have canceled a transaction. paypal.capture.id=Capture ID paypal.checkout.method=Paypal Checkout v2 paypal.enable=Enable Paypal +paypal.error.transaction=An error occured. paypal.invoice.id=Invoice ID paypal.menu.title=Paypal paypal.menu.title.alt=Configure Paypal Checkout paypal.order.id=Order ID (in Paypal) paypal.order.status=Status paypal.order.status.reason=Status reason +paypal.process.transaction=Your transaction is approved and is currently processed. +paypal.reservation.failed=There isn't currently an available place. paypal.segment.account=Account paypal.segment.transactions=Transactions paypal.transaction.amount=Price (Paypal) diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_fr.properties index 9f27010e779..cba735ce290 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_fr.properties @@ -1,6 +1,7 @@ #Mon May 04 19:54:01 CEST 2020 access.button=Payer access.paypal.desc=Cet objet didactique peut \u00EAtre achet\u00E9 avec un compte Paypal. Cliquez sur le bouton "Payer" pour enregistrer votre acc\u00E8s. +access.paypal.desc.smart.buttons=$\:access.paypal.desc access.paypal.title=Payer avec Paypal checkout.client.id=Client ID checkout.client.secret=Client secret @@ -22,10 +23,12 @@ offer.description=$org.olat.resource.accesscontrol.ui\:offer.description offer.price=Prix oo.order.nr=ID commande (OpenOlat) paypal.before.redirect.error=Erreur inattendue +paypal.cancelled.transaction=Vous avez annulez la transaction. La r\u00E9servation n'a pas pu \u00EAtre conclue et votre carte-de-cr\u00E9dit ne sera pas d\u00E9bit\u00E9e. paypal.capture.id=Capture ID paypal.checkout.method=Paypal Checkout v2 paypal.config.description=Configurez l'autorisation d'acc\u00E8s \u00E0 l'API PayPal\: Client ID et Client Secret. Ce sont les deux \u00E9l\u00E9ments de s\u00E9curit\u00E9 dont vous avez besoin pour cr\u00E9er votre compte PayPal Business. paypal.enable=Activ\u00E8 Paypal +paypal.error.transaction=Une erreur s'est produite. paypal.invoice.id=Facture ID paypal.menu.title=Paypal paypal.menu.title.alt=Configurer Paypal Checkout diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_it.properties index b8f2aa936d2..64570a270c9 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_it.properties +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/ui/_i18n/LocalStrings_it.properties @@ -22,9 +22,11 @@ offer.description=$org.olat.resource.accesscontrol.ui\:offer.description offer.price=Prezzo oo.order.nr=ID ordine (in OpenOlat) paypal.before.redirect.error=Errore inaspettato +paypal.cancelled.transaction=Hai annullato la transazione. La registrazione non \u00E8 stata conclusa e la carta di credito non sar\u00E0 addebitata. paypal.capture.id=Capture ID paypal.checkout.method=Paypal Checkout v2 paypal.enable=Abilitare Paypal +paypal.error.transaction=Si \u00E8 verificato un errore. paypal.invoice.id=ID della fattura paypal.menu.title=Paypal paypal.menu.title.alt=Configurare Paypal Checkout diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/token/TokenAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/token/TokenAccessHandler.java index bc7c9770a53..c3b3581dae4 100644 --- a/src/main/java/org/olat/resource/accesscontrol/provider/token/TokenAccessHandler.java +++ b/src/main/java/org/olat/resource/accesscontrol/provider/token/TokenAccessHandler.java @@ -61,6 +61,11 @@ public class TokenAccessHandler implements AccessMethodHandler { return false; } + @Override + public boolean isOverlapAllowed(AccessMethodHandler handler) { + return true; + } + @Override public String getType() { return METHOD_TYPE; diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java b/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java index 491850f55b5..10d40542712 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java +++ b/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java @@ -21,6 +21,7 @@ package org.olat.resource.accesscontrol.ui; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -290,7 +291,7 @@ public class AccessConfigurationController extends FormBasicController { protected void addConfiguration(OfferAccess link) { AccessMethodHandler handler = acModule.getAccessMethodHandler(link.getMethod().getType()); - AccessInfo infos = new AccessInfo(handler.getMethodName(getLocale()), handler.isPaymentMethod(), null, link); + AccessInfo infos = new AccessInfo(handler.getMethodName(getLocale()), null, link, handler); confControllers.add(infos); if(editable) { @@ -407,7 +408,8 @@ public class AccessConfigurationController extends FormBasicController { private void checkOverlap() { boolean overlap = false; - + boolean overlapAllowed = true; + // Take a controller from the list for (AccessInfo confControllerA : confControllers) { // Compare it to every other from the list @@ -421,27 +423,26 @@ public class AccessConfigurationController extends FormBasicController { // One unlimited booking method and another if (aFrom == null && aTo == null) { - overlap = true; - break; + overlap |= true; + overlapAllowed &= confControllerA.isOverlapAllowed(confControllerB); } // Start and end overlap else if (aTo != null && bFrom != null && aTo.compareTo(bFrom) >= 0){ // Exclude not overlapping methods // Negate condition for no overlap => condition for overlap if (!(aFrom != null && bTo != null && aFrom.compareTo(bTo) > 0)) { - overlap = true; - break; + overlap |= true; } } // Two booking methods without start date else if (aFrom == null && bFrom == null) { - overlap = true; - break; + overlap |= true; + overlapAllowed &= confControllerA.isOverlapAllowed(confControllerB); } // Two booking methods without end date else if (aTo == null && bTo == null) { - overlap = true; - break; + overlap |= true; + overlapAllowed &= confControllerA.isOverlapAllowed(confControllerB); } } } @@ -453,6 +454,7 @@ public class AccessConfigurationController extends FormBasicController { // Display a warning confControllerContainer.contextPut("overlappingConfigs", overlap); + confControllerContainer.contextPut("overlappingErrorConfigs", !overlapAllowed); confControllerContainer.setDirty(true); } @@ -461,16 +463,16 @@ public class AccessConfigurationController extends FormBasicController { private String infos; private String dates; private OfferAccess link; - private final boolean paymentMethod; + private final AccessMethodHandler handler; private FormLink editButton; private FormLink deleteButton; - public AccessInfo(String name, boolean paymentMethod, String infos, OfferAccess link) { + public AccessInfo(String name, String infos, OfferAccess link, AccessMethodHandler handler) { this.name = name; - this.paymentMethod = paymentMethod; this.infos = infos; this.link = link; + this.handler = handler; } public String getName() { @@ -482,7 +484,11 @@ public class AccessConfigurationController extends FormBasicController { } public boolean isPaymentMethod() { - return paymentMethod; + return handler.isPaymentMethod(); + } + + public boolean isOverlapAllowed(AccessInfo info) { + return handler.isOverlapAllowed(info.handler); } public String getDates() { @@ -510,7 +516,7 @@ public class AccessConfigurationController extends FormBasicController { String price = PriceFormat.fullFormat(link.getOffer().getPrice()); if(acModule.isVatEnabled()) { BigDecimal vat = acModule.getVat(); - String vatStr = vat == null ? "" : vat.setScale(3, BigDecimal.ROUND_HALF_EVEN).toPlainString(); + String vatStr = vat == null ? "" : vat.setScale(3, RoundingMode.HALF_EVEN).toPlainString(); return translate("access.info.price.vat", new String[]{price, vatStr}); } else { diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/OrderDetailController.java b/src/main/java/org/olat/resource/accesscontrol/ui/OrderDetailController.java index 78381f8f271..57f0596e5b2 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/OrderDetailController.java +++ b/src/main/java/org/olat/resource/accesscontrol/ui/OrderDetailController.java @@ -20,6 +20,7 @@ package org.olat.resource.accesscontrol.ui; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -119,7 +120,7 @@ public class OrderDetailController extends FormBasicController { String orderTotalStr; if(acModule.isVatEnabled()) { BigDecimal vat = acModule.getVat(); - String vatStr = vat == null ? "" : vat.setScale(3, BigDecimal.ROUND_HALF_EVEN).toPlainString(); + String vatStr = vat == null ? "" : vat.setScale(3, RoundingMode.HALF_EVEN).toPlainString(); orderTotalStr = translate("access.info.price.vat", new String[]{orderTotal, vatStr}); } else { orderTotalStr = translate("access.info.price.noVat", new String[]{orderTotal}); diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/PriceFormat.java b/src/main/java/org/olat/resource/accesscontrol/ui/PriceFormat.java index 5cf7774cd47..d235445baf4 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/PriceFormat.java +++ b/src/main/java/org/olat/resource/accesscontrol/ui/PriceFormat.java @@ -20,6 +20,7 @@ package org.olat.resource.accesscontrol.ui; import java.math.BigDecimal; +import java.math.RoundingMode; import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; @@ -49,12 +50,12 @@ public class PriceFormat { if(value != null && value.length() > 0) { double val = Double.parseDouble(value); BigDecimal bd = new BigDecimal(val); - price.setAmount(bd.setScale(2, BigDecimal.ROUND_HALF_UP)); + price.setAmount(bd.setScale(2, RoundingMode.HALF_UP)); } else { price.setAmount(BigDecimal.ZERO); } } catch (NumberFormatException e) { - log.error("Cannot format this value: " + value); + log.error("Cannot format this value: {}", value); } } @@ -64,13 +65,13 @@ public class PriceFormat { double val = Double.parseDouble(value); BigDecimal bd = new BigDecimal(val); - return bd.setScale(2, BigDecimal.ROUND_HALF_UP); + return bd.setScale(2, RoundingMode.HALF_UP); } return BigDecimal.ZERO; } private static String extractNumbers(String value) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); for(int i=0; i<value.length(); i++) { char ch = value.charAt(i); if(Character.isDigit(ch)) { @@ -108,6 +109,6 @@ public class PriceFormat { isoCurrencyCode = "CHF"; } - return isoCurrencyCode + '\u00A0' + price.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN).toString(); + return isoCurrencyCode + '\u00A0' + price.getAmount().setScale(2, RoundingMode.HALF_EVEN).toString(); } } diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/_content/configuration_list.html b/src/main/java/org/olat/resource/accesscontrol/ui/_content/configuration_list.html index 925f0a8a42c..292b613d58b 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/_content/configuration_list.html +++ b/src/main/java/org/olat/resource/accesscontrol/ui/_content/configuration_list.html @@ -1,4 +1,6 @@ -#if($overlappingConfigs) +#if($r.isTrue($overlappingErrorConfigs)) + <div class="o_block"><div class="o_error" role="alert"><i class="o_icon o_icon_important"> </i> $r.translate("accesscontrol.overlap.error")</div></div> +#elseif($r.isTrue($overlappingConfigs)) <div class="o_important"><i class="o_icon o_icon_important"> </i> $r.translate("accesscontrol.overlap.found")</div> #end diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties index 45ff7203382..fe103e45481 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties @@ -24,6 +24,7 @@ accesscontrol.desc.end=W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontr accesscontrol.no.methods.full.access=Es ist noch keine Buchungsmethode konfiguriert.<br />Der Zugang zu dieser Ressource steht allen Systembenutzern ohne Buchung offen. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um den Zugang einzuschr\u00E4nken und/oder eine Buchung zu erzwingen. accesscontrol.no.methods.no.access=Es ist noch keine Buchungsmethode konfiguriert.<br />Diese Ressource kann nur von den Teilnehmer der Ressource verwendet werden. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um eine Buchungsmethode zu w\u00E4hlen und die Ressource zu ver\u00F6ffentlichen. accesscontrol.overlap.found=Es wurden \u00FCberlappende Buchungsmethoden gefunden. Bitte stellen Sie sicher, dass dies beabsichtig und korrekt ist. +accesscontrol.overlap.error=Es wurden \u00FCberlappende Buchungsmethoden gefunden, die es nicht unterstützen. Bitte, ben\u00FCtzen Sie nur eine Methode gleichseitig. accesscontrol.table.from=g\u00FCltig von accesscontrol.table.method=Buchungsmethode accesscontrol.table.to=bis diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties index d00aff49e98..3d63ea907d7 100644 --- a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties @@ -24,6 +24,7 @@ accesscontrol.desc.end=Select the button "$\:add.accesscontrol" to select one or accesscontrol.no.methods.full.access=There is no booking method configured to restrict access. This resource can be accessed by all known users without an explicit booking. Select the button "$\:add.accesscontrol" to restrict access and/or to require an explicit booking. accesscontrol.no.methods.no.access=There is no booking method configured. This resource can only be accessed by explicit members of this resource. Select the button "$\:add.accesscontrol" to add a booking method and to publish the resource. accesscontrol.overlap.found=There are overlapping booking methods. Please make sure this is intended and correct. +accesscontrol.overlap.error=There are overlapping booking methods which don't support it. Please make sure you only one method at the same time. accesscontrol.table.from=valid from accesscontrol.table.method=Booking method accesscontrol.table.to=to diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java index 81d39a87a77..069bdf92ff8 100644 --- a/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java +++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java @@ -41,6 +41,8 @@ public class ShibbolethAutoAccessHandler extends AutoAccessHandler { public String getType() { return METHOD_TYPE; } + + @Override public String getMethodName(Locale locale) { diff --git a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java index 21b3f2faefc..ce082acd0c7 100644 --- a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java +++ b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java @@ -169,7 +169,7 @@ public class ACFrontendManagerTest extends OlatTestCase { Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("agp-" + UUID.randomUUID().toString()); Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("agp-" + UUID.randomUUID().toString()); Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); - BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null); + BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", Integer.valueOf(0), Integer.valueOf(2), false, false, null); businessGroupRelationDao.addRole(id1, group, GroupRoles.participant.name()); businessGroupRelationDao.addRole(id2, group, GroupRoles.participant.name()); @@ -200,7 +200,7 @@ public class ACFrontendManagerTest extends OlatTestCase { public void testFreeAccesToBusinessGroupWithWaitingList_enoughPlace() { //create a group with a free offer Identity id = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); - BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(10), true, false, null); + BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", Integer.valueOf(0), Integer.valueOf(10), true, false, null); Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)"); offer = acService.save(offer); List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class); @@ -230,7 +230,7 @@ public class ACFrontendManagerTest extends OlatTestCase { Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("agp-1"); Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("agp-2"); Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("agp-3"); - BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null); + BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", Integer.valueOf(0), Integer.valueOf(2), true, false, null); businessGroupRelationDao.addRole(id1, group, GroupRoles.participant.name()); businessGroupRelationDao.addRole(id2, group, GroupRoles.participant.name()); @@ -272,7 +272,7 @@ public class ACFrontendManagerTest extends OlatTestCase { Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); - BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null); + BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", Integer.valueOf(0), Integer.valueOf(2), true, false, null); Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)"); offer = acService.save(offer); List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class); @@ -321,7 +321,7 @@ public class ACFrontendManagerTest extends OlatTestCase { Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString()); - BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null); + BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", Integer.valueOf(0), Integer.valueOf(2), false, false, null); Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)"); offer = acService.save(offer); List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class); diff --git a/src/test/java/org/olat/resource/accesscontrol/ACOrderManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACOrderManagerTest.java index d36430c2af0..fdafd681897 100644 --- a/src/test/java/org/olat/resource/accesscontrol/ACOrderManagerTest.java +++ b/src/test/java/org/olat/resource/accesscontrol/ACOrderManagerTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.List; import java.util.UUID; @@ -373,9 +374,9 @@ public class ACOrderManagerTest extends OlatTestCase { assertEquals(ident7, retrivedOrder.getDelivery()); assertEquals(1, retrivedOrder.getParts().size()); - assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), retrivedOrder.getTotalOrderLines().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), retrivedOrder.getTotalOrderLines().getAmount().setScale(2, RoundingMode.HALF_EVEN)); assertEquals(price1.getCurrencyCode(), retrivedOrder.getTotalOrderLines().getCurrencyCode()); - assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), retrivedOrder.getTotal().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), retrivedOrder.getTotal().getAmount().setScale(2, RoundingMode.HALF_EVEN)); assertEquals(price1.getCurrencyCode(), retrivedOrder.getTotal().getCurrencyCode()); //check order part @@ -384,9 +385,9 @@ public class ACOrderManagerTest extends OlatTestCase { assertNotNull(orderPart.getTotal()); assertNotNull(orderPart.getTotalOrderLines()); assertEquals(1, orderPart.getOrderLines().size()); - assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), orderPart.getTotalOrderLines().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), orderPart.getTotalOrderLines().getAmount().setScale(2, RoundingMode.HALF_EVEN)); assertEquals(price1.getCurrencyCode(), orderPart.getTotalOrderLines().getCurrencyCode()); - assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), orderPart.getTotal().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), orderPart.getTotal().getAmount().setScale(2, RoundingMode.HALF_EVEN)); assertEquals(price1.getCurrencyCode(), orderPart.getTotal().getCurrencyCode()); //check order line @@ -396,9 +397,9 @@ public class ACOrderManagerTest extends OlatTestCase { Assert.assertNotNull(line.getUnitPrice()); Assert.assertNotNull(line.getTotal()); Assert.assertEquals(offer1, line.getOffer()); - Assert.assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), line.getUnitPrice().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + Assert.assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), line.getUnitPrice().getAmount().setScale(2, RoundingMode.HALF_EVEN)); Assert.assertEquals(price1.getCurrencyCode(), line.getUnitPrice().getCurrencyCode()); - Assert.assertEquals(price1.getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN), line.getTotal().getAmount().setScale(2, BigDecimal.ROUND_HALF_EVEN)); + Assert.assertEquals(price1.getAmount().setScale(2, RoundingMode.HALF_EVEN), line.getTotal().getAmount().setScale(2, RoundingMode.HALF_EVEN)); Assert.assertEquals(price1.getCurrencyCode(), line.getTotal().getCurrencyCode()); } diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAOTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAOTest.java index 4bebb1a909e..05c427376b1 100644 --- a/src/test/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAOTest.java +++ b/src/test/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalCheckoutTransactionDAOTest.java @@ -22,6 +22,7 @@ package org.olat.resource.accesscontrol.provider.paypalcheckout.manager; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.junit.Assert; import org.junit.Test; @@ -146,6 +147,39 @@ public class PaypalCheckoutTransactionDAOTest extends OlatTestCase { PaypalCheckoutTransaction unkownTrx = transactionDao.loadTransactionBySecureUuid("something-uuid-but-not-real"); Assert.assertNull(unkownTrx); } + + @Test + public void loadTransactionByPaypalOrderId() { + List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalCheckoutAccessMethod.class); + AccessMethod checkoutMethod = methods.get(0); + + //create an offer to buy + Identity author = JunitTestHelper.createAndPersistIdentityAsRndUser("customer-1"); + RepositoryEntry entry = JunitTestHelper.createRandomRepositoryEntry(author); + Offer offer = acService.createOffer(entry.getOlatResource(), "TestSaveTransaction"); + offer = acService.save(offer); + + Identity identity = JunitTestHelper.createAndPersistIdentityAsRndUser("customer-1"); + dbInstance.commit(); + + //create and save an order + Order order = acOrderManager.createOrder(identity); + OrderPart orderPart = acOrderManager.addOrderPart(order); + OrderLine item = acOrderManager.addOrderLine(orderPart, offer); + order = acOrderManager.save(order); + dbInstance.commit(); + Assert.assertNotNull(item); + + String paypalOrderId = UUID.randomUUID().toString(); + Price amount = new PriceImpl(new BigDecimal("5.00"), "CHF"); + PaypalCheckoutTransaction trx = transactionDao.createTransaction(amount, order, orderPart, checkoutMethod); + trx.setPaypalOrderId(paypalOrderId); + dbInstance.commitAndCloseSession(); + + PaypalCheckoutTransaction orderedTrx = transactionDao.loadTransactionByPaypalOrderId(paypalOrderId); + Assert.assertNotNull(orderedTrx); + Assert.assertEquals(trx, orderedTrx); + } @Test public void loadTransactionByOrder() { -- GitLab