in applications/accounting/src/main/java/org/apache/ofbiz/accounting/payment/PaymentGatewayServices.java [1208:1493]
public static Map<String, Object> captureOrderPayments(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");
String orderId = (String) context.get("orderId");
String invoiceId = (String) context.get("invoiceId");
String billingAccountId = (String) context.get("billingAccountId");
BigDecimal amountToCapture = (BigDecimal) context.get("captureAmount");
Locale locale = (Locale) context.get("locale");
amountToCapture = amountToCapture.setScale(DECIMALS, ROUNDING);
// get the order header and payment preferences
GenericValue orderHeader = null;
List<GenericValue> paymentPrefs = null;
List<GenericValue> paymentPrefsBa = null;
try {
orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
// get the payment prefs
paymentPrefs = EntityQuery.use(delegator).from("OrderPaymentPreference")
.where("orderId", orderId, "statusId", "PAYMENT_AUTHORIZED").orderBy("-maxAmount").queryList();
if (UtilValidate.isNotEmpty(billingAccountId)) {
paymentPrefsBa = EntityQuery.use(delegator).from("OrderPaymentPreference")
.where("orderId", orderId, "paymentMethodTypeId", "EXT_BILLACT", "statusId", "PAYMENT_NOT_RECEIVED")
.orderBy("-maxAmount").queryList();
}
} catch (GenericEntityException gee) {
Debug.logError(gee, "Problems getting entity record(s), see stack trace", MODULE);
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"OrderOrderNotFound", UtilMisc.toMap("orderId", orderId), locale) + " " + gee.toString());
}
// error if no order was found
if (orderHeader == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"OrderOrderNotFound", UtilMisc.toMap("orderId", orderId), locale));
}
// Check if the outstanding amount for the order is greater than the
// amount that we are going to capture.
OrderReadHelper orh = new OrderReadHelper(orderHeader);
BigDecimal orderGrandTotal = orh.getOrderGrandTotal();
orderGrandTotal = orderGrandTotal.setScale(DECIMALS, ROUNDING);
BigDecimal totalPayments = PaymentWorker.getPaymentsTotal(orh.getOrderPayments());
totalPayments = totalPayments.setScale(DECIMALS, ROUNDING);
BigDecimal remainingTotal = orderGrandTotal.subtract(totalPayments);
if (Debug.infoOn()) {
Debug.logInfo("The Remaining Total for order: " + orderId + " is: " + remainingTotal, MODULE);
}
// The amount to capture cannot be greater than the remaining total
amountToCapture = amountToCapture.min(remainingTotal);
if (Debug.infoOn()) {
Debug.logInfo("Actual Expected Capture Amount : " + amountToCapture, MODULE);
}
// Process billing accounts payments
if (UtilValidate.isNotEmpty(paymentPrefsBa)) {
Iterator<GenericValue> paymentsBa = paymentPrefsBa.iterator();
while (paymentsBa.hasNext()) {
GenericValue paymentPref = paymentsBa.next();
BigDecimal authAmount = paymentPref.getBigDecimal("maxAmount");
if (authAmount == null) {
authAmount = ZERO;
}
authAmount = authAmount.setScale(DECIMALS, ROUNDING);
if (authAmount.compareTo(ZERO) == 0) {
// nothing to capture
Debug.logInfo("Nothing to capture; authAmount = 0", MODULE);
continue;
}
// the amount for *this* capture
BigDecimal amountThisCapture = amountToCapture.min(authAmount);
// decrease amount of next payment preference to capture
amountToCapture = amountToCapture.subtract(amountThisCapture);
// If we have an invoice, we find unapplied payments associated
// to the billing account and we apply them to the invoice
if (UtilValidate.isNotEmpty(invoiceId)) {
Map<String, Object> captureResult = null;
try {
captureResult = dispatcher.runSync("captureBillingAccountPayments", UtilMisc.<String, Object>toMap("invoiceId", invoiceId,
"billingAccountId", billingAccountId,
"captureAmount", amountThisCapture,
"orderId", orderId,
"userLogin", userLogin));
if (ServiceUtil.isError(captureResult)) {
return ServiceUtil.returnError(ServiceUtil.getErrorMessage(captureResult));
}
} catch (GenericServiceException ex) {
return ServiceUtil.returnError(ex.getMessage());
}
if (captureResult != null) {
BigDecimal amountCaptured = BigDecimal.ZERO;
try {
amountCaptured = (BigDecimal) ObjectType.simpleTypeOrObjectConvert(captureResult.get("captureAmount"),
"BigDecimal", null, locale);
} catch (GeneralException e) {
Debug.logError(e, "Trouble processing the result; captureResult: " + captureResult, MODULE);
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"AccountingPaymentCannotBeCaptured", locale) + " " + captureResult);
}
if (Debug.infoOn()) {
Debug.logInfo("Amount captured for order [" + orderId + "] from unapplied payments associated to billing account ["
+ billingAccountId + "] is: " + amountCaptured, MODULE);
}
amountCaptured = amountCaptured.setScale(DECIMALS, ROUNDING);
if (amountCaptured.compareTo(BigDecimal.ZERO) == 0) {
continue;
}
// add the invoiceId to the result for processing
captureResult.put("invoiceId", invoiceId);
captureResult.put("captureResult", Boolean.TRUE);
captureResult.put("orderPaymentPreference", paymentPref);
if (context.get("captureRefNum") == null) {
captureResult.put("captureRefNum", "");
// FIXME: this is an hack to avoid a service validation error for processCaptureResult (captureRefNum is mandatory,
// but it is not used for billing accounts)
}
// process the capture's results
try {
// the following method will set on the OrderPaymentPreference:
// maxAmount = amountCaptured and
// statusId = PAYMENT_RECEIVED
processResult(dctx, captureResult, userLogin, paymentPref, locale);
} catch (GeneralException e) {
Debug.logError(e, "Trouble processing the result; captureResult: " + captureResult, MODULE);
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"AccountingPaymentCannotBeCaptured", locale) + " " + captureResult);
}
// create any splits which are needed
if (authAmount.compareTo(amountCaptured) > 0) {
BigDecimal splitAmount = authAmount.subtract(amountCaptured);
try {
Map<String, Object> splitCtx = UtilMisc.<String, Object>toMap("userLogin", userLogin, "orderPaymentPreference",
paymentPref, "splitAmount", splitAmount);
dispatcher.addCommitService("processCaptureSplitPayment", splitCtx, true);
} catch (GenericServiceException e) {
Debug.logWarning(e, "Problem processing the capture split payment", MODULE);
}
if (Debug.infoOn()) {
Debug.logInfo("Captured: " + amountThisCapture + " Remaining (re-auth): " + splitAmount, MODULE);
}
}
} else {
Debug.logError("Payment not captured for order [" + orderId + "] from billing account [" + billingAccountId + "]", MODULE);
}
}
}
}
// iterate over the prefs and capture each one until we meet our total
if (UtilValidate.isNotEmpty(paymentPrefs)) {
Iterator<GenericValue> payments = paymentPrefs.iterator();
while (payments.hasNext()) {
// DEJ20060708: Do we really want to just log and ignore the errors like this? I've improved a few of these in a review today,
// but it is being done all over...
GenericValue paymentPref = payments.next();
GenericValue authTrans = getAuthTransaction(paymentPref);
if (authTrans == null) {
Debug.logWarning("Authorized OrderPaymentPreference has no corresponding PaymentGatewayResponse, cannot capture payment: "
+ paymentPref, MODULE);
continue;
}
// check for an existing capture
GenericValue captureTrans = getCaptureTransaction(paymentPref);
if (captureTrans != null) {
Debug.logWarning("Attempt to capture and already captured preference: " + captureTrans, MODULE);
continue;
}
BigDecimal authAmount = authTrans.getBigDecimal("amount");
if (authAmount == null) {
authAmount = ZERO;
}
authAmount = authAmount.setScale(DECIMALS, ROUNDING);
if (authAmount.compareTo(ZERO) == 0) {
// nothing to capture
Debug.logInfo("Nothing to capture; authAmount = 0", MODULE);
continue;
}
// the amount for *this* capture
BigDecimal amountThisCapture;
// determine how much for *this* capture
if (isReplacementOrder(orderHeader)) {
// if it is a replacement order then just capture the auth amount
amountThisCapture = authAmount;
} else if (authAmount.compareTo(amountToCapture) >= 0) {
// if the auth amount is more then expected capture just capture what is expected
amountThisCapture = amountToCapture;
} else if (payments.hasNext()) {
// if we have more payments to capture; just capture what was authorized
amountThisCapture = authAmount;
} else {
// we need to capture more then what was authorized; re-auth for the new amount
// TODO: add what the billing account cannot support to the re-auth amount
// TODO: add support for re-auth for additional funds
// just in case; we will capture the authorized amount here; until this is implemented
Debug.logError("The amount to capture was more then what was authorized; we only captured the authorized amount : "
+ paymentPref, MODULE);
amountThisCapture = authAmount;
}
Map<String, Object> captureResult = capturePayment(dctx, userLogin, orh, paymentPref, amountThisCapture, locale);
if (captureResult != null && ServiceUtil.isSuccess(captureResult)) {
// credit card processors return captureAmount, but gift certificate processors return processAmount
BigDecimal amountCaptured = null;
try {
amountCaptured = (BigDecimal) ObjectType.simpleTypeOrObjectConvert(captureResult.get("captureAmount"), "BigDecimal",
null, locale);
if (amountCaptured == null) {
amountCaptured = (BigDecimal) captureResult.get("processAmount");
}
amountCaptured = amountCaptured.setScale(DECIMALS, ROUNDING);
// decrease amount of next payment preference to capture
amountToCapture = amountToCapture.subtract(amountCaptured);
// add the invoiceId to the result for processing, not for a replacement order
if (!isReplacementOrder(orderHeader)) {
captureResult.put("invoiceId", invoiceId);
}
// process the capture's results
processResult(dctx, captureResult, userLogin, paymentPref, locale);
} catch (GeneralException e) {
Debug.logError(e, "Trouble processing the result; captureResult: " + captureResult, MODULE);
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"AccountingPaymentCannotBeCaptured", locale) + " " + captureResult);
}
// create any splits which are needed
if (authAmount.compareTo(amountCaptured) > 0) {
BigDecimal splitAmount = authAmount.subtract(amountCaptured);
try {
Map<String, Object> splitCtx = UtilMisc.<String, Object>toMap("userLogin", userLogin, "orderPaymentPreference",
paymentPref, "splitAmount", splitAmount);
dispatcher.addCommitService("processCaptureSplitPayment", splitCtx, true);
} catch (GenericServiceException e) {
Debug.logWarning(e, "Problem processing the capture split payment", MODULE);
}
if (Debug.infoOn()) {
Debug.logInfo("Captured: " + amountThisCapture + " Remaining (re-auth): " + splitAmount, MODULE);
}
}
} else {
Debug.logError("Payment not captured", MODULE);
}
}
}
if (amountToCapture.compareTo(ZERO) > 0) {
GenericValue productStore = orh.getProductStore();
if (UtilValidate.isNotEmpty(productStore)) {
boolean shipIfCaptureFails = UtilValidate.isEmpty(productStore.get("shipIfCaptureFails"))
|| "Y".equalsIgnoreCase(productStore.getString("shipIfCaptureFails"));
if (!shipIfCaptureFails) {
return ServiceUtil.returnError(UtilProperties.getMessage(RES_ORDER,
"AccountingPaymentCannotBeCaptured", locale));
} else {
Debug.logWarning("Payment capture failed, shipping order anyway as per ProductStore setting (shipIfCaptureFails)", MODULE);
}
}
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("processResult", "FAILED");
return result;
} else {
Map<String, Object> result = ServiceUtil.returnSuccess();
result.put("processResult", "COMPLETE");
return result;
}
}