Added support for MarkForCapture and Reversal transactions.
authormark <mark>
Sun, 29 Nov 2009 21:02:06 +0000 (21:02 +0000)
committermark <mark>
Sun, 29 Nov 2009 21:02:06 +0000 (21:02 +0000)
Changes
lib/Business/OnlinePayment/PaymenTech.pm

diff --git a/Changes b/Changes
index a616435..acb3016 100644 (file)
--- a/Changes
+++ b/Changes
@@ -2,3 +2,5 @@ Revision history for Business-OnlinePayment-PaymenTech
 
 2.00    Wed Oct  7 09:25:34 PDT 2009
         Complete rewrite to use the XML interface directly.
+2.01    Sun Nov 29 12:59:44 PST 2009
+        Added support for MarkForCapture and Reversal transactions.
index bbbae48..0f23ee7 100644 (file)
@@ -8,7 +8,7 @@ use Tie::IxHash;
 use vars qw($VERSION $DEBUG @ISA $me);
 
 @ISA = qw(Business::OnlinePayment::HTTPS);
-$VERSION = '2.00';
+$VERSION = '2.01';
 $DEBUG = 0;
 $me='Business::OnlinePayment::PaymenTech';
 
@@ -17,7 +17,6 @@ my %request_header = (
   'Content-Transfer-Encoding' => 'text',
   'Request-Number'  =>    1,
   'Document-Type'   =>    'Request',
-  #'Trace-Number'    =>    1,
   'Interface-Version' =>  "$me $VERSION",
 ); # Content-Type has to be passed separately
 
@@ -36,7 +35,6 @@ tie my %new_order, 'Tie::IxHash', (
   CurrencyExponent          => ':currency_exp',
   CardSecValInd             => ':cvvind',
   CardSecVal                => ':cvv2',
-#  AVSname                   => ':name', not needed
   AVSzip                    => ':zip',
   AVSaddress1               => ':address',
   AVScity                   => ':city',
@@ -44,6 +42,31 @@ tie my %new_order, 'Tie::IxHash', (
   OrderID                   => ':invoice_number',
   Amount                    => ':amount',
   Comments                  => ':email', # as per B:OP:WesternACH
+  TxRefNum                  => ':order_number', # used only for Refund
+);
+
+tie my %mark_for_capture, 'Tie::IxHash', (
+  OrbitalConnectionUsername => ':login',
+  OrbitalConnectionPassword => ':password',
+  OrderID                   => ':invoice_number',
+  Amount                    => ':amount',
+  BIN                       => ':bin',
+  MerchantID                => ':merchant_id',
+  TerminalID                => ':terminal_id',
+  TxRefNum                  => ':order_number',
+);
+
+tie my %reversal, 'Tie::IxHash', (
+  OrbitalConnectionUsername => ':login',
+  OrbitalConnectionPassword => ':password',
+  TxRefNum                  => ':order_number',
+  TxRefIdx                  => 0,
+  OrderID                   => ':invoice_number',
+  BIN                       => ':bin',
+  MerchantID                => ':merchant_id',
+  TerminalID                => ':terminal_id',
+# Always attempt to reverse authorization.
+  OnlineReversalInd         => 'Y',
 );
 
 my %defaults = (
@@ -58,12 +81,6 @@ my @required = ( qw(
   action
   bin
   merchant_id
-  card_number
-  expiration
-  currency
-  address
-  city
-  zip
   invoice_number
   amount
   )
@@ -83,7 +100,17 @@ sub set_defaults {
     $self->port('443') unless $self->port;
     $self->path('/authorize') unless $self->path;
 
-    $self->build_subs(qw( TxRefNum ProcStatus ApprovalStatus StatusMsg Response ));
+    $self->build_subs(qw( 
+      order_number
+      ProcStatus 
+      ApprovalStatus 
+      StatusMsg 
+      Response
+      RespCode
+      AuthCode
+      AVSRespCode
+      CVV2RespCode
+     ));
 
 }
 
@@ -120,19 +147,15 @@ sub map_fields {
                   ('normal authorization' => 'AC',
                    'authorization only'   => 'A',
                    'credit'               => 'R',
+                   'void'                 => 'V',
                    'post authorization'   => 'MFC', # for our use, doesn't go in the request
                    ); 
     $content{'message_type'} = $message_type{lc($content{'action'})} 
       or die "unsupported action: '".$content{'action'}."'";
-    if($content{'message_type'} eq 'MFC') {
-      die 'MarkForCapture not implemented';
-      # for later implementation
-    }
 
     foreach (keys(%defaults) ) {
       $content{$_} = $defaults{$_} if !defined($content{$_});
     }
-    $DB::single=1;
     if(length($content{merchant_id}) == 12) {
       $content{bin} = '000002' # PNS
     }
@@ -158,12 +181,8 @@ sub map_fields {
     $content{name} = $content{first_name} . ' ' . $content{last_name};
 # According to the spec, the first 8 characters of this have to be unique.
 # The test server doesn't enforce this, but we comply anyway to the extent possible.
-    if($content{invoice_number}) {
-      # Mark it so that it's obvious that this is an invoice number
-      $content{invoice_number} = 'INV '.$content{invoice_number};
-    }
-    else {
-      # Otherwise, make something up!
+    if(! $content{invoice_number}) {
+      # Choose one arbitrarily
       $content{invoice_number} ||= sprintf("%04x%04x",time % 2**16,int(rand() * 2**16));
     }
 
@@ -178,14 +197,31 @@ sub submit {
   $DB::single = $DEBUG;
 
   $self->map_fields();
+  my %content = $self->content;
 
-  # This will change when we add e-check support
   my @required_fields = @required;
 
-  $self->required_fields(@required_fields);
+  my $request;
+  if( $content{'message_type'} eq 'MFC' ) {
+    $request = { MarkForCapture => $self->build(\%mark_for_capture) };
+    push @required_fields, 'order_number';
+  }
+  elsif( $content{'message_type'} eq 'V' ) {
+    $request = { Reversal => $self->build(\%reversal) };
+  }
+  else { 
+    $request = { NewOrder => $self->build(\%new_order) }; 
+    push @required_fields, qw(
+      card_number
+      expiration
+      currency
+      address
+      city
+      zip
+      );
+  }
 
-  # This will change when we add mark-for-capture support
-  my $request = { NewOrder => $self->build(\%new_order) }; 
+  $self->required_fields(@required_fields);
 
   my $post_data = XMLout({ Request => $request }, KeepRoot => 1, NoAttr => 1, NoSort => 1);
 
@@ -208,23 +244,36 @@ sub submit {
     $response = XMLin($page, KeepRoot => 0);
     $self->Response($response);
     my ($r) = values(%$response);
+    foreach(qw(ProcStatus RespCode AuthCode AVSRespCode CVV2RespCode)) {
+      if(exists($r->{$_}) and
+         !ref($r->{$_})) {
+        $self->$_($r->{$_});
+      }
+    }
     if(!exists($r->{'ProcStatus'})) {
       $error = "Malformed response: '$page'";
+      $self->is_success(0);
     }
-    elsif($r->{'ProcStatus'} != 0 || $r->{'ApprovalStatus'} != 1) {
+    elsif( $r->{'ProcStatus'} != 0 or 
+          # NewOrders get ApprovalStatus, Reversals don't.
+          ( exists($r->{'ApprovalStatus'}) ?
+            $r->{'ApprovalStatus'} != 1 :
+            $r->{'StatusMsg'} ne 'Approved' )
+          ) {
       $error = "Transaction error: '". ($r->{'ProcStatusMsg'} || $r->{'StatusMsg'}) . "'";
+      $self->is_success(0);
     }
     else {
       # success!
       $self->is_success(1);
-      $self->authorization($r->{'TxRefNum'});
+      # For credits, AuthCode is empty and gets converted to a hashref.
+      $self->authorization($r->{'AuthCode'}) if !ref($r->{'AuthCode'});
+      $self->order_number($r->{'TxRefNum'});
     }
-  }else{
+  } else {
     $error = "Server error: '$server_response'";
   }
   $self->error_message($error);
-  $self->is_success(0) if $error;
-
 }
 
 1;