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