Skip to content

Commit

Permalink
[BLC-839] - Add validation to ensure sum of accepted payments is equa…
Browse files Browse the repository at this point in the history
…l to the order total
  • Loading branch information
Andre Azzolini committed Feb 13, 2013
1 parent db00b57 commit 45f6b80
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 55 deletions.
Expand Up @@ -21,15 +21,13 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.time.SystemTime;
import org.broadleafcommerce.common.vendor.service.exception.FulfillmentPriceException;
import org.broadleafcommerce.core.checkout.service.exception.CheckoutException;
import org.broadleafcommerce.core.checkout.service.workflow.CheckoutResponse;
import org.broadleafcommerce.core.order.domain.FulfillmentGroup;
import org.broadleafcommerce.core.order.domain.FulfillmentOption;
import org.broadleafcommerce.core.order.domain.NullOrderImpl;
import org.broadleafcommerce.core.order.domain.Order;
import org.broadleafcommerce.core.order.service.type.OrderStatus;
import org.broadleafcommerce.core.payment.domain.CreditCardPaymentInfo;
import org.broadleafcommerce.core.payment.domain.PaymentInfo;
import org.broadleafcommerce.core.payment.domain.Referenced;
Expand All @@ -55,22 +53,20 @@
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.beans.PropertyEditorSupport;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* In charge of performing the various checkout operations
*
Expand Down Expand Up @@ -411,12 +407,9 @@ public String completeSecureCreditCardCheckout(HttpServletRequest request, HttpS

payments.put(ccInfo, ccReference);

initializeOrderForCheckout(cart);

CheckoutResponse checkoutResponse = checkoutService.performCheckout(cart, payments);

if (!checkoutResponse.getPaymentResponse().getResponseItems().get(ccInfo).getTransactionSuccess()){
processFailedOrderCheckout(cart);
populateModelWithShippingReferenceData(request, model);
result.rejectValue("creditCardNumber", "payment.exception", null, null);
return getCheckoutView();
Expand Down Expand Up @@ -454,34 +447,6 @@ protected void copyShippingAddressToBillingAddress(Order order, BillingInfoForm
}
}

/**
* This method initializes the order for checkout by setting
* the order number, status, and the submit date.
*
* @param order
*/
protected void initializeOrderForCheckout(Order order) {
order.setOrderNumber(new SimpleDateFormat("yyyyMMddHHmmssS").format(SystemTime.asDate()));
order.setStatus(OrderStatus.SUBMITTED);
order.setSubmitDate(Calendar.getInstance().getTime());
}

/**
* This method dictates what actions need to be taken if there is a failure during the checkout process.
* Normally called when either the transaction success is false (e.g. payment declined by gateway)
* or an unknown error occurs during the Checkout Workflow (e.g. a CheckoutException is thrown)
*
* The default behavior is to reverse the status of the order and set the submit date and order number to null.
*
* @param order
*/
protected void processFailedOrderCheckout(Order order) throws PricingException {
order.setOrderNumber(null);
order.setStatus(OrderStatus.IN_PROCESS);
order.setSubmitDate(null);
orderService.save(order, false);
}

/**
* A helper method used to determine the validity of the fulfillment groups
*
Expand Down
Expand Up @@ -16,24 +16,22 @@

package org.broadleafcommerce.core.checkout.service;

import org.broadleafcommerce.common.time.SystemTime;
import org.broadleafcommerce.core.checkout.service.exception.CheckoutException;
import org.broadleafcommerce.core.checkout.service.workflow.CheckoutResponse;
import org.broadleafcommerce.core.checkout.service.workflow.CheckoutSeed;
import org.broadleafcommerce.core.order.domain.Order;
import org.broadleafcommerce.core.order.service.OrderService;
import org.broadleafcommerce.core.payment.domain.PaymentInfo;
import org.broadleafcommerce.core.payment.domain.Referenced;
import org.broadleafcommerce.core.pricing.service.exception.PricingException;
import org.broadleafcommerce.core.workflow.SequenceProcessor;
import org.broadleafcommerce.core.workflow.WorkflowException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

@Service("blCheckoutService")
public class CheckoutServiceImpl implements CheckoutService {

Expand Down Expand Up @@ -68,15 +66,9 @@ public CheckoutResponse performCheckout(Order order, final Map<PaymentInfo, Refe

CheckoutSeed seed = null;
try {
order.setSubmitDate(SystemTime.asDate());
order = orderService.save(order, false);

seed = new CheckoutSeed(order, payments, new HashMap<String, Object>());
checkoutWorkflow.doActivities(seed);

return seed;
} catch (PricingException e) {
throw new CheckoutException("Unable to checkout order -- id: " + order.getId(), e, seed);
} catch (WorkflowException e) {
Throwable cause = e;
while (e.getCause() != null) {
Expand Down
Expand Up @@ -16,15 +16,23 @@

package org.broadleafcommerce.core.checkout.service.workflow;

import org.broadleafcommerce.common.time.SystemTime;
import org.broadleafcommerce.core.order.service.type.OrderStatus;
import org.broadleafcommerce.core.workflow.BaseActivity;
import org.broadleafcommerce.core.workflow.ProcessContext;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class CompleteOrderActivity extends BaseActivity {

public ProcessContext execute(ProcessContext context) throws Exception {
CheckoutSeed seed = ((CheckoutContext) context).getSeedData();

seed.getOrder().setStatus(OrderStatus.SUBMITTED);
seed.getOrder().setOrderNumber(new SimpleDateFormat("yyyyMMddHHmmssS").format(SystemTime.asDate()));
seed.getOrder().setSubmitDate(Calendar.getInstance().getTime());

return context;
}

Expand Down
Expand Up @@ -18,17 +18,19 @@

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.money.Money;
import org.broadleafcommerce.core.payment.domain.PaymentInfo;
import org.broadleafcommerce.core.payment.domain.PaymentResponseItem;
import org.broadleafcommerce.core.payment.service.CompositePaymentService;
import org.broadleafcommerce.core.payment.service.exception.InsufficientFundsException;
import org.broadleafcommerce.core.payment.service.workflow.CompositePaymentResponse;
import org.broadleafcommerce.core.workflow.BaseActivity;
import org.broadleafcommerce.core.workflow.ProcessContext;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;
import java.util.Map.Entry;

import java.util.Map;
import javax.annotation.Resource;

public class PaymentServiceActivity extends BaseActivity {

Expand All @@ -38,14 +40,14 @@ public class PaymentServiceActivity extends BaseActivity {
private CompositePaymentService compositePaymentService;

@Value("${stop.checkout.on.single.payment.failure}")
protected String stopCheckoutOnSinglePaymentFailure;
protected Boolean stopCheckoutOnSinglePaymentFailure;

@Override
public ProcessContext execute(ProcessContext context) throws Exception {
CheckoutSeed seed = ((CheckoutContext) context).getSeedData();
CompositePaymentResponse response = compositePaymentService.executePayment(seed.getOrder(), seed.getInfos(), seed.getPaymentResponse());

for (Map.Entry<PaymentInfo, PaymentResponseItem> entry : response.getPaymentResponse().getResponseItems().entrySet()) {
for (Entry<PaymentInfo, PaymentResponseItem> entry : response.getPaymentResponse().getResponseItems().entrySet()) {
checkTransactionStatus(context, entry.getValue());
if (context.isStopped()) {
String log = "Stopping checkout workflow due to payment response code: ";
Expand All @@ -58,11 +60,24 @@ public ProcessContext execute(ProcessContext context) throws Exception {
}
}

// Validate that the total amount collected is not less than the order total
Money paidAmount = new Money(0);
for (Entry<PaymentInfo, PaymentResponseItem> entry : response.getPaymentResponse().getResponseItems().entrySet()) {
if (entry.getValue().getTransactionSuccess()) {
paidAmount = paidAmount.add(entry.getValue().getAmountPaid());
}
}

if (paidAmount.lessThan(seed.getOrder().getTotal())) {
throw new InsufficientFundsException(String.format("Order total was [%s] but paid amount was [%s]",
seed.getOrder().getTotal(), paidAmount));
}

return context;
}

protected void checkTransactionStatus(ProcessContext context, PaymentResponseItem paymentResponseItem) {
if ("true".equalsIgnoreCase(stopCheckoutOnSinglePaymentFailure) &&
if ((stopCheckoutOnSinglePaymentFailure != null && stopCheckoutOnSinglePaymentFailure) &&
paymentResponseItem.getTransactionSuccess() != null && !paymentResponseItem.getTransactionSuccess()) {
context.stopProcess();
}
Expand Down
@@ -0,0 +1,39 @@
/*
* Copyright 2008-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.broadleafcommerce.core.payment.service.exception;

public class InsufficientFundsException extends PaymentException {

private static final long serialVersionUID = 1L;

public InsufficientFundsException() {
super();
}

public InsufficientFundsException(String message, Throwable cause) {
super(message, cause);
}

public InsufficientFundsException(String message) {
super(message);
}

public InsufficientFundsException(Throwable cause) {
super(cause);
}

}
Expand Up @@ -6,7 +6,7 @@
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

<bean id="blTaxModule" class="org.broadleafcommerce.core.pricing.service.module.SimpleTaxModule">
<property name="factor" value="0.05"/>
<property name="factor" value="0.00"/>
</bean>

<bean id="blFulfillmentLocationResolver" class="org.broadleafcommerce.core.pricing.service.fulfillment.SimpleFulfillmentLocationResolver" />
Expand Down
@@ -1,4 +1,6 @@
solr.index.product.pageSize=100

pricing.retry.count.for.lock.failure=3
pricing.retry.wait.interval.for.lock.failure=500
pricing.retry.wait.interval.for.lock.failure=500

stop.checkout.on.single.payment.failure=false

1 comment on commit 45f6b80

@phillipuniverse
Copy link
Contributor

Choose a reason for hiding this comment

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

Applicable to #54

Please sign in to comment.