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

        

Reply via email to