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