typo
[Business-OnlinePayment-AuthorizeNet.git] / AuthorizeNet.pm
1 package Business::OnlinePayment::AuthorizeNet;
2
3 # $Id: AuthorizeNet.pm,v 1.7 2002-04-23 01:34:54 ivan Exp $
4
5 use strict;
6 use Business::OnlinePayment;
7 use Net::SSLeay qw/make_form post_https/;
8 use Text::CSV_XS;
9 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
10
11 require Exporter;
12
13 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
14 @EXPORT = qw();
15 @EXPORT_OK = qw();
16 $VERSION = '3.11';
17
18 sub set_defaults {
19     my $self = shift;
20
21     $self->server('secure.authorize.net');
22     $self->port('443');
23     $self->path('/gateway/transact.dll');
24
25     $self->build_subs('order_number'); #no idea how it worked for jason w/o this
26 }
27
28 sub map_fields {
29     my($self) = @_;
30
31     my %content = $self->content();
32
33     # ACTION MAP
34     my %actions = ('normal authorization' => 'AUTH_CAPTURE',
35                    'authorization only'   => 'AUTH_ONLY',
36                    'credit'               => 'CREDIT',
37                    'post authorization'   => 'PRIOR_AUTH_CAPTURE',
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     # stuff it back into %content
52     $self->content(%content);
53 }
54
55 sub remap_fields {
56     my($self,%map) = @_;
57
58     my %content = $self->content();
59     foreach(keys %map) {
60         $content{$map{$_}} = $content{$_};
61     }
62     $self->content(%content);
63 }
64
65 sub get_fields {
66     my($self,@fields) = @_;
67
68     my %content = $self->content();
69     my %new = ();
70     foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
71     return %new;
72 }
73
74 sub submit {
75     my($self) = @_;
76
77     $self->map_fields();
78     $self->remap_fields(
79         type           => 'x_Method',
80         login          => 'x_Login',
81         password       => 'x_Password',
82         action         => 'x_Type',
83         description    => 'x_Description',
84         amount         => 'x_Amount',
85         invoice_number => 'x_Invoice_Num',
86         customer_id    => 'x_Cust_ID',
87         last_name      => 'x_Last_Name',
88         first_name     => 'x_First_Name',
89         address        => 'x_Address',
90         city           => 'x_City',
91         state          => 'x_State',
92         zip            => 'x_Zip',
93         card_number    => 'x_Card_Num',
94         expiration     => 'x_Exp_Date',
95         account_number => 'x_Bank_Acct_Num',
96         routing_code   => 'x_Bank_ABA_Code',
97         bank_name      => 'x_Bank_Name',
98         country        => 'x_Country',
99         phone          => 'x_Phone',
100         fax            => 'x_Fax',
101         email          => 'x_Email',
102         company        => 'x_Company',
103         order_number   => 'x_Trans_ID',
104     );
105
106     if($self->transaction_type() eq "ECHECK") {
107         $self->required_fields(qw/type login password action amount last_name
108                                   first_name account_number routing_code
109                                   bank_name/);
110     } elsif($self->transaction_type() eq 'CC' ) {
111       if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
112         $self->required_fields(qw/type login password action amount
113                                   card_number expiration/);
114       } else {
115         $self->required_fields(qw/type login password action amount last_name
116                                   first_name card_number expiration/);
117       }
118     } else {
119         Carp::croak("AuthorizeNet can't handle transaction type: ".
120                     $self->transaction_type());
121     }
122
123     my %post_data = $self->get_fields(qw/x_Login x_Password x_Invoice_Num
124                                          x_Description x_Amount x_Cust_ID
125                                          x_Method x_Type x_Card_Num x_Exp_Date
126                                          x_Auth_Code x_Bank_Acct_Num
127                                          x_Bank_ABA_Code x_Bank_Name
128                                          x_Last_Name x_First_Name x_Address
129                                          x_City x_State x_Zip x_Country x_Phone
130                                          x_Fax x_Email x_Email_Customer
131                                          x_Company x_Country x_Trans_ID/); 
132     $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
133     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
134     $post_data{'x_ADC_URL'} = 'FALSE';
135     $post_data{'x_Version'} = '3.1';
136
137     my $pd = make_form(%post_data);
138     my $s = $self->server();
139     my $p = $self->port();
140     my $t = $self->path();
141     my($page,$server_response,%headers) = post_https($s,$p,$t,'',$pd);
142
143     my $csv = new Text::CSV_XS();
144     $csv->parse($page);
145     my @col = $csv->fields();
146
147     $self->server_response($page);
148     if($col[0] eq "1" ) { # Authorized/Pending/Test
149         $self->is_success(1);
150         $self->result_code($col[0]);
151         $self->authorization($col[4]);
152         $self->order_number($col[6]);
153     } else {
154         $self->is_success(0);
155         $self->result_code($col[2]);
156         $self->error_message($col[3]);
157         unless ( $self->result_code() ) { #additional logging information
158           $self->error_message($col[3].
159             " DEBUG: No x_response_code from server, ".
160             "(HTTPS response: $server_response) ".
161             "(HTTPS headers: ".
162               join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") "
163             "(Raw HTTPS content: $page)"
164           );
165         }
166     }
167 }
168
169 1;
170 __END__
171
172 =head1 NAME
173
174 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
175
176 =head1 SYNOPSIS
177
178   use Business::OnlinePayment;
179
180   my $tx = new Business::OnlinePayment("AuthorizeNet");
181   $tx->content(
182       type           => 'VISA',
183       login          => 'testdrive',
184       password       => '',
185       action         => 'Normal Authorization',
186       description    => 'Business::OnlinePayment test',
187       amount         => '49.95',
188       invoice_number => '100100',
189       customer_id    => 'jsk',
190       first_name     => 'Jason',
191       last_name      => 'Kohles',
192       address        => '123 Anystreet',
193       city           => 'Anywhere',
194       state          => 'UT',
195       zip            => '84058',
196       card_number    => '4007000000027',
197       expiration     => '09/02',
198   );
199   $tx->submit();
200
201   if($tx->is_success()) {
202       print "Card processed successfully: ".$tx->authorization."\n";
203   } else {
204       print "Card was rejected: ".$tx->error_message."\n";
205   }
206
207 =head1 SUPPORTED TRANSACTION TYPES
208
209 =head2 Visa, MasterCard, American Express, Discover
210
211 Content required: type, login, password, action, amount, first_name, last_name, card_number, expiration.
212
213 =head2 Check
214
215 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, bank_name.
216
217 =head1 DESCRIPTION
218
219 For detailed information see L<Business::OnlinePayment>.
220
221 =head1 NOTE
222
223 Unlike Business::OnlinePayment or pre-3.0 verisons of
224 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
225 last_name fields.
226
227 To settle an authorization-only transaction (where you set action to
228 'Authorization Only'), submit the nine-digit transaction id code in
229 the field "order_number" with the action set to "Post Authorization".
230 You can get the transaction id from the authorization by calling the
231 order_number method on the object returned from the authorization.
232 You must also submit the amount field with a value less than or equal
233 to the amount specified in the original authorization.
234
235 Recently (February 2002), Authorize.Net has turned address
236 verification on by default for all merchants.  If you do not have
237 valid address information for your customer (such as in an IVR
238 application), you must disable address verification in the Merchant
239 Menu page at https://secure.authorize.net/ so that the transactions
240 aren't denied due to a lack of address information.
241
242 =head1 COMPATIBILITY
243
244 This module implements Authorize.Net's API verison 3.1 using the ADC
245 Direct Response method.  See
246 https://secure.authorize.net/docs/developersguide.pml for details.
247
248 =head1 AUTHOR
249
250 Jason Kohles, jason@mediabang.com
251
252 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
253 3.0/3.1 and is the current maintainer.
254
255 Jason Spence <jspence@lightconsulting.com> contributed support for separate
256 Authorization Only and Post Authorization steps and wrote some docs.
257 OST <services@ostel.com> paid for it.
258
259 =head1 SEE ALSO
260
261 perl(1). L<Business::OnlinePayment>.
262
263 =cut
264