Java tutorial
/* * [y] hybris Platform * * Copyright (c) 2000-2013 hybris AG * All rights reserved. * * This software is the confidential and proprietary information of hybris * ("Confidential Information"). You shall not disclose such Confidential * Information and shall use it only in accordance with the terms of the * license agreement you entered into with hybris. * * */ package com.pedra.storefront.controllers.pages.checkout; import de.hybris.platform.acceleratorfacades.payment.PaymentFacade; import de.hybris.platform.acceleratorfacades.payment.data.PaymentSubscriptionResultData; import de.hybris.platform.acceleratorservices.customer.CustomerLocationService; import de.hybris.platform.acceleratorservices.payment.constants.PaymentConstants; import de.hybris.platform.acceleratorservices.payment.data.PaymentData; import de.hybris.platform.acceleratorservices.payment.data.PaymentErrorField; import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException; import de.hybris.platform.cms2.model.pages.ContentPageModel; import de.hybris.platform.commercefacades.address.data.AddressVerificationResult; import de.hybris.platform.commercefacades.order.CartFacade; import de.hybris.platform.commercefacades.order.data.CCPaymentInfoData; import de.hybris.platform.commercefacades.order.data.CardTypeData; import de.hybris.platform.commercefacades.order.data.CartData; import de.hybris.platform.commercefacades.order.data.OrderData; import de.hybris.platform.commercefacades.order.data.OrderEntryData; import de.hybris.platform.commercefacades.product.ProductFacade; import de.hybris.platform.commercefacades.product.ProductOption; import de.hybris.platform.commercefacades.product.data.ProductData; import de.hybris.platform.commercefacades.user.data.AddressData; import de.hybris.platform.commercefacades.user.data.CountryData; import de.hybris.platform.commercefacades.user.data.RegionData; import de.hybris.platform.commercefacades.user.data.TitleData; import de.hybris.platform.commerceservices.address.AddressVerificationDecision; import de.hybris.platform.commerceservices.order.CommerceCartModificationException; import de.hybris.platform.order.InvalidCartException; import de.hybris.platform.util.Config; import com.pedra.core.enums.CheckoutPciOptionEnum; import com.pedra.storefront.annotations.RequireHardLogIn; import com.pedra.storefront.breadcrumb.ResourceBreadcrumbBuilder; import com.pedra.storefront.breadcrumb.impl.ContentPageBreadcrumbBuilder; import com.pedra.storefront.constants.WebConstants; import com.pedra.storefront.controllers.ControllerConstants; import com.pedra.storefront.controllers.util.GlobalMessages; import com.pedra.storefront.forms.AddressForm; import com.pedra.storefront.forms.PaymentDetailsForm; import com.pedra.storefront.forms.PlaceOrderForm; import com.pedra.storefront.forms.SopPaymentDetailsForm; import com.pedra.storefront.forms.address.verification.AddressVerificationResultHandler; import com.pedra.storefront.forms.validation.AddressValidator; import com.pedra.storefront.forms.validation.PaymentDetailsValidator; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Enumeration; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; /** * MultiStepCheckoutController */ @Controller @RequestMapping(value = "/checkout/multi") public class MultiStepCheckoutController extends AbstractCheckoutController { private static final Logger LOG = Logger.getLogger(MultiStepCheckoutController.class); private static final String MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL = "multiStepCheckoutSummary"; private static final String REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS = REDIRECT_PREFIX + "/checkout/multi/choose-delivery-address"; private static final String REDIRECT_URL_ADD_DELIVERY_ADDRESS = REDIRECT_PREFIX + "/checkout/multi/add-delivery-address"; private static final String REDIRECT_URL_CHOOSE_DELIVERY_METHOD = REDIRECT_PREFIX + "/checkout/multi/choose-delivery-method"; private static final String REDIRECT_URL_ADD_PAYMENT_METHOD = REDIRECT_PREFIX + "/checkout/multi/add-payment-method"; private static final String REDIRECT_URL_CHOOSE_DELIVERY_LOCATION = REDIRECT_PREFIX + "/checkout/multi/choose-delivery-location"; private static final String REDIRECT_URL_SUMMARY = REDIRECT_PREFIX + "/checkout/multi/summary"; private static final String REDIRECT_URL_CART = REDIRECT_PREFIX + "/cart"; private static final String REDIRECT_URL_ERROR = REDIRECT_PREFIX + "/checkout/multi/hop-error"; private static final String REDIRECT_URL_ORDER_CONFIRMATION = REDIRECT_PREFIX + "/checkout/orderConfirmation/"; private static final Map<String, String> cybersourceSopCardTypes = new HashMap<String, String>(); static { // Map hybris card type to Cybersource SOP credit card cybersourceSopCardTypes.put("visa", "001"); cybersourceSopCardTypes.put("master", "002"); cybersourceSopCardTypes.put("amex", "003"); cybersourceSopCardTypes.put("diners", "005"); cybersourceSopCardTypes.put("maestro", "024"); } @Resource(name = "paymentDetailsValidator") private PaymentDetailsValidator paymentDetailsValidator; @Resource(name = "accProductFacade") private ProductFacade productFacade; @Resource(name = "multiStepCheckoutBreadcrumbBuilder") private ResourceBreadcrumbBuilder resourceBreadcrumbBuilder; @Resource(name = "paymentFacade") private PaymentFacade paymentFacade; @Resource(name = "addressValidator") private AddressValidator addressValidator; @Resource(name = "customerLocationService") private CustomerLocationService customerLocationService; @Resource(name = "cartFacade") private CartFacade cartFacade; @Resource(name = "addressVerificationResultHandler") private AddressVerificationResultHandler addressVerificationResultHandler; @Resource(name = "contentPageBreadcrumbBuilder") private ContentPageBreadcrumbBuilder contentPageBreadcrumbBuilder; protected CartFacade getCartFacade() { return cartFacade; } protected ProductFacade getProductFacade() { return productFacade; } protected PaymentDetailsValidator getPaymentDetailsValidator() { return paymentDetailsValidator; } protected ResourceBreadcrumbBuilder getResourceBreadcrumbBuilder() { return resourceBreadcrumbBuilder; } protected PaymentFacade getPaymentFacade() { return paymentFacade; } protected AddressValidator getAddressValidator() { return addressValidator; } protected CustomerLocationService getCustomerLocationService() { return customerLocationService; } protected AddressVerificationResultHandler getAddressVerificationResultHandler() { return addressVerificationResultHandler; } @ModelAttribute("titles") public Collection<TitleData> getTitles() { return getUserFacade().getTitles(); } @ModelAttribute("countries") public Collection<CountryData> getCountries() { return getCheckoutFacade().getDeliveryCountries(); } @ModelAttribute("countryDataMap") public Map<String, CountryData> getCountryDataMap() { final Map<String, CountryData> countryDataMap = new HashMap<String, CountryData>(); for (final CountryData countryData : getCountries()) { countryDataMap.put(countryData.getIsocode(), countryData); } return countryDataMap; } @ModelAttribute("billingCountries") public Collection<CountryData> getBillingCountries() { return getCheckoutFacade().getBillingCountries(); } @ModelAttribute("cardTypes") public Collection<CardTypeData> getCardTypes() { return getCheckoutFacade().getSupportedCardTypes(); } @ModelAttribute("months") public List<SelectOption> getMonths() { final List<SelectOption> months = new ArrayList<SelectOption>(); months.add(new SelectOption("1", "01")); months.add(new SelectOption("2", "02")); months.add(new SelectOption("3", "03")); months.add(new SelectOption("4", "04")); months.add(new SelectOption("5", "05")); months.add(new SelectOption("6", "06")); months.add(new SelectOption("7", "07")); months.add(new SelectOption("8", "08")); months.add(new SelectOption("9", "09")); months.add(new SelectOption("10", "10")); months.add(new SelectOption("11", "11")); months.add(new SelectOption("12", "12")); return months; } @ModelAttribute("startYears") public List<SelectOption> getStartYears() { final List<SelectOption> startYears = new ArrayList<SelectOption>(); final Calendar calender = new GregorianCalendar(); for (int i = calender.get(Calendar.YEAR); i > (calender.get(Calendar.YEAR) - 6); i--) { startYears.add(new SelectOption(String.valueOf(i), String.valueOf(i))); } return startYears; } @ModelAttribute("expiryYears") public List<SelectOption> getExpiryYears() { final List<SelectOption> expiryYears = new ArrayList<SelectOption>(); final Calendar calender = new GregorianCalendar(); for (int i = calender.get(Calendar.YEAR); i < (calender.get(Calendar.YEAR) + 11); i++) { expiryYears.add(new SelectOption(String.valueOf(i), String.valueOf(i))); } return expiryYears; } @ModelAttribute("checkoutSteps") public List<CheckoutSteps> addCheckoutStepsToModel(final HttpServletRequest request) { final List<CheckoutSteps> checkoutSteps = new ArrayList<CheckoutSteps>(); checkoutSteps.add(new CheckoutSteps("deliveryAddress", "/checkout/multi/choose-delivery-address")); checkoutSteps.add(new CheckoutSteps("deliveryMethod", "/checkout/multi/choose-delivery-method")); checkoutSteps.add(new CheckoutSteps("paymentMethod", "/checkout/multi/choose-payment-method")); checkoutSteps.add(new CheckoutSteps("confirmOrder", "/checkout/multi/summary")); return checkoutSteps; } @RequestMapping(method = RequestMethod.GET) public String gotoFirstStep() { if (hasValidCart()) { if (hasShippingItems()) { // user has already gone through the checkout steps if (!hasNoDeliveryAddress() && !hasNoDeliveryMode() && !hasNoPaymentInfo()) { return REDIRECT_URL_SUMMARY; } else { return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } } else { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } } LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String doLogin(final Model model) { return ControllerConstants.Views.Pages.MultiStepCheckout.CheckoutLoginPage; // TODO: Add login page view with 3 login options } /** * This is the entry point (first page) for the the multi-step checkout process. The page returned by this call will * show a list of customer addresses. If there is a default address, this will be selected in the view. If there are * no address then we redirect to the create new delivery address page. * * @param model * - the model for the view. * @return - the deliver address step page. * @throws CMSItemNotFoundException * - when a CMS page is not found */ @RequestMapping(value = "/choose-delivery-address", method = RequestMethod.GET) @RequireHardLogIn public String doChooseDeliveryAddress(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (!hasShippingItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } if (hasNoDeliveryAddress() && getUserFacade().isAddressBookEmpty()) { return REDIRECT_URL_ADD_DELIVERY_ADDRESS; } // set the default delivery address and calculate getCheckoutFacade().setDeliveryAddressIfAvailable(); final CartData cartData = getCheckoutFacade().getCheckoutCart(); model.addAttribute("cartData", cartData); model.addAttribute("deliveryAddresses", getDeliveryAddresses(cartData.getDeliveryAddress())); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryAddress.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.ChooseDeliveryAddressPage; } /** * This method gets called when the "Use this Address" button is clicked. It sets the selected delivery address on * the checkout facade - if it has changed, and reloads the page highlighting the selected delivery address. * * @param selectedAddressCode * - the id of the delivery address. * @return - a URL to the page to load. */ @RequestMapping(value = "/select-delivery-address", method = RequestMethod.GET) @RequireHardLogIn public String doSelectDeliveryAddress(@RequestParam("selectedAddressCode") final String selectedAddressCode) { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (!hasShippingItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } if (StringUtils.isNotBlank(selectedAddressCode)) { final AddressData selectedAddressData = getCheckoutFacade() .getDeliveryAddressForCode(selectedAddressCode); final boolean hasSelectedAddressData = selectedAddressData != null; if (hasSelectedAddressData) { final AddressData cartCheckoutDeliveryAddress = getCheckoutFacade().getCheckoutCart() .getDeliveryAddress(); if (isAddressIdChanged(cartCheckoutDeliveryAddress, selectedAddressData)) { getCheckoutFacade().setDeliveryAddress(selectedAddressData); if (cartCheckoutDeliveryAddress != null && !cartCheckoutDeliveryAddress.isVisibleInAddressBook()) { // temporary address should be removed getUserFacade().removeAddress(cartCheckoutDeliveryAddress); } } } } return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } @RequestMapping(value = "/add-delivery-address", method = RequestMethod.GET) @RequireHardLogIn public String addDeliveryAddress(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (!hasShippingItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); model.addAttribute("noAddress", Boolean.valueOf(hasNoDeliveryAddress())); model.addAttribute("addressForm", new AddressForm()); model.addAttribute("showSaveToAddressBook", Boolean.TRUE); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryAddress.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } @RequestMapping(value = "/add-delivery-address", method = RequestMethod.POST) @RequireHardLogIn public String addDeliveryAddress(final AddressForm addressForm, final BindingResult bindingResult, final Model model, final RedirectAttributes redirectModel) throws CMSItemNotFoundException { getAddressValidator().validate(addressForm, bindingResult); if (bindingResult.hasErrors()) { model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); GlobalMessages.addErrorMessage(model, "address.error.formentry.invalid"); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } model.addAttribute("noAddress", Boolean.valueOf(hasNoDeliveryAddress())); model.addAttribute("showSaveToAddressBook", Boolean.TRUE); final AddressData newAddress = new AddressData(); newAddress.setTitleCode(addressForm.getTitleCode()); newAddress.setFirstName(addressForm.getFirstName()); newAddress.setLastName(addressForm.getLastName()); newAddress.setLine1(addressForm.getLine1()); newAddress.setLine2(addressForm.getLine2()); newAddress.setTown(addressForm.getTownCity()); newAddress.setPostalCode(addressForm.getPostcode()); newAddress.setBillingAddress(false); newAddress.setShippingAddress(true); if (addressForm.getCountryIso() != null) { final CountryData countryData = getI18NFacade().getCountryForIsocode(addressForm.getCountryIso()); newAddress.setCountry(countryData); } if (addressForm.getRegionIso() != null && !StringUtils.isEmpty(addressForm.getRegionIso())) { final RegionData regionData = getI18NFacade().getRegion(addressForm.getCountryIso(), addressForm.getRegionIso()); newAddress.setRegion(regionData); } if (addressForm.getSaveInAddressBook() != null) { newAddress.setVisibleInAddressBook(addressForm.getSaveInAddressBook().booleanValue()); if (addressForm.getSaveInAddressBook().booleanValue() && getUserFacade().isAddressBookEmpty()) { newAddress.setDefaultAddress(true); } } else if (getCheckoutCustomerStrategy().isAnonymousCheckout()) { newAddress.setDefaultAddress(true); newAddress.setVisibleInAddressBook(true); } // Verify the address data. final AddressVerificationResult<AddressVerificationDecision> verificationResult = getAddressVerificationFacade() .verifyAddressData(newAddress); final boolean addressRequiresReview = getAddressVerificationResultHandler().handleResult(verificationResult, newAddress, model, redirectModel, bindingResult, getAddressVerificationFacade().isCustomerAllowedToIgnoreAddressSuggestions(), "checkout.multi.address.updated"); if (addressRequiresReview) { model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } getUserFacade().addAddress(newAddress); final AddressData previousSelectedAddress = getCheckoutFacade().getCheckoutCart().getDeliveryAddress(); // Set the new address as the selected checkout delivery address getCheckoutFacade().setDeliveryAddress(newAddress); if (previousSelectedAddress != null && !previousSelectedAddress.isVisibleInAddressBook()) { // temporary address should be removed getUserFacade().removeAddress(previousSelectedAddress); } // Set the new address as the selected checkout delivery address getCheckoutFacade().setDeliveryAddress(newAddress); return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } @RequestMapping(value = "/remove-address", method = { RequestMethod.GET, RequestMethod.POST }) @RequireHardLogIn public String removeAddress(@RequestParam("addressCode") final String addressCode, final RedirectAttributes redirectModel, final Model model) throws CMSItemNotFoundException { final AddressData addressData = new AddressData(); addressData.setId(addressCode); getUserFacade().removeAddress(addressData); GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "account.confirmation.address.removed"); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute("addressForm", new AddressForm()); return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } @RequestMapping(value = "/edit-delivery-address", method = RequestMethod.POST) @RequireHardLogIn public String editDeliveryAddress(final AddressForm addressForm, final BindingResult bindingResult, final Model model, final RedirectAttributes redirectModel) throws CMSItemNotFoundException { getAddressValidator().validate(addressForm, bindingResult); if (bindingResult.hasErrors()) { model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); GlobalMessages.addErrorMessage(model, "address.error.formentry.invalid"); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } model.addAttribute("noAddress", Boolean.valueOf(hasNoDeliveryAddress())); final AddressData newAddress = new AddressData(); newAddress.setId(addressForm.getAddressId()); newAddress.setTitleCode(addressForm.getTitleCode()); newAddress.setFirstName(addressForm.getFirstName()); newAddress.setLastName(addressForm.getLastName()); newAddress.setLine1(addressForm.getLine1()); newAddress.setLine2(addressForm.getLine2()); newAddress.setTown(addressForm.getTownCity()); newAddress.setPostalCode(addressForm.getPostcode()); newAddress.setBillingAddress(false); newAddress.setShippingAddress(true); if (addressForm.getCountryIso() != null) { final CountryData countryData = getI18NFacade().getCountryForIsocode(addressForm.getCountryIso()); newAddress.setCountry(countryData); } if (addressForm.getRegionIso() != null && !StringUtils.isEmpty(addressForm.getRegionIso())) { final RegionData regionData = getI18NFacade().getRegion(addressForm.getCountryIso(), addressForm.getRegionIso()); newAddress.setRegion(regionData); } if (addressForm.getSaveInAddressBook() == null) { newAddress.setVisibleInAddressBook(true); } else { newAddress.setVisibleInAddressBook(Boolean.TRUE.equals(addressForm.getSaveInAddressBook())); } newAddress.setDefaultAddress( getUserFacade().isAddressBookEmpty() || getUserFacade().getAddressBook().size() == 1 || Boolean.TRUE.equals(addressForm.getDefaultAddress())); // Verify the address data. final AddressVerificationResult<AddressVerificationDecision> verificationResult = getAddressVerificationFacade() .verifyAddressData(newAddress); final boolean addressRequiresReview = getAddressVerificationResultHandler().handleResult(verificationResult, newAddress, model, redirectModel, bindingResult, getAddressVerificationFacade().isCustomerAllowedToIgnoreAddressSuggestions(), "checkout.multi.address.updated"); if (addressRequiresReview) { model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); if (StringUtils.isNotEmpty(addressForm.getAddressId())) { final AddressData addressData = getCheckoutFacade() .getDeliveryAddressForCode(addressForm.getAddressId()); if (addressData != null) { model.addAttribute("showSaveToAddressBook", Boolean.valueOf(!addressData.isVisibleInAddressBook())); model.addAttribute("edit", Boolean.TRUE); } } return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } getUserFacade().editAddress(newAddress); getCheckoutFacade().setDeliveryModeIfAvailable(); getCheckoutFacade().setDeliveryAddress(newAddress); return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } @RequestMapping(value = "/edit-delivery-address", method = RequestMethod.GET) @RequireHardLogIn public String editDeliveryAddress(@RequestParam("editAddressCode") final String editAddressCode, final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (!hasShippingItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } AddressData addressData = null; if (StringUtils.isNotEmpty(editAddressCode)) { addressData = getCheckoutFacade().getDeliveryAddressForCode(editAddressCode); } final AddressForm addressForm = new AddressForm(); final boolean hasAddressData = addressData != null; if (hasAddressData) { addressForm.setAddressId(addressData.getId()); addressForm.setTitleCode(addressData.getTitleCode()); addressForm.setFirstName(addressData.getFirstName()); addressForm.setLastName(addressData.getLastName()); addressForm.setLine1(addressData.getLine1()); addressForm.setLine2(addressData.getLine2()); addressForm.setTownCity(addressData.getTown()); addressForm.setPostcode(addressData.getPostalCode()); addressForm.setCountryIso(addressData.getCountry().getIsocode()); addressForm.setSaveInAddressBook(Boolean.valueOf(addressData.isVisibleInAddressBook())); addressForm.setShippingAddress(Boolean.valueOf(addressData.isShippingAddress())); addressForm.setBillingAddress(Boolean.valueOf(addressData.isBillingAddress())); if (addressData.getRegion() != null && !StringUtils.isEmpty(addressData.getRegion().getIsocode())) { addressForm.setRegionIso(addressData.getRegion().getIsocode()); } } model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); model.addAttribute("noAddress", Boolean.valueOf(hasNoDeliveryAddress())); model.addAttribute("edit", Boolean.valueOf(hasAddressData)); model.addAttribute("addressForm", addressForm); model.addAttribute("showSaveToAddressBook", Boolean.valueOf(!addressData.isVisibleInAddressBook())); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryAddress.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage; } @RequestMapping(value = "/select-suggested-address", method = RequestMethod.POST) @RequireHardLogIn public String doSelectSuggestedAddress(final AddressForm addressForm, final RedirectAttributes redirectModel) { final Set<String> resolveCountryRegions = org.springframework.util.StringUtils .commaDelimitedListToSet(Config.getParameter("resolve.country.regions")); final AddressData selectedAddress = new AddressData(); selectedAddress.setId(addressForm.getAddressId()); selectedAddress.setTitleCode(addressForm.getTitleCode()); selectedAddress.setFirstName(addressForm.getFirstName()); selectedAddress.setLastName(addressForm.getLastName()); selectedAddress.setLine1(addressForm.getLine1()); selectedAddress.setLine2(addressForm.getLine2()); selectedAddress.setTown(addressForm.getTownCity()); selectedAddress.setPostalCode(addressForm.getPostcode()); selectedAddress.setBillingAddress(false); selectedAddress.setShippingAddress(true); final CountryData countryData = getI18NFacade().getCountryForIsocode(addressForm.getCountryIso()); selectedAddress.setCountry(countryData); if (resolveCountryRegions.contains(countryData.getIsocode())) { if (addressForm.getRegionIso() != null && !StringUtils.isEmpty(addressForm.getRegionIso())) { final RegionData regionData = getI18NFacade().getRegion(addressForm.getCountryIso(), addressForm.getRegionIso()); selectedAddress.setRegion(regionData); } } if (addressForm.getSaveInAddressBook() != null) { selectedAddress.setVisibleInAddressBook(addressForm.getSaveInAddressBook().booleanValue()); } if (Boolean.TRUE.equals(addressForm.getEditAddress())) { getUserFacade().editAddress(selectedAddress); } else { getUserFacade().addAddress(selectedAddress); } final AddressData previousSelectedAddress = getCheckoutFacade().getCheckoutCart().getDeliveryAddress(); // Set the new address as the selected checkout delivery address getCheckoutFacade().setDeliveryAddress(selectedAddress); if (previousSelectedAddress != null && !previousSelectedAddress.isVisibleInAddressBook()) { // temporary address should be removed getUserFacade().removeAddress(previousSelectedAddress); } GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "checkout.multi.address.added"); return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } @RequestMapping(value = "/choose-delivery-method", method = RequestMethod.GET) @RequireHardLogIn public String doChooseDeliveryModes(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (!hasShippingItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } if (hasNoDeliveryAddress()) { return REDIRECT_URL_ADD_DELIVERY_ADDRESS; } // Try to set default delivery mode getCheckoutFacade().setDeliveryModeIfAvailable(); final CartData cartData = getCheckoutFacade().getCheckoutCart(); model.addAttribute("cartData", cartData); model.addAttribute("deliveryMethods", getCheckoutFacade().getSupportedDeliveryModes()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryMethod.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.ChooseDeliveryMethodPage; } @RequestMapping(value = "/choose-delivery-location", method = RequestMethod.GET) @RequireHardLogIn public String doChooseDeliveryLocation(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (hasNoDeliveryAddress()) { return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } if (hasNoDeliveryMode()) { return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } // Try to set default delivery mode getCheckoutFacade().setDeliveryModeIfAvailable(); model.addAttribute("cartData", getCheckoutFacade().getCheckoutCart()); model.addAttribute("pickupConsolidationOptions", getCheckoutFacade().getConsolidatedPickupOptions()); model.addAttribute("userLocation", getCustomerLocationService().getUserLocation()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryMethod.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.ChoosePickupLocationPage; } @RequestMapping(value = "/select-delivery-location", method = RequestMethod.POST) @RequireHardLogIn public String doSelectDeliveryLocation(@RequestParam(value = "posName") final String posName, final Model model) throws CMSItemNotFoundException, CommerceCartModificationException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (hasNoDeliveryAddress()) { return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } if (hasNoDeliveryMode()) { return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } //Consolidate the cart and add unsuccessful modifications to page model.addAttribute("validationData", getCheckoutFacade().consolidateCheckoutCart(posName)); model.addAttribute("cartData", getCheckoutFacade().getCheckoutCart()); model.addAttribute("userLocation", getCustomerLocationService().getUserLocation()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.deliveryMethod.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.ChoosePickupLocationPage; } /** * This method gets called when the "Use Selected Delivery Method" button is clicked. It sets the selected delivery * mode on the checkout facade and reloads the page highlighting the selected delivery Mode. * * @param selectedDeliveryMethod * - the id of the delivery mode. * @return - a URL to the page to load. */ @RequestMapping(value = "/select-delivery-method", method = RequestMethod.GET) @RequireHardLogIn public String doSelectDeliveryMode(@RequestParam("delivery_method") final String selectedDeliveryMethod) { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (StringUtils.isNotEmpty(selectedDeliveryMethod)) { getCheckoutFacade().setDeliveryMode(selectedDeliveryMethod); } if (hasPickUpItems()) { return REDIRECT_URL_CHOOSE_DELIVERY_LOCATION; } else { return REDIRECT_URL_ADD_PAYMENT_METHOD; } } @RequestMapping(value = "/choose-payment-method", method = RequestMethod.GET) @RequireHardLogIn public String doChoosePaymentMethod(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (hasNoDeliveryAddress()) { return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } if (hasNoDeliveryMode()) { return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } if (hasNoPaymentInfo()) { return REDIRECT_URL_ADD_PAYMENT_METHOD; } // Try to set the default paymentInfo getCheckoutFacade().setPaymentInfoIfAvailable(); final List<CCPaymentInfoData> paymentMethods = getUserFacade().getCCPaymentInfos(true); final CartData cartData = getCheckoutFacade().getCheckoutCart(); final CCPaymentInfoData selectedPaymentInfo = cartData.getPaymentInfo(); if (selectedPaymentInfo != null) { model.addAttribute("selectedPaymentMethodId", selectedPaymentInfo.getId()); if (!selectedPaymentInfo.isSaved()) { paymentMethods.add(selectedPaymentInfo); } } model.addAttribute("cartData", cartData); model.addAttribute("paymentMethods", paymentMethods); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.paymentMethod.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.ChoosePaymentMethodPage; } /** * This method gets called when the "Use These Payment Details" button is clicked. It sets the selected payment * method on the checkout facade and reloads the page highlighting the selected payment method. * * @param selectedPaymentMethodId * - the id of the payment method to use. * @return - a URL to the page to load. */ @RequestMapping(value = "/select-payment-method", method = RequestMethod.GET) @RequireHardLogIn public String doSelectPaymentMethod( @RequestParam("selectedPaymentMethodId") final String selectedPaymentMethodId) { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (StringUtils.isNotBlank(selectedPaymentMethodId)) { getCheckoutFacade().setPaymentDetails(selectedPaymentMethodId); } return REDIRECT_URL_SUMMARY; } @RequestMapping(value = "/add-payment-method", method = RequestMethod.GET) @RequireHardLogIn public String doAddPaymentMethod(final Model model) throws CMSItemNotFoundException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } setupAddPaymentPage(model); // Use the checkout PCI strategy for getting the URL for creating new subscriptions. final CheckoutPciOptionEnum subscriptionPciOption = getCheckoutFlowFacade().getSubscriptionPciOption(); if (CheckoutPciOptionEnum.HOP.equals(subscriptionPciOption)) { // Redirect the customer to the HOP page or show error message if it fails (e.g. no HOP configurations). try { final PaymentData hostedOrderPageData = getPaymentFacade().beginHopCreateSubscription( "/checkout/multi/hop-response", "/integration/merchant_callback"); model.addAttribute("hostedOrderPageData", hostedOrderPageData); final boolean hopDebugMode = getSiteConfigService() .getBoolean(PaymentConstants.PaymentProperties.HOP_DEBUG_MODE, false); model.addAttribute("hopDebugMode", Boolean.valueOf(hopDebugMode)); return ControllerConstants.Views.Pages.MultiStepCheckout.HostedOrderPostPage; } catch (final Exception e) { LOG.error("Failed to build beginCreateSubscription request", e); GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.addPaymentDetails.generalError"); return doChoosePaymentMethod(model); } } else if (CheckoutPciOptionEnum.SOP.equals(subscriptionPciOption)) { // Build up the SOP form data and render page containing form final SopPaymentDetailsForm sopPaymentDetailsForm = new SopPaymentDetailsForm(); try { setupSilentOrderPostPage(sopPaymentDetailsForm, model); } catch (final Exception e) { LOG.error("Failed to build beginCreateSubscription request", e); GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.addPaymentDetails.generalError"); model.addAttribute("sopPaymentDetailsForm", sopPaymentDetailsForm); return doChoosePaymentMethod(model); } model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); return ControllerConstants.Views.Pages.MultiStepCheckout.SilentOrderPostPage; } // If not using HOP or SOP we need to build up the payment details form final PaymentDetailsForm paymentDetailsForm = new PaymentDetailsForm(); final AddressForm addressForm = new AddressForm(); paymentDetailsForm.setBillingAddress(addressForm); model.addAttribute(paymentDetailsForm); final CartData cartData = getCheckoutFacade().getCheckoutCart(); model.addAttribute("cartData", cartData); return ControllerConstants.Views.Pages.MultiStepCheckout.AddPaymentMethodPage; } protected void setupSilentOrderPostPage(final SopPaymentDetailsForm sopPaymentDetailsForm, final Model model) { try { final PaymentData silentOrderPageData = getPaymentFacade() .beginSopCreateSubscription("/checkout/multi/sop-response", "/integration/merchant_callback"); model.addAttribute("silentOrderPageData", silentOrderPageData); sopPaymentDetailsForm.setParameters(silentOrderPageData.getParameters()); model.addAttribute("paymentFormUrl", silentOrderPageData.getPostUrl()); } catch (final IllegalArgumentException e) { model.addAttribute("paymentFormUrl", ""); model.addAttribute("silentOrderPageData", null); LOG.warn("Failed to set up silent order post page " + e.getMessage()); GlobalMessages.addErrorMessage(model, "checkout.multi.sop.globalError"); } final CartData cartData = getCheckoutFacade().getCheckoutCart(); model.addAttribute("silentOrderPostForm", new PaymentDetailsForm()); model.addAttribute("cartData", cartData); model.addAttribute("deliveryAddress", cartData.getDeliveryAddress()); model.addAttribute("sopPaymentDetailsForm", sopPaymentDetailsForm); model.addAttribute("paymentInfos", getUserFacade().getCCPaymentInfos(true)); model.addAttribute("sopCardTypes", getSopCardTypes()); } protected void setupAddPaymentPage(final Model model) throws CMSItemNotFoundException { model.addAttribute("metaRobots", "no-index,no-follow"); model.addAttribute("hasNoPaymentInfo", Boolean.valueOf(hasNoPaymentInfo())); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.paymentMethod.breadcrumb")); final ContentPageModel contentPage = getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL); storeCmsPageInModel(model, contentPage); setUpMetaDataForContentPage(model, contentPage); } protected Collection<CardTypeData> getSopCardTypes() { final Collection<CardTypeData> sopCardTypes = new ArrayList<CardTypeData>(); final List<CardTypeData> supportedCardTypes = getCheckoutFacade().getSupportedCardTypes(); for (final CardTypeData supportedCardType : supportedCardTypes) { // Add credit cards for all supported cards that have mappings for cybersource SOP if (cybersourceSopCardTypes.containsKey(supportedCardType.getCode())) { sopCardTypes.add(createCardTypeData(cybersourceSopCardTypes.get(supportedCardType.getCode()), supportedCardType.getName())); } } return sopCardTypes; } protected CardTypeData createCardTypeData(final String code, final String name) { final CardTypeData cardTypeData = new CardTypeData(); cardTypeData.setCode(code); cardTypeData.setName(name); return cardTypeData; } @RequestMapping(value = { "/add-payment-method" }, method = RequestMethod.POST) @RequireHardLogIn public String doSavePaymentMethod(final Model model, @Valid final PaymentDetailsForm paymentDetailsForm, final BindingResult bindingResult) throws CMSItemNotFoundException { getPaymentDetailsValidator().validate(paymentDetailsForm, bindingResult); setupAddPaymentPage(model); final CartData cartData = getCheckoutFacade().getCheckoutCart(); model.addAttribute("cartData", cartData); if (bindingResult.hasErrors()) { GlobalMessages.addErrorMessage(model, "checkout.error.paymentethod.formentry.invalid"); return ControllerConstants.Views.Pages.MultiStepCheckout.AddPaymentMethodPage; } final CCPaymentInfoData paymentInfoData = new CCPaymentInfoData(); paymentInfoData.setId(paymentDetailsForm.getPaymentId()); paymentInfoData.setCardType(paymentDetailsForm.getCardTypeCode()); paymentInfoData.setAccountHolderName(paymentDetailsForm.getNameOnCard()); paymentInfoData.setCardNumber(paymentDetailsForm.getCardNumber()); paymentInfoData.setStartMonth(paymentDetailsForm.getStartMonth()); paymentInfoData.setStartYear(paymentDetailsForm.getStartYear()); paymentInfoData.setExpiryMonth(paymentDetailsForm.getExpiryMonth()); paymentInfoData.setExpiryYear(paymentDetailsForm.getExpiryYear()); if (Boolean.TRUE.equals(paymentDetailsForm.getSaveInAccount()) || getCheckoutCustomerStrategy().isAnonymousCheckout()) { paymentInfoData.setSaved(true); } paymentInfoData.setIssueNumber(paymentDetailsForm.getIssueNumber()); final AddressData addressData; if (Boolean.FALSE.equals(paymentDetailsForm.getNewBillingAddress())) { addressData = getCheckoutFacade().getCheckoutCart().getDeliveryAddress(); if (addressData == null) { GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.createSubscription.billingAddress.noneSelectedMsg"); return ControllerConstants.Views.Pages.MultiStepCheckout.AddPaymentMethodPage; } addressData.setBillingAddress(true); // mark this as billing address } else { final AddressForm addressForm = paymentDetailsForm.getBillingAddress(); addressData = new AddressData(); if (addressForm != null) { addressData.setId(addressForm.getAddressId()); addressData.setTitleCode(addressForm.getTitleCode()); addressData.setFirstName(addressForm.getFirstName()); addressData.setLastName(addressForm.getLastName()); addressData.setLine1(addressForm.getLine1()); addressData.setLine2(addressForm.getLine2()); addressData.setTown(addressForm.getTownCity()); addressData.setPostalCode(addressForm.getPostcode()); addressData.setCountry(getI18NFacade().getCountryForIsocode(addressForm.getCountryIso())); if (addressForm.getRegionIso() != null) { addressData.setRegion( getI18NFacade().getRegion(addressForm.getCountryIso(), addressForm.getRegionIso())); } addressData.setShippingAddress(Boolean.TRUE.equals(addressForm.getShippingAddress())); addressData.setBillingAddress(Boolean.TRUE.equals(addressForm.getBillingAddress())); } } getAddressVerificationFacade().verifyAddressData(addressData); paymentInfoData.setBillingAddress(addressData); final CCPaymentInfoData newPaymentSubscription = getCheckoutFacade() .createPaymentSubscription(paymentInfoData); if (newPaymentSubscription != null && StringUtils.isNotBlank(newPaymentSubscription.getSubscriptionId())) { if (Boolean.TRUE.equals(paymentDetailsForm.getSaveInAccount()) && getUserFacade().getCCPaymentInfos(true).size() <= 1) { getUserFacade().setDefaultPaymentInfo(newPaymentSubscription); } getCheckoutFacade().setPaymentDetails(newPaymentSubscription.getId()); } else { GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.createSubscription.failedMsg"); return ControllerConstants.Views.Pages.MultiStepCheckout.AddPaymentMethodPage; } model.addAttribute("paymentId", newPaymentSubscription.getId()); return REDIRECT_URL_SUMMARY; } @RequestMapping(value = "/remove-payment-method", method = RequestMethod.POST) @RequireHardLogIn public String removePaymentMethod(@RequestParam(value = "paymentInfoId") final String paymentMethodId, final RedirectAttributes redirectAttributes) throws CMSItemNotFoundException { getUserFacade().unlinkCCPaymentInfo(paymentMethodId); GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER, "text.account.profile.paymentCart.removed"); return REDIRECT_URL_ADD_PAYMENT_METHOD; } @RequestMapping(value = "/hop-response", method = RequestMethod.POST) @RequireHardLogIn public String doHandleHopResponse(final HttpServletRequest request) { final Map<String, String> resultMap = getRequestParameterMap(request); final PaymentSubscriptionResultData paymentSubscriptionResultData = getPaymentFacade() .completeHopCreateSubscription(resultMap, true); if (paymentSubscriptionResultData.isSuccess() && paymentSubscriptionResultData.getStoredCard() != null && StringUtils.isNotBlank(paymentSubscriptionResultData.getStoredCard().getSubscriptionId())) { final CCPaymentInfoData newPaymentSubscription = paymentSubscriptionResultData.getStoredCard(); if (getUserFacade().getCCPaymentInfos(true).size() <= 1) { getUserFacade().setDefaultPaymentInfo(newPaymentSubscription); } getCheckoutFacade().setPaymentDetails(newPaymentSubscription.getId()); } else { // HOP ERROR! LOG.error("Failed to create subscription. Please check the log files for more information"); return REDIRECT_URL_ERROR + "/?decision=" + paymentSubscriptionResultData.getDecision() + "&reasonCode=" + paymentSubscriptionResultData.getResultCode(); } return REDIRECT_URL_SUMMARY; } @RequestMapping(value = "/hop-error", method = RequestMethod.GET) public String doHostedOrderPageError(@RequestParam(required = true) final String decision, @RequestParam(required = true) final String reasonCode, final Model model) throws CMSItemNotFoundException { String redirectUrl = REDIRECT_URL_ADD_PAYMENT_METHOD; if (!hasValidCart()) { redirectUrl = REDIRECT_URL_CART; } if (StringUtils.isBlank(redirectUrl) && hasNoDeliveryAddress()) { redirectUrl = REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } if (StringUtils.isBlank(redirectUrl) && hasNoDeliveryMode()) { redirectUrl = REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } model.addAttribute("decision", decision); model.addAttribute("reasonCode", reasonCode); model.addAttribute("redirectUrl", redirectUrl.replace(REDIRECT_PREFIX, "")); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.hostedOrderPageError.breadcrumb")); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); GlobalMessages.addErrorMessage(model, "checkout.multi.hostedOrderPageError.globalError"); return ControllerConstants.Views.Pages.MultiStepCheckout.HostedOrderPageErrorPage; } @RequestMapping(value = "/sop-response", method = RequestMethod.POST) @RequireHardLogIn public String doHandleSopResponse(final HttpServletRequest request, @Valid final SopPaymentDetailsForm sopPaymentDetailsForm, final BindingResult bindingResult, final Model model) throws CMSItemNotFoundException { final Map<String, String> resultMap = getRequestParameterMap(request); final boolean savePaymentInfo = "on".equals(resultMap.get("_savePaymentInfo")) || getCheckoutCustomerStrategy().isAnonymousCheckout(); final PaymentSubscriptionResultData paymentSubscriptionResultData = this.getPaymentFacade() .completeSopCreateSubscription(resultMap, savePaymentInfo); if (paymentSubscriptionResultData.isSuccess() && paymentSubscriptionResultData.getStoredCard() != null && StringUtils.isNotBlank(paymentSubscriptionResultData.getStoredCard().getSubscriptionId())) { final CCPaymentInfoData newPaymentSubscription = paymentSubscriptionResultData.getStoredCard(); if (getUserFacade().getCCPaymentInfos(true).size() <= 1) { getUserFacade().setDefaultPaymentInfo(newPaymentSubscription); } getCheckoutFacade().setPaymentDetails(newPaymentSubscription.getId()); } else if ((paymentSubscriptionResultData.getDecision() != null && paymentSubscriptionResultData.getDecision().equalsIgnoreCase("error")) || (paymentSubscriptionResultData.getErrors() != null && !paymentSubscriptionResultData.getErrors().isEmpty())) { // Have SOP errors that we can display if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } setupAddPaymentPage(model); // Build up the SOP form data and render page containing form try { setupSilentOrderPostPage(sopPaymentDetailsForm, model); } catch (final Exception e) { LOG.error("Failed to build beginCreateSubscription request", e); GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.addPaymentDetails.generalError"); return doChoosePaymentMethod(model); } if (paymentSubscriptionResultData.getErrors() != null && !paymentSubscriptionResultData.getErrors().isEmpty()) { GlobalMessages.addErrorMessage(model, "checkout.error.paymentethod.formentry.invalid"); // Add in specific errors for invalid fields for (final PaymentErrorField paymentErrorField : paymentSubscriptionResultData.getErrors() .values()) { if (paymentErrorField.isMissing()) { bindingResult.rejectValue(paymentErrorField.getName(), "checkout.error.paymentethod.formentry.sop.missing." + paymentErrorField.getName(), "Please enter a value for this field"); } if (paymentErrorField.isInvalid()) { bindingResult.rejectValue(paymentErrorField.getName(), "checkout.error.paymentethod.formentry.sop.invalid." + paymentErrorField.getName(), "This value is invalid for this field"); } } } else if (paymentSubscriptionResultData.getDecision().equalsIgnoreCase("error")) { LOG.error( "Failed to create subscription. Error occurred while contacting external payment services."); GlobalMessages.addErrorMessage(model, "checkout.multi.paymentMethod.addPaymentDetails.generalError"); } model.addAttribute("regionsForCountries", getI18NFacade().getRegionsForAllCountries()); return ControllerConstants.Views.Pages.MultiStepCheckout.SilentOrderPostPage; } else { // SOP ERROR! LOG.error("Failed to create subscription. Please check the log files for more information"); return REDIRECT_URL_ERROR + "/?decision=" + paymentSubscriptionResultData.getDecision() + "&reasonCode=" + paymentSubscriptionResultData.getResultCode(); } return REDIRECT_URL_SUMMARY; } protected Map<String, String> getRequestParameterMap(final HttpServletRequest request) { final Map<String, String> map = new HashMap<String, String>(); final Enumeration myEnum = request.getParameterNames(); while (myEnum.hasMoreElements()) { final String paramName = (String) myEnum.nextElement(); final String paramValue = request.getParameter(paramName); map.put(paramName, paramValue); } return map; } @RequestMapping(value = "/summary", method = RequestMethod.GET) @RequireHardLogIn public String checkoutSummary(final Model model, final RedirectAttributes redirectModel) throws CMSItemNotFoundException, CommerceCartModificationException { if (!hasValidCart()) { LOG.info("Missing, empty or unsupported cart"); return REDIRECT_URL_CART; } if (hasNoDeliveryAddress()) { return REDIRECT_URL_CHOOSE_DELIVERY_ADDRESS; } if (hasNoDeliveryMode()) { return REDIRECT_URL_CHOOSE_DELIVERY_METHOD; } if (hasNoPaymentInfo()) { return REDIRECT_URL_ADD_PAYMENT_METHOD; } final CartData cartData = getCheckoutFacade().getCheckoutCart(); if (cartData.getEntries() != null && !cartData.getEntries().isEmpty()) { for (final OrderEntryData entry : cartData.getEntries()) { final String productCode = entry.getProduct().getCode(); final ProductData product = getProductFacade().getProductForCodeAndOptions(productCode, Arrays.asList(ProductOption.BASIC, ProductOption.PRICE)); entry.setProduct(product); } } model.addAttribute("cartData", cartData); model.addAttribute("allItems", cartData.getEntries()); model.addAttribute("deliveryAddress", cartData.getDeliveryAddress()); model.addAttribute("deliveryMode", cartData.getDeliveryMode()); model.addAttribute("paymentInfo", cartData.getPaymentInfo()); // Only request the security code if the SubscriptionPciOption is set to Default. final boolean requestSecurityCode = (CheckoutPciOptionEnum.DEFAULT .equals(getCheckoutFlowFacade().getSubscriptionPciOption())); model.addAttribute("requestSecurityCode", Boolean.valueOf(requestSecurityCode)); model.addAttribute(new PlaceOrderForm()); storeCmsPageInModel(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(MULTI_CHECKOUT_SUMMARY_CMS_PAGE_LABEL)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.multi.summary.breadcrumb")); model.addAttribute("metaRobots", "no-index,no-follow"); return ControllerConstants.Views.Pages.MultiStepCheckout.CheckoutSummaryPage; } @RequestMapping(value = "/termsAndConditions") @RequireHardLogIn public String getTermsAndConditions(final Model model) throws CMSItemNotFoundException { final ContentPageModel pageForRequest = getCmsPageService().getPageForLabel("/termsAndConditions"); storeCmsPageInModel(model, pageForRequest); setUpMetaDataForContentPage(model, pageForRequest); model.addAttribute(WebConstants.BREADCRUMBS_KEY, contentPageBreadcrumbBuilder.getBreadcrumbs(pageForRequest)); return ControllerConstants.Views.Fragments.Checkout.TermsAndConditionsPopup; } @RequestMapping(value = "/placeOrder") @RequireHardLogIn public String placeOrder(@ModelAttribute("placeOrderForm") final PlaceOrderForm placeOrderForm, final Model model, final HttpServletRequest request, final RedirectAttributes redirectModel) throws CMSItemNotFoundException, InvalidCartException, CommerceCartModificationException { if (validateOrderForm(placeOrderForm, model)) { return checkoutSummary(model, redirectModel); } //Validate the cart if (validateCart(redirectModel)) { // Invalid cart. Bounce back to the cart page. return REDIRECT_PREFIX + "/cart"; } // authorize, if failure occurs don't allow to place the order if (!getCheckoutFacade().authorizePayment(placeOrderForm.getSecurityCode())) { GlobalMessages.addErrorMessage(model, "checkout.error.authorization.failed"); return checkoutSummary(model, redirectModel); } final OrderData orderData; try { orderData = getCheckoutFacade().placeOrder(); } catch (Exception e) { LOG.error("Failed to place Order", e); GlobalMessages.addErrorMessage(model, "checkout.placeOrder.failed"); return checkoutSummary(model, redirectModel); } return redirectToOrderConfirmationPage(orderData); } /** * Validates the order form before to filter out invalid order states * * @param placeOrderForm * The spring form of the order being submitted * @param model * A spring Model * @return True if the order form is invalid and false if everything is valid. */ protected boolean validateOrderForm(final PlaceOrderForm placeOrderForm, final Model model) { final String securityCode = placeOrderForm.getSecurityCode(); boolean invalid = false; if (hasNoDeliveryAddress()) { GlobalMessages.addErrorMessage(model, "checkout.deliveryAddress.notSelected"); invalid = true; } if (hasNoDeliveryMode()) { GlobalMessages.addErrorMessage(model, "checkout.deliveryMethod.notSelected"); invalid = true; } if (hasNoPaymentInfo()) { GlobalMessages.addErrorMessage(model, "checkout.paymentMethod.notSelected"); invalid = true; } else { // Only require the Security Code to be entered on the summary page if the SubscriptionPciOption is set to Default. if (CheckoutPciOptionEnum.DEFAULT.equals(getCheckoutFlowFacade().getSubscriptionPciOption()) && StringUtils.isBlank(securityCode)) { GlobalMessages.addErrorMessage(model, "checkout.paymentMethod.noSecurityCode"); invalid = true; } } if (!placeOrderForm.isTermsCheck()) { GlobalMessages.addErrorMessage(model, "checkout.error.terms.not.accepted"); invalid = true; return invalid; } final CartData cartData = getCheckoutFacade().getCheckoutCart(); if (!getCheckoutFacade().containsTaxValues()) { LOG.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())); GlobalMessages.addErrorMessage(model, "checkout.error.tax.missing"); invalid = true; } if (Boolean.FALSE.equals(cartData.isCalculated())) { LOG.error(String.format("Cart %s has a calculated flag of FALSE, placement of order can't continue", cartData.getCode())); GlobalMessages.addErrorMessage(model, "checkout.error.cart.notcalculated"); invalid = true; } return invalid; } }