Skip to content
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

AD-252 Override "Place an Order" Endpoint to Include PaymentRequest a… #420

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ private AdyencheckoutaddonapiWebConstants()
//empty to avoid instantiating this constant class
}

public static final String ADYEN_CHECKOUT_API_PREFIX = "/api/checkout";
public static final String ADYEN_CHECKOUT_PAGE_PREFIX = "/checkout/multi";
public static final String ADYEN_CHECKOUT_ORDER_CONFIRMATION = "/adyen/order-confirmation";
public static final String ADYEN_CHECKOUT_SELECT_PAYMENT = "/adyen/payment-method";
public static final String AUTHORISE_3D_SECURE_PAYMENT_URL = "/authorise-3d-adyen-response";

public static final String CART_PREFIX = "/cart";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,61 +1,30 @@
package com.adyen.commerce.controllers.api;

import com.adyen.commerce.controllerbase.PlaceOrderControllerBase;
import com.adyen.commerce.exception.AdyenControllerException;
import com.adyen.commerce.facades.AdyenCheckoutApiFacade;
import com.adyen.commerce.request.PlaceOrderRequest;
import com.adyen.commerce.response.PlaceOrderResponse;
import com.adyen.commerce.validators.PaymentRequestValidator;
import com.adyen.model.checkout.PaymentDetailsRequest;
import com.adyen.model.checkout.PaymentResponse;
import com.adyen.service.exception.ApiException;
import com.adyen.v6.exceptions.AdyenNonAuthorizedPaymentException;
import de.hybris.platform.acceleratorfacades.flow.CheckoutFlowFacade;
import de.hybris.platform.acceleratorservices.urlresolver.SiteBaseUrlResolutionService;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn;
import de.hybris.platform.basecommerce.model.site.BaseSiteModel;
import de.hybris.platform.commercefacades.order.CartFacade;
import de.hybris.platform.commercefacades.order.data.CartData;
import de.hybris.platform.commercefacades.order.data.OrderData;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.site.BaseSiteService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;

import static com.adyen.commerce.constants.AdyencheckoutaddonapiWebConstants.ADYEN_CHECKOUT_API_PREFIX;
import static com.adyen.commerce.constants.AdyencheckoutaddonapiWebConstants.AUTHORISE_3D_SECURE_PAYMENT_URL;
import static com.adyen.commerce.constants.AdyenwebcommonsConstants.CHECKOUT_ERROR_AUTHORIZATION_FAILED;
import static com.adyen.commerce.util.ErrorMessageUtil.getErrorMessageByRefusalReason;
import static com.adyen.commerce.util.FieldValidationUtil.getFieldCodesFromValidation;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.CHALLENGESHOPPER;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.IDENTIFYSHOPPER;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.PENDING;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.PRESENTTOSHOPPER;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.REDIRECTSHOPPER;
import static com.adyen.model.checkout.PaymentResponse.ResultCodeEnum.REFUSED;


@RequestMapping("/api/checkout")
@Controller
public class AdyenPlaceOrderController extends PlaceOrderControllerBase {
private static final Logger LOGGER = Logger.getLogger(AdyenPlaceOrderController.class);

private static final String CHECKOUT_ERROR_FORM_ENTRY_INVALID = "checkout.error.paymentethod.formentry.invalid";
public static final String GET_TYPE = "getType";


@Autowired
private CheckoutFlowFacade checkoutFlowFacade;
Expand All @@ -66,9 +35,6 @@ public class AdyenPlaceOrderController extends PlaceOrderControllerBase {
@Autowired
private AdyenCheckoutApiFacade adyenCheckoutApiFacade;

@Autowired
private ConfigurationService configurationService;

@Resource(name = "siteBaseUrlResolutionService")
private SiteBaseUrlResolutionService siteBaseUrlResolutionService;

Expand All @@ -77,163 +43,43 @@ public class AdyenPlaceOrderController extends PlaceOrderControllerBase {

@RequireHardLogIn
@PostMapping("/place-order")
public ResponseEntity<PlaceOrderResponse> placeOrder(@RequestBody PlaceOrderRequest placeOrderRequest, HttpServletRequest request) throws Exception {
public ResponseEntity<PlaceOrderResponse> onPlaceOrder(@RequestBody PlaceOrderRequest placeOrderRequest, HttpServletRequest request) throws Exception {
PlaceOrderResponse placeOrderResponse = super.placeOrder(placeOrderRequest, request);

String adyenPaymentMethodType = extractPaymentMethodType(placeOrderRequest);

preHandleAndValidateRequest(placeOrderRequest, adyenPaymentMethodType);

if (!isCartValid()) {
LOGGER.warn("Cart is invalid.");
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}

return handlePayment(request, placeOrderRequest, adyenPaymentMethodType);
return ResponseEntity.ok(placeOrderResponse);
}

@RequireHardLogIn
@PostMapping("/additional-details")
public ResponseEntity<PlaceOrderResponse> onAdditionalDetails(@RequestBody PaymentDetailsRequest detailsRequest, HttpServletRequest request) throws Exception {
try {
OrderData orderData = adyenCheckoutApiFacade.placeOrderWithAdditionalDetails(detailsRequest);
PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setOrderNumber(orderData.getCode());
return ResponseEntity.status(HttpStatus.OK).body(placeOrderResponse);
} catch (ApiException e) {
LOGGER.error("ApiException: " + e);
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
} catch (Exception e) {
LOGGER.error("Exception", e);
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}
}
PlaceOrderResponse placeOrderResponse = super.handleAdditionalDetails(detailsRequest);

private static String extractPaymentMethodType(PlaceOrderRequest placeOrderRequest) throws AdyenControllerException {
if (placeOrderRequest == null || placeOrderRequest.getPaymentRequest() == null || placeOrderRequest.getPaymentRequest().getPaymentMethod() == null) {
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}
Object actualInstance = placeOrderRequest.getPaymentRequest().getPaymentMethod().getActualInstance();
if (actualInstance == null) {
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}
Class<?> aClass = actualInstance.getClass();
try {
return aClass.getMethod(GET_TYPE).invoke(actualInstance).toString();
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}
}

private ResponseEntity<PlaceOrderResponse> handlePayment(HttpServletRequest request, PlaceOrderRequest placeOrderRequest, String adyenPaymentMethod) {
final CartData cartData = cartFacade.getSessionCart();

String errorMessage = CHECKOUT_ERROR_AUTHORIZATION_FAILED;

try {
cartData.setAdyenReturnUrl(getPaymentRedirectReturnUrl());
OrderData orderData = adyenCheckoutApiFacade.placeOrderWithPayment(request, cartData, placeOrderRequest.getPaymentRequest());

PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setOrderNumber(orderData.getCode());
return ResponseEntity.status(HttpStatus.OK).body(placeOrderResponse);

} catch (ApiException e) {
LOGGER.error("API exception: ", e);
} catch (AdyenNonAuthorizedPaymentException e) {
LOGGER.info("Handling AdyenNonAuthorizedPaymentException. Checking PaymentResponse.");
PaymentResponse paymentsResponse = e.getPaymentsResponse();
if (REDIRECTSHOPPER == paymentsResponse.getResultCode() || CHALLENGESHOPPER == paymentsResponse.getResultCode() ||
IDENTIFYSHOPPER == paymentsResponse.getResultCode() || PENDING == paymentsResponse.getResultCode() ||
PRESENTTOSHOPPER == paymentsResponse.getResultCode()) {
LOGGER.debug("PaymentResponse is " + paymentsResponse.getResultCode() + ", executing action for pspReference: " + paymentsResponse.getPspReference());
return executeAction(paymentsResponse);
} else if (REFUSED == paymentsResponse.getResultCode()) {
LOGGER.info("PaymentResponse is REFUSED, pspReference: " + paymentsResponse.getPspReference());
errorMessage = getErrorMessageByRefusalReason(paymentsResponse.getRefusalReason());
} else if (PaymentResponse.ResultCodeEnum.ERROR == paymentsResponse.getResultCode()) {
LOGGER.error("PaymentResponse is ERROR, reason: " + paymentsResponse.getRefusalReason() + " pspReference: " + paymentsResponse.getPspReference());
}
} catch (Exception e) {
LOGGER.error(ExceptionUtils.getStackTrace(e));
}

throw new AdyenControllerException(errorMessage);
return ResponseEntity.ok(placeOrderResponse);
}

private void preHandleAndValidateRequest(PlaceOrderRequest placeOrderRequest, String adyenPaymentMethod) {
final BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(placeOrderRequest, "placeOrderRequest");

boolean showRememberDetails = adyenCheckoutApiFacade.showRememberDetails();
boolean holderNameRequired = adyenCheckoutApiFacade.getHolderNameRequired();

PaymentRequestValidator paymentRequestValidator = new PaymentRequestValidator(adyenCheckoutApiFacade.getStoredCards(), showRememberDetails, holderNameRequired);
paymentRequestValidator.validate(placeOrderRequest, bindingResult);

if (bindingResult.hasErrors()) {
LOGGER.warn("Payment form is invalid.");
LOGGER.warn(bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getCode).reduce((x, y) -> (x + " " + y)));
throw new AdyenControllerException(CHECKOUT_ERROR_FORM_ENTRY_INVALID, getFieldCodesFromValidation(bindingResult));
}

adyenCheckoutApiFacade.preHandlePlaceOrder(placeOrderRequest.getPaymentRequest(),adyenPaymentMethod,
placeOrderRequest.getBillingAddress(), placeOrderRequest.isUseAdyenDeliveryAddress());
@Override
public AdyenCheckoutApiFacade getAdyenCheckoutApiFacade() {
return adyenCheckoutApiFacade;
}

private ResponseEntity<PlaceOrderResponse> executeAction(PaymentResponse paymentsResponse) {
PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setPaymentsResponse(paymentsResponse);
placeOrderResponse.setExecuteAction(true);
placeOrderResponse.setPaymentsAction(paymentsResponse.getAction());
return ResponseEntity.ok(placeOrderResponse);
@Override
public CheckoutFlowFacade getCheckoutFlowFacade() {
return checkoutFlowFacade;
}

private boolean isCartValid() {

if (checkoutFlowFacade.hasNoDeliveryAddress()) {
LOGGER.error("No delivery address.");
return false;
}

if (checkoutFlowFacade.hasNoDeliveryMode()) {
LOGGER.error("No delivery mode.");
return false;
}

if (checkoutFlowFacade.hasNoPaymentInfo()) {
LOGGER.error("No payment info.");
return false;
}

final CartData cartData = checkoutFlowFacade.getCheckoutCart();

if (!checkoutFlowFacade.containsTaxValues()) {
LOGGER.error(String.format("Cart %s does not have any tax values, which means the tax cacluation was not properly done, placement of order can't continue", cartData.getCode()));
LOGGER.error("Tax missing.");
return false;

}

if (!cartData.isCalculated()) {
LOGGER.error(String.format("Cart %s has a calculated flag of FALSE, placement of order can't continue", cartData.getCode()));
LOGGER.error("Cart not calculated.");
return false;

}

return true;
@Override
public CartFacade getCartFacade() {
return cartFacade;
}

private String getPaymentRedirectReturnUrl() {
String url = ADYEN_CHECKOUT_API_PREFIX + AUTHORISE_3D_SECURE_PAYMENT_URL;

BaseSiteModel currentBaseSite = baseSiteService.getCurrentBaseSite();

return siteBaseUrlResolutionService.getWebsiteUrlForSite(currentBaseSite, true, url);
@Override
public BaseSiteService getBaseSiteService() {
return baseSiteService;
}


@Override
public AdyenCheckoutApiFacade getAdyenCheckoutApiFacade() {
return adyenCheckoutApiFacade;
public SiteBaseUrlResolutionService getSiteBaseUrlResolutionService() {
return siteBaseUrlResolutionService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.Base64;

import static com.adyen.commerce.constants.AdyencheckoutaddonapiWebConstants.*;
import static com.adyen.commerce.constants.AdyenwebcommonsConstants.ADYEN_CHECKOUT_API_PREFIX;
import static com.adyen.commerce.constants.AdyenwebcommonsConstants.AUTHORISE_3D_SECURE_PAYMENT_URL;
import static de.hybris.platform.acceleratorstorefrontcommons.controllers.AbstractController.REDIRECT_PREFIX;

@Controller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
import com.adyen.commerce.constants.AdyenoccConstants;
import com.adyen.commerce.controllerbase.PlaceOrderControllerBase;
import com.adyen.commerce.facades.AdyenCheckoutApiFacade;
import com.adyen.commerce.request.PlaceOrderRequest;
import com.adyen.commerce.response.PlaceOrderResponse;
import com.adyen.model.checkout.PaymentDetailsRequest;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.hybris.platform.acceleratorfacades.flow.CheckoutFlowFacade;
import de.hybris.platform.acceleratorservices.urlresolver.SiteBaseUrlResolutionService;
import de.hybris.platform.commercefacades.order.CartFacade;
import de.hybris.platform.commerceservices.request.mapping.annotation.ApiVersion;
import de.hybris.platform.site.BaseSiteService;
import de.hybris.platform.webservicescommons.swagger.ApiBaseSiteIdUserIdAndCartIdParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -19,6 +24,9 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

@Controller
@RequestMapping(value = AdyenoccConstants.ADYEN_USER_CART_PREFIX)
@ApiVersion("v2")
Expand All @@ -28,9 +36,31 @@ public class PlaceOrderController extends PlaceOrderControllerBase {
@Autowired
private AdyenCheckoutApiFacade adyenCheckoutApiFacade;

@Autowired
private CheckoutFlowFacade checkoutFlowFacade;

@Autowired
private CartFacade cartFacade;

@Resource(name = "siteBaseUrlResolutionService")
private SiteBaseUrlResolutionService siteBaseUrlResolutionService;

@Resource(name = "baseSiteService")
private BaseSiteService baseSiteService;

@Secured({ "ROLE_CUSTOMERGROUP", "ROLE_CLIENT", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT" })
@PostMapping(value = "/additional-details", produces= MediaType.APPLICATION_JSON_VALUE)
@Secured({"ROLE_CUSTOMERGROUP", "ROLE_CLIENT", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT"})
@PostMapping(value = "/place-order", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(operationId = "placeOrder", summary = "Handle place order request", description =
"Places order based on request data")
@ApiBaseSiteIdUserIdAndCartIdParam
public ResponseEntity<PlaceOrderResponse> onPlaceOrder(@RequestBody PlaceOrderRequest placeOrderRequest, HttpServletRequest request) throws Exception {
PlaceOrderResponse placeOrderResponse = super.placeOrder(placeOrderRequest, request);

return ResponseEntity.ok(placeOrderResponse);
}

@Secured({"ROLE_CUSTOMERGROUP", "ROLE_CLIENT", "ROLE_CUSTOMERMANAGERGROUP", "ROLE_TRUSTED_CLIENT"})
@PostMapping(value = "/additional-details", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(operationId = "additionalDetails", summary = "Handle additional details action", description =
"Places pending order based on additional details request")
@ApiBaseSiteIdUserIdAndCartIdParam
Expand All @@ -45,4 +75,24 @@ public ResponseEntity<String> onAdditionalDetails(@RequestBody PaymentDetailsReq
public AdyenCheckoutApiFacade getAdyenCheckoutApiFacade() {
return adyenCheckoutApiFacade;
}

@Override
public CheckoutFlowFacade getCheckoutFlowFacade() {
return checkoutFlowFacade;
}

@Override
public CartFacade getCartFacade() {
return cartFacade;
}

@Override
public BaseSiteService getBaseSiteService() {
return baseSiteService;
}

@Override
public SiteBaseUrlResolutionService getSiteBaseUrlResolutionService() {
return siteBaseUrlResolutionService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public final class AdyenwebcommonsConstants extends GeneratedAdyenwebcommonsCons

public static final String CHECKOUT_ERROR_AUTHORIZATION_FAILED = "checkout.error.authorization.failed";

public static final String ADYEN_CHECKOUT_API_PREFIX = "/api/checkout";
public static final String AUTHORISE_3D_SECURE_PAYMENT_URL = "/authorise-3d-adyen-response";



private AdyenwebcommonsConstants()
{
Expand Down
Loading
Loading