1 package Business::OnlinePayment::AuthorizeNet::AIM;
5 use Business::OnlinePayment::AuthorizeNet;
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::AuthorizeNet);
20 $self->server('secure.authorize.net') unless $self->server;
21 $self->port('443') unless $self->port;
22 $self->path('/gateway/transact.dll') unless $self->path;
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'});
54 my %account_types = ('personal checking' => 'CHECKING',
55 'personal savings' => 'SAVINGS',
56 'business checking' => 'CHECKING',
57 'business savings' => 'SAVINGS',
59 $content{'account_type'} = $account_types{lc($content{'account_type'})}
60 || $content{'account_type'};
62 $content{'referer'} = defined( $content{'referer'} )
63 ? make_headers( 'Referer' => $content{'referer'} )
66 if (length $content{'password'} == 15) {
67 $content{'transaction_key'} = delete $content{'password'};
70 # stuff it back into %content
71 $self->content(%content);
77 my %content = $self->content();
79 $content{$map{$_}} = $content{$_};
81 $self->content(%content);
85 my($self,@fields) = @_;
87 my %content = $self->content();
89 foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
100 password => 'x_Password',
101 transaction_key => 'x_Tran_Key',
103 description => 'x_Description',
104 amount => 'x_Amount',
105 currency => 'x_Currency_Code',
106 invoice_number => 'x_Invoice_Num',
107 order_number => 'x_Trans_ID',
108 auth_code => 'x_Auth_Code',
109 customer_id => 'x_Cust_ID',
110 customer_ip => 'x_Customer_IP',
111 last_name => 'x_Last_Name',
112 first_name => 'x_First_Name',
113 company => 'x_Company',
114 address => 'x_Address',
118 country => 'x_Country',
119 ship_last_name => 'x_Ship_To_Last_Name',
120 ship_first_name => 'x_Ship_To_First_Name',
121 ship_company => 'x_Ship_To_Company',
122 ship_address => 'x_Ship_To_Address',
123 ship_city => 'x_Ship_To_City',
124 ship_state => 'x_Ship_To_State',
125 ship_zip => 'x_Ship_To_Zip',
126 ship_country => 'x_Ship_To_Country',
128 freight => 'x_Freight',
130 tax_exempt => 'x_Tax_Exempt',
131 po_number => 'x_Po_Num',
135 email_customer => 'x_Email_Customer',
136 card_number => 'x_Card_Num',
137 expiration => 'x_Exp_Date',
138 cvv2 => 'x_Card_Code',
139 check_type => 'x_Echeck_Type',
140 account_name => 'x_Bank_Acct_Name',
141 account_number => 'x_Bank_Acct_Num',
142 account_type => 'x_Bank_Acct_Type',
143 bank_name => 'x_Bank_Name',
144 routing_code => 'x_Bank_ABA_Code',
145 check_number => 'x_Bank_Check_Number',
146 customer_org => 'x_Customer_Organization_Type',
147 customer_ssn => 'x_Customer_Tax_ID',
148 license_num => 'x_Drivers_License_Num',
149 license_state => 'x_Drivers_License_State',
150 license_dob => 'x_Drivers_License_DOB',
151 recurring_billing => 'x_Recurring_Billing',
152 duplicate_window => 'x_Duplicate_Window',
153 track1 => 'x_Track1',
154 track2 => 'x_Track2',
157 my $auth_type = $self->{_content}->{transaction_key}
161 my @required_fields = ( qw(type action login), $auth_type );
163 unless ( $self->{_content}->{action} eq 'VOID' ) {
165 if ($self->transaction_type() eq "ECHECK") {
167 push @required_fields, qw(
168 amount routing_code account_number account_type bank_name
172 if (defined $self->{_content}->{customer_org} and
173 length $self->{_content}->{customer_org}
175 push @required_fields, qw( customer_org customer_ssn );
177 push @required_fields, qw(license_num license_state license_dob);
180 } elsif ($self->transaction_type() eq 'CC' ) {
182 if ( $self->{_content}->{action} eq 'PRIOR_AUTH_CAPTURE' ) {
183 if ( $self->{_content}->{order_number} ) {
184 push @required_fields, qw( amount order_number );
186 push @required_fields, qw( amount card_number expiration );
188 } elsif ( $self->{_content}->{action} eq 'CREDIT' ) {
189 push @required_fields, qw( amount order_number card_number );
191 push @required_fields, qw(
192 amount last_name first_name card_number expiration
196 Carp::croak( "AuthorizeNet can't handle transaction type: ".
197 $self->transaction_type() );
202 $self->required_fields(@required_fields);
204 my %post_data = $self->get_fields(qw/
205 x_Login x_Password x_Tran_Key x_Invoice_Num
206 x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
207 x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
208 x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
210 x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
211 x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
212 x_Last_Name x_First_Name x_Company
213 x_Address x_City x_State x_Zip
215 x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
216 x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
218 x_Tax x_Freight x_Duty x_Tax_Exempt x_Po_Num
219 x_Phone x_Fax x_Email x_Email_Customer x_Country
220 x_Currency_Code x_Trans_ID x_Duplicate_Window x_Track1 x_Track2/);
222 $post_data{'x_Test_Request'} = $self->test_transaction() ? 'TRUE' : 'FALSE';
224 #deal with perl-style bool
225 if ( $post_data{'x_Email_Customer'}
226 && $post_data{'x_Email_Customer'} !~ /^FALSE$/i ) {
227 $post_data{'x_Email_Customer'} = 'TRUE';
229 $post_data{'x_Email_Customer'} = 'FALSE';
232 $post_data{'x_ADC_Delim_Data'} = 'TRUE';
233 $post_data{'x_delim_char'} = ',';
234 $post_data{'x_encap_char'} = '"';
235 $post_data{'x_ADC_URL'} = 'FALSE';
236 $post_data{'x_Version'} = '3.1';
238 my $pd = make_form(%post_data);
239 my $s = $self->server();
240 my $p = $self->port();
241 my $t = $self->path();
242 my $r = $self->{_content}->{referer};
243 my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
244 #escape NULL (binary 0x00) values
245 $page =~ s/\x00/\^0/g;
247 #trim 'ip_addr="1.2.3.4"' added by eProcessingNetwork Authorize.Net compat
248 $page =~ s/,ip_addr="[\d\.]+"$//;
250 my $csv = new Text::CSV_XS({ binary=>1, escape_char=>'' });
252 my @col = $csv->fields();
254 $self->server_response($page);
255 $self->avs_code($col[5]);
256 $self->order_number($col[6]);
257 $self->md5($col[37]);
258 $self->cvv2_response($col[38]);
259 $self->cavv_response($col[39]);
261 if($col[0] eq "1" ) { # Authorized/Pending/Test
262 $self->is_success(1);
263 $self->result_code($col[0]);
264 if ($col[4] =~ /^(.*)\s+(\d+)$/) { #eProcessingNetwork extra bits..
265 $self->authorization($2);
267 $self->authorization($col[4]);
270 $self->is_success(0);
271 $self->result_code($col[2]);
272 $self->error_message($col[3]);
273 unless ( $self->result_code() ) { #additional logging information
274 #$page =~ s/\x00/\^0/g;
275 $self->error_message($col[3].
276 " DEBUG: No x_response_code from server, ".
277 "(HTTPS response: $server_response) ".
279 join(", ", map { "$_ => ". $headers{$_} } keys %headers ). ") ".
280 "(Raw HTTPS content: $page)"
291 Business::OnlinePayment::AuthorizeNet::AIM - AuthorizeNet AIM backend for Business::OnlinePayment
295 Jason Kohles, jason@mediabang.com
297 Ivan Kohler <ivan-authorizenet@420.am> updated it for Authorize.Net protocol
298 3.0/3.1 and is the current maintainer. Please send patches as unified diffs
301 Jason Spence <jspence@lightconsulting.com> contributed support for separate
302 Authorization Only and Post Authorization steps and wrote some docs.
303 OST <services@ostel.com> paid for it.
305 T.J. Mather <tjmather@maxmind.com> sent a number of CVV2 patches.
307 Mike Barry <mbarry@cos.com> sent in a patch for the referer field.
309 Yuri V. Mkrtumyan <yuramk@novosoft.ru> sent in a patch to add the void action.
311 Paul Zimmer <AuthorizeNetpm@pzimmer.box.bepress.com> sent in a patch for
312 card-less post authorizations.
314 Daemmon Hughes <daemmon@daemmonhughes.com> sent in a patch for "transaction
315 key" authentication as well support for the recurring_billing flag and the md5
316 method that returns the MD5 hash which is returned by the gateway.
318 Steve Simitzis contributed a patch for better compatibility with
319 eProcessingNetwork's AuthorizeNet compatability mode.
323 perl(1). L<Business::OnlinePayment> L<Business::OnlinePayment::AuthorizeNet>.