Added referer field, patch from Mike Barry <mbarry@cos.com>
[Business-OnlinePayment-AuthorizeNet.git] / AuthorizeNet.pm
1 package Business::OnlinePayment::AuthorizeNet;
2
3 # $Id: AuthorizeNet.pm,v 1.12 2002-08-03 21:20:51 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         invoice_number => 'x_Invoice_Num',
91         customer_id    => 'x_Cust_ID',
92         last_name      => 'x_Last_Name',
93         first_name     => 'x_First_Name',
94         address        => 'x_Address',
95         city           => 'x_City',
96         state          => 'x_State',
97         zip            => 'x_Zip',
98         card_number    => 'x_Card_Num',
99         expiration     => 'x_Exp_Date',
100         account_number => 'x_Bank_Acct_Num',
101         routing_code   => 'x_Bank_ABA_Code',
102         bank_name      => 'x_Bank_Name',
103         country        => 'x_Country',
104         phone          => 'x_Phone',
105         fax            => 'x_Fax',
106         email          => 'x_Email',
107         company        => 'x_Company',
108         order_number   => 'x_Trans_ID',
109         cvv2           => 'x_Card_Code',
110     );
111
112     if($self->transaction_type() eq "ECHECK") {
113         $self->required_fields(qw/type login password action amount last_name
114                                   first_name account_number routing_code
115                                   bank_name/);
116     } elsif($self->transaction_type() eq 'CC' ) {
117       if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
118         $self->required_fields(qw/type login password action amount
119                                   card_number expiration/);
120       } else {
121         $self->required_fields(qw/type login password action amount last_name
122                                   first_name card_number expiration/);
123       }
124     } else {
125         Carp::croak("AuthorizeNet can't handle transaction type: ".
126                     $self->transaction_type());
127     }
128
129     my %post_data = $self->get_fields(qw/x_Login x_Password x_Invoice_Num
130                                          x_Description x_Amount x_Cust_ID
131                                          x_Method x_Type x_Card_Num x_Exp_Date
132                                          x_Auth_Code x_Bank_Acct_Num
133                                          x_Bank_ABA_Code x_Bank_Name
134                                          x_Last_Name x_First_Name x_Address
135                                          x_City x_State x_Zip x_Country x_Phone
136                                          x_Fax x_Email x_Email_Customer
137                                          x_Company x_Country x_Trans_ID
138                                          x_Card_Code /); 
139     $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
140     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
141     $post_data{'x_ADC_URL'} = 'FALSE';
142     $post_data{'x_Version'} = '3.1';
143
144     my $pd = make_form(%post_data);
145     my $s = $self->server();
146     my $p = $self->port();
147     my $t = $self->path();
148     my $r = $self->{_content}->{referer};
149     my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
150     #escape NULL (binary 0x00) values
151     $page =~ s/\x00/\^0/g;
152
153     my $csv = new Text::CSV_XS();
154     $csv->parse($page);
155     my @col = $csv->fields();
156
157     $self->server_response($page);
158     if($col[0] eq "1" ) { # Authorized/Pending/Test
159         $self->is_success(1);
160         $self->result_code($col[0]);
161         $self->authorization($col[4]);
162         $self->order_number($col[6]);
163     } else {
164         $self->is_success(0);
165         $self->result_code($col[2]);
166         $self->error_message($col[3]);
167         unless ( $self->result_code() ) { #additional logging information
168           #$page =~ s/\x00/\^0/g;
169           $self->error_message($col[3].
170             " DEBUG: No x_response_code from server, ".
171             "(HTTPS response: $server_response) ".
172             "(HTTPS headers: ".
173               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
174             "(Raw HTTPS content: $page)"
175           );
176         }
177     }
178 }
179
180 1;
181 __END__
182
183 =head1 NAME
184
185 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
186
187 =head1 SYNOPSIS
188
189   use Business::OnlinePayment;
190
191   my $tx = new Business::OnlinePayment("AuthorizeNet");
192   $tx->content(
193       type           => 'VISA',
194       login          => 'testdrive',
195       password       => '',
196       action         => 'Normal Authorization',
197       description    => 'Business::OnlinePayment test',
198       amount         => '49.95',
199       invoice_number => '100100',
200       customer_id    => 'jsk',
201       first_name     => 'Jason',
202       last_name      => 'Kohles',
203       address        => '123 Anystreet',
204       city           => 'Anywhere',
205       state          => 'UT',
206       zip            => '84058',
207       card_number    => '4007000000027',
208       expiration     => '09/02',
209       cvv2           => '1234', #optional
210       referer        => 'http://valid.referer.url/',
211   );
212   $tx->submit();
213
214   if($tx->is_success()) {
215       print "Card processed successfully: ".$tx->authorization."\n";
216   } else {
217       print "Card was rejected: ".$tx->error_message."\n";
218   }
219
220 =head1 SUPPORTED TRANSACTION TYPES
221
222 =head2 Visa, MasterCard, American Express, Discover
223
224 Content required: type, login, password, action, amount, first_name, last_name, card_number, expiration.
225
226 =head2 Check
227
228 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, bank_name.
229
230 =head1 DESCRIPTION
231
232 For detailed information see L<Business::OnlinePayment>.
233
234 =head1 NOTE
235
236 Unlike Business::OnlinePayment or pre-3.0 verisons of
237 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
238 last_name fields.
239
240 To settle an authorization-only transaction (where you set action to
241 'Authorization Only'), submit the nine-digit transaction id code in
242 the field "order_number" with the action set to "Post Authorization".
243 You can get the transaction id from the authorization by calling the
244 order_number method on the object returned from the authorization.
245 You must also submit the amount field with a value less than or equal
246 to the amount specified in the original authorization.
247
248 Recently (February 2002), Authorize.Net has turned address
249 verification on by default for all merchants.  If you do not have
250 valid address information for your customer (such as in an IVR
251 application), you must disable address verification in the Merchant
252 Menu page at https://secure.authorize.net/ so that the transactions
253 aren't denied due to a lack of address information.
254
255 =head1 COMPATIBILITY
256
257 This module implements Authorize.Net's API verison 3.1 using the ADC
258 Direct Response method.  See
259 https://secure.authorize.net/docs/developersguide.pml for details.
260
261 =head1 AUTHOR
262
263 Jason Kohles, jason@mediabang.com
264
265 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
266 3.0/3.1 and is the current maintainer.
267
268 Jason Spence <jspence@lightconsulting.com> contributed support for separate
269 Authorization Only and Post Authorization steps and wrote some docs.
270 OST <services@ostel.com> paid for it.
271
272 T.J. Mather <tjmather@maxmind.com> sent a patch for the CVV2 field.
273
274 =head1 SEE ALSO
275
276 perl(1). L<Business::OnlinePayment>.
277
278 =cut
279