allow "Post Authorize" with order_number but no card info
[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.13';
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('order_number'); #no idea how it worked for jason w/o this
25 }
26
27 sub map_fields {
28     my($self) = @_;
29
30     my %content = $self->content();
31
32     # ACTION MAP
33     my %actions = ('normal authorization' => 'AUTH_CAPTURE',
34                    'authorization only'   => 'AUTH_ONLY',
35                    'credit'               => 'CREDIT',
36                    'post authorization'   => 'PRIOR_AUTH_CAPTURE',
37                    'void'                 => 'VOID',
38                   );
39     $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
40
41     # TYPE MAP
42     my %types = ('visa'               => 'CC',
43                  'mastercard'         => 'CC',
44                  'american express'   => 'CC',
45                  'discover'           => 'CC',
46                  'check'              => 'ECHECK',
47                 );
48     $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
49     $self->transaction_type($content{'type'});
50
51     $content{'referer'} = defined( $content{'referer'} )
52                             ? make_headers( 'Referer' => $content{'referer'} )
53                             : "";
54
55     # stuff it back into %content
56     $self->content(%content);
57 }
58
59 sub remap_fields {
60     my($self,%map) = @_;
61
62     my %content = $self->content();
63     foreach(keys %map) {
64         $content{$map{$_}} = $content{$_};
65     }
66     $self->content(%content);
67 }
68
69 sub get_fields {
70     my($self,@fields) = @_;
71
72     my %content = $self->content();
73     my %new = ();
74     foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
75     return %new;
76 }
77
78 sub submit {
79     my($self) = @_;
80
81     $self->map_fields();
82     $self->remap_fields(
83         type           => 'x_Method',
84         login          => 'x_Login',
85         password       => 'x_Password',
86         action         => 'x_Type',
87         description    => 'x_Description',
88         amount         => 'x_Amount',
89         currency       => 'x_Currency_Code',
90         invoice_number => 'x_Invoice_Num',
91         order_number   => 'x_Trans_ID',
92         auth_code      => 'x_Auth_Code',
93         customer_id    => 'x_Cust_ID',
94         customer_ip    => 'x_Customer_IP',
95         last_name      => 'x_Last_Name',
96         first_name     => 'x_First_Name',
97         address        => 'x_Address',
98         city           => 'x_City',
99         state          => 'x_State',
100         zip            => 'x_Zip',
101         country        => 'x_Country',
102         phone          => 'x_Phone',
103         fax            => 'x_Fax',
104         email          => 'x_Email',
105         company        => 'x_Company',
106         card_number    => 'x_Card_Num',
107         expiration     => 'x_Exp_Date',
108         cvv2           => 'x_Card_Code',
109         check_type     => 'x_Echeck_Type',
110         account_name   => 'x_Bank_Account_Name',
111         account_number => 'x_Bank_Acct_Num',
112         account_type   => 'x_Bank_Acct_Type',
113         bank_name      => 'x_Bank_Name',
114         routing_code   => 'x_Bank_ABA_Code',
115         customer_org   => 'x_Customer_Organization_Type', 
116         customer_ssn   => 'x_Customer_Tax_ID',
117         license_num    => 'x_Drivers_License_Num',
118         license_state  => 'x_Drivers_License_State',
119         license_dob    => 'x_Drivers_License_DOB',
120     );
121
122     if ($self->transaction_type() eq "ECHECK") {
123         if ($self->{_content}->{customer_org} ne '') {
124             $self->required_fields(qw/type login password amount routing_code
125                                   account_number account_type bank_name
126                                   account_name account_type check_type
127                                   customer_org customer_ssn/);
128         } else {
129             $self->required_fields(qw/type login password amount routing_code
130                                   account_number account_type bank_name
131                                   account_name account_type check_type
132                                   license_num license_state license_dob/);
133         }
134     } elsif ($self->transaction_type() eq 'CC' ) {
135       if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
136           if ( $self->{_content}->{order_number}) {
137               $self->required_fields(qw/type login password action amount/);
138           } else {
139               $self->required_fields(qw/type login password action amount 
140                                         card_number expiration/);
141           }
142       } elsif ( $self->{_content}->{action} eq 'VOID' ) {
143         $self->required_fields(qw/login password action/);
144       } else {
145         $self->required_fields(qw/type login password action amount last_name
146                                   first_name card_number expiration/);
147       }
148     } else {
149         Carp::croak("AuthorizeNet can't handle transaction type: ".
150                     $self->transaction_type());
151     }
152
153     my %post_data = $self->get_fields(qw/x_Login x_Password x_Invoice_Num
154         x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
155         x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
156         x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
157         x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
158         x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
159         x_Last_Name x_First_Name x_Address x_City x_State x_Zip x_Country
160         x_Phone x_Fax x_Email x_Email_Customer x_Company x_Country
161         x_Currency_Code x_Trans_ID/);
162     $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
163     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
164     $post_data{'x_ADC_URL'} = 'FALSE';
165     $post_data{'x_Version'} = '3.1';
166
167     my $pd = make_form(%post_data);
168     my $s = $self->server();
169     my $p = $self->port();
170     my $t = $self->path();
171     my $r = $self->{_content}->{referer};
172     my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
173     #escape NULL (binary 0x00) values
174     $page =~ s/\x00/\^0/g;
175
176     my $csv = new Text::CSV_XS({ 'binary'=>1 });
177     $csv->parse($page);
178     my @col = $csv->fields();
179
180     $self->server_response($page);
181     if($col[0] eq "1" ) { # Authorized/Pending/Test
182         $self->is_success(1);
183         $self->result_code($col[0]);
184         $self->authorization($col[4]);
185         $self->order_number($col[6]);
186     } else {
187         $self->is_success(0);
188         $self->result_code($col[2]);
189         $self->error_message($col[3]);
190         unless ( $self->result_code() ) { #additional logging information
191           #$page =~ s/\x00/\^0/g;
192           $self->error_message($col[3].
193             " DEBUG: No x_response_code from server, ".
194             "(HTTPS response: $server_response) ".
195             "(HTTPS headers: ".
196               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
197             "(Raw HTTPS content: $page)"
198           );
199         }
200     }
201 }
202
203 1;
204 __END__
205
206 =head1 NAME
207
208 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
209
210 =head1 SYNOPSIS
211
212   use Business::OnlinePayment;
213
214   my $tx = new Business::OnlinePayment("AuthorizeNet");
215   $tx->content(
216       type           => 'VISA',
217       login          => 'testdrive',
218       password       => '',
219       action         => 'Normal Authorization',
220       description    => 'Business::OnlinePayment test',
221       amount         => '49.95',
222       invoice_number => '100100',
223       customer_id    => 'jsk',
224       first_name     => 'Jason',
225       last_name      => 'Kohles',
226       address        => '123 Anystreet',
227       city           => 'Anywhere',
228       state          => 'UT',
229       zip            => '84058',
230       card_number    => '4007000000027',
231       expiration     => '09/02',
232       cvv2           => '1234', #optional
233       referer        => 'http://valid.referer.url/',
234   );
235   $tx->submit();
236
237   if($tx->is_success()) {
238       print "Card processed successfully: ".$tx->authorization."\n";
239   } else {
240       print "Card was rejected: ".$tx->error_message."\n";
241   }
242
243 =head1 SUPPORTED TRANSACTION TYPES
244
245 =head2 Visa, MasterCard, American Express, Discover
246
247 Content required: type, login, password, action, amount, first_name, last_name, card_number, expiration.
248
249 =head2 Check
250
251 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, bank_name.
252
253 =head1 DESCRIPTION
254
255 For detailed information see L<Business::OnlinePayment>.
256
257 =head1 NOTE
258
259 Unlike Business::OnlinePayment or pre-3.0 verisons of
260 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
261 last_name fields.
262
263 Business::OnlinePayment::AuthorizeNet uses the ADC direct response method,
264 and sends a username and password with every transaction.  Therefore,
265 Authorize.Net's referrer "security" is not necessary.  In your Authorize.Net
266 interface at https://secure.authorize.net/ make sure the list of allowable
267 referers is blank.  Alternatively, set the B<referer> field in the transaction
268 content.
269
270 To settle an authorization-only transaction (where you set action to
271 'Authorization Only'), submit the nine-digit transaction id code in
272 the field "order_number" with the action set to "Post Authorization".
273 You can get the transaction id from the authorization by calling the
274 order_number method on the object returned from the authorization.
275 You must also submit the amount field with a value less than or equal
276 to the amount specified in the original authorization.
277
278 Recently (February 2002), Authorize.Net has turned address
279 verification on by default for all merchants.  If you do not have
280 valid address information for your customer (such as in an IVR
281 application), you must disable address verification in the Merchant
282 Menu page at https://secure.authorize.net/ so that the transactions
283 aren't denied due to a lack of address information.
284
285 =head1 COMPATIBILITY
286
287 This module implements Authorize.Net's API verison 3.1 using the ADC
288 Direct Response method.  See
289 https://secure.authorize.net/docs/developersguide.pml for details.
290
291 =head1 AUTHOR
292
293 Jason Kohles, jason@mediabang.com
294
295 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
296 3.0/3.1 and is the current maintainer.  Please send patches as unified diffs
297 (diff -u).
298
299 Jason Spence <jspence@lightconsulting.com> contributed support for separate
300 Authorization Only and Post Authorization steps and wrote some docs.
301 OST <services@ostel.com> paid for it.
302
303 T.J. Mather <tjmather@maxmind.com> sent a patch for the CVV2 field.
304
305 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
306
307 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
308
309 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
310 card-less post authorizations.
311
312 =head1 SEE ALSO
313
314 perl(1). L<Business::OnlinePayment>.
315
316 =cut
317