Skip to content

Commit

Permalink
Merge pull request #384 from Adyen/feature/AD-219-1
Browse files Browse the repository at this point in the history
AD-219 Enhance Error Handling for Payment and Order Placement Steps
  • Loading branch information
Lukasz756 authored Apr 3, 2024
2 parents b98b70d + 360d1c4 commit 5ce94dc
Show file tree
Hide file tree
Showing 38 changed files with 495 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package com.adyen.commerce.constants;

/**
* Global class for all Adyenv6b2ccheckoutaddonv2 web constants. You can add global constants for your extension into this class.
* Global class for all adyencheckoutaddonapi web constants. You can add global constants for your extension into this class.
*/
public final class AdyencheckoutaddonapiWebConstants
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.adyen.commerce.controllers.api;

import com.adyen.commerce.exceptions.AdyenControllerException;

import com.adyen.commerce.response.ErrorResponse;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn;
import de.hybris.platform.acceleratorstorefrontcommons.forms.AddressForm;
import de.hybris.platform.acceleratorstorefrontcommons.forms.validation.AddressValidator;
Expand Down Expand Up @@ -55,11 +58,11 @@ public ResponseEntity<List<AddressData>> getAllDeliveryAddresses() {

@RequireHardLogIn
@PostMapping(value = "/delivery-address")
public ResponseEntity<AddressData> addDeliveryAddress(@RequestBody AddressForm addressForm) {
public ResponseEntity<Void> addDeliveryAddress(@RequestBody AddressForm addressForm) {
final Errors errors = new BeanPropertyBindingResult(addressForm, "address");
addressValidator.validate(addressForm, errors);
if (errors.hasErrors()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException("checkout.deliveryAddress.notSelected");
}

AddressData addressData = addressDataUtil.convertToAddressData(addressForm);
Expand All @@ -82,17 +85,17 @@ public ResponseEntity<AddressData> addDeliveryAddress(@RequestBody AddressForm a

return ResponseEntity.status(HttpStatus.CREATED).build();
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException();
}
}

@RequireHardLogIn
@PutMapping(value = "/delivery-address")
public ResponseEntity<AddressData> updateDeliveryAddress(@RequestBody AddressForm addressForm) {
public ResponseEntity<Void> updateDeliveryAddress(@RequestBody AddressForm addressForm) {
final Errors errors = new BeanPropertyBindingResult(addressForm, "address");
addressValidator.validate(addressForm, errors);
if (errors.hasErrors()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException("checkout.deliveryAddress.notSelected");
}

AddressData addressData = addressDataUtil.convertToAddressData(addressForm);
Expand All @@ -105,16 +108,16 @@ public ResponseEntity<AddressData> updateDeliveryAddress(@RequestBody AddressFor

return ResponseEntity.status(HttpStatus.OK).build();
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException();
}
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException();
}
}

@RequireHardLogIn
@DeleteMapping(value = "/delivery-address/{addressId}")
public ResponseEntity<HttpStatus> removeDeliveryAddress(@PathVariable String addressId) {
public ResponseEntity<Void> removeDeliveryAddress(@PathVariable String addressId) {
if (userFacade.getAddressBook().stream().anyMatch(ad -> Objects.equals(ad.getId(), addressId))) {
AddressData addressData = userFacade.getAddressForCode(addressId);

Expand All @@ -123,7 +126,7 @@ public ResponseEntity<HttpStatus> removeDeliveryAddress(@PathVariable String add

return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

@Controller
@RequestMapping(value = "/api/checkout")
public class TempCartController {
public class AdyenCartController {

@Autowired
private CartFacade cartFacade;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.adyen.commerce.controllers.api;


import com.adyen.commerce.exceptions.AdyenControllerException;
import com.adyen.commerce.response.ErrorResponse;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn;
import de.hybris.platform.commercefacades.order.CheckoutFacade;
import de.hybris.platform.commercefacades.user.UserFacade;
Expand Down Expand Up @@ -27,29 +30,24 @@ public class AdyenDeliveryAddressContoller {

@PostMapping(value = "/delivery-address")
@RequireHardLogIn
public ResponseEntity<HttpStatus> doSelectDeliveryAddress(@RequestBody final String selectedAddressCode)
{
if (StringUtils.isNotBlank(selectedAddressCode))
{
public ResponseEntity<Void> doSelectDeliveryAddress(@RequestBody final String selectedAddressCode) {
if (StringUtils.isNotBlank(selectedAddressCode)) {
final AddressData selectedAddressData = getCheckoutFacade().getDeliveryAddressForCode(selectedAddressCode);
final boolean hasSelectedAddressData = selectedAddressData != null;
if (hasSelectedAddressData)
{
if (hasSelectedAddressData) {
setDeliveryAddress(selectedAddressData);
return ResponseEntity.status(HttpStatus.ACCEPTED).build();
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException("checkout.deliveryAddress.notSelected");
}

@GetMapping(value = "/delivery-address")
@RequireHardLogIn
public ResponseEntity<AddressData> getSelectedDeliveryAddress()
{
public ResponseEntity<AddressData> getSelectedDeliveryAddress() {
final AddressData selectedAddressData = getCheckoutFacade().getCheckoutCart().getDeliveryAddress();
if (selectedAddressData != null)
{
if (selectedAddressData != null) {
return ResponseEntity.status(HttpStatus.OK).body(selectedAddressData);
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
Expand All @@ -65,8 +63,7 @@ protected void setDeliveryAddress(final AddressData selectedAddressData) {
}
}

protected boolean isAddressIdChanged(final AddressData cartCheckoutDeliveryAddress, final AddressData selectedAddressData)
{
protected boolean isAddressIdChanged(final AddressData cartCheckoutDeliveryAddress, final AddressData selectedAddressData) {
return cartCheckoutDeliveryAddress == null || !selectedAddressData.getId().equals(cartCheckoutDeliveryAddress.getId());
}

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

import com.adyen.commerce.exceptions.AdyenControllerException;
import com.adyen.commerce.response.ErrorResponse;
import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn;
import de.hybris.platform.commercefacades.order.CheckoutFacade;
import de.hybris.platform.commercefacades.order.data.CartData;
import de.hybris.platform.commercefacades.order.data.DeliveryModeData;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
Expand All @@ -15,6 +15,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;


@Controller
@RequestMapping(value = "/api/checkout")
Expand All @@ -31,12 +33,12 @@ public ResponseEntity<List<? extends DeliveryModeData>> getAllDeliveryMethods()

@RequireHardLogIn
@PostMapping(value = "/select-delivery-method")
public ResponseEntity<CartData> selectDeliveryMethod(@RequestBody String deliveryMethodCode) {
public ResponseEntity selectDeliveryMethod(@RequestBody String deliveryMethodCode) {
if (StringUtils.isNotEmpty(deliveryMethodCode)) {
checkoutFacade.setDeliveryMode(deliveryMethodCode);
return ResponseEntity.ok(checkoutFacade.getCheckoutCart());
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
throw new AdyenControllerException("checkout.deliveryMethod.notSelected");
}
}

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

import com.adyen.commerce.exceptions.AdyenControllerException;
import com.adyen.commerce.response.PlaceOrderResponse;
import com.adyen.constants.ApiConstants;
import com.adyen.model.checkout.PaymentResponse;
Expand Down Expand Up @@ -82,17 +83,13 @@ public ResponseEntity<PlaceOrderResponse> placeOrder(@RequestBody AdyenPaymentFo
final boolean selectPaymentMethodSuccess = selectPaymentMethod(adyenPaymentForm);

if (!selectPaymentMethodSuccess) {
PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setError(CHECKOUT_ERROR_FORM_ENTRY_INVALID);
LOGGER.warn("Payment form is invalid.");
return ResponseEntity.badRequest().body(placeOrderResponse);
throw new AdyenControllerException(CHECKOUT_ERROR_FORM_ENTRY_INVALID);
}

if (!isCartValid()) {
LOGGER.warn("Cart is invalid.");
PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setError(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
return ResponseEntity.badRequest().body(placeOrderResponse);
throw new AdyenControllerException(CHECKOUT_ERROR_AUTHORIZATION_FAILED);
}

final CartData cartData = cartFacade.getSessionCart();
Expand Down Expand Up @@ -140,9 +137,7 @@ private ResponseEntity<PlaceOrderResponse> handleRatepay(HttpServletRequest requ
LOGGER.error(ExceptionUtils.getStackTrace(e));
}

PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setError(errorMessage);
return ResponseEntity.badRequest().body(placeOrderResponse);
throw new AdyenControllerException(errorMessage);
}

private ResponseEntity<PlaceOrderResponse> handlePOS(HttpServletRequest request, CartData cartData) {
Expand Down Expand Up @@ -190,9 +185,7 @@ private ResponseEntity<PlaceOrderResponse> handlePOS(HttpServletRequest request,
LOGGER.error("Exception", e);
}

PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setError(errorMessage);
return ResponseEntity.badRequest().body(placeOrderResponse);
throw new AdyenControllerException(errorMessage);
}

private ResponseEntity<PlaceOrderResponse> handleOther(HttpServletRequest request, CartData cartData, String adyenPaymentMethod) {
Expand Down Expand Up @@ -236,9 +229,7 @@ private ResponseEntity<PlaceOrderResponse> handleOther(HttpServletRequest reques
LOGGER.error(ExceptionUtils.getStackTrace(e));
}

PlaceOrderResponse placeOrderResponse = new PlaceOrderResponse();
placeOrderResponse.setError(errorMessage);
return ResponseEntity.badRequest().body(placeOrderResponse);
throw new AdyenControllerException(errorMessage);
}

private boolean selectPaymentMethod(AdyenPaymentForm adyenPaymentForm) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.adyen.commerce.controllers.exceptionhandlers;

import com.adyen.commerce.exceptions.AdyenControllerException;
import com.adyen.commerce.response.ErrorResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class AdyenControllerExceptionHandler {

@ExceptionHandler(value = AdyenControllerException.class)
public ResponseEntity<ErrorResponse> handleAdyenControllerException(AdyenControllerException exception) {
return ResponseEntity.badRequest().body(exception.getErrorResponse());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.adyen.commerce.exceptions;

import com.adyen.commerce.response.ErrorResponse;

import java.util.ArrayList;
import java.util.List;

public class AdyenControllerException extends RuntimeException {

private final ErrorResponse errorResponse;

public AdyenControllerException(String errorCode, List<String> invalidFields) {
errorResponse = new ErrorResponse(errorCode, invalidFields);
}

public AdyenControllerException() {
errorResponse = new ErrorResponse("checkout.error.default", new ArrayList<>());
}

public AdyenControllerException(String errorCode) {
errorResponse = new ErrorResponse(errorCode);
}

public ErrorResponse getErrorResponse() {
return errorResponse;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.adyen.commerce.response;

import java.util.ArrayList;
import java.util.List;

public class ErrorResponse {
private String errorCode;
private List<String> invalidFields;

public ErrorResponse(String errorCode, List<String> invalidFields) {
this.errorCode = errorCode;
this.invalidFields = invalidFields;
}

public ErrorResponse() {
this.errorCode = "checkout.error.default";
this.invalidFields = new ArrayList<>();
}

public ErrorResponse(String errorCode) {
this.errorCode = errorCode;
this.invalidFields = new ArrayList<>();
}

public String getErrorCode() {
return errorCode;
}

public List<String> getInvalidFields() {
return invalidFields;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

public class PlaceOrderResponse {
private String orderNumber;
private String error;
private boolean isRedirectTo3DS;

public String getOrderNumber() {
Expand All @@ -14,14 +13,6 @@ public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}

public String getError() {
return error;
}

public void setError(String error) {
this.error = error;
}

public boolean isRedirectTo3DS() {
return isRedirectTo3DS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ checkout.error.authorization.payment.success=Bezahlung erfolgreich
checkout.error.authorization.payment.failed=Bezahlung fehlgeschlagen
checkout.error.authorization.payment.rejected=Ihre Zahlung wurde abgelehnt
checkout.error.authorization.payment.timeout=Die Zahlungsabwicklung dauerte länger als erwartet. Bitte versuchen Sie es in ein paar Minuten noch einmal.
checkout.error.authorization.payment.pending=Ihre Zahlung wird bearbeitet
checkout.error.authorization.payment.pending=Ihre Zahlung wird bearbeitet

checkout.error.default=Es tut uns leid, es ist ein Problem aufgetreten. Bitte versuchen Sie es später noch einmal
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ checkout.error.authorization.payment.failed=Payment failed
checkout.error.authorization.payment.rejected=Your payment was rejected
checkout.error.authorization.payment.timeout=Payment processing took longer than expected. Please check again in few minutes.
checkout.error.authorization.payment.pending=Your payment is being processed

checkout.error.default=Sorry, there was a problem, please try again later

Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@
margin-top: 30px;
padding: 15px;
border-radius: 12px;
}

.alert {
scroll-margin-top: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {AxiosError} from "axios";
import {ErrorResponse} from "../../types/errorResponse";
import {isEmpty} from "../../util/stringUtil";
import {store} from "../../store/store";
import {createDefaultResponseData, createError} from "../../util/notificationUtil";

export class ErrorHandler {

public static handleError(error?: AxiosError<ErrorResponse>) {
let errorResponseData
if (!error) {
errorResponseData = createDefaultResponseData()
} else {
errorResponseData = error.response.data;

if (!errorResponseData || isEmpty(errorResponseData.errorCode)) {
errorResponseData = createDefaultResponseData()
}
}
store.dispatch({type: "notifications/addNotification", payload: createError(errorResponseData)})
}
}
Loading

0 comments on commit 5ce94dc

Please sign in to comment.