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 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_Ship_To_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
149 if (defined $self->{_content}->{customer_org} and
150 length $self->{_content}->{customer_org}
152 push @required_fields, qw( customer_org customer_ssn );
154 push @required_fields, qw(license_num license_state license_dob);
157 } elsif ($self->transaction_type() eq 'CC' ) {
159 if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
160 if ( $self->{_content}->{order_number} ) {
161 push @required_fields, qw( amount order_number );
163 push @required_fields, qw( amount card_number expiration );
165 } elsif ( $self->{_content}->{action} eq 'CREDIT' ) {
166 push @required_fields, qw( amount order_number card_number );
168 push @required_fields, qw(
169 amount last_name first_name card_number expiration
173 Carp::croak( "AuthorizeNet can't handle transaction type: ".
174 $self->transaction_type() );
179 $self->required_fields(@required_fields);
181 my %post_data = $self->get_fields(qw/
182 x_Login x_Password x_Tran_Key x_Invoice_Num
183 x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
184 x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
185 x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
186 x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
187 x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
188 x_Last_Name x_First_Name x_Company
189 x_Address x_City x_State x_Zip
191 x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
192 x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
194 x_Phone x_Fax x_Email x_Email_Customer x_Country
195 x_Currency_Code x_Trans_ID/);
196 $post_data{'x_Test_Request'} = $self->test_transaction()?"TRUE":"FALSE";
197 $post_data{'x_ADC_Delim_Data'} = 'TRUE';
198 $post_data{'x_delim_char'} = ',';
199 $post_data{'x_encap_char'} = '"';
200 $post_data{'x_ADC_URL'} = 'FALSE';
201 $post_data{'x_Version'} = '3.1';
203 my $pd = make_form(%post_data);
204 my $s = $self->server();
205 my $p = $self->port();
206 my $t = $self->path();
207 my $r = $self->{_content}->{referer};
208 my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
209 #escape NULL (binary 0x00) values
210 $page =~ s/\x00/\^0/g;
212 my $csv = new Text::CSV_XS({ 'binary'=>1 });
214 my @col = $csv->fields();
216 $self->server_response($page);
217 $self->avs_code($col[5]);
218 $self->order_number($col[6]);
219 $self->md5($col[37]);
220 $self->cvv2_response($col[38]);
221 $self->cavv_response($col[39]);
223 if($col[0] eq "1" ) { # Authorized/Pending/Test
224 $self->is_success(1);
225 $self->result_code($col[0]);
226 $self->authorization($col[4]);
228 $self->is_success(0);
229 $self->result_code($col[2]);
230 $self->error_message($col[3]);
231 unless ( $self->result_code() ) { #additional logging information
232 #$page =~ s/\x00/\^0/g;
233 $self->error_message($col[3].
234 " DEBUG: No x_response_code from server, ".
235 "(HTTPS response: $server_response) ".
237 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
238 "(Raw HTTPS content: $page)"
249 Business::OnlinePayment::AuthorizeNet - AuthorizeNet backend for Business::OnlinePayment
253 use Business::OnlinePayment;
256 # One step transaction, the simple case.
259 my $tx = new Business::OnlinePayment("AuthorizeNet");
262 login => 'testdrive',
264 action => 'Normal Authorization',
265 description => 'Business::OnlinePayment test',
267 invoice_number => '100100',
268 customer_id => 'jsk',
269 first_name => 'Jason',
270 last_name => 'Kohles',
271 address => '123 Anystreet',
275 card_number => '4007000000027',
276 expiration => '09/02',
277 cvv2 => '1234', #optional
278 referer => 'http://valid.referer.url/',
282 if($tx->is_success()) {
283 print "Card processed successfully: ".$tx->authorization."\n";
285 print "Card was rejected: ".$tx->error_message."\n";
289 # Two step transaction, authorization and capture.
290 # If you don't need to review order before capture, you can
291 # process in one step as above.
294 my $tx = new Business::OnlinePayment("AuthorizeNet");
297 login => 'testdrive',
299 action => 'Authorization Only',
300 description => 'Business::OnlinePayment test',
302 invoice_number => '100100',
303 customer_id => 'jsk',
304 first_name => 'Jason',
305 last_name => 'Kohles',
306 address => '123 Anystreet',
310 card_number => '4007000000027',
311 expiration => '09/02',
312 cvv2 => '1234', #optional
313 referer => 'http://valid.referer.url/',
317 if($tx->is_success()) {
318 # get information about authorization
319 $authorization = $tx->authorization
320 $ordernum = $tx->order_number;
321 $avs_code = $tx->avs_code; # AVS Response Code
322 $cvv2_response = $tx->cvv2_response; # CVV2/CVC2/CID Response Code
323 $cavv_response = $tx->cavv_response; # Cardholder Authentication
324 # Verification Value (CAVV) Response
327 # now capture transaction
328 my $capture = new Business::OnlinePayment("AuthorizeNet");
332 action => 'Post Authorization',
334 password => 'YOURPASSWORD',
335 order_number => $ordernum,
341 if($capture->is_success()) {
342 print "Card captured successfully: ".$capture->authorization."\n";
344 print "Card was rejected: ".$capture->error_message."\n";
348 print "Card was rejected: ".$tx->error_message."\n";
351 =head1 SUPPORTED TRANSACTION TYPES
353 =head2 CC, Visa, MasterCard, American Express, Discover
355 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, card_number, expiration.
359 Content required: type, login, password|transaction_key, action, amount, first_name, last_name, account_number, routing_code, bank_name.
363 For detailed information see L<Business::OnlinePayment>.
367 Unlike Business::OnlinePayment or pre-3.0 verisons of
368 Business::OnlinePayment::AuthorizeNet, 3.1 requires separate first_name and
371 Business::OnlinePayment::AuthorizeNet uses Authorize.Net's "Advanced
372 Integration Method (AIM) (formerly known as ADC direct response)", sending a
373 username and transaction_key or password with every transaction. Therefore, Authorize.Net's
374 referrer "security" is not necessary. In your Authorize.Net interface at
375 https://secure.authorize.net/ make sure the list of allowable referers is
376 blank. Alternatively, set the B<referer> field in the transaction content.
378 To settle an authorization-only transaction (where you set action to
379 'Authorization Only'), submit the nine-digit transaction id code in
380 the field "order_number" with the action set to "Post Authorization".
381 You can get the transaction id from the authorization by calling the
382 order_number method on the object returned from the authorization.
383 You must also submit the amount field with a value less than or equal
384 to the amount specified in the original authorization.
386 Recently (February 2002), Authorize.Net has turned address
387 verification on by default for all merchants. If you do not have
388 valid address information for your customer (such as in an IVR
389 application), you must disable address verification in the Merchant
390 Menu page at https://secure.authorize.net/ so that the transactions
391 aren't denied due to a lack of address information.
395 This module implements Authorize.Net's API verison 3.1 using the Advanced
396 Integration Method (AIM), formerly known as ADC Direct Response. See
397 http://www.authorize.net/support/AIM_guide.pdf for details.
401 Jason Kohles, jason@mediabang.com
403 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
404 3.0/3.1 and is the current maintainer. Please send patches as unified diffs
407 Jason Spence <jspence@lightconsulting.com> contributed support for separate
408 Authorization Only and Post Authorization steps and wrote some docs.
409 OST <services@ostel.com> paid for it.
411 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
413 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
415 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
417 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
418 card-less post authorizations.
420 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
421 key" authentication as well support for the recurring_billing flag and the md5
422 method that returns the MD5 hash which is returned by the gateway.
426 perl(1). L<Business::OnlinePayment>.