diff --git a/pom.xml b/pom.xml index 29b479551de824fbe6a7499d9a4bfc550133df29..92784181694e60c0febbc95a991891569ce0f817 100644 --- a/pom.xml +++ b/pom.xml @@ -2263,6 +2263,11 @@ <artifactId>checkout-sdk</artifactId> <version>1.0.2</version> </dependency> + <dependency> + <groupId>com.paypal.sdk</groupId> + <artifactId>rest-api-sdk</artifactId> + <version>1.14.0</version> + </dependency> <!-- end Paypal --> <!-- Start test dependencies --> @@ -2347,7 +2352,6 @@ <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> - <scope>test</scope> </dependency> <dependency> <groupId>com.google.guava</groupId> 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 6b417248dfa609621cc2bddf024e597949a0c88d..e57ecd8c5a0d244002a728b3c5bfffb3c24f2c28 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 @@ -19,11 +19,16 @@ */ package org.olat.resource.accesscontrol.provider.paypalcheckout; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.logging.log4j.Logger; import org.olat.core.configuration.AbstractSpringModule; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.resource.accesscontrol.provider.paypalcheckout.manager.PaypalRESTWebhookProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -37,10 +42,13 @@ import org.springframework.stereotype.Service; @Service public class PaypalCheckoutModule extends AbstractSpringModule { + private static final Logger log = Tracing.createLoggerFor(PaypalCheckoutModule.class); + 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 PAYPAL_WEBHOOK_ID = "paypal.webhhok.id"; private static final String[] currencies = new String[] { "AUD", @@ -77,6 +85,11 @@ public class PaypalCheckoutModule extends AbstractSpringModule { private String paypalCurrency; @Value("${paypal.checkout.v2.smart.buttons:true}") private boolean smartButtons; + @Value("${paypal.webhhok.id:#{null}}") + private String webhookId; + + @Autowired + private PaypalRESTWebhookProvider webhookProvider; @Autowired public PaypalCheckoutModule(CoordinatorManager coordinatorManager) { @@ -93,7 +106,32 @@ public class PaypalCheckoutModule extends AbstractSpringModule { clientId = getStringPropertyValue(PAYPAL_CLIENT_ID, clientId); clientSecret = getStringPropertyValue(PAYPAL_CLIENT_SECRET, clientSecret); paypalCurrency = getStringPropertyValue(PAYPAL_CURRENCY, paypalCurrency); + webhookId = getStringPropertyValue(PAYPAL_WEBHOOK_ID, webhookId); smartButtons = "true".equals(getStringPropertyValue(PAYPAL_SMART_BUTTONS, Boolean.toString(smartButtons))); + + if(!StringHelper.containsNonWhitespace(webhookId) && StringHelper.containsNonWhitespace(clientId) && StringHelper.containsNonWhitespace(clientSecret)) { + updateWebhook(clientId, clientSecret); + } + } + + public void updateWebhook() { + updateWebhook(clientId, clientSecret); + } + + private void updateWebhook(String id, String secret) { + webhookId = webhookProvider.hasWebhook(id, secret, sandbox); + if(webhookId == null) { + List<String> eventNames = new ArrayList<>(); + eventNames.add("PAYMENT.CAPTURE.COMPLETED"); + eventNames.add("PAYMENT.CAPTURE.DENIED"); + eventNames.add("PAYMENT.CAPTURE.PENDING"); + webhookId = webhookProvider.createWebhook(id, secret, eventNames, sandbox); + if(StringHelper.containsNonWhitespace(webhookId)) { + setStringProperty(PAYPAL_WEBHOOK_ID, webhookId, true); + } + } else { + log.info("PayPal webhook successfully retrieved: {}", webhookId); + } } public String getClientId() { diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalRESTWebhookProvider.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalRESTWebhookProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..08bedbf78a2e378194ddd54015f6620faf038ed3 --- /dev/null +++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypalcheckout/manager/PaypalRESTWebhookProvider.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.manager; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.Logger; +import org.olat.core.helpers.Settings; +import org.olat.core.logging.Tracing; +import org.springframework.stereotype.Service; + +import com.paypal.api.payments.EventType; +import com.paypal.api.payments.Webhook; +import com.paypal.api.payments.WebhookList; +import com.paypal.base.Constants; +import com.paypal.base.rest.APIContext; +import com.paypal.base.rest.PayPalRESTException; + +/** + * Helper methods to manage PayPal Webhooks (deprecated but useful). + * + * Initial date: 12 nov. 2020<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class PaypalRESTWebhookProvider { + + private static final Logger log = Tracing.createLoggerFor(PaypalRESTWebhookProvider.class); + + public String hasWebhook(String clientId, String clientSecret, boolean sandbox) { + try { + String mode = sandbox ? Constants.SANDBOX : Constants.LIVE; + APIContext apiContext = new APIContext(clientId, clientSecret, mode); + + WebhookList webhookList = new WebhookList(); + webhookList = webhookList.getAll(apiContext); + List<Webhook> webhooks = webhookList.getWebhooks(); + + String webhookEndpoint = webhookEndpoint(); + + for(Webhook webhook:webhooks) { + String url = webhook.getUrl(); + if(url.equals(webhookEndpoint)) { + return webhook.getId(); + } + } + } catch (PayPalRESTException e) { + log.error("", e); + } + return null; + } + + public String webhookEndpoint() { + String serverContext = Settings.getServerContextPathURI(); + if(!serverContext.endsWith("/")) { + serverContext += "/"; + } + return serverContext + "checkoutv2"; + } + + public String createWebhook(String clientId, String clientSecret, List<String> eventNames, boolean sandbox) { + List<EventType> eventTypes = new ArrayList<>(); + for(String eventName:eventNames) { + EventType eventType = new EventType(); + eventType.setName(eventName); + eventTypes.add(eventType); + } + + String webhookEndpoint = webhookEndpoint(); + Webhook webhook = new Webhook(); + webhook.setUrl(webhookEndpoint); + webhook.setEventTypes(eventTypes); + + try{ + String mode = sandbox ? Constants.SANDBOX : Constants.LIVE; + APIContext apiContext = new APIContext(clientId, clientSecret, mode); + Webhook createdWebhook = webhook.create(apiContext, webhook); + String webhookId = createdWebhook.getId(); + log.info(Tracing.M_AUDIT, "PayPal webhook created: {}", webhookId); + return webhookId; + } catch (PayPalRESTException e) { + log.error("", e); + return null; + } + } +} 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 2cf4ce25806e4eba86b724c5c04c4fd1d3b87523..d4b5a9795981ae3f50ee2db09adbd49356fd93bc 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 @@ -169,10 +169,19 @@ public class PaypalCheckoutAccountConfigurationController extends FormBasicContr paypalModule.setPaypalCurrency(currencyEl.getSelectedKey()); } paypalModule.setSmartButtons(smartButtonsEl.isOneSelected() && smartButtonsEl.isSelected(0)); + doUpdateWebhook(); } else { paypalModule.setClientId(null); paypalModule.setClientSecret(null); } showInfo("saved"); } + + private void doUpdateWebhook() { + try { + paypalModule.updateWebhook(); + } catch (Exception e) { + logError("", e); + } + } } \ No newline at end of file