Trim the extra 'ip_addr="1.2.3.4"' added by eProcessingNetwork's AuthorizetNet compat...
[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.17';
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     #trim 'ip_addr="1.2.3.4"' added by eProcessingNetwork Authorize.Net compat
213     $page =~ s/,ip_addr="[\d\.]+"$//;
214
215     my $csv = new Text::CSV_XS({ 'binary'=>1 });
216     $csv->parse($page);
217     my @col = $csv->fields();
218
219     $self->server_response($page);
220     $self->avs_code($col[5]);
221     $self->order_number($col[6]);
222     $self->md5($col[37]);
223     $self->cvv2_response($col[38]);
224     $self->cavv_response($col[39]);
225
226     if($col[0] eq "1" ) { # Authorized/Pending/Test
227         $self->is_success(1);
228         $self->result_code($col[0]);
229         $self->authorization($col[4]);
230     } else {
231         $self->is_success(0);
232         $self->result_code($col[2]);
233         $self->error_message($col[3]);
234         unless ( $self->result_code() ) { #additional logging information
235           #$page =~ s/\x00/\^0/g;
236           $self->error_message($col[3].
237             " DEBUG: No x_response_code from server, ".
238             "(HTTPS response: $server_response) ".
239             "(HTTPS headers: ".
240               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
241             "(Raw HTTPS content: $page)"
242           );
243         }
244     }
245 }
246
247 1;
248 __END__
249
250 =head1 NAME
251
252 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
253
254 =head1 SYNOPSIS
255
256   use Business::OnlinePayment;
257
258   ####
259   # One step transaction, the simple case.
260   ####
261
262   my $tx = new Business::OnlinePayment("AuthorizeNet");
263   $tx->content(
264       type           => 'VISA',
265       login          => 'testdrive',
266       password       => '',
267       action         => 'Normal Authorization',
268       description    => 'Business::OnlinePayment test',
269       amount         => '49.95',
270       invoice_number => '100100',
271       customer_id    => 'jsk',
272       first_name     => 'Jason',
273       last_name      => 'Kohles',
274       address        => '123 Anystreet',
275       city           => 'Anywhere',
276       state          => 'UT',
277       zip            => '84058',
278       card_number    => '4007000000027',
279       expiration     => '09/02',
280       cvv2           => '1234', #optional
281       referer        => 'http://valid.referer.url/',
282   );
283   $tx->submit();
284
285   if($tx->is_success()) {
286       print "Card processed successfully: ".$tx->authorization."\n";
287   } else {
288       print "Card was rejected: ".$tx->error_message."\n";
289   }
290
291   ####
292   # Two step transaction, authorization and capture.
293   # If you don't need to review order before capture, you can
294   # process in one step as above.
295   ####
296
297   my $tx = new Business::OnlinePayment("AuthorizeNet");
298   $tx->content(
299       type           => 'VISA',
300       login          => 'testdrive',
301       password       => '',
302       action         => 'Authorization Only',
303       description    => 'Business::OnlinePayment test',
304       amount         => '49.95',
305       invoice_number => '100100',
306       customer_id    => 'jsk',
307       first_name     => 'Jason',
308       last_name      => 'Kohles',
309       address        => '123 Anystreet',
310       city           => 'Anywhere',
311       state          => 'UT',
312       zip            => '84058',
313       card_number    => '4007000000027',
314       expiration     => '09/02',
315       cvv2           => '1234', #optional
316       referer        => 'http://valid.referer.url/',
317   );
318   $tx->submit();
319
320   if($tx->is_success()) {
321       # get information about authorization
322       $authorization = $tx->authorization
323       $ordernum = $tx->order_number;
324       $avs_code = $tx->avs_code; # AVS Response Code
325       $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code
326       $cavv_response = $tx->cavv_response; # Cardholder Authentication
327                                            # Verification Value (CAVV) Response
328                                            # Code
329
330       # now capture transaction
331       my $capture = new Business::OnlinePayment("AuthorizeNet");
332
333       $capture->content(
334           type           => 'CC',
335           action         => 'Post Authorization',
336           login          => 'YOURLOGIN
337           password       => 'YOURPASSWORD',
338           order_number   => $ordernum,
339           amount         => '49.95',
340       );
341
342       $capture->submit();
343
344       if($capture->is_success()) { 
345           print "Card captured successfully: ".$capture->authorization."\n";
346       } else {
347           print "Card was rejected: ".$capture->error_message."\n";
348       }
349
350   } else {
351       print "Card was rejected: ".$tx->error_message."\n";
352   }
353
354 =head1 SUPPORTED TRANSACTION TYPES
355
356 =head2 CC, Visa, MasterCard, American Express, Discover
357
358 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration.
359
360 =head2 Check
361
362 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name.
363
364 =head1 DESCRIPTION
365
366 For detailed information see L<Business::OnlinePayment>.
367
368 =head1 METHODS AND FUNCTIONS
369
370 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
371
372 =head2 result_code
373
374 Returns the response reason code (this is different than the response code).
375
376 =head2 error_message
377
378 Returns the response reason text.
379
380 =head2 server_response
381
382 Returns the complete response from the server.
383
384 =head1 NOTE
385
386 Unlike Business::OnlinePayment or pre-3.0 verisons of
387 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
388 last_name fields.
389
390 Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced
391 Integration Method (AIM) (formerly known as ADC direct response)", sending a
392 username and transaction_key or password with every transaction.  Therefore, Authorize.Net's
393 referrer "security" is not necessary.  In your Authorize.Net interface at
394 https://secure.authorize.net/ make sure the list of allowable referers is
395 blank.  Alternatively, set the B<referer> field in the transaction content.
396
397 To settle an authorization-only transaction (where you set action to
398 'Authorization Only'), submit the nine-digit transaction id code in
399 the field "order_number" with the action set to "Post Authorization".
400 You can get the transaction id from the authorization by calling the
401 order_number method on the object returned from the authorization.
402 You must also submit the amount field with a value less than or equal
403 to the amount specified in the original authorization.
404
405 Recently (February 2002), Authorize.Net has turned address
406 verification on by default for all merchants.  If you do not have
407 valid address information for your customer (such as in an IVR
408 application), you must disable address verification in the Merchant
409 Menu page at https://secure.authorize.net/ so that the transactions
410 aren't denied due to a lack of address information.
411
412 =head1 COMPATIBILITY
413
414 This module implements Authorize.Net's API verison 3.1 using the Advanced
415 Integration Method (AIM), formerly known as ADC Direct Response.  See
416 http://www.authorize.net/support/AIM_guide.pdf for details.
417
418 =head1 AUTHOR
419
420 Jason Kohles, jason@mediabang.com
421
422 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
423 3.0/3.1 and is the current maintainer.  Please send patches as unified diffs
424 (diff -u).
425
426 Jason Spence <jspence@lightconsulting.com> contributed support for separate
427 Authorization Only and Post Authorization steps and wrote some docs.
428 OST <services@ostel.com> paid for it.
429
430 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
431
432 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
433
434 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
435
436 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
437 card-less post authorizations.
438
439 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
440 key" authentication as well support for the recurring_billing flag and the md5
441 method that returns the MD5 hash which is returned by the gateway.
442
443 =head1 SEE ALSO
444
445 perl(1). L<Business::OnlinePayment>.
446
447 =cut
448