Authorize.NET Payment Gateway Implementation
--------------------------------------------
Key: OFBIZ-274
URL: http://issues.apache.org/jira/browse/OFBIZ-274
Project: OFBiz (The Open for Business Project)
Issue Type: New Feature
Components: accounting
Affects Versions: SVN trunk
Reporter: Marco Risaliti
A lot of people over the years have requested and implemented Authorize.NET
payment gateway for OFBiz. This issue is for an official implementation that's
part of the standard OFBiz distribution.
I'm going to start by posting some suggestions from Andy on how to implement it
and also some documentation and samples. If you have code that you can share,
please use this issue tracker.
All Comments Work Log Change History Sort Order:
Comment by Si Chen [26/Jul/05 01:52 PM] [ Permlink ]
Andy's suggestions (from email):
> Looks like this will be very simple to implement. It does not appear that any
> external libraries will be needed so this integration will be able to come
> working with OFBiz out of the box.
>
> Just follow the examples included in OFBiz for CyberSource or any of the
> other currently implemented gateways this should give you a good start.
>
> You'll want to add the necessary configuration fields to the payment
> properties file. The transaction key, etc.
>
> -Andy
Comment by Si Chen [26/Jul/05 01:53 PM] [ Permlink ]
Sample Java code for Authorize.NET.
Comment by Si Chen [26/Jul/05 01:56 PM] [ Permlink ]
Official API guide
Comment by Si Chen [26/Jul/05 01:57 PM] [ Permlink ]
More developer's guide.
Comment by David E. Jones [30/Nov/05 11:06 PM] [ Permlink ]
BTW, this last comment was from Brett Palmer, not from me. It is a re-post with
some small changes of what Brett sent before.
Comment by David E. Jones [30/Nov/05 11:22 PM] [ Permlink ]
Here is our implementation of the Authorize.net interface. We use a simple
service to invoke the Authorize.net credit card processor. The service calls
the processCard method and returns the response from Authorize.net. Authorize
uses a simple HTTP protocol so it is pretty easy to work with. You will also
notice that we Authorize's test credit whenever we process a credit card that
starts with "7777". This is helpful when you want to test your implementation
and if you want to verify if Authorize is working.
We wrote this service before we started using common ofbiz credit card
processors like Verisign. We would like to see a common Authorize.net
implementation to follows the ofbiz standards.
/*********************************************/
public class AuthorizeNet {
public static final String module = AuthorizeNet.class.getName();
public static HashMap validateCCard(Map params) {
HashMap results = new HashMap();
if ("7777".equals(params.get("ccNumber"))) {
String number = "4111111111111111";
String monthExp = "01";
String yearExp = "01";
String type = "VISA";
String orderId = "1234";
String testReq = "TRUE";
String transAmt = "100";
String description = "test authorization";
Map testParams = new HashMap();
testParams.put("ccType",type);
testParams.put("ccNumber",number);
testParams.put("ccMonth",monthExp);
testParams.put("ccYear",yearExp);
testParams.put("orderId",orderId);
testParams.put("testReq",testReq);
testParams.put("ccTransDescription",description);
testParams.put("ccTransAmount",transAmt);
results = processCard(testParams);
} else {
params.put("testReq","FALSE");
params.put("ccTransDescription","authorization transaction");
results = processCard(params);
}
return results;
}
private static String getUrl() {
Properties props = UtilProperties.getProperties("authorize.properties");
String url = (String) props.get("authorize.net.url");
Debug.logInfo("Authorize url: " + url ,module);
//return "https://transact.authorize.net/gateway/transact.dll";
return url;
}
private static HashMap processCard(Map params) {
HashMap result = new HashMap();
String type = (String) UtilFormatOut.checkNull((String)params.get("ccType"));
String number = (String)
UtilFormatOut.checkNull((String)params.get("ccNumber"));
String monthExp = (String)
UtilFormatOut.checkNull((String)params.get("ccMonth"));
String yearExp = (String)
UtilFormatOut.checkNull((String)params.get("ccYear"));
String orderId = (String)
UtilFormatOut.checkNull((String)params.get("orderId"));
String testReq = (String)
UtilFormatOut.checkNull((String)params.get("testReq"));
String amount = (String)
UtilFormatOut.checkNull((String)params.get("ccTransAmount"));
String description = (String)
UtilFormatOut.checkNull((String)params.get("ccTransDescription"));
String url = getUrl();
String expDate = monthExp + yearExp;
Debug.logInfo("type = " + type,module);
Debug.logInfo("number = " + number,module);
Debug.logInfo("amount = " + amount,module);
Debug.logInfo("monthExp = " + monthExp,module);
Debug.logInfo("yearExp = " + yearExp,module);
Debug.logInfo("orderId = " + orderId,module);
Debug.logInfo("testReq = " + testReq,module);
Debug.logInfo("amount = " + amount,module);
Debug.logInfo("description = " + description,module);
// check minimum required values
if ((amount.length() == 0) || (number.length() == 0) ||
(testReq.length() == 0)||(expDate.length() == 0) ||
(type.length() == 0) || (orderId.length() == 0) ||
(amount.length() == 0) || (description.length() == 0)) {
result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_ERROR);
result.put(ModelService.ERROR_MESSAGE, "Minimum required - invalid values");
return result;
}
Map data = new HashMap();
String phone1 =
(String)UtilFormatOut.checkNull((String)params.get("contactPhone1"));
String phone2 =
(String)UtilFormatOut.checkNull((String)params.get("contactPhone2"));
String phone3 =
(String)UtilFormatOut.checkNull((String)params.get("contactPhone3"));
String phone = phone1 + "-" + phone2 + "-" + phone3; // not sure of the format
here
// contact information
data.put("x_first_name",UtilFormatOut.checkNull((String)params.get("billingFirstName")));
data.put("x_last_name",UtilFormatOut.checkNull((String)params.get("billinglastName")));
data.put("x_phone",phone);
data.put("x_address",UtilFormatOut.checkNull((String)params.get("billingAddress1")));
data.put("x_city",UtilFormatOut.checkNull((String)params.get("billingCity")));
data.put("x_state",UtilFormatOut.checkNull((String)params.get("billingState")));
data.put("x_zip",UtilFormatOut.checkNull((String)params.get("billingZip")));
data.put("x_country",UtilFormatOut.checkNull((String)params.get("billingCountry")));
data.put("x_email",UtilFormatOut.checkNull((String)params.get("email")));
// order information
data.put("x_card_num",number);
data.put("x_exp_date",expDate);
data.put("x_amount",amount);
data.put("x_cust_id","in2m-" + orderId);
data.put("x_invoice_num","in2m-" + orderId);
data.put("x_description",description);
// shipping information
if( "true".equals(params.get(UserEnroll.SETUP_SHIPPING))) {
data.put("x_ship_to_first_name",UtilFormatOut.checkNull((String)params.get("shippingFirstName")));
data.put("x_ship_to_last_name",UtilFormatOut.checkNull((String)params.get("shippingLastName")));
data.put("x_ship_to_address",UtilFormatOut.checkNull((String)params.get("shippingAddress")));
data.put("x_ship_to_city",UtilFormatOut.checkNull((String)params.get("shippingCity")));
data.put("x_ship_to_state",UtilFormatOut.checkNull((String)params.get("shippingState")));
data.put("x_ship_to_zip",UtilFormatOut.checkNull((String)params.get("shippingZip")));
data.put("x_ship_to_country",UtilFormatOut.checkNull((String)params.get("shippingCountry")));
}else{
data.put("x_ship_to_first_name",UtilFormatOut.checkNull((String)params.get("billingFirstName")));
data.put("x_ship_to_last_name",UtilFormatOut.checkNull((String)params.get("billingLastName")));
data.put("x_ship_to_address",UtilFormatOut.checkNull((String)params.get("billingAddress")));
data.put("x_ship_to_city",UtilFormatOut.checkNull((String)params.get("billingCity")));
data.put("x_ship_to_state",UtilFormatOut.checkNull((String)params.get("billingState")));
data.put("x_ship_to_zip",UtilFormatOut.checkNull((String)params.get("billingZip")));
data.put("x_ship_to_country",UtilFormatOut.checkNull((String)params.get("billingCountry")));
}
// authorize configuration defaults
data.put("x_version","3.1");
data.put("x_delim_data","TRUE");
data.put("x_method","CC");
data.put("x_type","AUTH_ONLY");
data.put("x_email_customer","FALSE");
data.put("x_email_merchant","TRUE");
data.put("x_Test_Request",testReq);
data.put("x_relay_reponse","FALSE");
data.put("x_login","XXXXX");
data.put("x_tran_key","XXXXX");
Debug.logInfo("Authorize.net data string " + data.toString(),module);
String respCode = EcommerceServices.VALID;
try {
HttpClient httpClient = new HttpClient(url,data);
httpClient.setClientCertificateAlias("AUTHORIZE_NET");
String httpResponse = httpClient.post();
AuthorizeResponse authorizeResponse = new AuthorizeResponse(httpResponse);
String authRespCode = authorizeResponse.get("x_response_code");
if (!authRespCode.equals("1")) {
respCode = EcommerceServices.DECLINED;
}
result.put("httpResponse",httpResponse);
result.put("authorizeResponse",authorizeResponse);
} catch (HttpClientException e) {
respCode = EcommerceServices.PENDING;
}
result.put("respCode",respCode);
result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_SUCCESS);
return result;
}
}
Comment by Kyle Tippetts [17/Jan/06 06:59 PM] [ Permlink ]
Skeletal Authorize.Net implementation
Comment by Kyle Tippetts [17/Jan/06 07:04 PM] [ Permlink ]
Here's a skeletal implementation based on what David posted here earlier, and
designed (to the best of my knowledge/ability) to work with the OFBiz credit
card processing system. (See the attached AIMPaymentServices and
AuthorizeResponse classes above). I only implemented the AUTH_CAPTURE process,
since that's what my client wanted (and didn't want OFBiz to handle the capture
-- go figure). Anyway, it's a start, I suppose. Any suggestions would be
*greatly* appreciated.
Here is a snippet of the authorizedotnet payment configuration from
payment.properties and below it the service definition. I'm sure the service
def could be cleaned up, but there it is:
############################################
# Authorize.Net Configuration
############################################
# Transaction Url
payment.authorizedotnet.url=https://secure.authorize.net/gateway/transact.dll
# Version
payment.authorizedotnet.version=3.1
# Delimited data
payment.authorizedotnet.delimited=TRUE
# Delimited Character
payment.authorizedotnet.delimiter=|
# Method
payment.authorizedotnet.method=CC
# Transaction Type
payment.authorizedotnet.type=AUTH_CAPTURE
# Email Customer?
payment.authorizedotnet.emailcustomer=FALSE
# Email Merchant?
payment.authorizedotnet.emailmerchant=TRUE
# Test Mode
payment.authorizedotnet.test=TRUE
# Relay Response?
payment.authorizedotnet.relay=FALSE
# Username
payment.authorizedotnet.login=
# Password
payment.authorizedotnet.password=
# Default Transaction Description
payment.authorizedotnet.transdescription=
--------------------------------------------------------
### Service Definition ###
<service name="aimAuthCapture" engine="java"
location="org.ofbiz.accounting.thirdparty.authorizedotnet.AIMPaymentServices"
invoke="ccAuthCapture">
<attribute name="billingAddress" type="GenericValue" mode="IN"
optional="true"/>
<attribute name="cardSecurityCode" type="Object" mode="IN" optional="true"/>
<attribute name="contactEmail" type="Object" mode="IN" optional="true"/>
<attribute name="contactPerson" type="Object" mode="IN" optional="true"/>
<attribute name="creditCard" type="GenericValue" mode="IN" optional="true"/>
<attribute name="currency" type="Object" mode="IN" optional="true"/>
<attribute name="orderId" type="String" mode="IN" optional="true"/>
<attribute name="orderItems" type="Object" mode="IN" optional="true"/>
<attribute name="orderPaymentPreference" type="Object" mode="IN"
optional="true"/>
<attribute name="paymentConfig" type="Object" mode="IN" optional="true"/>
<attribute name="processAmount" type="Double" mode="INOUT" optional="true"/>
<attribute name="shippingAddress" type="GenericValue" mode="IN"
optional="true"/>
<attribute name="authResult" type="Boolean" mode="OUT" optional="true"/>
<attribute name="captureResult" type="Boolean" mode="OUT" optional="true"/>
<attribute name="captureAmount" type="Double" mode="OUT" optional="true"/>
<attribute name="captureFlag" type="String" mode="OUT" optional="true"/>
<attribute name="captureMessage" type="String" mode="OUT" optional="true"/>
<attribute name="authCode" type="String" mode="OUT" optional="true"/>
<attribute name="authRefNum" type="String" mode="OUT" optional="false"/>
<attribute name="authFlag" type="String" mode="OUT" optional="true"/>
<attribute name="authMessage" type="String" mode="OUT" optional="true"/>
<attribute name="cvCode" type="String" mode="OUT" optional="true"/>
<attribute name="avsCode" type="String" mode="OUT" optional="true"/>
<attribute name="scoreCode" type="String" mode="OUT" optional="true"/>
<attribute name="captureCode" type="String" mode="OUT" optional="true"/>
<attribute name="captureRefNum" type="String" mode="OUT" optional="true"/>
<attribute name="internalRespMsgs" type="List" mode="OUT" optional="true"/>
<attribute name="customerRespMsgs" type="List" mode="OUT" optional="true"/>
</service>
Comment by Si Chen [20/Jan/06 06:00 PM] [ Permlink ]
Kyle,
I'll help you with this. Would you mind sending in one patch file including the
config, the service definitions, and the .java files you made? In general that
is a good thing to do--copying and pasting can lead to errors.
Thanks,
Si
Comment by Kyle Tippetts [21/Jan/06 11:02 PM] [ Permlink ]
Si,
Thanks for helping me out -- I'm new to all of this. To that end, can you
direct me to some instructions on how to create a patch file? I can't seem to
find anything online that provides that information. I think I need to use diff
and patch, but I want to make sure I do it right....
Thanks,
--Kyle
Comment by Si Chen [25/Jan/06 09:11 AM] [ Permlink ]
Go to your ofbiz/ directory and from the command line
$ svn diff > ofbiz385.patch
Then upload that file ofbiz385.patch
If you don't have the command line svn, go download it from here:
http://subversion.tigris.org/project_packages.html
There may also be plugins for eclipse, etc., but the idea is the same.
Comment by Fred Forester [10/Jun/06 01:10 PM] [ Permlink ]
Si,
have you done any work on this? If not I may run with it if that's ok.
Thanx
Fred
Comment by Si Chen [13/Jun/06 11:59 AM] [ Permlink ]
If you could that'd be great!
Comment by Fred Forester [14/Jun/06 10:05 AM] [ Permlink ]
Si,
I have something that works with version 3.0 of the certification gateway.
before I submit it I wanted to know the proper license info I should add if
any.
Thanx
Fred
Comment by Jacopo Cappellato [14/Jun/06 10:08 AM] [ Permlink ]
Hi Fred,
you'll find the license header in the file "APACHE2_HEADER" in the OFBiz
folder.
Thanks for asking this.
Comment by Fred Forester [14/Jun/06 10:48 AM] [ Permlink ]
Si,
version 3.0 only allows Auth,Capture,AuthCapture. there was some paranoia in
setting up a transkey on our account so I couldn't use 3.1.
The test property will log more data but testing is done via the certification
url. you can use live cards or test card #s. I havent tried the live url but
everything passes on the certification url.
thanx
Fred
Comment by Fred Forester [22/Jun/06 01:23 PM] [ Permlink ]
I recently founnd that it is possible to handle a credit transaction with AIM
3.0.
I'll try to get to it in a few weeks.
Fred
Comment by Si Chen [23/Jun/06 01:00 PM] [ Permlink ]
Ok, that'd be great!
Comment by Fred Forester [26/Jun/06 12:48 PM] [ Permlink ]
si,
this should add support for the CREDIT transaction. I was not able to do much
testing on this since the test gateway doesn't return a transid. according to
authdotnet you have to "test" these "live" :).
Comment by Si Chen [26/Jun/06 04:25 PM] [ Permlink ]
So should I wait for your AIM 3.0 implementation or look at these as they are?
Comment by Fred Forester [26/Jun/06 04:33 PM] [ Permlink ]
Si,
these should do it.
authnet_20060614.tgz, authnetcredit.patch
and this is the 3.0 implementation.
Thanx.
Fred
Comment by Leon Torres [27/Jun/06 05:38 PM] [ Permlink ]
Hi Fred,
The authnet_20060614.tgz, authnetcredit.patch have been committed into ofbiz
accounting as of r 7883. It would be nice if you could add some documentation
too, like how the test is triggered. Some of the comments and logging could
also be improved or cleaned up, otherwise it's looking good. Just send a patch
against 7883.
Leon
Comment by Fred Forester [27/Jun/06 05:47 PM] [ Permlink ]
Hi Leon
Sure, I can put something together for doc.
thanx for getting this in.
Fred
Comment by Fred Forester [28/Jun/06 05:07 PM] [ Permlink ]
Hi Leon,
Here is a text file with a short writeup.
Thanx
Fred
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira