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-219 Enhance Error Handling for Payment and Order Placement Steps #384

Merged
merged 6 commits into from
Apr 3, 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 @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about sample translation for DE?


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
Loading