fix rqeuired fields for echeck voiding
[Business-OnlinePayment-AuthorizeNet.git] / AuthorizeNet.pm
1 package Business::OnlinePayment::AuthorizeNet;
2
3 use strict;
4 use Carp;
5 use Business::OnlinePayment;
6 use Net::SSLeay qw/make_form post_https make_headers/;
7 use Text::CSV_XS;
8 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
9
10 require Exporter;
11
12 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
13 @EXPORT = qw();
14 @EXPORT_OK = qw();
15 $VERSION = '3.14';
16
17 sub set_defaults {
18     my $self = shift;
19
20     $self->server('secure.authorize.net');
21     $self->port('443');
22     $self->path('/gateway/transact.dll');
23
24     $self->build_subs(qw( order_number md5 avs_code cvv2_response
25                           cavv_response
26                      ));
27 }
28
29 sub map_fields {
30     my($self) = @_;
31
32     my %content = $self->content();
33
34     # ACTION MAP
35     my %actions = ('normal authorization' => 'AUTH_CAPTURE',
36                    'authorization only'   => 'AUTH_ONLY',
37                    'credit'               => 'CREDIT',
38                    'post authorization'   => 'PRIOR_AUTH_CAPTURE',
39                    'void'                 => 'VOID',
40                   );
41     $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
42
43     # TYPE MAP
44     my %types = ('visa'               => 'CC',
45                  'mastercard'         => 'CC',
46                  'american express'   => 'CC',
47                  'discover'           => 'CC',
48                  'check'              => 'ECHECK',
49                 );
50     $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
51     $self->transaction_type($content{'type'});
52
53     $content{'referer'} = defined( $content{'referer'} )
54                             ? make_headers( 'Referer' => $content{'referer'} )
55                             : "";
56
57     # stuff it back into %content
58     $self->content(%content);
59 }
60
61 sub remap_fields {
62     my($self,%map) = @_;
63
64     my %content = $self->content();
65     foreach(keys %map) {
66         $content{$map{$_}} = $content{$_};
67     }
68     $self->content(%content);
69 }
70
71 sub get_fields {
72     my($self,@fields) = @_;
73
74     my %content = $self->content();
75     my %new = ();
76     foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
77     return %new;
78 }
79
80 sub submit {
81     my($self) = @_;
82
83     $self->map_fields();
84     $self->remap_fields(
85         type              => 'x_Method',
86         login             => 'x_Login',
87         password          => 'x_Password',
88         transaction_key   => 'x_Tran_Key',
89         action            => 'x_Type',
90         description       => 'x_Description',
91         amount            => 'x_Amount',
92         currency          => 'x_Currency_Code',
93         invoice_number    => 'x_Invoice_Num',
94         order_number      => 'x_Trans_ID',
95         auth_code         => 'x_Auth_Code',
96         customer_id       => 'x_Cust_ID',
97         customer_ip       => 'x_Customer_IP',
98         last_name         => 'x_Last_Name',
99         first_name        => 'x_First_Name',
100         company           => 'x_Company',
101         address           => 'x_Address',
102         city              => 'x_City',
103         state             => 'x_State',
104         zip               => 'x_Zip',
105         country           => 'x_Country',
106         ship_last_name    => 'x_Ship_To_Last_Name',
107         ship_first_name   => 'x_Ship_To_First_Name',
108         ship_company      => 'x_Company',
109         ship_address      => 'x_Ship_To_Address',
110         ship_city         => 'x_Ship_To_City',
111         ship_state        => 'x_Ship_To_State',
112         ship_zip          => 'x_Ship_To_Zip',
113         ship_country      => 'x_Ship_To_Country',
114         phone             => 'x_Phone',
115         fax               => 'x_Fax',
116         email             => 'x_Email',
117         card_number       => 'x_Card_Num',
118         expiration        => 'x_Exp_Date',
119         cvv2              => 'x_Card_Code',
120         check_type        => 'x_Echeck_Type',
121         account_name      => 'x_Bank_Acct_Name',
122         account_number    => 'x_Bank_Acct_Num',
123         account_type      => 'x_Bank_Acct_Type',
124         bank_name         => 'x_Bank_Name',
125         routing_code      => 'x_Bank_ABA_Code',
126         customer_org      => 'x_Customer_Organization_Type', 
127         customer_ssn      => 'x_Customer_Tax_ID',
128         license_num       => 'x_Drivers_License_Num',
129         license_state     => 'x_Drivers_License_State',
130         license_dob       => 'x_Drivers_License_DOB',
131         recurring_billing => 'x_Recurring_Billing',
132     );
133
134     my $auth_type = $self->{_content}->{transaction_key}
135                       ? 'transaction_key'
136                       : 'password';
137
138     my @required_fields = ( qw(type action login), $auth_type );
139
140     unless ( $self->{_content}->{action} eq 'VOID' ) {
141
142       if ($self->transaction_type() eq "ECHECK") {
143
144         push @required_fields, qw(
145           amount routing_code account_number account_type bank_name
146           account_name account_type
147         );
148
149         if ($self->{_content}->{customer_org} ne '') {
150           push @required_fields, qw( customer_org customer_ssn );
151         } else {
152           push @required_fields, qw(license_num license_state license_dob);
153         }
154
155       } elsif ($self->transaction_type() eq 'CC' ) {
156
157         if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
158           if ( $self->{_content}->{order_number} ) {
159             push @required_fields, qw( amount order_number );
160           } else {
161             push @required_fields, qw( amount card_number expiration );
162           }
163         } else {
164           push @required_fields, qw(
165             amount last_name first_name card_number expiration
166           );
167         }
168       } else {
169         Carp::croak( "AuthorizeNet can't handle transaction type: ".
170                      $self->transaction_type() );
171       }
172
173     }
174
175     $self->required_fields(@required_fields);
176
177     my %post_data = $self->get_fields(qw/
178         x_Login x_Password x_Tran_Key x_Invoice_Num
179         x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
180         x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
181         x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
182         x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
183         x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
184         x_Last_Name x_First_Name x_Company
185         x_Address x_City x_State x_Zip
186         x_Country
187         x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
188         x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
189         x_Ship_To_Country
190         x_Phone x_Fax x_Email x_Email_Customer x_Country
191         x_Currency_Code x_Trans_ID/);
192     $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
193     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
194     $post_data{'x_ADC_URL'} = 'FALSE';
195     $post_data{'x_Version'} = '3.1';
196
197     my $pd = make_form(%post_data);
198     my $s = $self->server();
199     my $p = $self->port();
200     my $t = $self->path();
201     my $r = $self->{_content}->{referer};
202     my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
203     #escape NULL (binary 0x00) values
204     $page =~ s/\x00/\^0/g;
205
206     my $csv = new Text::CSV_XS({ 'binary'=>1 });
207     $csv->parse($page);
208     my @col = $csv->fields();
209
210     $self->server_response($page);
211     $self->avs_code($col[5]);
212     $self->order_number($col[6]);
213     $self->md5($col[37]);
214     $self->cvv2_response($col[38]);
215     $self->cavv_response($col[39]);
216
217     if($col[0] eq "1" ) { # Authorized/Pending/Test
218         $self->is_success(1);
219         $self->result_code($col[0]);
220         $self->authorization($col[4]);
221     } else {
222         $self->is_success(0);
223         $self->result_code($col[2]);
224         $self->error_message($col[3]);
225         unless ( $self->result_code() ) { #additional logging information
226           #$page =~ s/\x00/\^0/g;
227           $self->error_message($col[3].
228             " DEBUG: No x_response_code from server, ".
229             "(HTTPS response: $server_response) ".
230             "(HTTPS headers: ".
231               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
232             "(Raw HTTPS content: $page)"
233           );
234         }
235     }
236 }
237
238 1;
239 __END__
240
241 =head1 NAME
242
243 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
244
245 =head1 SYNOPSIS
246
247   use Business::OnlinePayment;
248
249   ####
250   # One step transaction, the simple case.
251   ####
252
253   my $tx = new Business::OnlinePayment("AuthorizeNet");
254   $tx->content(
255       type           => 'VISA',
256       login          => 'testdrive',
257       password       => '',
258       action         => 'Normal Authorization',
259       description    => 'Business::OnlinePayment test',
260       amount         => '49.95',
261       invoice_number => '100100',
262       customer_id    => 'jsk',
263       first_name     => 'Jason',
264       last_name      => 'Kohles',
265       address        => '123 Anystreet',
266       city           => 'Anywhere',
267       state          => 'UT',
268       zip            => '84058',
269       card_number    => '4007000000027',
270       expiration     => '09/02',
271       cvv2           => '1234', #optional
272       referer        => 'http://valid.referer.url/',
273   );
274   $tx->submit();
275
276   if($tx->is_success()) {
277       print "Card processed successfully: ".$tx->authorization."\n";
278   } else {
279       print "Card was rejected: ".$tx->error_message."\n";
280   }
281
282   ####
283   # Two step transaction, authorization and capture.
284   # If you don't need to review order before capture, you can
285   # process in one step as above.
286   ####
287
288   my $tx = new Business::OnlinePayment("AuthorizeNet");
289   $tx->content(
290       type           => 'VISA',
291       login          => 'testdrive',
292       password       => '',
293       action         => 'Authorization Only',
294       description    => 'Business::OnlinePayment test',
295       amount         => '49.95',
296       invoice_number => '100100',
297       customer_id    => 'jsk',
298       first_name     => 'Jason',
299       last_name      => 'Kohles',
300       address        => '123 Anystreet',
301       city           => 'Anywhere',
302       state          => 'UT',
303       zip            => '84058',
304       card_number    => '4007000000027',
305       expiration     => '09/02',
306       cvv2           => '1234', #optional
307       referer        => 'http://valid.referer.url/',
308   );
309   $tx->submit();
310
311   if($tx->is_success()) {
312       # get information about authorization
313       $authorization = $tx->authorization
314       $ordernum = $tx->order_number;
315       $avs_code = $tx->avs_code; # AVS Response Code
316       $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code
317       $cavv_response = $tx->cavv_response; # Cardholder Authentication
318                                            # Verification Value (CAVV) Response
319                                            # Code
320
321       # now capture transaction
322       my $capture = new Business::OnlinePayment("AuthorizeNet");
323
324       $capture->content(
325           type           => 'CC',
326           action         => 'Post Authorization',
327           login          => 'YOURLOGIN
328           password       => 'YOURPASSWORD',
329           order_number   => $ordernum,
330           amount         => '49.95',
331       );
332
333       $capture->submit();
334
335       if($capture->is_success()) { 
336           print "Card captured successfully: ".$capture->authorization."\n";
337       } else {
338           print "Card was rejected: ".$capture->error_message."\n";
339       }
340
341   } else {
342       print "Card was rejected: ".$tx->error_message."\n";
343   }
344
345 =head1 SUPPORTED TRANSACTION TYPES
346
347 =head2 CC, Visa, MasterCard, American Express, Discover
348
349 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration.
350
351 =head2 Check
352
353 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name.
354
355 =head1 DESCRIPTION
356
357 For detailed information see L<Business::OnlinePayment>.
358
359 =head1 NOTE
360
361 Unlike Business::OnlinePayment or pre-3.0 verisons of
362 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
363 last_name fields.
364
365 Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced
366 Integration Method (AIM) (formerly known as ADC direct response)", sending a
367 username and transaction_key or password with every transaction.  Therefore, Authorize.Net's
368 referrer "security" is not necessary.  In your Authorize.Net interface at
369 https://secure.authorize.net/ make sure the list of allowable referers is
370 blank.  Alternatively, set the B<referer> field in the transaction content.
371
372 To settle an authorization-only transaction (where you set action to
373 'Authorization Only'), submit the nine-digit transaction id code in
374 the field "order_number" with the action set to "Post Authorization".
375 You can get the transaction id from the authorization by calling the
376 order_number method on the object returned from the authorization.
377 You must also submit the amount field with a value less than or equal
378 to the amount specified in the original authorization.
379
380 Recently (February 2002), Authorize.Net has turned address
381 verification on by default for all merchants.  If you do not have
382 valid address information for your customer (such as in an IVR
383 application), you must disable address verification in the Merchant
384 Menu page at https://secure.authorize.net/ so that the transactions
385 aren't denied due to a lack of address information.
386
387 =head1 COMPATIBILITY
388
389 This module implements Authorize.Net's API verison 3.1 using the ADC
390 Direct Response method.  See
391 https://secure.authorize.net/docs/developersguide.pml for details.
392
393 =head1 AUTHOR
394
395 Jason Kohles, jason@mediabang.com
396
397 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
398 3.0/3.1 and is the current maintainer.  Please send patches as unified diffs
399 (diff -u).
400
401 Jason Spence <jspence@lightconsulting.com> contributed support for separate
402 Authorization Only and Post Authorization steps and wrote some docs.
403 OST <services@ostel.com> paid for it.
404
405 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
406
407 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
408
409 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
410
411 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
412 card-less post authorizations.
413
414 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
415 key" authentication as well support for the recurring_billing flag and the md5'
416 method that returns the MD5 hash which is returned by the gateway.
417
418 =head1 SEE ALSO
419
420 perl(1). L<Business::OnlinePayment>.
421
422 =cut
423