Skip to content

Commit

Permalink
Merge pull request #464 from Adyen/feature/AD-301
Browse files Browse the repository at this point in the history
AD-301: Add support for Enhanced Scheme Data (L2/L3 data)
  • Loading branch information
kpieloch authored Oct 23, 2024
2 parents 4fe5ae6 + f971936 commit f36a06f
Show file tree
Hide file tree
Showing 19 changed files with 257 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public void testAuthorizeCardPayment() throws Exception {
paymentsResponse.setResultCode(PaymentResponse.ResultCodeEnum.AUTHORISED);
when(checkoutCustomerStrategyMock.isAnonymousCheckout()).thenReturn(true);
when(checkoutCustomerStrategyMock.getCurrentUserForCheckout()).thenReturn(null);
when(adyenPaymentServiceMock.authorisePayment(any(CartData.class), any(RequestInfo.class), any(CustomerModel.class))).thenReturn(paymentsResponse);
when(adyenPaymentServiceMock.processPaymentRequest(any(CartData.class),null, any(RequestInfo.class), any(CustomerModel.class))).thenReturn(paymentsResponse);
when(orderRepositoryMock.getOrderModel(CODE)).thenReturn(orderModelMock);
when(cartDataMock.getAdyenPaymentMethod()).thenReturn(PAYMENT_METHOD_CC);
when(requestMock.getHeader(USER_AGENT_HEADER)).thenReturn("userAgent");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<editorArea:section name="hmc.adyen.advanced">
<editorArea:attribute qualifier="adyenBoleto"/>
<editorArea:attribute qualifier="adyenPaypalMerchantId"/>
<editorArea:attribute qualifier="l2L3ESDEnabled"/>
</editorArea:section>
</editorArea:tab>
</editorArea:editorArea>
Expand Down
5 changes: 5 additions & 0 deletions adyenv6core/resources/adyenv6core-beans.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@

<bean class="de.hybris.platform.commercefacades.order.data.OrderEntryData">
<property name="taxValues" type="java.util.Collection&lt;de.hybris.platform.util.TaxValue>"/>
<property name="unitOfMeasure" type="String" />
</bean>

<bean class="de.hybris.platform.commercefacades.product.data.ProductData">
<property name="commodityCode" type="String"/>
</bean>

<bean class="de.hybris.platform.commercewebservicescommons.dto.order.PaymentDetailsWsDTO">
Expand Down
13 changes: 13 additions & 0 deletions adyenv6core/resources/adyenv6core-items.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@
</attributes>
</itemtype>

<itemtype code="Product" autocreate="false" generate="false">
<attributes>
<attribute qualifier="CommodityCode" type="java.lang.String" >
<persistence type="property"/>
</attribute>
</attributes>
</itemtype>

<itemtype code="NotificationItem" generate="true" autocreate="true">
<deployment table="AdyenNotificationItem" typecode="22936"/>
<attributes>
Expand Down Expand Up @@ -332,6 +340,11 @@
<description>Merchant config list</description>
<persistence type="property"/>
</attribute>
<attribute qualifier="l2L3ESDEnabled" type="java.lang.boolean">
<persistence type="property"/>
<defaultvalue>Boolean.FALSE</defaultvalue>
<modifiers optional="false"/>
</attribute>
</attributes>
</itemtype>

Expand Down
14 changes: 14 additions & 0 deletions adyenv6core/resources/adyenv6core-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@
</property>
</bean>

<alias name="defaultAdyenRequestService" alias="adyenRequestService" />
<bean id="defaultAdyenRequestService" class="com.adyen.commerce.services.impl.DefaultAdyenRequestService">
<constructor-arg name="baseStoreService" ref="baseStoreService"/>
<constructor-arg name="cartService" ref="cartService"/>
<constructor-arg name="configurationService" ref="configurationService"/>
</bean>

<bean id="adyenRequestFactory" class="com.adyen.v6.factory.AdyenRequestFactory">
<constructor-arg name="configurationService" ref="configurationService" />
</bean>
Expand Down Expand Up @@ -275,6 +282,11 @@
<property name="add" ref="adyenCartPopulator"/>
</bean>

<bean parent="modifyPopulatorList">
<property name="list" ref="productConverter"/>
<property name="add" ref="adyenProductPopulator"/>
</bean>

<bean parent="modifyPopulatorList">
<property name="list" ref="extendedCartConverter"/>
<property name="add" ref="adyenCartPopulator"/>
Expand Down Expand Up @@ -302,6 +314,8 @@
</bean>

<bean name="adyenCartPopulator" class="com.adyen.v6.populator.CartPopulator" />

<bean name="adyenProductPopulator" class="com.adyen.v6.populator.ProductPopulator" />
<!-- To obtain the oder number before the order is created -->
<alias alias="createOrderFromCartStrategy" name="adyenCreateOrderFromCartStrategy"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type.basestore.amazonpayRegion.name=AmazonPay Region
type.basestore.amazonpayRegion.description=The region of the Amazon Seller shop
type.basestore.adyenMerchantConfig.name=Adyen Merchant Config
type.basestore.adyenMerchantConfig.description=List of merchants that can be used for payments
type.basestore.l2L3ESDEnabled.name=L2/L3 Enhanced Data Support
type.BaseStore.l2L3ESDEnabled.description=Enable or disable L2/L3 EDS for US MasterCard and Visa transactions (only for US merchants)

type.paymentinfo.adyenPaymentMethod.name=Payment Method
type.paymentinfo.adyenIssuerId.name=Issuer ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public OrderData placeOrderWithPayment(final HttpServletRequest request, final C
RequestInfo requestInfo = new RequestInfo(request);
requestInfo.setShopperLocale(getShopperLocale());

PaymentResponse paymentResponse = getAdyenPaymentService().componentPayment(cartData, paymentRequest, requestInfo, getCheckoutCustomerStrategy().getCurrentUserForCheckout());
PaymentResponse paymentResponse = getAdyenPaymentService().processPaymentRequest(cartData, paymentRequest, requestInfo, getCheckoutCustomerStrategy().getCurrentUserForCheckout());
if (PaymentResponse.ResultCodeEnum.PENDING == paymentResponse.getResultCode()
|| PaymentResponse.ResultCodeEnum.REDIRECTSHOPPER == paymentResponse.getResultCode()
|| PaymentResponse.ResultCodeEnum.CHALLENGESHOPPER == paymentResponse.getResultCode()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.adyen.commerce.services;

import com.adyen.model.checkout.PaymentRequest;
import de.hybris.platform.commercefacades.order.data.CartData;

import java.util.Map;

public interface AdyenRequestService {

String TOTAL_TAX_AMOUNT = "enhancedSchemeData.totalTaxAmount";
String CUSTOMER_REFERENCE = "enhancedSchemeData.customerReference";
String FREIGHT_AMOUNT = "enhancedSchemeData.freightAmount";
String SHIP_FROM_POSTAL_CODE = "enhancedSchemeData.shipFromPostalCode";
String ORDER_DATE = "enhancedSchemeData.orderDate";
String DESTINATION_POSTAL_CODE = "enhancedSchemeData.destinationPostalCode";
String DESTINATION_STATE_PROVINCE_CODE = "enhancedSchemeData.destinationStateProvinceCode";
String DESTINATION_COUNTRY_CODE = "enhancedSchemeData.destinationCountryCode";
String DUTY_AMOUNT = "enhancedSchemeData.dutyAmount";

String ITEM_DETAIL_DESCRIPTION = "enhancedSchemeData.itemDetailLine%d.description";
String ITEM_DETAIL_PRODUCT_CODE = "enhancedSchemeData.itemDetailLine%d.productCode";
String ITEM_DETAIL_COMMODITY_CODE = "enhancedSchemeData.itemDetailLine%d.commodityCode";
String ITEM_DETAIL_QUANTITY = "enhancedSchemeData.itemDetailLine%d.quantity";
String ITEM_DETAIL_UNIT_OF_MEASURE = "enhancedSchemeData.itemDetailLine%d.unitOfMeasure";
String ITEM_DETAIL_UNIT_PRICE = "enhancedSchemeData.itemDetailLine%d.unitPrice";
String ITEM_DETAIL_DISCOUNT_AMOUNT = "enhancedSchemeData.itemDetailLine%d.discountAmount";
String ITEM_DETAIL_TOTAL_AMOUNT = "enhancedSchemeData.itemDetailLine%d.totalAmount";


void populateL2L3AdditionalData(final Map<String, String> additionalData, final CartData cartData);


void applyAdditionalData(CartData cartData, PaymentRequest paymentsRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.adyen.commerce.services.impl;

import com.adyen.commerce.services.AdyenRequestService;
import com.adyen.model.checkout.CardDetails;
import com.adyen.model.checkout.PaymentRequest;
import de.hybris.platform.commercefacades.order.data.CartData;
import de.hybris.platform.commercefacades.order.data.OrderEntryData;
import de.hybris.platform.core.model.order.CartModel;
import de.hybris.platform.order.CartService;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.store.services.BaseStoreService;
import org.apache.commons.lang3.StringUtils;
import org.spockframework.util.CollectionUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;

public class DefaultAdyenRequestService implements AdyenRequestService {


private BaseStoreService baseStoreService;
private CartService cartService;
private ConfigurationService configurationService;

private static final String L2L3_EDS_SUPPORTED_BRANDS = "adyen.l2l3eds.supported.brands";
private static final String L2L3_EDS_SUPPORTED_COUNTRIES = "adyen.l2l3eds.supported.countries";

public DefaultAdyenRequestService(BaseStoreService baseStoreService, CartService cartService, ConfigurationService configurationService) {
this.baseStoreService = baseStoreService;
this.cartService = cartService;
this.configurationService = configurationService;
}

@Override
public void populateL2L3AdditionalData(final Map<String, String> additionalData, final CartData cartData) {
// required fields
if (cartData.getTotalTax() != null) {
additionalData.put(TOTAL_TAX_AMOUNT, String.valueOf(cartData.getTotalTax().getValue()));
}
if (StringUtils.isNotEmpty(cartData.getMerchantCustomerId())) {
additionalData.put(CUSTOMER_REFERENCE, cartData.getMerchantCustomerId());
}
// not required but available
if (cartData.getDeliveryCost() != null) {
additionalData.put(FREIGHT_AMOUNT, String.valueOf(cartData.getDeliveryCost().getValue()));
}
if (cartData.getDeliveryAddress() != null) {
if (cartData.getDeliveryAddress().getPostalCode() != null) {
additionalData.put(DESTINATION_POSTAL_CODE, cartData.getDeliveryAddress().getPostalCode());
}
if (cartData.getDeliveryAddress().getCountry() != null && cartData.getDeliveryAddress().getCountry().getIsocode() != null) {
additionalData.put(DESTINATION_COUNTRY_CODE, cartData.getDeliveryAddress().getCountry().getIsocode());
}
}

// Extract item details from cartData and populate additionalData using a stream
if (cartData.getEntries() != null) {
cartData.getEntries().forEach(
entry -> {
if (entry != null && entry.getProduct() != null) {
additionalData.put(String.format(ITEM_DETAIL_PRODUCT_CODE, entry.getEntryNumber()),
Optional.ofNullable(entry.getProduct().getCode()).orElse(StringUtils.EMPTY));
additionalData.put(String.format(ITEM_DETAIL_DESCRIPTION, entry.getEntryNumber()),
Optional.ofNullable(entry.getProduct().getName()).orElse(StringUtils.EMPTY));

additionalData.put(String.format(ITEM_DETAIL_QUANTITY, entry.getEntryNumber()), String.valueOf(entry.getQuantity()));

additionalData.put(String.format(ITEM_DETAIL_UNIT_OF_MEASURE, entry.getEntryNumber()),
Optional.ofNullable(entry.getUnitOfMeasure()).orElse(StringUtils.EMPTY));

additionalData.put(String.format(ITEM_DETAIL_COMMODITY_CODE, entry.getEntryNumber()),
Optional.ofNullable(entry.getProduct().getCommodityCode()).orElse(StringUtils.EMPTY));

if (entry.getTotalPrice() != null) {
additionalData.put(String.format(ITEM_DETAIL_TOTAL_AMOUNT, entry.getEntryNumber()),
String.valueOf(Optional.ofNullable(entry.getTotalPrice().getValue()).orElse(BigDecimal.ZERO)));
}

if (entry.getBasePrice() != null) {
additionalData.put(String.format(ITEM_DETAIL_UNIT_PRICE, entry.getEntryNumber()),
String.valueOf(Optional.ofNullable(entry.getBasePrice().getValue()).orElse(BigDecimal.ZERO)));
}
}
}
);
}
}

@Override
public void applyAdditionalData(CartData cartData, PaymentRequest paymentsRequest) {
Map<String, String> additionalData = new HashMap<>();
CartModel sessionCart = cartService.getSessionCart();
if(canL23EdsBeSent(paymentsRequest, sessionCart)) {
populateL2L3AdditionalData(additionalData, cartData);
}

paymentsRequest.setAdditionalData(additionalData);

}

protected boolean canL23EdsBeSent(PaymentRequest paymentsRequest, CartModel sessionCart) {
return Optional.ofNullable(baseStoreService.getCurrentBaseStore())
.map(store -> store.getL2L3ESDEnabled())
.orElse(false) &&
Optional.ofNullable(paymentsRequest.getPaymentMethod())
.map(method -> method.getActualInstance())
.filter(instance -> instance instanceof CardDetails)
.map(instance -> (CardDetails) instance)
.map(cardDetails -> getL2L3SupportedBrands().contains(cardDetails.getBrand()))
.orElse(false) &&
Optional.ofNullable(sessionCart.getDeliveryAddress())
.map(address -> address.getCountry())
.map(country -> getL2L3SupportedCountries().contains(country.getIsocode()))
.orElse(false);
}

protected List<String> getL2L3SupportedBrands() {
String property = configurationService.getConfiguration().getString(L2L3_EDS_SUPPORTED_BRANDS);
return property != null ? List.of(property.split(",")) : new ArrayList<>();
}

protected List<String> getL2L3SupportedCountries() {
String property = configurationService.getConfiguration().getString(L2L3_EDS_SUPPORTED_COUNTRIES);
return property != null ? List.of(property.split(",")) : new ArrayList<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ public OrderData authorisePayment(final HttpServletRequest request, final CartDa
RequestInfo requestInfo = new RequestInfo(request);
requestInfo.setShopperLocale(getShopperLocale());

PaymentResponse paymentResponse = getAdyenPaymentService().authorisePayment(cartData, requestInfo, customer);
PaymentResponse paymentResponse = getAdyenPaymentService().processPaymentRequest(cartData,null, requestInfo, customer);
PaymentResponse.ResultCodeEnum resultCode = paymentResponse.getResultCode();
PaymentResponseAction action = paymentResponse.getAction();

Expand Down Expand Up @@ -477,7 +477,7 @@ public PaymentResponse componentPayment(final HttpServletRequest request, final
RequestInfo requestInfo = new RequestInfo(request);
requestInfo.setShopperLocale(getShopperLocale());

PaymentResponse paymentResponse = getAdyenPaymentService().componentPayment(cartData, paymentRequest, requestInfo, getCheckoutCustomerStrategy().getCurrentUserForCheckout());
PaymentResponse paymentResponse = getAdyenPaymentService().processPaymentRequest(cartData, paymentRequest, requestInfo, getCheckoutCustomerStrategy().getCurrentUserForCheckout());
if (PaymentResponse.ResultCodeEnum.PENDING == paymentResponse.getResultCode() ||
PaymentResponse.ResultCodeEnum.REDIRECTSHOPPER == paymentResponse.getResultCode() ||
PaymentResponse.ResultCodeEnum.PRESENTTOSHOPPER == paymentResponse.getResultCode()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,38 @@
*/
package com.adyen.v6.factory;

import com.adyen.commerce.services.impl.DefaultAdyenRequestService;
import com.adyen.v6.service.AdyenCheckoutApiService;
import com.adyen.v6.service.AdyenModificationsApiService;
import com.adyen.v6.service.DefaultAdyenCheckoutApiService;
import com.adyen.v6.service.DefaultAdyenModificationsApiService;
import com.adyen.v6.strategy.AdyenMerchantAccountStrategy;
import de.hybris.platform.store.BaseStoreModel;

/**
* Factory class for AdyenPaymentService
*/

public class AdyenPaymentServiceFactory {

private final AdyenRequestFactory adyenRequestFactory;
protected final AdyenMerchantAccountStrategy adyenMerchantAccountStrategy;
private final DefaultAdyenRequestService defaultAdyenRequestService;


public AdyenPaymentServiceFactory(final AdyenRequestFactory adyenRequestFactory, final AdyenMerchantAccountStrategy adyenMerchantAccountStrategy) {
public AdyenPaymentServiceFactory(final AdyenRequestFactory adyenRequestFactory, final AdyenMerchantAccountStrategy adyenMerchantAccountStrategy, DefaultAdyenRequestService defaultAdyenRequestService) {
this.adyenRequestFactory = adyenRequestFactory;
this.adyenMerchantAccountStrategy = adyenMerchantAccountStrategy;
this.defaultAdyenRequestService = defaultAdyenRequestService;
}

public AdyenCheckoutApiService createAdyenCheckoutApiService(final BaseStoreModel baseStoreModel) {
String webMerchantAccount = adyenMerchantAccountStrategy.getWebMerchantAccount(baseStoreModel);
DefaultAdyenCheckoutApiService defaultAdyenCheckoutApiService = new DefaultAdyenCheckoutApiService(baseStoreModel, webMerchantAccount);
DefaultAdyenCheckoutApiService defaultAdyenCheckoutApiService = new DefaultAdyenCheckoutApiService(baseStoreModel, webMerchantAccount, defaultAdyenRequestService);
defaultAdyenCheckoutApiService.setAdyenRequestFactory(adyenRequestFactory);
return defaultAdyenCheckoutApiService;
}

public AdyenModificationsApiService createAdyenModificationsApiService(final BaseStoreModel baseStoreModel) {
String webMerchantAccount = adyenMerchantAccountStrategy.getWebMerchantAccount(baseStoreModel);
DefaultAdyenModificationsApiService adyenModificationsApiService = new DefaultAdyenModificationsApiService(baseStoreModel, webMerchantAccount);
DefaultAdyenModificationsApiService adyenModificationsApiService = new DefaultAdyenModificationsApiService(baseStoreModel, webMerchantAccount, defaultAdyenRequestService);
adyenModificationsApiService.setAdyenRequestFactory(adyenRequestFactory);
return adyenModificationsApiService;
}
Expand Down
12 changes: 11 additions & 1 deletion adyenv6core/src/com/adyen/v6/factory/AdyenRequestFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.util.TaxValue;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
Expand Down Expand Up @@ -78,6 +77,14 @@
import static com.adyen.v6.constants.Adyenv6coreConstants.PLUGIN_VERSION;
import static com.adyen.v6.constants.Adyenv6coreConstants.RATEPAY;

/**
* Factory class to create Adyen API requests, do not add new code to this class.
* This class is deprecated and will be removed in future versions.
* Please use the new {@link com.adyen.commerce.services.AdyenRequestService} instead.
* @deprecated since v13.1. Please use the new {@link com.adyen.commerce.services.AdyenRequestService} instead.
* @see com.adyen.commerce.services.AdyenRequestService
*/
@Deprecated(since = "v13.1", forRemoval = true)
public class AdyenRequestFactory {
private static final Logger LOG = Logger.getLogger(AdyenRequestFactory.class);

Expand Down Expand Up @@ -794,4 +801,7 @@ public ConfigurationService getConfigurationService() {
return configurationService;
}


}


1 change: 1 addition & 0 deletions adyenv6core/src/com/adyen/v6/populator/CartPopulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public void populate(final CartModel source, final CartData target) throws Conve
target.setAdyenGiftCardBrand(paymentInfo.getAdyenGiftCardBrand());
target.setAdyenAmazonPayConfiguration(source.getAdyenAmazonPayConfiguration());
}

}

protected boolean isAdyenPaymentInfo(final PaymentInfoModel paymentInfo) {
Expand Down
Loading

0 comments on commit f36a06f

Please sign in to comment.