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