[freeside-commits] branch FREESIDE_4_BRANCH updated. b2bc6e42d27b8bab7fa58500fe292a921b786854

Jonathan Prykop jonathan at 420.am
Wed Feb 1 15:37:15 PST 2017


The branch, FREESIDE_4_BRANCH has been updated
       via  b2bc6e42d27b8bab7fa58500fe292a921b786854 (commit)
       via  090f86537bd81e8194c5e75dec43208ca0a5944a (commit)
       via  bd1d715bf1d1c192c79413998b6f3e75d8252777 (commit)
       via  b5bcd08f8966b3daeb43bc42f0c540d59cfb923b (commit)
       via  d48efd5cb9637fd49f4c3f5004863e3477241939 (commit)
       via  14fde47eaa85df2ff08cd68024720e9cf23806b9 (commit)
      from  81b52d260514fb03ba962fbf891b91c1ac93f102 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit b2bc6e42d27b8bab7fa58500fe292a921b786854
Merge: 090f865 81b52d2
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Feb 1 17:34:51 2017 -0600

    Merge branch 'FREESIDE_4_BRANCH' of git.freeside.biz:/home/git/freeside into FREESIDE_4_BRANCH


commit 090f86537bd81e8194c5e75dec43208ca0a5944a
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Wed Feb 1 14:49:03 2017 -0800

    never blank an existing paycardtype inadvertantly

diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm
index e25d73e..136acf1 100644
--- a/FS/FS/cust_payby.pm
+++ b/FS/FS/cust_payby.pm
@@ -357,14 +357,14 @@ sub check {
       or return gettext('invalid_card'); # . ": ". $self->payinfo;
 
     # see parallel checks in check_payinfo_cardtype & payinfo_Mixin::payinfo_check
-    my $cardtype = '';
+    my $cardtype = $self->paycardtype;
     if ( $self->tokenized ) {
       if ( $self->paymask =~ /^\d+x/ ) {
         $cardtype = cardtype($self->paymask);
       } else {
-        $cardtype = '';
         #return "paycardtype required ".
-        #       "(can't derive from a token and no paymask w/prefix provided)";
+        #       "(can't derive from a token and no paymask w/prefix provided)"
+        #  unless $cardtype;
       }
     } else {
       $cardtype = cardtype($self->payinfo);
diff --git a/FS/FS/payinfo_Mixin.pm b/FS/FS/payinfo_Mixin.pm
index cb9b723..84759cc 100644
--- a/FS/FS/payinfo_Mixin.pm
+++ b/FS/FS/payinfo_Mixin.pm
@@ -202,7 +202,7 @@ sub payinfo_check {
       if ( $self->paymask =~ /^\d+x/ ) {
         $self->set('paycardtype', cardtype($self->paymask));
       } else {
-        $self->set('paycardtype', '');
+        $self->set('paycardtype', '') unless $self->paycardtype;
         #return "paycardtype required ".
         #       "(can't derive from a token and no paymask w/prefix provided)";
       }
@@ -233,7 +233,7 @@ sub payinfo_check {
       # if we can't decrypt the card, at least detect the cardtype
       $self->set('paycardtype', cardtype($self->paymask));
     } else {
-      $self->set('paycardtype', '');
+      $self->set('paycardtype', '') unless $self->paycardtype;
       # return "paycardtype required ".
       #        "(can't derive from a token and no paymask w/prefix provided)";
     }

commit bd1d715bf1d1c192c79413998b6f3e75d8252777
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Feb 1 16:31:56 2017 -0600

    71513: Card tokenization [American Express tests, v4]

diff --git a/FS/t/suite/13-tokenization.t b/FS/t/suite/13-tokenization.t
index edb0f38..ebded87 100755
--- a/FS/t/suite/13-tokenization.t
+++ b/FS/t/suite/13-tokenization.t
@@ -8,11 +8,13 @@ use FS::cust_main;
 use Business::CreditCard qw(generate_last_digit);
 use DateTime;
 if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
-  plan tests => 20;
+  plan tests => 27;
 } else {
   plan skip_all => 'CardFortress test encryption key is not installed.';
 }
 
+#local $FS::cust_main::Billing_Realtime::DEBUG = 2;
+
 ### can only run on test database (company name "Freeside Test")
 ### will run upgrade, which uses lots of prints & warns beyond regular test output
 
@@ -101,6 +103,32 @@ my $pending_failed = FS::cust_pay_pending->new({
 $err = $pending_failed->insert;
 ok( !$err, "create a failed payment attempt" ) or BAIL_OUT($err);
 
+# create two customers with an AmEx card & paycvv,
+# then run a payment with one, just to generate some test AmEx data
+
+my $amex_cust;
+foreach my $i (0,1) {
+  my $cust_main = $fs->new_customer("AmEx $i");
+  isa_ok ( $cust_main, 'FS::cust_main', "AmEx $i customer" ) or BAIL_OUT('');
+  $err = $cust_main->insert;
+  ok( !$err, "insert AmEx $i customer" ) or BAIL_OUT($err);
+  # add card
+  my $cust_payby;
+  my %card = random_card();
+  $card{'payinfo'} = '347594362484937';
+  $card{'paycvv'}  = '1234';
+  $err = $cust_main->save_cust_payby(
+    %card,
+    payment_payby => $card{'payby'},
+    auto => 1,
+    saved_cust_payby => \$cust_payby
+  );
+  ok( !$err, "save AmEx $i card" ) or BAIL_OUT($err);
+  $amex_cust = $cust_main;
+}
+$err = $amex_cust->realtime_cust_payby( amount => '1.00' );
+ok( !$err, "run AmEx payment" ) or BAIL_OUT($err);
+
 # find two stored credit cards.
 my @cust = map { FS::cust_main->by_key($_) } (10, 12);
 my @payby = map { ($_->cust_payby)[0] } @cust;
diff --git a/FS/t/suite/14-tokenization_refund.t b/FS/t/suite/14-tokenization_refund.t
index 1a0f840..a7d9fa8 100755
--- a/FS/t/suite/14-tokenization_refund.t
+++ b/FS/t/suite/14-tokenization_refund.t
@@ -229,8 +229,10 @@ foreach my $voiding (0,1) {
 exit;
 
 sub random_card {
-  my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
-  $payinfo .= generate_last_digit($payinfo);
+#  my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
+#  $payinfo .= generate_last_digit($payinfo);
+# Use AmEx for everything, to make sure cardtype gets set correctly
+  my $payinfo = '347594362484937'; #American Express
   my $paydate = DateTime->now
                 ->add('years' => 1)
                 ->truncate(to => 'month')
@@ -239,6 +241,7 @@ sub random_card {
            'payinfo'  => $payinfo,
            'paydate'  => $paydate,
            'payname'  => 'Tokenize Me',
+           'paycvv'   => '1234', #American Express
   );
 }
 

commit b5bcd08f8966b3daeb43bc42f0c540d59cfb923b
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Feb 1 15:06:52 2017 -0600

    71513: Card tokenization [bug fix to upgrade]

diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 3559409..9e0fda4 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -5871,11 +5871,12 @@ sub _upgrade_next_recnum {
   my $recnum = shift @$recnums;
   return $recnum if $recnum;
   my $tclass = 'FS::'.$table;
+  my $paycardtypecheck = ($table ne 'cust_pay_pending') ? q( OR paycardtype = 'Tokenized') : '';
   my $sql = 'SELECT '.$tclass->primary_key.
             ' FROM '.$table.
             ' WHERE '.$tclass->primary_key.' > '.$$lastrecnum.
             "   AND payby IN ( 'CARD', 'DCRD', 'CHEK', 'DCHK' ) ".
-            "   AND ( length(payinfo) < 80 OR paycardtype = 'Tokenized' ) ".
+            "   AND ( length(payinfo) < 80$paycardtypecheck ) ".
             ' ORDER BY '.$tclass->primary_key.' LIMIT 500';
   my $sth = $dbh->prepare($sql) or die $dbh->errstr;
   $sth->execute() or die $sth->errstr;

commit d48efd5cb9637fd49f4c3f5004863e3477241939
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Feb 1 14:35:38 2017 -0600

    71513: Card tokenization [refactor only tokenize cards; job critical error handling]

diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index ab2e39c..12f1979 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -2372,7 +2372,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
@@ -2385,6 +2385,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;
 
@@ -2481,6 +2482,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
@@ -2517,6 +2519,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
@@ -2549,6 +2552,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;
@@ -2579,6 +2586,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;
@@ -2681,6 +2689,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;
@@ -2696,7 +2705,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!
@@ -2708,8 +2717,6 @@ sub _token_check_next_recnum {
   my $sth = $dbh->prepare(
     'SELECT '.$tclass->primary_key.
     ' FROM '.$table.
-    " WHERE payby IN ( 'CARD', 'DCRD' ) ".
-    "   AND ( length(payinfo) > 80 OR payinfo NOT LIKE '99%' )".
     ' ORDER BY '.$tclass->primary_key.
     ' LIMIT '.$step.
     ' OFFSET '.$$offset

commit 14fde47eaa85df2ff08cd68024720e9cf23806b9
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Feb 1 14:00:53 2017 -0600

    71513: Card tokenization [tweaks to paycardtype checks]

diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm
index 36f90df..e25d73e 100644
--- a/FS/FS/cust_payby.pm
+++ b/FS/FS/cust_payby.pm
@@ -356,7 +356,19 @@ sub check {
     validate($payinfo)
       or return gettext('invalid_card'); # . ": ". $self->payinfo;
 
-    my $cardtype = $self->paycardtype || cardtype($payinfo);
+    # see parallel checks in check_payinfo_cardtype & payinfo_Mixin::payinfo_check
+    my $cardtype = '';
+    if ( $self->tokenized ) {
+      if ( $self->paymask =~ /^\d+x/ ) {
+        $cardtype = cardtype($self->paymask);
+      } else {
+        $cardtype = '';
+        #return "paycardtype required ".
+        #       "(can't derive from a token and no paymask w/prefix provided)";
+      }
+    } else {
+      $cardtype = cardtype($self->payinfo);
+    }
     
     return gettext('unknown_card_type') if $cardtype eq "Unknown";
     
@@ -545,7 +557,15 @@ sub check_payinfo_cardtype {
   my $payinfo = $self->payinfo;
   $payinfo =~ s/\D//g;
 
+  # see parallel checks in cust_payby::check & payinfo_Mixin::payinfo_check
   if ( $self->tokenized($payinfo) ) {
+    if ( $self->paymask =~ /^\d+x/ ) {
+      $self->set('paycardtype', cardtype($self->paymask));
+    } else {
+      $self->set('paycardtype', '');
+      #return "paycardtype required ".
+      #       "(can't derive from a token and no paymask w/prefix provided)";
+    }
     return '';
   }
 
diff --git a/FS/FS/payinfo_Mixin.pm b/FS/FS/payinfo_Mixin.pm
index 7401eb9..cb9b723 100644
--- a/FS/FS/payinfo_Mixin.pm
+++ b/FS/FS/payinfo_Mixin.pm
@@ -197,20 +197,17 @@ sub payinfo_check {
 
   if ( $self->payby eq 'CARD' && ! $self->is_encrypted($self->payinfo) ) {
 
-    unless ( $self->paycardtype ) {
-
-      if ( $self->tokenized ) {
-        if ( $self->paymask =~ /^\d+x/ ) {
-          $self->set('paycardtype', cardtype($self->paymask));
-        } else {
-          $self->set('paycardtype', '');
-         # return "paycardtype required ".
-         #        "(can't derive from a token and no paymask w/prefix provided)";
-        }
+    # see parallel checks in cust_payby::check & cust_payby::check_payinfo_cardtype
+    if ( $self->tokenized ) {
+      if ( $self->paymask =~ /^\d+x/ ) {
+        $self->set('paycardtype', cardtype($self->paymask));
       } else {
-        $self->set('paycardtype', cardtype($self->payinfo));
+        $self->set('paycardtype', '');
+        #return "paycardtype required ".
+        #       "(can't derive from a token and no paymask w/prefix provided)";
       }
-
+    } else {
+      $self->set('paycardtype', cardtype($self->payinfo));
     }
 
     if ( $ignore_masked_payinfo and $self->mask_payinfo eq $self->payinfo ) {
@@ -232,17 +229,13 @@ sub payinfo_check {
 
   } else {
 
-    unless ( $self->paycardtype ) {
-
-      if ( $self->payby eq 'CARD' && $self->paymask =~ /^\d+x/  ) {
-        # if we can't decrypt the card, at least detect the cardtype
-        $self->set('paycardtype', cardtype($self->paymask));
-      } else {
-        $self->set('paycardtype', '');
-        # return "paycardtype required ".
-        #        "(can't derive from a token and no paymask w/prefix provided)";
-      }
-
+    if ( $self->payby eq 'CARD' && $self->paymask =~ /^\d+x/  ) {
+      # if we can't decrypt the card, at least detect the cardtype
+      $self->set('paycardtype', cardtype($self->paymask));
+    } else {
+      $self->set('paycardtype', '');
+      # return "paycardtype required ".
+      #        "(can't derive from a token and no paymask w/prefix provided)";
     }
 
     if ( $self->is_encrypted($self->payinfo) ) {

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/cust_main.pm                  |    3 ++-
 FS/FS/cust_main/Billing_Realtime.pm |   15 ++++++++++----
 FS/FS/cust_payby.pm                 |   22 +++++++++++++++++++-
 FS/FS/payinfo_Mixin.pm              |   39 ++++++++++++++---------------------
 FS/t/suite/13-tokenization.t        |   30 ++++++++++++++++++++++++++-
 FS/t/suite/14-tokenization_refund.t |    7 +++++--
 6 files changed, 84 insertions(+), 32 deletions(-)




More information about the freeside-commits mailing list