1 package Business::OnlinePayment::AuthorizeNet;
5 use Business::OnlinePayment;
6 use Net::SSLeay qw/make_form post_https make_headers/;
8 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
12 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
20 $self->server('secure.authorize.net');
22 $self->path('/gateway/transact.dll');
24 $self->build_subs(qw( order_number md5 avs_code cvv2_response
32 my %content = $self->content();
35 my %actions = ('normal authorization' => 'AUTH_CAPTURE',
36 'authorization only' => 'AUTH_ONLY',
38 'post authorization' => 'PRIOR_AUTH_CAPTURE',
41 $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
44 my %types = ('visa' => 'CC',
46 'american express' => 'CC',
50 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
51 $self->transaction_type($content{'type'});
53 $content{'referer'} = defined( $content{'referer'} )
54 ? make_headers( 'Referer' => $content{'referer'} )
57 # stuff it back into %content
58 $self->content(%content);
64 my %content = $self->content();
66 $content{$map{$_}} = $content{$_};
68 $self->content(%content);
72 my($self,@fields) = @_;
74 my %content = $self->content();
76 foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
87 password => 'x_Password',
88 transaction_key => 'x_Tran_Key',
90 description => 'x_Description',
92 currency => 'x_Currency_Code',
93 invoice_number => 'x_Invoice_Num',
94 order_number => 'x_Trans_ID',
95 auth_code => 'x_Auth_Code',
96 customer_id => 'x_Cust_ID',
97 customer_ip => 'x_Customer_IP',
98 last_name => 'x_Last_Name',
99 first_name => 'x_First_Name',
100 company => 'x_Company',
101 address => 'x_Address',
105 country => 'x_Country',
106 ship_last_name => 'x_Ship_To_Last_Name',
107 ship_first_name => 'x_Ship_To_First_Name',
108 ship_company => 'x_Company',
109 ship_address => 'x_Ship_To_Address',
110 ship_city => 'x_Ship_To_City',
111 ship_state => 'x_Ship_To_State',
112 ship_zip => 'x_Ship_To_Zip',
113 ship_country => 'x_Ship_To_Country',
117 card_number => 'x_Card_Num',
118 expiration => 'x_Exp_Date',
119 cvv2 => 'x_Card_Code',
120 check_type => 'x_Echeck_Type',
121 account_name => 'x_Bank_Acct_Name',
122 account_number => 'x_Bank_Acct_Num',
123 account_type => 'x_Bank_Acct_Type',
124 bank_name => 'x_Bank_Name',
125 routing_code => 'x_Bank_ABA_Code',
126 customer_org => 'x_Customer_Organization_Type',
127 customer_ssn => 'x_Customer_Tax_ID',
128 license_num => 'x_Drivers_License_Num',
129 license_state => 'x_Drivers_License_State',
130 license_dob => 'x_Drivers_License_DOB',
131 recurring_billing => 'x_Recurring_Billing',
134 my $auth_type = $self->{_content}->{transaction_key}
138 my @required_fields = ( qw(type action login), $auth_type );
140 unless ( $self->{_content}->{action} eq 'VOID' ) {
142 if ($self->transaction_type() eq "ECHECK") {
144 push @required_fields, qw(
145 amount routing_code account_number account_type bank_name
146 account_name account_type
149 if ($self->{_content}->{customer_org} ne '') {
150 push @required_fields, qw( customer_org customer_ssn );
152 push @required_fields, qw(license_num license_state license_dob);
155 } elsif ($self->transaction_type() eq 'CC' ) {
157 if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
158 if ( $self->{_content}->{order_number} ) {
159 push @required_fields, qw( amount order_number );
161 push @required_fields, qw( amount card_number expiration );
164 push @required_fields, qw(
165 amount last_name first_name card_number expiration
169 Carp::croak( "AuthorizeNet can't handle transaction type: ".
170 $self->transaction_type() );
175 $self->required_fields(@required_fields);
177 my %post_data = $self->get_fields(qw/
178 x_Login x_Password x_Tran_Key x_Invoice_Num
179 x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
180 x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
181 x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
182 x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
183 x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
184 x_Last_Name x_First_Name x_Company
185 x_Address x_City x_State x_Zip
187 x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
188 x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
190 x_Phone x_Fax x_Email x_Email_Customer x_Country
191 x_Currency_Code x_Trans_ID/);
192 $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
193 $post_data{'x_ADC_Delim_Data'} = 'TRUE';
194 $post_data{'x_delim_char'} = ',';
195 $post_data{'x_encap_char'} = '"';
196 $post_data{'x_ADC_URL'} = 'FALSE';
197 $post_data{'x_Version'} = '3.1';
199 my $pd = make_form(%post_data);
200 my $s = $self->server();
201 my $p = $self->port();
202 my $t = $self->path();
203 my $r = $self->{_content}->{referer};
204 my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
205 #escape NULL (binary 0x00) values
206 $page =~ s/\x00/\^0/g;
208 my $csv = new Text::CSV_XS({ 'binary'=>1 });
210 my @col = $csv->fields();
212 $self->server_response($page);
213 $self->avs_code($col[5]);
214 $self->order_number($col[6]);
215 $self->md5($col[37]);
216 $self->cvv2_response($col[38]);
217 $self->cavv_response($col[39]);
219 if($col[0] eq "1" ) { # Authorized/Pending/Test
220 $self->is_success(1);
221 $self->result_code($col[0]);
222 $self->authorization($col[4]);
224 $self->is_success(0);
225 $self->result_code($col[2]);
226 $self->error_message($col[3]);
227 unless ( $self->result_code() ) { #additional logging information
228 #$page =~ s/\x00/\^0/g;
229 $self->error_message($col[3].
230 " DEBUG: No x_response_code from server, ".
231 "(HTTPS response: $server_response) ".
233 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
234 "(Raw HTTPS content: $page)"
245 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
249 use Business::OnlinePayment;
252 # One step transaction, the simple case.
255 my $tx = new Business::OnlinePayment("AuthorizeNet");
258 login => 'testdrive',
260 action => 'Normal Authorization',
261 description => 'Business::OnlinePayment test',
263 invoice_number => '100100',
264 customer_id => 'jsk',
265 first_name => 'Jason',
266 last_name => 'Kohles',
267 address => '123 Anystreet',
271 card_number => '4007000000027',
272 expiration => '09/02',
273 cvv2 => '1234', #optional
274 referer => 'http://valid.referer.url/',
278 if($tx->is_success()) {
279 print "Card processed successfully: ".$tx->authorization."\n";
281 print "Card was rejected: ".$tx->error_message."\n";
285 # Two step transaction, authorization and capture.
286 # If you don't need to review order before capture, you can
287 # process in one step as above.
290 my $tx = new Business::OnlinePayment("AuthorizeNet");
293 login => 'testdrive',
295 action => 'Authorization Only',
296 description => 'Business::OnlinePayment test',
298 invoice_number => '100100',
299 customer_id => 'jsk',
300 first_name => 'Jason',
301 last_name => 'Kohles',
302 address => '123 Anystreet',
306 card_number => '4007000000027',
307 expiration => '09/02',
308 cvv2 => '1234', #optional
309 referer => 'http://valid.referer.url/',
313 if($tx->is_success()) {
314 # get information about authorization
315 $authorization = $tx->authorization
316 $ordernum = $tx->order_number;
317 $avs_code = $tx->avs_code; # AVS Response Code
318 $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code
319 $cavv_response = $tx->cavv_response; # Cardholder Authentication
320 # Verification Value (CAVV) Response
323 # now capture transaction
324 my $capture = new Business::OnlinePayment("AuthorizeNet");
328 action => 'Post Authorization',
330 password => 'YOURPASSWORD',
331 order_number => $ordernum,
337 if($capture->is_success()) {
338 print "Card captured successfully: ".$capture->authorization."\n";
340 print "Card was rejected: ".$capture->error_message."\n";
344 print "Card was rejected: ".$tx->error_message."\n";
347 =head1 SUPPORTED TRANSACTION TYPES
349 =head2 CC, Visa, MasterCard, American Express, Discover
351 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration.
355 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name.
359 For detailed information see L<Business::OnlinePayment>.
363 Unlike Business::OnlinePayment or pre-3.0 verisons of
364 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
367 Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced
368 Integration Method (AIM) (formerly known as ADC direct response)", sending a
369 username and transaction_key or password with every transaction. Therefore, Authorize.Net's
370 referrer "security" is not necessary. In your Authorize.Net interface at
371 https://secure.authorize.net/ make sure the list of allowable referers is
372 blank. Alternatively, set the B<referer> field in the transaction content.
374 To settle an authorization-only transaction (where you set action to
375 'Authorization Only'), submit the nine-digit transaction id code in
376 the field "order_number" with the action set to "Post Authorization".
377 You can get the transaction id from the authorization by calling the
378 order_number method on the object returned from the authorization.
379 You must also submit the amount field with a value less than or equal
380 to the amount specified in the original authorization.
382 Recently (February 2002), Authorize.Net has turned address
383 verification on by default for all merchants. If you do not have
384 valid address information for your customer (such as in an IVR
385 application), you must disable address verification in the Merchant
386 Menu page at https://secure.authorize.net/ so that the transactions
387 aren't denied due to a lack of address information.
391 This module implements Authorize.Net's API verison 3.1 using the ADC
392 Direct Response method. See
393 https://secure.authorize.net/docs/developersguide.pml for details.
397 Jason Kohles, jason@mediabang.com
399 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
400 3.0/3.1 and is the current maintainer. Please send patches as unified diffs
403 Jason Spence <jspence@lightconsulting.com> contributed support for separate
404 Authorization Only and Post Authorization steps and wrote some docs.
405 OST <services@ostel.com> paid for it.
407 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
409 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
411 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
413 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
414 card-less post authorizations.
416 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
417 key" authentication as well support for the recurring_billing flag and the md5'
418 method that returns the MD5 hash which is returned by the gateway.
422 perl(1). L<Business::OnlinePayment>.