- Incorporate Business::OnlinePayment::AuthorizeNet::AIM::ErrorCodes by
[Business-OnlinePayment-AuthorizeNet.git] / AuthorizeNet / AIM.pm
index 7e4e963..0039502 100644 (file)
@@ -2,17 +2,14 @@ package Business::OnlinePayment::AuthorizeNet::AIM;
 
 use strict;
 use Carp;
+use Business::OnlinePayment::HTTPS;
 use Business::OnlinePayment::AuthorizeNet;
-use Net::SSLeay qw/make_form post_https make_headers/;
+use Business::OnlinePayment::AuthorizeNet::AIM::ErrorCodes '%ERRORS';
 use Text::CSV_XS;
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
 
-require Exporter;
-
-@ISA = qw(Exporter Business::OnlinePayment::AuthorizeNet);
-@EXPORT = qw();
-@EXPORT_OK = qw();
-$VERSION = '3.18';
+@ISA = qw(Business::OnlinePayment::AuthorizeNet Business::OnlinePayment::HTTPS);
+$VERSION = '3.22';
 
 sub set_defaults {
     my $self = shift;
@@ -59,10 +56,6 @@ sub map_fields {
     $content{'account_type'} = $account_types{lc($content{'account_type'})}
                                || $content{'account_type'};
 
-    $content{'referer'} = defined( $content{'referer'} )
-                            ? make_headers( 'Referer' => $content{'referer'} )
-                            : "";
-
     if (length $content{'password'} == 15) {
         $content{'transaction_key'} = delete $content{'password'};
     }
@@ -124,6 +117,11 @@ sub submit {
         ship_state        => 'x_Ship_To_State',
         ship_zip          => 'x_Ship_To_Zip',
         ship_country      => 'x_Ship_To_Country',
+        tax               => 'x_Tax',
+        freight           => 'x_Freight',
+        duty              => 'x_Duty',
+        tax_exempt        => 'x_Tax_Exempt',
+        po_number         => 'x_Po_Num',
         phone             => 'x_Phone',
         fax               => 'x_Fax',
         email             => 'x_Email',
@@ -137,12 +135,16 @@ sub submit {
         account_type      => 'x_Bank_Acct_Type',
         bank_name         => 'x_Bank_Name',
         routing_code      => 'x_Bank_ABA_Code',
+        check_number      => 'x_Bank_Check_Number',
         customer_org      => 'x_Customer_Organization_Type', 
         customer_ssn      => 'x_Customer_Tax_ID',
         license_num       => 'x_Drivers_License_Num',
         license_state     => 'x_Drivers_License_State',
         license_dob       => 'x_Drivers_License_DOB',
         recurring_billing => 'x_Recurring_Billing',
+        duplicate_window  => 'x_Duplicate_Window',
+        track1            => 'x_Track1',
+        track2            => 'x_Track2',
     );
 
     my $auth_type = $self->{_content}->{transaction_key}
@@ -197,6 +199,7 @@ sub submit {
         x_Description x_Amount x_Cust_ID x_Method x_Type x_Card_Num x_Exp_Date
         x_Card_Code x_Auth_Code x_Echeck_Type x_Bank_Acct_Num
         x_Bank_Account_Name x_Bank_ABA_Code x_Bank_Name x_Bank_Acct_Type
+        x_Bank_Check_Number
         x_Customer_Organization_Type x_Customer_Tax_ID x_Customer_IP
         x_Drivers_License_Num x_Drivers_License_State x_Drivers_License_DOB
         x_Last_Name x_First_Name x_Company
@@ -205,8 +208,9 @@ sub submit {
         x_Ship_To_Last_Name x_Ship_To_First_Name x_Ship_To_Company
         x_Ship_To_Address x_Ship_To_City x_Ship_To_State x_Ship_To_Zip
         x_Ship_To_Country
+        x_Tax x_Freight x_Duty x_Tax_Exempt x_Po_Num
         x_Phone x_Fax x_Email x_Email_Customer x_Country
-        x_Currency_Code x_Trans_ID/);
+        x_Currency_Code x_Trans_ID x_Duplicate_Window x_Track1 x_Track2/);
 
     $post_data{'x_Test_Request'} = $self->test_transaction() ? 'TRUE' : 'FALSE';
 
@@ -214,29 +218,54 @@ sub submit {
     if (    $post_data{'x_Email_Customer'}
          && $post_data{'x_Email_Customer'} !~ /^FALSE$/i ) {
       $post_data{'x_Email_Customer'} = 'TRUE';
-    } else {
+    } elsif ( exists $post_data{'x_Email_Customer'} ) {
       $post_data{'x_Email_Customer'} = 'FALSE';
     }
 
+    my $data_string = join("", values %post_data);
+
+    my $encap_character;
+    # The first set of characters here are recommended by authorize.net in their
+    #   encapsulating character example.
+    # The second set we made up hoping they will work if the first fail.
+    # The third chr(31) is the binary 'unit separator' and is our final last
+    #   ditch effort to find something not in the input.
+    foreach my $char( qw( | " ' : ; / \ - * ), '#', qw( ^ + < > [ ] ~), chr(31) ){
+      if( index($data_string, $char) == -1 ){ # found one.
+        $encap_character = $char;
+        last;
+      }
+    }
+
+    if(!$encap_character){
+      $self->is_success(0);
+      $self->error_message(
+                          "DEBUG: Input contains all encapsulating characters."
+                          . " Please remove | or ^ from your input if possible."
+                         );
+      return;
+    }
+
     $post_data{'x_ADC_Delim_Data'} = 'TRUE';
     $post_data{'x_delim_char'} = ',';
-    $post_data{'x_encap_char'} = '"';
+    $post_data{'x_encap_char'} = $encap_character;
     $post_data{'x_ADC_URL'} = 'FALSE';
     $post_data{'x_Version'} = '3.1';
 
-    my $pd = make_form(%post_data);
-    my $s = $self->server();
-    my $p = $self->port();
-    my $t = $self->path();
-    my $r = $self->{_content}->{referer};
-    my($page,$server_response,%headers) = post_https($s,$p,$t,$r,$pd);
+    my $opt = defined( $self->{_content}->{referer} )
+                ? { 'headers' => { 'Referer' => $self->{_content}->{referer} } }
+                : {};
+
+    my($page, $server_response, %headers) =
+      $self->https_post( $opt, \%post_data );
+
     #escape NULL (binary 0x00) values
     $page =~ s/\x00/\^0/g;
 
     #trim 'ip_addr="1.2.3.4"' added by eProcessingNetwork Authorize.Net compat
     $page =~ s/,ip_addr="[\d\.]+"$//;
 
-    my $csv = new Text::CSV_XS({ binary=>1, escape_char=>'' });
+    my $csv = new Text::CSV_XS({ binary=>1, escape_char=>'', quote_char => $encap_character });
     $csv->parse($page);
     my @col = $csv->fields();
 
@@ -259,7 +288,11 @@ sub submit {
         $self->is_success(0);
         $self->result_code($col[2]);
         $self->error_message($col[3]);
-        unless ( $self->result_code() ) { #additional logging information
+        if ( $self->result_code ) {
+          my $addl = $ERRORS{ $self->result_code };
+          $self->error_message( $self->error_message. ' - '. $addl->{notes})
+            if $addl && ref($addl) eq 'HASH' && $addl->{notes};
+        } else { #additional logging information
           #$page =~ s/\x00/\^0/g;
           $self->error_message($col[3].
             " DEBUG: No x_response_code from server, ".