patch from Michael G. Schwern: Eliminate inheriting from AutoLoader. We're not using...
[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 Business::OnlinePayment);
13 @EXPORT = qw();
14 @EXPORT_OK = qw();
15 $VERSION = '3.16';
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_Ship_To_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
147         );
148
149         if (defined $self->{_content}->{customer_org} and
150             length  $self->{_content}->{customer_org}
151         ) {
152           push @required_fields, qw( customer_org customer_ssn );
153         } else {
154           push @required_fields, qw(license_num license_state license_dob);
155         }
156
157       } elsif ($self->transaction_type() eq 'CC' ) {
158
159         if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
160           if ( $self->{_content}->{order_number} ) {
161             push @required_fields, qw( amount order_number );
162           } else {
163             push @required_fields, qw( amount card_number expiration );
164           }
165         } elsif ( $self->{_content}->{action} eq 'CREDIT' ) {
166           push @required_fields, qw( amount order_number card_number );
167         } else {
168           push @required_fields, qw(
169             amount last_name first_name card_number expiration
170           );
171         }
172       } else {
173         Carp::croak( "AuthorizeNet can't handle transaction type: ".
174                      $self->transaction_type() );
175       }
176
177     }
178
179     $self->required_fields(@required_fields);
180
181     my %post_data = $self->get_fields(qw/
182         x_Login x_Password x_Tran_Key x_Invoice_Num
183         x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
184         x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
185         x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
186         x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
187         x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
188         x_Last_Name x_First_Name x_Company
189         x_Address x_City x_State x_Zip
190         x_Country
191         x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
192         x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
193         x_Ship_To_Country
194         x_Phone x_Fax x_Email x_Email_Customer x_Country
195         x_Currency_Code x_Trans_ID/);
196     $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
197     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
198     $post_data{'x_delim_char'} = ',';
199     $post_data{'x_encap_char'} = '"';
200     $post_data{'x_ADC_URL'} = 'FALSE';
201     $post_data{'x_Version'} = '3.1';
202
203     my $pd = make_form(%post_data);
204     my $s = $self->server();
205     my $p = $self->port();
206     my $t = $self->path();
207     my $r = $self->{_content}->{referer};
208     my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
209     #escape NULL (binary 0x00) values
210     $page =~ s/\x00/\^0/g;
211
212     my $csv = new Text::CSV_XS({ 'binary'=>1 });
213     $csv->parse($page);
214     my @col = $csv->fields();
215
216     $self->server_response($page);
217     $self->avs_code($col[5]);
218     $self->order_number($col[6]);
219     $self->md5($col[37]);
220     $self->cvv2_response($col[38]);
221     $self->cavv_response($col[39]);
222
223     if($col[0] eq "1" ) { # Authorized/Pending/Test
224         $self->is_success(1);
225         $self->result_code($col[0]);
226         $self->authorization($col[4]);
227     } else {
228         $self->is_success(0);
229         $self->result_code($col[2]);
230         $self->error_message($col[3]);
231         unless ( $self->result_code() ) { #additional logging information
232           #$page =~ s/\x00/\^0/g;
233           $self->error_message($col[3].
234             " DEBUG: No x_response_code from server, ".
235             "(HTTPS response: $server_response) ".
236             "(HTTPS headers: ".
237               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
238             "(Raw HTTPS content: $page)"
239           );
240         }
241     }
242 }
243
244 1;
245 __END__
246
247 =head1 NAME
248
249 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
250
251 =head1 SYNOPSIS
252
253   use Business::OnlinePayment;
254
255   ####
256   # One step transaction, the simple case.
257   ####
258
259   my $tx = new Business::OnlinePayment("AuthorizeNet");
260   $tx->content(
261       type           => 'VISA',
262       login          => 'testdrive',
263       password       => '',
264       action         => 'Normal Authorization',
265       description    => 'Business::OnlinePayment test',
266       amount         => '49.95',
267       invoice_number => '100100',
268       customer_id    => 'jsk',
269       first_name     => 'Jason',
270       last_name      => 'Kohles',
271       address        => '123 Anystreet',
272       city           => 'Anywhere',
273       state          => 'UT',
274       zip            => '84058',
275       card_number    => '4007000000027',
276       expiration     => '09/02',
277       cvv2           => '1234', #optional
278       referer        => 'http://valid.referer.url/',
279   );
280   $tx->submit();
281
282   if($tx->is_success()) {
283       print "Card processed successfully: ".$tx->authorization."\n";
284   } else {
285       print "Card was rejected: ".$tx->error_message."\n";
286   }
287
288   ####
289   # Two step transaction, authorization and capture.
290   # If you don't need to review order before capture, you can
291   # process in one step as above.
292   ####
293
294   my $tx = new Business::OnlinePayment("AuthorizeNet");
295   $tx->content(
296       type           => 'VISA',
297       login          => 'testdrive',
298       password       => '',
299       action         => 'Authorization Only',
300       description    => 'Business::OnlinePayment test',
301       amount         => '49.95',
302       invoice_number => '100100',
303       customer_id    => 'jsk',
304       first_name     => 'Jason',
305       last_name      => 'Kohles',
306       address        => '123 Anystreet',
307       city           => 'Anywhere',
308       state          => 'UT',
309       zip            => '84058',
310       card_number    => '4007000000027',
311       expiration     => '09/02',
312       cvv2           => '1234', #optional
313       referer        => 'http://valid.referer.url/',
314   );
315   $tx->submit();
316
317   if($tx->is_success()) {
318       # get information about authorization
319       $authorization = $tx->authorization
320       $ordernum = $tx->order_number;
321       $avs_code = $tx->avs_code; # AVS Response Code
322       $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code
323       $cavv_response = $tx->cavv_response; # Cardholder Authentication
324                                            # Verification Value (CAVV) Response
325                                            # Code
326
327       # now capture transaction
328       my $capture = new Business::OnlinePayment("AuthorizeNet");
329
330       $capture->content(
331           type           => 'CC',
332           action         => 'Post Authorization',
333           login          => 'YOURLOGIN
334           password       => 'YOURPASSWORD',
335           order_number   => $ordernum,
336           amount         => '49.95',
337       );
338
339       $capture->submit();
340
341       if($capture->is_success()) { 
342           print "Card captured successfully: ".$capture->authorization."\n";
343       } else {
344           print "Card was rejected: ".$capture->error_message."\n";
345       }
346
347   } else {
348       print "Card was rejected: ".$tx->error_message."\n";
349   }
350
351 =head1 SUPPORTED TRANSACTION TYPES
352
353 =head2 CC, Visa, MasterCard, American Express, Discover
354
355 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration.
356
357 =head2 Check
358
359 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name.
360
361 =head1 DESCRIPTION
362
363 For detailed information see L<Business::OnlinePayment>.
364
365 =head1 NOTE
366
367 Unlike Business::OnlinePayment or pre-3.0 verisons of
368 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
369 last_name fields.
370
371 Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced
372 Integration Method (AIM) (formerly known as ADC direct response)", sending a
373 username and transaction_key or password with every transaction.  Therefore, Authorize.Net's
374 referrer "security" is not necessary.  In your Authorize.Net interface at
375 https://secure.authorize.net/ make sure the list of allowable referers is
376 blank.  Alternatively, set the B<referer> field in the transaction content.
377
378 To settle an authorization-only transaction (where you set action to
379 'Authorization Only'), submit the nine-digit transaction id code in
380 the field "order_number" with the action set to "Post Authorization".
381 You can get the transaction id from the authorization by calling the
382 order_number method on the object returned from the authorization.
383 You must also submit the amount field with a value less than or equal
384 to the amount specified in the original authorization.
385
386 Recently (February 2002), Authorize.Net has turned address
387 verification on by default for all merchants.  If you do not have
388 valid address information for your customer (such as in an IVR
389 application), you must disable address verification in the Merchant
390 Menu page at https://secure.authorize.net/ so that the transactions
391 aren't denied due to a lack of address information.
392
393 =head1 COMPATIBILITY
394
395 This module implements Authorize.Net's API verison 3.1 using the Advanced
396 Integration Method (AIM), formerly known as ADC Direct Response.  See
397 http://www.authorize.net/support/AIM_guide.pdf for details.
398
399 =head1 AUTHOR
400
401 Jason Kohles, jason@mediabang.com
402
403 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
404 3.0/3.1 and is the current maintainer.  Please send patches as unified diffs
405 (diff -u).
406
407 Jason Spence <jspence@lightconsulting.com> contributed support for separate
408 Authorization Only and Post Authorization steps and wrote some docs.
409 OST <services@ostel.com> paid for it.
410
411 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
412
413 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
414
415 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
416
417 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
418 card-less post authorizations.
419
420 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
421 key" authentication as well support for the recurring_billing flag and the md5
422 method that returns the MD5 hash which is returned by the gateway.
423
424 =head1 SEE ALSO
425
426 perl(1). L<Business::OnlinePayment>.
427
428 =cut
429