71513: Card tokenization [bug fixes to previous checkpoint]
[freeside.git] / FS / FS / cust_main / Billing_Realtime.pm
index ced3b23..e7a8030 100644 (file)
@@ -376,28 +376,16 @@ sub _bop_content {
 }
 
 sub _tokenize_card {
-  my ($self,$transaction,$cust_payby,$log,%opt) = @_;
-
-  if ( $cust_payby
-       and $transaction->can('card_token') 
+  my ($self,$transaction,$options) = @_;
+  if ( $transaction->can('card_token') 
        and $transaction->card_token 
-       and $cust_payby->payinfo !~ /^99\d{14}$/ #not already tokenized
+       and !$self->tokenized($options->{'payinfo'})
   ) {
-
-    $cust_payby->payinfo($transaction->card_token);
-
-    my $error;
-    $error = $cust_payby->replace if $opt{'replace'};
-    if ( $error ) {
-      $log->error('Error storing token for cust '.$self->custnum.', cust_payby '.$cust_payby->custpaybynum.': '.$error);
-      return $error;
-    } else {
-      $log->debug('Tokenized card for cust '.$self->custnum.', cust_payby '.$cust_payby->custpaybynum);
-      return '';
-    }
-
+    $options->{'payinfo'} = $transaction->card_token; #for creating cust_pay
+    $options->{'cust_payby'}->payinfo($transaction->card_token) if $options->{'cust_payby'};
+    return $transaction->card_token;
   }
-
+  return '';
 }
 
 my %bop_method2payby = (
@@ -436,8 +424,8 @@ sub realtime_bop {
 
   my $cc_surcharge = 0;
   my $cc_surcharge_pct = 0;
-  $cc_surcharge_pct = $conf->config('credit-card-surcharge-percentage') 
-    if $conf->config('credit-card-surcharge-percentage')
+  $cc_surcharge_pct = $conf->config('credit-card-surcharge-percentage', $self->agentnum
+    if $conf->config('credit-card-surcharge-percentage', $self->agentnum)
     && $options{method} eq 'CC';
 
   # always add cc surcharge if called from event 
@@ -803,6 +791,8 @@ sub realtime_bop {
   ) {
     my $error = $self->remove_cvv_from_cust_payby($options{payinfo});
     if ( $error ) {
+      $log->critical('Error removing cvv for cust '.$self->custnum.': '.$error);
+      #not returning error, should at least attempt to handle results of an otherwise valid transaction
       warn "WARNING: error removing cvv: $error\n";
     }
   }
@@ -811,8 +801,15 @@ sub realtime_bop {
   # Tokenize
   ###
 
-  my $error = $self->_tokenize_card($transaction,$options{'cust_payby'},$log,'replace' => 1);
-  return $error if $error;
+  if (my $card_token = $self->_tokenize_card($transaction,\%options)) {
+    # cpp will be replaced in _realtime_bop_result
+    $cust_pay_pending->payinfo($card_token);
+    if ($options{'cust_payby'} and my $error = $options{'cust_payby'}->replace) {
+      $log->critical('Error storing token for cust '.$self->custnum.', cust_payby '.$options{'cust_payby'}->custpaybynum.': '.$error);
+      #not returning error, should at least attempt to handle results of an otherwise valid transaction
+      #this leaves real card number in cust_payby, but can't do much else if cust_payby won't replace
+    }
+  }
 
   ###
   # result handling
@@ -849,9 +846,7 @@ sub fake_bop {
      'paid'     => $options{amount},
      '_date'    => '',
      'payby'    => $bop_method2payby{$options{method}},
-     #'payinfo'  => $payinfo,
      'payinfo'  => '4111111111111111',
-     #'paydate'  => $paydate,
      'paydate'  => '2012-05-01',
      'processor'      => 'FakeProcessor',
      'auth'           => '54',
@@ -911,7 +906,7 @@ sub _realtime_bop_result {
     or return "no payment gateway in arguments to _realtime_bop_result";
 
   $cust_pay_pending->status($transaction->is_success() ? 'captured' : 'declined');
-  my $cpp_captured_err = $cust_pay_pending->replace;
+  my $cpp_captured_err = $cust_pay_pending->replace; #also saves tokenization
   return $cpp_captured_err if $cpp_captured_err;
 
   if ( $transaction->is_success() ) {
@@ -2110,12 +2105,22 @@ sub realtime_verify_bop {
   }
 
   ###
+  # remove paycvv here?  need to find out if a reversed auth
+  #   counts as an initial transaction for paycvv retention requirements
+  ###
+
+  ###
   # Tokenize
   ###
 
-  #important that we not pass replace option here,
+  #important that we not replace cust_payby here,
   #because cust_payby->replace uses realtime_verify_bop!
-  $self->_tokenize_card($transaction,$options{'cust_payby'},$log);
+  if (my $card_token = $self->_tokenize_card($transaction,\%options)) {
+    $cust_pay_pending->payinfo($card_token);
+    my $cpp_token_err = $cust_pay_pending->replace;
+    #this leaves real card number in cust_payby, but can't do much else if cust_payby won't replace
+    return $cpp_token_err if $cpp_token_err;
+  }
 
   ###
   # result handling
@@ -2162,7 +2167,7 @@ sub realtime_tokenize {
   return "No cust_payby" unless $options{'cust_payby'};
   $self->_bop_cust_payby_options(\%options);
   return '' unless $options{method} eq 'CC';
-  return '' if $options{payinfo} =~ /^99\d{14}$/; #already tokenized
+  return '' if $self->tokenized($options{payinfo}); #already tokenized
 
   ###
   # select a gateway
@@ -2187,7 +2192,8 @@ sub realtime_tokenize {
                                   );
 
   my %supported_actions = $transaction->info('supported_actions');
-  return '' unless $supported_actions{'CC'} and grep(/^Tokenize$/,@{$supported_actions{'CC'}});
+  return '' unless $supported_actions{'CC'}
+                && grep /^Tokenize$/, @{$supported_actions{'CC'}};
 
   ###
   # check for banned credit card/ACH
@@ -2251,9 +2257,16 @@ sub realtime_tokenize {
 
   if ( $transaction->card_token() ) { # no is_success flag
 
-    #important that we not pass replace option here, 
+    # realtime_tokenize should not clear paycvv at this time.  it might be
+    # needed for the first transaction, and a tokenize isn't actually a
+    # transaction that hits the gateway.  at some point in the future, card
+    # fortress should take on the "store paycvv until first transaction"
+    # functionality and we should fix this in freeside, but i that's a bigger
+    # project for another time.
+
+    #important that we not replace cust_payby here, 
     #because cust_payby->replace uses realtime_tokenize!
-    $self->_tokenize_card($transaction,$options{'cust_payby'},$log);
+    $self->_tokenize_card($transaction,\%options);
 
   } else {
 
@@ -2265,6 +2278,21 @@ sub realtime_tokenize {
 
 }
 
+
+=item tokenized PAYINFO
+
+Convenience wrapper for L<FS::payinfo_Mixin/tokenized>
+
+PAYINFO is required
+
+=cut
+
+sub tokenized {
+  my $this = shift;
+  my $payinfo = shift;
+  FS::cust_pay->tokenized($payinfo);
+}
+
 =back
 
 =head1 BUGS