Author: jleroux
Date: Fri Sep 21 12:53:56 2007
New Revision: 578246
URL: http://svn.apache.org/viewvc?rev=578246&view=rev
Log:
Applied fix from trunk for revision: 571249
Modified:
ofbiz/branches/release4.0/applications/accounting/src/org/ofbiz/accounting/thirdparty/authorizedotnet/AIMPaymentServices.java
Modified:
ofbiz/branches/release4.0/applications/accounting/src/org/ofbiz/accounting/thirdparty/authorizedotnet/AIMPaymentServices.java
URL:
http://svn.apache.org/viewvc/ofbiz/branches/release4.0/applications/accounting/src/org/ofbiz/accounting/thirdparty/authorizedotnet/AIMPaymentServices.java?rev=578246&r1=578245&r2=578246&view=diff
==============================================================================
---
ofbiz/branches/release4.0/applications/accounting/src/org/ofbiz/accounting/thirdparty/authorizedotnet/AIMPaymentServices.java
(original)
+++
ofbiz/branches/release4.0/applications/accounting/src/org/ofbiz/accounting/thirdparty/authorizedotnet/AIMPaymentServices.java
Fri Sep 21 12:53:56 2007
@@ -19,21 +19,50 @@
package org.ofbiz.accounting.thirdparty.authorizedotnet;
-import java.util.*;
-
+import org.ofbiz.accounting.payment.PaymentGatewayServices;
import org.ofbiz.base.util.*;
-import org.ofbiz.entity.*;
-import org.ofbiz.service.*;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.service.DispatchContext;
+import org.ofbiz.service.ModelService;
+import org.ofbiz.service.ServiceUtil;
-import org.ofbiz.accounting.payment.PaymentGatewayServices;
+import java.sql.Timestamp;
+import java.util.*;
public class AIMPaymentServices {
public static final String module = AIMPaymentServices.class.getName();
+ // TODO: Reformat the comments below to fit JavaDocs specs
+
+ // The list of refund failure response codes that would cause the ccRefund
service
+ // to attempt to void the refund's associated authorization transaction.
This list
+ // contains the responses where the voiding does not need to be done
within a certain
+ // time limit
+ private static final List VOIDABLE_RESPONSES_NO_TIME_LIMIT =
UtilMisc.toList("50");
+
+ // A list of refund failure response codes that would cause the ccRefund
service
+ // to first check whether the refund's associated authorization
transaction has occurred
+ // within a certain time limit, and if so, cause it to void the transaction
+ private static final List VOIDABLE_RESPONSES_TIME_LIMIT =
UtilMisc.toList("54");
+
+ // The number of days in the time limit when one can safely consider an
unsettled
+ // transaction to be still valid
+ private static int TIME_LIMIT_VERIFICATION_DAYS = 120;
+
private static Properties AIMProperties = null;
+ // A routine to check whether a given refund failure response code will
cause the
+ // ccRefund service to attempt to void the refund's associated
authorization transaction
+ private static boolean isVoidableResponse(String responseCode) {
+ return
+ VOIDABLE_RESPONSES_NO_TIME_LIMIT.contains(responseCode) ||
+ VOIDABLE_RESPONSES_TIME_LIMIT.contains(responseCode);
+ }
+
public static Map ccAuth(DispatchContext ctx, Map context) {
Map results = ServiceUtil.returnSuccess();
Map request = new HashMap();
@@ -112,13 +141,6 @@
return results;
}
- public static Map ccRelease(DispatchContext ctx, Map context) {
- Map results = new HashMap();
- results.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_ERROR);
- results.put(ModelService.ERROR_MESSAGE, "Authorize.net ccRelease
unsupported with version 3.0");
- return results;
- }
-
public static Map ccRefund(DispatchContext ctx, Map context) {
GenericDelegator delegator = ctx.getDelegator();
GenericValue orderPaymentPreference = (GenericValue)
context.get("orderPaymentPreference");
@@ -160,11 +182,110 @@
}
Map reply = processCard(request, props);
+ results.putAll( processRefundTransResult(reply) );
+
+ boolean refundResult =
((Boolean)results.get("refundResult")).booleanValue();
+ String refundFlag = (String)results.get("refundFlag");
+
+ // Since the refund failed, we are going to void the previous
authorization against
+ // which ccRefunds attempted to issue the refund. This happens
because Authorize.NET requires
+ // that settled transactions need to be voided the same day.
unfortunately they provide no method for
+ // determining what transactions can be voided and what can be
refunded, so we'll have to try it with timestamps
+ if (!refundResult && isVoidableResponse(refundFlag)) {
+ boolean canDoVoid = false;
+
+ if (VOIDABLE_RESPONSES_TIME_LIMIT.contains(refundFlag)) {
+ // We are calculating the timestamp that is at the beginning
of a time limit,
+ // since we can safely assume that, within this time limit, an
unsettled transaction
+ // can still be considered valid
+ Calendar startCalendar =
UtilDateTime.toCalendar(UtilDateTime.nowTimestamp());
+ startCalendar.add(Calendar.DATE,
-TIME_LIMIT_VERIFICATION_DAYS);
+ Timestamp startTimestamp = new
java.sql.Timestamp(startCalendar.getTime().getTime());
+
+ Timestamp authTimestamp =
authTransaction.getTimestamp("transactionDate");
+
+ if (startTimestamp.before(authTimestamp)) {
+ canDoVoid = true;
+ }
+ } else {
+ // Since there's no time limit to check, the voiding of the
transaction will go
+ // through as usual
+ canDoVoid = true;
+ }
+
+ if (canDoVoid) {
+ Double authAmountObj = authTransaction.getDouble("amount");
+ Double refundAmountObj = (Double)context.get("refundAmount");
+
+ double authAmount = authAmountObj != null?
authAmountObj.doubleValue() : 0.0;
+ double refundAmount = refundAmountObj != null?
refundAmountObj.doubleValue() : 0.0;
+
+ if (authAmount == refundAmount) {
+ reply = voidTransaction(authTransaction, context);
+ if (ServiceUtil.isError(reply)) return reply;
+
+ results = ServiceUtil.returnSuccess();
+ results.putAll( processRefundTransResult(reply) );
+ return results;
+ } else {
+ // TODO: Modify the code to (a) do a void of the whole
transaction, and (b)
+ // create a new auth-capture of the difference.
+ return ServiceUtil.returnError("Cannot perform a VOID
transaction: authAmount [" + authAmount + "] is different than refundAmount ["
+ refundAmount + "]");
+ }
+ }
+ }
- processRefundTransResult(reply,results);
return results;
}
+ public static Map ccRelease(DispatchContext ctx, Map context) {
+ GenericValue orderPaymentPreference = (GenericValue)
context.get("orderPaymentPreference");
+
+ GenericValue creditCard = null;
+ try {
+ creditCard = orderPaymentPreference.getRelatedOne("CreditCard");
+ } catch (GenericEntityException e) {
+ Debug.logError(e, module);
+ return ServiceUtil.returnError("Unable to obtain cc information
from payment preference [ID = " +
orderPaymentPreference.getString("orderPaymentPreferenceId") + "]");
+ }
+
+ GenericValue authTransaction =
PaymentGatewayServices.getAuthTransaction(orderPaymentPreference);
+ if (authTransaction == null) {
+ return ServiceUtil.returnError("No authorization transaction found
for the OrderPaymentPreference [ID = " +
orderPaymentPreference.getString("orderPaymentPreferenceId") + "]; cannot
void");
+ }
+
+ Map reply = voidTransaction(authTransaction, context);
+ if (ServiceUtil.isError(reply)) return reply;
+
+ Map results = ServiceUtil.returnSuccess();
+ results.putAll( processReleaseTransResult(reply) );
+ return results;
+ }
+
+ private static Map voidTransaction(GenericValue authTransaction, Map
context) {
+ context.put("authTransaction",authTransaction);
+ Map results = ServiceUtil.returnSuccess();
+ Map request = new HashMap();
+
+ Properties props = buildAIMProperties(context);
+ buildMerchantInfo(context,props,request);
+ buildGatewayResponeConfig(context,props,request);
+ buildEmailSettings(context,props,request);
+ props.put("transType","VOID");
+ buildVoidTransaction(context,props,request);
+
+ Map validateResults = validateRequest(context,props,request);
+ String respMsg =
(String)validateResults.get(ModelService.RESPONSE_MESSAGE);
+ if(respMsg != null) {
+ if(respMsg.equals(ModelService.RESPOND_ERROR)) {
+ results.put(ModelService.ERROR_MESSAGE, "Validation Failed -
invalid values");
+ return results;
+ }
+ }
+
+ return processCard(request, props);
+ }
+
public static Map ccCredit(DispatchContext ctx, Map context) {
Map results = new HashMap();
results.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_ERROR);
@@ -460,6 +581,19 @@
Debug.logInfo("buildCaptureTransaction. " + at.toString(),module);
}
+ private static void buildVoidTransaction(Map params, Properties props, Map
AIMRequest) {
+ GenericValue at = (GenericValue)params.get("authTransaction");
+ String currency = (String) params.get("currency");
+
+ AIMRequest.put("x_Currency_Code",currency);
+ AIMRequest.put("x_Method", props.getProperty("method"));
+ AIMRequest.put("x_Type", props.getProperty("transType"));
+ AIMRequest.put("x_Trans_ID",at.get("referenceNum"));
+ AIMRequest.put("x_Auth_Code",at.get("gatewayCode"));
+
+ Debug.logInfo("buildVoidTransaction. " + at.toString(),module);
+ }
+
private static Map validateRequest(Map params, Properties props, Map
AIMRequest) {
Map result = new HashMap();
result.put(ModelService.RESPONSE_MESSAGE,
ModelService.RESPOND_SUCCESS);
@@ -510,23 +644,45 @@
Debug.logInfo("processCaptureTransResult: " +
results.toString(),module);
}
- private static void processRefundTransResult(Map reply, Map results) {
+ private static Map processRefundTransResult(Map reply) {
+ Map results = new HashMap();
AuthorizeResponse ar =
(AuthorizeResponse)reply.get("authorizeResponse");
Boolean captureResult = (Boolean)reply.get("authResult");
results.put("refundResult", new Boolean(captureResult.booleanValue()));
results.put("refundFlag",ar.getReasonCode());
results.put("refundMessage",ar.getReasonText());
+ results.put("refundRefNum",
ar.getResponseField(AuthorizeResponse.TRANSACTION_ID));
if(captureResult.booleanValue()) { //passed
results.put("refundCode",
ar.getResponseField(AuthorizeResponse.AUTHORIZATION_CODE));
- results.put("refundRefNum",
ar.getResponseField(AuthorizeResponse.TRANSACTION_ID));
results.put("refundAmount", new
Double(ar.getResponseField(AuthorizeResponse.AMOUNT)));
} else {
results.put("refundAmount", new Double("0.00"));
-
}
Debug.logInfo("processRefundTransResult: " +
results.toString(),module);
+ return results;
+ }
+
+ private static Map processReleaseTransResult(Map reply) {
+ Map results = new HashMap();
+ AuthorizeResponse ar =
(AuthorizeResponse)reply.get("authorizeResponse");
+ Boolean captureResult = (Boolean)reply.get("authResult");
+ results.put("releaseResult", new
Boolean(captureResult.booleanValue()));
+ results.put("releaseFlag",ar.getReasonCode());
+ results.put("releaseMessage",ar.getReasonText());
+ results.put("releaseRefNum",
ar.getResponseField(AuthorizeResponse.TRANSACTION_ID));
+
+ if(captureResult.booleanValue()) { //passed
+ results.put("releaseCode",
ar.getResponseField(AuthorizeResponse.AUTHORIZATION_CODE));
+ results.put("releaseAmount", new
Double(ar.getResponseField(AuthorizeResponse.AMOUNT)));
+ } else {
+ results.put("releaseAmount", new Double("0.00"));
+
+ }
+
+ Debug.logInfo("processReleaseTransResult: " +
results.toString(),module);
+ return results;
}
private static void processAuthCaptureTransResult(Map reply, Map results) {