KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcommercesql > gateway > authorizenet > AuthorizeNet


1 package com.jcommercesql.gateway.authorizenet;
2
3 /**
4  * JCommerceSQL Authorize.net Gateway
5  *
6  * <b>Copyright 2003 by Richard Roehl. All Rights Reserved.</b>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */

23
24 import java.util.Hashtable JavaDoc;
25 import java.util.Properties JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27 import java.util.Vector JavaDoc;
28 import java.security.MessageDigest JavaDoc;
29 import java.io.*;
30
31 /**
32  *
33  * This class was designed using the Authorize.net Advanced Integration Method Implementation Guide v1.0.
34  * It is recommended that you download a copy of this guide to fully understand what gateway methods should
35  * be used for your particular situation.
36  *
37  */

38 public abstract class AuthorizeNet {
39
40   Properties JavaDoc properties = null;
41   String JavaDoc propFileName="";
42   Hashtable JavaDoc responseValues = new Hashtable JavaDoc();
43   Hashtable JavaDoc additionalHTTPHeaderFields = new Hashtable JavaDoc();
44
45   protected String JavaDoc host="";
46   protected int port=443;
47   protected String JavaDoc path="/";
48   protected char x_Delim_Char=',';
49
50   protected String JavaDoc postData="";
51   protected String JavaDoc merchantMD5HashValue="";
52   protected byte[] rawResponse=null;
53   Hashtable JavaDoc optionalFields=new Hashtable JavaDoc();
54   Vector JavaDoc merchantDefinedFields= new Vector JavaDoc();
55   
56   // Common to both Credit Card and eCheck transactions
57
protected String JavaDoc x_Version="3.1";
58   protected String JavaDoc x_Delim_Data="True";
59   protected String JavaDoc x_Login="";
60   protected String JavaDoc x_Tran_Key="";
61   protected String JavaDoc x_Amount="";
62
63   // Minimum information required for credit card = common fields above + following 3 fields
64
//x_Card_Num="";
65
//x_Exp_Date="";
66
//x_Type="";
67

68   // Minimum information required for eCheck = common fields above + following 6 fields
69
//x_Bank_aba_code
70
//x_Bank_Acct_Num
71
//x_Bank_Acct_Type
72
//x_Bank_Name
73
//x_bank_Acct_name
74
//x_Type
75

76
77 //======================================================
78

79   protected AuthorizeNet() {}
80
81 //------------------------------------------------------
82
/**
83     * Load the properties file
84     *
85     *
86    */

87   protected Properties JavaDoc loadPropFile(String JavaDoc propFileName) throws IOException, InvalidPropException {
88
89       Properties JavaDoc properties = new Properties JavaDoc();
90       properties.load(new FileInputStream(propFileName));
91       validateProperties(properties);
92       return properties;
93   }
94
95 //------------------------------------------------------
96
/**
97     * Re-Load the Properties File
98     *
99     * Useful if you want to modify the existing properties in real time without restarting the application.
100     * Reloads the properties from the file name used in the class initializer. If class was initialized
101     * without a properties file then an IOException occurs;
102    */

103   public void reloadProperties() throws IOException, InvalidPropException {
104     reloadProperties(this.propFileName);
105   }
106
107 //------------------------------------------------------
108

109   /**
110     * Re-Load the Properties File
111     *
112     * Useful if you want to modify the properties in real time without restarting the application.
113     * If an exception is thrown, it will not affect the existing properties.
114     * @param propFileName the name of the file to read the properties from.
115    */

116   public void reloadProperties(String JavaDoc propFileName) throws IOException, InvalidPropException {
117
118       Properties JavaDoc tempProp=loadPropFile(propFileName);
119       this.properties=tempProp;
120       this.propFileName=propFileName;
121   }
122
123 //----------------------------------------------------------------------------
124
/**
125     * Validate Properties
126     * @param p Properties object
127     */

128   protected void validateProperties(Properties JavaDoc p) throws InvalidPropException {
129     if (p == null) { throw new InvalidPropException("validateProperties(): null Properties argument"); }
130
131     if (p.getProperty("host","").equals("")) {
132     throw new InvalidPropException("'host' is invalid or not found in properties file.");
133     } else {
134     setHost(p.getProperty("host"));
135     }
136
137     if (p.getProperty("port","").equals("")) {
138     throw new InvalidPropException("'host' is invalid or not found in properties file.");
139     }
140
141     try {
142       setPort(Integer.parseInt(p.getProperty("port")));
143     } catch(NumberFormatException JavaDoc nfe) {
144     throw new InvalidPropException("port '"+ p.getProperty("port")+"' does not appear to be an integer");
145     }
146
147     if (p.getProperty("path","").equals("")) {
148     throw new InvalidPropException("'path' is invalid or not found in properties file.");
149     } else {
150     setPath(p.getProperty("path"));
151     }
152
153     if (p.getProperty("loginid","").equals("")) {
154     throw new InvalidPropException("'login' is invalid or not found in properties file.");
155     } else {
156     setLogin(p.getProperty("loginid"));
157     }
158
159     if (p.getProperty("trankey","").equals("")) {
160     throw new InvalidPropException("'trankey' is invalid or not found in properties file.");
161     } else {
162     setTranKey(p.getProperty("trankey"));
163     }
164
165   }
166
167 //------------------------------------------------------
168
/**
169    * set Version
170    *
171    */

172    protected void setVersion(String JavaDoc version ) { this.x_Version=version; }
173
174 //------------------------------------------------------
175
/**
176    * get Version
177    *
178    */

179    protected String JavaDoc getVersion() { return x_Version; }
180
181 //------------------------------------------------------
182
/**
183    * set Delimit Data
184    * @param delimData
185    *
186    * The default value is true.
187    */

188    protected void setDelimData(boolean delimData) {
189     this.x_Delim_Data=(new Boolean JavaDoc(delimData).toString()).toUpperCase();
190    }
191
192 //------------------------------------------------------
193
/**
194    * is data delimited
195    *
196    */

197
198    protected boolean getDelimData() { return (new Boolean JavaDoc(x_Delim_Data)).booleanValue(); }
199
200 //------------------------------------------------------
201
/**
202    * set delimiter character
203    * @param delimChar the character used to delimit data in the response
204    *
205    */

206    public void setDelimChar(char delimChar) { this.x_Delim_Char=delimChar; }
207
208 //------------------------------------------------------
209
/**
210    * get delimiter character
211    *
212    */

213    protected char getDelimChar() { return x_Delim_Char; }
214
215 //------------------------------------------------------
216
/**
217    * Sets the Authorize.net Merchant Login ID and Transaction Key.
218    *
219    *<p>This is an alternative method to setting the login ID and transaction key with individual calls.
220    * @param loginID Merchant Login ID
221    * @param tranKey
222    */

223    public void setMerchantInfo(String JavaDoc loginID, String JavaDoc tranKey ) {
224     setLogin(loginID);
225     setTranKey(tranKey);
226    }
227
228 //------------------------------------------------------
229
/**
230    * Sets the Authorize.net Merchant Login ID
231    * @param loginID Merchant Login ID
232    * @see AuthorizeNet#setMerchantInfo(String,String) setMerchantInfo()
233    */

234    public void setLogin(String JavaDoc loginID ) { this.x_Login=loginID; }
235
236 //------------------------------------------------------
237
/**
238    * Returns the Authorize.net Merchant Login ID as set by the <code>setLogin</code> method.
239    *
240    */

241    public String JavaDoc getLogin() { return x_Login; }
242
243 //------------------------------------------------------
244
/**
245    * Sets the Authorize.net Merchant Transaction Key
246    * @param tranKey transaction key
247    * @see AuthorizeNet#setMerchantInfo(String loginID, String trandKey) setMerchantInfo()
248    */

249    public void setTranKey(String JavaDoc tranKey) { this.x_Tran_Key=tranKey; }
250
251 //------------------------------------------------------
252
/**
253    * Returns the Authorize.net Merchant Transaction Key as set by the <code>setTranKey()</code> method.
254    *
255    */

256    public String JavaDoc getTranKey() { return x_Tran_Key; }
257
258 //------------------------------------------------------
259
/**
260    * Sets the amount value for this transaction.
261    * @param amount the amount of the transaction.
262    */

263    public void setAmount(String JavaDoc amount) { this.x_Amount=amount; }
264
265 //------------------------------------------------------
266
/**
267    * Returns the amount value for this transaction as set by the <code>setAmount()</code> method.
268    *
269    */

270    public String JavaDoc getAmount() { return x_Amount; }
271
272 //------------------------------------------------------
273
/**
274    * Sets the Authorize.net gateway host.
275    * @param host the Authorize.net gatway host
276    * @see AuthorizeNet#setURL(String host, int port, String path) setURL()
277    */

278    public void setHost(String JavaDoc host) { this.host=host; }
279
280 //------------------------------------------------------
281
/**
282    * Returns the Authorize.net gateway host value as set by the <code>setHost()</code> method.
283    *
284    */

285    public String JavaDoc getHost() { return host; }
286
287 //------------------------------------------------------
288
/**
289    * Sets the Authorize.net gateway port
290    *
291    * Defaults to 443.
292    * @param port the port the gateway is running on
293    * @see AuthorizeNet#setURL(String host, int port, String path) setURL()
294    */

295    public void setPort(int port) { this.port=port; }
296
297 //------------------------------------------------------
298
/**
299    * Returns the Authorize.net gateway port as set by the <code>setPort</code> method or <code>setURL()</code>.
300    *
301    */

302    public int getPort() { return port; }
303
304 //------------------------------------------------------
305
/**
306    * Sets the Authorize.net gateway path.
307    * @param path the Authorize.net gateway path
308    * @see AuthorizeNet#setURL(String host, int port, String path) setURL()
309    */

310    public void setPath(String JavaDoc path) { this.path=path; }
311
312 //------------------------------------------------------
313
/**
314    * Returns the path portion of the Authorize.net URL as set by <code>setPath()</code> or <code>setURL()</code>.
315    *
316    */

317    public String JavaDoc getPath() { return path; }
318
319 //------------------------------------------------------
320
/**
321    * set the components of the Authorize.net URL
322    * @param host tbd
323    * @param port tbd
324    * @param path tbd
325    */

326    public void setURL(String JavaDoc host, int port, String JavaDoc path) {
327       setHost(host);
328       setPort(port);
329       setPath(path);
330    }
331
332 //------------------------------------------------------
333
/**
334    * Returns the request string that was submitted to the Authorize.net gateway.
335    *
336    * Can be used for troubleshooting and will only return data after <code>submit()</code> has been called.
337    *
338    */

339    public String JavaDoc getPostData() { return this.postData; }
340
341 //------------------------------------------------------
342
/**
343    * Returns the response byte array received from the Authorize.net gateway.
344    *
345    * Can be used for troubleshooting and will only return data after <code>submit()</code> has been called.
346    *
347    */

348    public byte[] getResponseBytes() { return this.rawResponse; }
349
350 //------------------------------------------------------
351
/**
352     * Set Test Mode
353     *
354     * Authorize.net supports testing by passing the argument x_Test_Request=true. When test mode is set to true
355     * all transactions appear to be processed as real transactions but are not passed on to a financial institution.
356     * Accordingly, all transactions will be approved by the gatway when test mode is turned on.
357     *
358     * @param value true or false
359     * @see AuthorizeNet#removeTestMode() removeTestMode()
360     */

361   public void setTestMode(boolean value) {
362     addOptionalField(AuthorizeNetCodes.REQ_FIELD_TEST_REQUEST,(new Boolean JavaDoc(value).toString()).toUpperCase());
363   }
364
365 //------------------------------------------------------
366
/**
367     * Remove Test Mode
368     *
369     * Remove Test Mode omits the argument x_Test_Request completely. This is the default behavior.
370     * @see AuthorizeNet#setTestMode(boolean value) setTestMode()
371     */

372   public void removeTestMode() {
373     removeOptionalField(AuthorizeNetCodes.REQ_FIELD_TEST_REQUEST);
374   }
375
376 //------------------------------------------------------
377
/**
378     * Add an optional field to the Authorize.net transaction request.
379     *
380     * The Authorize.net transaction gateway has a number of optional fields that can be passed when submitting a
381     * transaction. For example if you wish to utilize the Address Verification System you must pass the customer's
382     * street address and zip code as part of the request. According to the Advanced Implementation Manual the correct
383     * field for the customers address is <code>x_Address</code> and for the zip it is <code>x_Zip</code>. Therefore you
384     * would call this method twice with the following values:<code><pre>
385     * addOptionalField("x_Address","123 Main St.")
386     * addOptionalField("x_Zip","10101")
387     *
388     * </pre></code>
389     *
390     * @param fieldName field name as defined in the AIM
391     * @param fieldValue value of field being set
392     */

393   public void addOptionalField(String JavaDoc fieldName, String JavaDoc fieldValue) {
394     optionalFields.put(fieldName,fieldValue);
395   }
396
397 //------------------------------------------------------
398
/**
399     * Remove Optional Field
400     *
401     * If you have previously set an optional field you can remove the field with the method. Nothing is done
402     * if the fieldName passed does not previously exist.
403     *
404     * @param fieldName Name of optional parameter to be removed
405     */

406   public String JavaDoc removeOptionalField(String JavaDoc fieldName) {
407     return (String JavaDoc)optionalFields.remove(fieldName);
408   }
409
410 //------------------------------------------------------
411
protected void parseANetResponse(byte[] httpResponse) {
412
413     String JavaDoc in = new String JavaDoc(httpResponse);
414     String JavaDoc httpHeaders;
415     String JavaDoc response;
416
417     int offset=in.indexOf("\r\n\r\n");
418
419     if (offset==-1) {
420        System.out.println("Header/Body Break Not Found!\n"+in);
421     } else {
422         httpHeaders=new String JavaDoc(httpResponse,0,offset);
423     //storeHTTPHeaders(httpHeaders)
424
offset+=4;
425         response=new String JavaDoc(httpResponse,offset,httpResponse.length-offset);
426         parseANetResponse(response);
427     }
428     // store result in case needed for troubleshooting
429
this.rawResponse=httpResponse;
430   }
431
432  
433 //------------------------------------------------------
434
private void parseANetResponse(String JavaDoc responseString) {
435     // was the delimiter reset changed from default character of comma?
436
String JavaDoc delimiter = (new Character JavaDoc(getDelimChar())).toString();
437     boolean currentTokenIsDelimeter = false;
438     boolean lastTokenWasDelimeter = false;
439     int positionCounter=0;
440     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(responseString, delimiter, true);
441     while (st.hasMoreTokens()) {
442         String JavaDoc fieldValue = st.nextToken();
443         
444         // determine if current token is a delimeter
445
if (fieldValue.equals(delimiter))
446             currentTokenIsDelimeter = true;
447         else
448             currentTokenIsDelimeter = false;
449             
450         // if we read 2 delimeters in a row inset an empty string
451
if (currentTokenIsDelimeter && lastTokenWasDelimeter)
452             responseValues.put(new Integer JavaDoc(++positionCounter),"");
453         // if current token is not a delimeter insert it
454
else if (!currentTokenIsDelimeter)
455             responseValues.put(new Integer JavaDoc(++positionCounter),fieldValue);
456
457         // set last token flag to be the same as the token we just read
458
lastTokenWasDelimeter = currentTokenIsDelimeter;
459     }
460   }
461
462 //------------------------------------------------------
463
// private void storeHTTPHeaders(String httpHeaders) {
464
// //convert newlines crs to newline only
465
// // Tokenize on newline and then split on colon
466
// }
467

468 //------------------------------------------------------
469
protected boolean verifyMD5Hash() {
470     String JavaDoc md5hash=getResponseValue(AuthorizeNetCodes.RESP_FIELD_MD5HASH);
471     return verifyMD5Hash(md5hash);
472   }
473
474 //------------------------------------------------------
475
protected boolean verifyMD5Hash(String JavaDoc md5HashValue) {
476
477     // hash value is built from merchantMD5HashValue,loginid,transid,amount
478
String JavaDoc hashKey;
479     hashKey=merchantMD5HashValue + x_Login +
480     getResponseValue(AuthorizeNetCodes.RESP_FIELD_TRANSACTIONID) +
481     getResponseValue(AuthorizeNetCodes.RESP_FIELD_AMOUNT);
482     String JavaDoc localMD5Result=calcMD5Hash(hashKey);
483     if (localMD5Result.equals(md5HashValue)) {
484        return true;
485     } else {
486        return false;
487     }
488   }
489
490 //------------------------------------------------------
491
protected String JavaDoc calcMD5Hash(String JavaDoc input) {
492     try {
493       MessageDigest JavaDoc md = MessageDigest.getInstance("MD5");
494       return new String JavaDoc( md.digest(input.getBytes()) );
495     } catch(Exception JavaDoc e) { System.out.println("Can't load MD5 algorithm"); }
496     return "";
497   }
498
499 //------------------------------------------------------
500
/**
501     * Get Response Value by index from Authorize.net Gateway Transaction.
502     *
503     * <p>When a transaction is submitted to the Authoirze.net gateway, a comma
504     * seperated list of result fields is returned. The first seven fields are
505     * accessible using method from this class as they are the most important in
506     * determining whether a transaction was successful or not. The remaining 50+ fields
507     * can be access by index number using this method. Refer to the AIM manual
508     * for definitions of what other response fields are available.
509     *
510     * @param responsePosition index number of the reponse field
511     * @see AuthorizeNet#getResponseCode() getResponseCode()
512     * @see AuthorizeNet#getResponseSubCode() getResponseSubCode()
513     * @see AuthorizeNet#getResponseReasonCode() getResponseReasonCode()
514     * @see AuthorizeNet#getResponseReasonText() getResponseReasonText()
515     * @see AuthorizeNet#getResponseApprovalCode() getResponseApprovalCode()
516     * @see AuthorizeNet#getResponseAVSResultCode() getResponseAVSResultCode()
517     * @see AuthorizeNet#getResponseTransactionID getResponseTransactionID
518     *
519     */

520   public String JavaDoc getResponseValue(int responsePosition) {
521     return (String JavaDoc)responseValues.get(new Integer JavaDoc(responsePosition));
522   }
523
524 /**
525   * Gets the response code after the transaction has been submitted.
526   *
527   * Possible values are: 1=Success, 2=Declined, 3=Error.
528   * See the Advanced Integration Method Implementation Guide for more information.
529   *
530   */

531   public String JavaDoc getResponseCode() { return getResponseValue(1); }
532
533 /**
534   * Gets the Response Sub Code after the transaction has been submitted.
535   * See the Advanced Integration Method Implementation Guide for more information.
536   */

537   public String JavaDoc getResponseSubCode() { return getResponseValue(2); }
538
539 /**
540   * gets the response reason code after the transaction has been submitted.
541   * See the Advanced Integration Method Implementation Guide for more information.
542   */

543   public String JavaDoc getResponseReasonCode() { return getResponseValue(3); }
544
545 /**
546   * Gets the Response Reason Text after the transaction has been submitted.
547   * See the Advanced Integration Method Implementation Guide for more information.
548   */

549 public String JavaDoc getResponseReasonText() { return getResponseValue(4); }
550
551 /**
552   * Gets the ResponseApprovalCode after transaction has been submitted.
553   * See the Advanced Integration Method Implementation Guide for more information.
554   */

555 public String JavaDoc getResponseApprovalCode() { return getResponseValue(5); }
556
557 /**
558   * Gets the Address Verification Result Code aftger the transaction has been submitted.
559   * See the Advanced Integration Method Implementation Guide for more information.
560   */

561 public String JavaDoc getResponseAVSResultCode() { return getResponseValue(6); }
562
563 /**
564   * Gets Response Transaction ID after the transaction has been submitted.
565   * See the Advanced Integration Method Implementation Guide for more information.
566   */

567 public String JavaDoc getResponseTransactionID() { return getResponseValue(7); }
568
569   /**
570     * Add Merchant Defined Field
571     *
572     * The Authorize.net transaction gateway has the ability to handle merchant defined fields in addition to
573     * the required and optional fields defined in the AIM manual.
574     *
575     * The transaction gateway will accept any key/value String pair if the key is not already defined by the
576     * AIM manual. The merchant defined fields will be echoed back in the response API starting in position
577     * 69 in the same order they were entered.
578     * <code><pre>
579     * // echoed in position 69 of the response API
580     * addMerchantDefinedField("any_key_you_want","This can be any information you want associated with the transaction")
581     * // echoed in position 70 of the response API
582     * addMerchantDefinedField("another_key","Some more information")
583     * // echoed in position 71 of the responseAPI
584     * addMerchantDefinedField("merchant_comment","This is a test transaction")
585     * </pre></code>
586     *
587     * @param fieldName name
588     * @param fieldValue value
589     *
590     */

591   public void addMerchantDefinedField(String JavaDoc fieldName, String JavaDoc fieldValue) {
592       merchantDefinedFields.add(fieldName);
593       merchantDefinedFields.add(fieldValue);
594   }
595
596 //------------------------------------------------------
597
protected boolean verifyMinimumFieldsSet() {
598
599     boolean result=true;
600     if (x_Version==null) result = false;
601     if (x_Delim_Data==null || x_Delim_Data.equals("") ) result = false;
602     if (x_Login==null || x_Login.equals("") ) result = false;
603     if (x_Tran_Key==null || x_Tran_Key.equals("") ) result = false;
604     if (x_Amount==null || x_Amount.equals("") ) result = false;
605
606     return result;
607   }
608
609 //------------------------------------------------------
610
protected void submitANetGet() {
611
612   }
613
614 //------------------------------------------------------
615
protected void submitANetPost(String JavaDoc host, int port, String JavaDoc path, String JavaDoc postdata) throws Exception JavaDoc {
616     submitANetPost(host,port,path,postdata.getBytes());
617   }
618 //------------------------------------------------------
619
protected void submitANetPost(String JavaDoc host, int port, String JavaDoc path, byte[] postdata) throws Exception JavaDoc {
620
621     Gateway gw=new Gateway();
622     byte[] rawResponse = gw.submitPost(host,port,path,postdata);
623     parseANetResponse(rawResponse);
624   }
625
626 //------------------------------------------------------
627
public abstract void submit() throws Exception JavaDoc;
628
629 }
630
Popular Tags