-
Notifications
You must be signed in to change notification settings - Fork 33
Feature/ad 368 #579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Feature/ad 368 #579
Changes from all commits
cfe5b2f
6fb7cb0
504021a
2d2da4c
42cc696
932563f
176ded5
d5dc404
3d8562d
82e2867
661f7f5
25cfc7a
c63c3af
d76e18d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.adyen.commerce.services.impl; | ||
|
|
||
| import com.adyen.model.checkout.CardDetails; | ||
| import com.adyen.model.checkout.CheckoutPaymentMethod; | ||
| import com.adyen.model.checkout.PaymentRequest; | ||
| import com.adyen.v6.enums.RecurringContractMode; | ||
| import de.hybris.platform.commercefacades.order.data.CartData; | ||
| import de.hybris.platform.core.model.user.CustomerModel; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Objects; | ||
|
|
||
| public class CreditCardSubscriptionHandler implements PaymentMethodHandler { | ||
| @Override | ||
| public boolean canHandle(String paymentMethod) { | ||
| return Arrays.stream(CardDetails.TypeEnum.values()).map(CardDetails.TypeEnum::toString).anyMatch(type -> type.equalsIgnoreCase(paymentMethod)); | ||
| } | ||
|
|
||
| @Override | ||
| public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, RecurringContractMode recurringContractMode, CustomerModel customerModel, Boolean is3DS2Allowed, Boolean guestUserTokenizationEnabled) { | ||
| if (StringUtils.isNotEmpty(cartData.getAdyenSelectedReference())) { | ||
|
|
||
| CardDetails cardDetails; | ||
|
|
||
| if (Objects.isNull(paymentRequest.getPaymentMethod()) || Objects.isNull(paymentRequest.getPaymentMethod().getCardDetails())) { | ||
| cardDetails = new CardDetails(); | ||
| } else { | ||
| cardDetails = paymentRequest.getPaymentMethod().getCardDetails(); | ||
| } | ||
|
|
||
| cardDetails.setStoredPaymentMethodId(cartData.getAdyenSelectedReference()); | ||
| CheckoutPaymentMethod checkoutPaymentMethod = new CheckoutPaymentMethod(); | ||
| checkoutPaymentMethod.setActualInstance(cardDetails); | ||
| paymentRequest.setPaymentMethod(checkoutPaymentMethod); | ||
|
|
||
| paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION); | ||
|
|
||
| paymentRequest.setShopperInteraction(PaymentRequest.ShopperInteractionEnum.CONTAUTH); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,8 @@ public PaymentRequest createPaymentsRequest(final String merchantAccount, | |
| handlePaymentMethodSpecificLogic(paymentRequest, cartData, originPaymentsRequest, | ||
| recurringContractMode, customerModel, guestUserTokenizationEnabled); | ||
|
|
||
| addPaymentMethodTokenizationToPaymentRequest(paymentRequest, cartData); | ||
|
|
||
| return paymentRequest; | ||
| } | ||
|
|
||
|
|
@@ -396,7 +398,7 @@ protected void handlePaymentMethodSpecificLogic(PaymentRequest paymentRequest, C | |
|
|
||
| // Use payment method handler | ||
| paymentMethodHandlerFactory.getHandler(paymentMethod) | ||
| .ifPresent(handler -> handler.updatePaymentRequest(paymentRequest, cartData, | ||
| .forEach(handler -> handler.updatePaymentRequest(paymentRequest, cartData, | ||
| recurringContractMode, customerModel, is3DS2Allowed, guestUserTokenizationEnabled)); | ||
| } | ||
|
|
||
|
|
@@ -406,6 +408,18 @@ protected void copySchemePaymentSettings(PaymentRequest paymentRequest, PaymentR | |
| paymentRequest.setStorePaymentMethod(originPaymentsRequest.getStorePaymentMethod()); | ||
| } | ||
|
|
||
| protected void addPaymentMethodTokenizationToPaymentRequest(PaymentRequest paymentRequest, CartData cartData) { | ||
| if (tokenizeForSubscriptionProducts(cartData)) { | ||
| paymentRequest.setStorePaymentMethod(true); | ||
| paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION); | ||
| } | ||
| } | ||
|
|
||
| protected boolean tokenizeForSubscriptionProducts(CartData cartData) { | ||
| return !cartData.getSubscriptionOrder() && cartData.getEntries().stream() | ||
| .anyMatch(entry -> Objects.nonNull(entry.getProduct().getSubscriptionTerm())); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic assumes that cartData.getSubscriptionOrder() is false for the initial checkout and true only for the subsequent recurring runs. This might throw a NullPointerException (unless it's a primitive boolean, but the bean definition showed java.lang.Boolean). |
||
| } | ||
|
|
||
| protected AddressData getBillingAddress(CartData cartData) { | ||
| return Optional.ofNullable(cartData.getPaymentInfo()) | ||
| .map(paymentInfo -> paymentInfo.getBillingAddress()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.adyen.commerce.services.impl; | ||
|
|
||
| import com.adyen.model.checkout.CheckoutPaymentMethod; | ||
| import com.adyen.model.checkout.IdealDetails; | ||
| import com.adyen.model.checkout.PaymentRequest; | ||
| import com.adyen.model.checkout.SepaDirectDebitDetails; | ||
| import com.adyen.v6.enums.RecurringContractMode; | ||
| import de.hybris.platform.commercefacades.order.data.CartData; | ||
| import de.hybris.platform.core.model.user.CustomerModel; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public class IdealSubscriptionHandler implements PaymentMethodHandler { | ||
| @Override | ||
| public boolean canHandle(String paymentMethod) { | ||
| return Arrays.stream(IdealDetails.TypeEnum.values()).map(IdealDetails.TypeEnum::toString).anyMatch(type -> type.equalsIgnoreCase(paymentMethod)); | ||
|
|
||
| } | ||
|
|
||
| @Override | ||
| public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, RecurringContractMode recurringContractMode, CustomerModel customerModel, Boolean is3DS2Allowed, Boolean guestUserTokenizationEnabled) { | ||
| if (StringUtils.isNotEmpty(cartData.getAdyenSelectedReference())) { | ||
|
|
||
|
|
||
| SepaDirectDebitDetails sepaDirectDebitDetails = new SepaDirectDebitDetails(); | ||
|
|
||
|
|
||
| sepaDirectDebitDetails.setStoredPaymentMethodId(cartData.getAdyenSelectedReference()); | ||
| CheckoutPaymentMethod checkoutPaymentMethod = new CheckoutPaymentMethod(); | ||
| checkoutPaymentMethod.setActualInstance(sepaDirectDebitDetails); | ||
| paymentRequest.setPaymentMethod(checkoutPaymentMethod); | ||
|
|
||
| paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION); | ||
|
|
||
| paymentRequest.setShopperInteraction(PaymentRequest.ShopperInteractionEnum.CONTAUTH); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.adyen.commerce.services.impl; | ||
|
|
||
| import com.adyen.model.checkout.CheckoutPaymentMethod; | ||
| import com.adyen.model.checkout.KlarnaDetails; | ||
| import com.adyen.model.checkout.PaymentRequest; | ||
| import com.adyen.v6.enums.RecurringContractMode; | ||
| import de.hybris.platform.commercefacades.order.data.CartData; | ||
| import de.hybris.platform.core.model.user.CustomerModel; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Objects; | ||
|
|
||
| public class KlarnaSubscriptionHandler implements PaymentMethodHandler { | ||
| @Override | ||
| public boolean canHandle(String paymentMethod) { | ||
| return Arrays.stream(KlarnaDetails.TypeEnum.values()).map(KlarnaDetails.TypeEnum::toString).anyMatch(type -> type.equalsIgnoreCase(paymentMethod)); | ||
| } | ||
|
|
||
| @Override | ||
| public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, RecurringContractMode recurringContractMode, CustomerModel customerModel, Boolean is3DS2Allowed, Boolean guestUserTokenizationEnabled) { | ||
| if (StringUtils.isNotEmpty(cartData.getAdyenSelectedReference())) { | ||
|
|
||
| KlarnaDetails klarnaDetails; | ||
|
|
||
| if (Objects.isNull(paymentRequest.getPaymentMethod()) || Objects.isNull(paymentRequest.getPaymentMethod().getKlarnaDetails())) { | ||
| klarnaDetails = new KlarnaDetails(); | ||
| } else { | ||
| klarnaDetails = paymentRequest.getPaymentMethod().getKlarnaDetails(); | ||
| } | ||
|
|
||
| klarnaDetails.setStoredPaymentMethodId(cartData.getAdyenSelectedReference()); | ||
| CheckoutPaymentMethod checkoutPaymentMethod = new CheckoutPaymentMethod(); | ||
| checkoutPaymentMethod.setActualInstance(klarnaDetails); | ||
| paymentRequest.setPaymentMethod(checkoutPaymentMethod); | ||
|
|
||
| paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION); | ||
|
|
||
| paymentRequest.setShopperInteraction(PaymentRequest.ShopperInteractionEnum.CONTAUTH); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.adyen.commerce.services.impl; | ||
|
|
||
| import com.adyen.model.checkout.CheckoutPaymentMethod; | ||
| import com.adyen.model.checkout.PayPalDetails; | ||
| import com.adyen.model.checkout.PaymentRequest; | ||
| import com.adyen.v6.enums.RecurringContractMode; | ||
| import de.hybris.platform.commercefacades.order.data.CartData; | ||
| import de.hybris.platform.core.model.user.CustomerModel; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Objects; | ||
|
|
||
| public class PayPalSubscriptionHandler implements PaymentMethodHandler { | ||
| @Override | ||
| public boolean canHandle(String paymentMethod) { | ||
| return Arrays.stream(PayPalDetails.TypeEnum.values()).map(PayPalDetails.TypeEnum::toString).anyMatch(type -> type.equalsIgnoreCase(paymentMethod)); | ||
| } | ||
|
|
||
| @Override | ||
| public void updatePaymentRequest(PaymentRequest paymentRequest, CartData cartData, RecurringContractMode recurringContractMode, CustomerModel customerModel, Boolean is3DS2Allowed, Boolean guestUserTokenizationEnabled) { | ||
| if (StringUtils.isNotEmpty(cartData.getAdyenSelectedReference())) { | ||
|
|
||
| PayPalDetails payPalDetails; | ||
|
|
||
| if (Objects.isNull(paymentRequest.getPaymentMethod()) || Objects.isNull(paymentRequest.getPaymentMethod().getPayPalDetails())) { | ||
| payPalDetails = new PayPalDetails(); | ||
| } else { | ||
| payPalDetails = paymentRequest.getPaymentMethod().getPayPalDetails(); | ||
| } | ||
|
|
||
| payPalDetails.setStoredPaymentMethodId(cartData.getAdyenSelectedReference()); | ||
| CheckoutPaymentMethod checkoutPaymentMethod = new CheckoutPaymentMethod(); | ||
| checkoutPaymentMethod.setActualInstance(payPalDetails); | ||
| paymentRequest.setPaymentMethod(checkoutPaymentMethod); | ||
|
|
||
| paymentRequest.setRecurringProcessingModel(PaymentRequest.RecurringProcessingModelEnum.SUBSCRIPTION); | ||
|
|
||
| paymentRequest.setShopperInteraction(PaymentRequest.ShopperInteractionEnum.CONTAUTH); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.adyen.v6.events; | ||
|
|
||
| import com.adyen.commerce.data.TokenWebhookRequestData; | ||
| import de.hybris.platform.servicelayer.event.events.AbstractEvent; | ||
|
|
||
| public class TokenizationEvent extends AbstractEvent { | ||
| private final TokenWebhookRequestData data; | ||
|
|
||
| public TokenizationEvent(final TokenWebhookRequestData data) { | ||
| this.data = data; | ||
| } | ||
|
|
||
| public TokenWebhookRequestData getData() { | ||
| return data; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||
| package com.adyen.v6.listeners; | ||||||
|
|
||||||
| import com.adyen.commerce.data.TokenWebhookRequestData; | ||||||
| import com.adyen.v6.events.TokenizationEvent; | ||||||
| import com.adyen.v6.repository.PaymentTransactionRepository; | ||||||
| import de.hybris.platform.core.model.order.AbstractOrderModel; | ||||||
| import de.hybris.platform.core.model.order.payment.PaymentInfoModel; | ||||||
| import de.hybris.platform.core.model.user.CustomerModel; | ||||||
| import de.hybris.platform.payment.model.PaymentTransactionModel; | ||||||
| import de.hybris.platform.servicelayer.event.impl.AbstractEventListener; | ||||||
| import de.hybris.platform.servicelayer.model.ModelService; | ||||||
| import org.apache.commons.lang.NotImplementedException; | ||||||
| import org.apache.log4j.Logger; | ||||||
|
|
||||||
| import java.util.Objects; | ||||||
|
|
||||||
|
|
||||||
| public class TokenizationWebhookEventListener extends AbstractEventListener<TokenizationEvent> { | ||||||
| private static final Logger LOG = Logger.getLogger(TokenizationWebhookEventListener.class); | ||||||
|
|
||||||
| private PaymentTransactionRepository paymentTransactionRepository; | ||||||
| private ModelService modelService; | ||||||
|
|
||||||
| protected static String TOKEN_CREATED = "recurring.token.created"; | ||||||
|
|
||||||
| public TokenizationWebhookEventListener() { | ||||||
| super(); | ||||||
| } | ||||||
|
|
||||||
| @Override | ||||||
| protected void onEvent(TokenizationEvent tokenizationEvent) { | ||||||
| LOG.debug("Processing Tokenization event"); | ||||||
|
|
||||||
| TokenWebhookRequestData data = tokenizationEvent.getData(); | ||||||
| PaymentTransactionModel transactionModel = paymentTransactionRepository.getTransactionModel(data.getEventId()); | ||||||
| if (Objects.isNull(transactionModel)) { | ||||||
| throw new IllegalStateException("No PaymentTransactionModel found for eventId(pspReference): " + data.getEventId()); | ||||||
| } | ||||||
|
|
||||||
| AbstractOrderModel order = transactionModel.getOrder(); | ||||||
|
|
||||||
| if (Objects.isNull(order)) { | ||||||
| throw new IllegalStateException("No Order connected to PaymentTransaction with code: " + data.getEventId()); | ||||||
| } | ||||||
|
|
||||||
| crosscheckWithOrder(order, data); | ||||||
|
|
||||||
| if (TOKEN_CREATED.equals(data.getEventType())) { | ||||||
| PaymentInfoModel paymentInfo = order.getPaymentInfo(); | ||||||
| paymentInfo.setAdyenSelectedReference(data.getStoredPaymentMethodId()); | ||||||
| modelService.save(paymentInfo); | ||||||
| } else { | ||||||
| throw new NotImplementedException("TokenizationWebhookEventListener not implemented for type " + data.getEventType()); | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throwing NotImplementedException inside an Event Listener is risky. If the event system retries.
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| } | ||||||
|
|
||||||
| protected void crosscheckWithOrder(final AbstractOrderModel order, final TokenWebhookRequestData tokenWebhookRequestData) { | ||||||
| boolean validationResult = true; | ||||||
| validationResult &= ((CustomerModel) order.getPaymentInfo().getUser()).getCustomerID().equals(tokenWebhookRequestData.getShopperReference()); | ||||||
|
|
||||||
| validationResult &= order.getStore().getAdyenMerchantAccount().equals(tokenWebhookRequestData.getMerchantAccount()); | ||||||
|
|
||||||
| validationResult &= (order.getStore().getAdyenTestMode() && "test".equals(tokenWebhookRequestData.getEnvironment())) || | ||||||
| (!order.getStore().getAdyenTestMode() && "live".equals(tokenWebhookRequestData.getEnvironment())); | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The strings "test" and "live" are hardcoded in the cross-check logic. |
||||||
|
|
||||||
| if (!validationResult) { | ||||||
| throw new IllegalArgumentException("Token webhook request is not valid. EventId (pspReference): " + tokenWebhookRequestData.getEventId() + | ||||||
| " type: " + tokenWebhookRequestData.getEventType() + " shopperReference: " + tokenWebhookRequestData.getShopperReference() + | ||||||
| " createdAt: " + tokenWebhookRequestData.getCreatedAt()); | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
|
|
||||||
|
|
||||||
| public void setPaymentTransactionRepository(PaymentTransactionRepository paymentTransactionRepository) { | ||||||
| this.paymentTransactionRepository = paymentTransactionRepository; | ||||||
| } | ||||||
|
|
||||||
| public void setModelService(ModelService modelService) { | ||||||
| this.modelService = modelService; | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CardSubscriptionHandler.java, IdealSubscriptionHandler.java, KlarnaSubscriptionHandler.java, PayPalSubscriptionHandler.java
These four files share almost identical logic in updatePaymentRequest. Did you consider a generalisation of this code ? For example: