71513: Card tokenization [refactor only tokenize cards; job critical error handling]
[freeside.git] / FS / FS / cust_main / Billing_Realtime.pm
index 5d376e6..c2cb2e3 100644 (file)
@@ -416,6 +416,13 @@ sub realtime_bop {
   # set fields from passed cust_payby
   _bop_cust_payby_options(\%options);
 
+  # check for banned credit card/ACH
+  my $ban = FS::banned_pay->ban_search(
+    'payby'   => $bop_method2payby{$options{method}},
+    'payinfo' => $options{payinfo},
+  );
+  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
+
   # possibly run a separate transaction to tokenize card number,
   #   so that we never store tokenized card info in cust_pay_pending
   if (($options{method} eq 'CC') && !$self->tokenized($options{'payinfo'})) {
@@ -502,16 +509,6 @@ sub realtime_bop {
   die $@ if $@;
 
   ###
-  # check for banned credit card/ACH
-  ###
-
-  my $ban = FS::banned_pay->ban_search(
-    'payby'   => $bop_method2payby{$options{method}},
-    'payinfo' => $options{payinfo},
-  );
-  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
-
-  ###
   # check for term discount validity
   ###
 
@@ -1793,6 +1790,13 @@ sub realtime_verify_bop {
   return "No cust_payby" unless $options{'cust_payby'};
   _bop_cust_payby_options(\%options);
 
+  # check for banned credit card/ACH
+  my $ban = FS::banned_pay->ban_search(
+    'payby'   => $bop_method2payby{'CC'},
+    'payinfo' => $options{payinfo},
+  );
+  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
+
   # possibly run a separate transaction to tokenize card number,
   #   so that we never store tokenized card info in cust_pay_pending
   if (($options{method} eq 'CC') && !$self->tokenized($options{'payinfo'})) {
@@ -1813,16 +1817,6 @@ sub realtime_verify_bop {
   die $@ if $@;
 
   ###
-  # check for banned credit card/ACH
-  ###
-
-  my $ban = FS::banned_pay->ban_search(
-    'payby'   => $bop_method2payby{'CC'},
-    'payinfo' => $options{payinfo},
-  );
-  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
-
-  ###
   # massage data
   ###
 
@@ -2230,6 +2224,13 @@ sub realtime_tokenize {
   return '' unless $options{method} eq 'CC';
   return '' if $self->tokenized($options{payinfo}); #already tokenized
 
+  # check for banned credit card/ACH
+  my $ban = FS::banned_pay->ban_search(
+    'payby'   => $bop_method2payby{'CC'},
+    'payinfo' => $options{payinfo},
+  );
+  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
+
   ###
   # select a gateway
   ###
@@ -2258,16 +2259,6 @@ sub realtime_tokenize {
                 && grep /^Tokenize$/, @{$supported_actions{'CC'}};
 
   ###
-  # check for banned credit card/ACH
-  ###
-
-  my $ban = FS::banned_pay->ban_search(
-    'payby'   => $bop_method2payby{'CC'},
-    'payinfo' => $options{payinfo},
-  );
-  return "Banned credit card" if $ban && $ban->bantype ne 'warn';
-
-  ###
   # massage data
   ###
 
@@ -2382,7 +2373,7 @@ is set, this will instead cause a critical error to be recorded in the log,
 and any other tokenizable records will still be committed.
 
 If the I<daily> flag is also set, detection of existing untokenized records will 
-record a critical error in the system log (because they should have never appeared 
+record an info message in the system log (because they should have never appeared 
 in the first place.)  Tokenization will still be attempted.
 
 If any configured gateways do NOT have the ability to tokenize, or if a
@@ -2395,6 +2386,7 @@ sub token_check {
   #acts on all customers
   my %opt = @_;
   my $debug = !$opt{'quiet'} || $DEBUG;
+  my $hascritical = 0;
 
   warn "token_check called with opts\n".Dumper(\%opt) if $debug;
 
@@ -2472,7 +2464,7 @@ CUSTLOOP:
       }
 
       if ($require_tokenized && $opt{'daily'}) {
-        $log->critical("Untokenized card number detected in cust_payby ".$cust_payby->custpaybynum);
+        $log->info("Untokenized card number detected in cust_payby ".$cust_payby->custpaybynum. '; tokenizing');
         $dbh->commit or die $dbh->errstr; # commit log message
       }
 
@@ -2491,6 +2483,7 @@ CUSTLOOP:
         }
         my $error = "No gateway found for custnum ".$cust_main->custnum;
         if ($opt{'queue'}) {
+          $hascritical = 1;
           $log->critical($error);
           $dbh->commit or die $dbh->errstr; # commit error message
           next; # not next CUSTLOOP, want to record error for every cust_payby
@@ -2527,6 +2520,7 @@ CUSTLOOP:
       if ($error) {
         $error = "Error tokenizing cust_payby ".$cust_payby->custpaybynum.": ".$error;
         if ($opt{'queue'}) {
+          $hascritical = 1;
           $log->critical($error);
           $dbh->commit or die $dbh->errstr; # commit log message, release mutex
           next; # not next CUSTLOOP, want to record error for every cust_payby
@@ -2559,6 +2553,10 @@ CUSTLOOP:
 
     while (my $recnum = _token_check_next_recnum($dbh,$table,$step,\$offset,\@recnums)) {
       my $record = $tclass->by_key($recnum);
+      unless ($record->payby eq 'CARD') {
+        warn "Skipping non-card record for $table ".$record->get($record->primary_key) if $debug;
+        next;
+      }
       if (FS::cust_main::Billing_Realtime->tokenized($record->payinfo)) {
         warn "Skipping tokenized record for $table ".$record->get($record->primary_key) if $debug;
         next;
@@ -2573,7 +2571,7 @@ CUSTLOOP:
       }
 
       if ($require_tokenized && $opt{'daily'}) {
-        $log->critical("Untokenized card number detected in $table ".$record->get($record->primary_key));
+        $log->info("Untokenized card number detected in $table ".$record->get($record->primary_key). ';tokenizing');
         $dbh->commit or die $dbh->errstr; # commit log message
       }
 
@@ -2589,6 +2587,7 @@ CUSTLOOP:
         } else {
           my $error = "Could not load cust_main for $table ".$record->get($record->primary_key);
           if ($opt{'queue'}) {
+            $hascritical = 1;
             $log->critical($error);
             $dbh->commit or die $dbh->errstr; # commit log message
             next;
@@ -2691,6 +2690,7 @@ CUSTLOOP:
       if ($error) {
         $error = "Error tokenizing $table ".$record->get($record->primary_key).": ".$error;
         if ($opt{'queue'}) {
+          $hascritical = 1;
           $log->critical($error);
           $dbh->commit or die $dbh->errstr; # commit log message, release mutex
           next;
@@ -2706,7 +2706,7 @@ CUSTLOOP:
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
-  return '';
+  return $hascritical ? 'Critical errors occurred on some records, see system log' : '';
 }
 
 # not a method!
@@ -2715,7 +2715,13 @@ sub _token_check_next_recnum {
   my $recnum = shift @$recnums;
   return $recnum if $recnum;
   my $tclass = 'FS::'.$table;
-  my $sth = $dbh->prepare('SELECT '.$tclass->primary_key.' FROM '.$table.' ORDER BY '.$tclass->primary_key.' LIMIT '.$step.' OFFSET '.$$offset) or die $dbh->errstr;
+  my $sth = $dbh->prepare(
+    'SELECT '.$tclass->primary_key.
+    ' FROM '.$table.
+    ' ORDER BY '.$tclass->primary_key.
+    ' LIMIT '.$step.
+    ' OFFSET '.$$offset
+  ) or die $dbh->errstr;
   $sth->execute() or die $sth->errstr;
   my @recnums;
   while (my $rec = $sth->fetchrow_hashref) {