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