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