b312a289525f78de3add4025e5cef8793d9c29e3
[Business-OnlinePayment-AuthorizeNet.git] / AuthorizeNet.pm
1 package Business::OnlinePayment::AuthorizeNet;
2
3 # $Id: AuthorizeNet.pm,v 1.6 2002-03-13 16:11:55 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.10';
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     }
158 }
159
160 1;
161 __END__
162
163 =head1 NAME
164
165 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
166
167 =head1 SYNOPSIS
168
169   use Business::OnlinePayment;
170
171   my $tx = new Business::OnlinePayment("AuthorizeNet");
172   $tx->content(
173       type           => 'VISA',
174       login          => 'testdrive',
175       password       => '',
176       action         => 'Normal Authorization',
177       description    => 'Business::OnlinePayment test',
178       amount         => '49.95',
179       invoice_number => '100100',
180       customer_id    => 'jsk',
181       first_name     => 'Jason',
182       last_name      => 'Kohles',
183       address        => '123 Anystreet',
184       city           => 'Anywhere',
185       state          => 'UT',
186       zip            => '84058',
187       card_number    => '4007000000027',
188       expiration     => '09/02',
189   );
190   $tx->submit();
191
192   if($tx->is_success()) {
193       print "Card processed successfully: ".$tx->authorization."\n";
194   } else {
195       print "Card was rejected: ".$tx->error_message."\n";
196   }
197
198 =head1 SUPPORTED TRANSACTION TYPES
199
200 =head2 Visa, MasterCard, American Express, Discover
201
202 Content required: type, login, password, action, amount, first_name, last_name, card_number, expiration.
203
204 =head2 Check
205
206 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, bank_name.
207
208 =head1 DESCRIPTION
209
210 For detailed information see L<Business::OnlinePayment>.
211
212 =head1 NOTE
213
214 Unlike Business::OnlinePayment or pre-3.0 verisons of
215 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
216 last_name fields.
217
218 To settle an authorization-only transaction (where you set action to
219 'Authorization Only'), submit the nine-digit transaction id code in
220 the field "order_number" with the action set to "Post Authorization".
221 You can get the transaction id from the authorization by calling the
222 order_number method on the object returned from the authorization.
223 You must also submit the amount field with a value less than or equal
224 to the amount specified in the original authorization.
225
226 Recently (February 2002), Authorize.Net has turned address
227 verification on by default for all merchants.  If you do not have
228 valid address information for your customer (such as in an IVR
229 application), you must disable address verification in the Merchant
230 Menu page at https://secure.authorize.net/ so that the transactions
231 aren't denied due to a lack of address information.
232
233 =head1 COMPATIBILITY
234
235 This module implements Authorize.Net's API verison 3.1 using the ADC
236 Direct Response method.  See
237 https://secure.authorize.net/docs/developersguide.pml for details.
238
239 =head1 AUTHOR
240
241 Jason Kohles, jason@mediabang.com
242
243 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
244 3.0/3.1 and is the current maintainer.
245
246 Jason Spence <jspence@lightconsulting.com> contributed support for separate
247 Authorization Only and Post Authorization steps and wrote some docs.
248 OST <services@ostel.com> paid for it.
249
250 =head1 SEE ALSO
251
252 perl(1). L<Business::OnlinePayment>.
253
254 =cut
255