[freeside-commits] branch FREESIDE_4_BRANCH_71513 updated. 439ec8b79f5a430e4850d4620287e7774b4fb1e4

Jonathan Prykop jonathan at 420.am
Mon Dec 5 08:10:24 PST 2016


The branch, FREESIDE_4_BRANCH_71513 has been updated
       via  439ec8b79f5a430e4850d4620287e7774b4fb1e4 (commit)
       via  6bf73cace9d0cc630f54ec8b2cdb2d0fb6132cf5 (commit)
       via  b1fac2d6e401888a496ca1ac48fae1f46498333b (commit)
       via  998d7aa2fd1394d70a4043fac9eac8fbec8cc41d (commit)
       via  32d2dd6ca5cbf5fd557583502211cbc4c036e22e (commit)
      from  553c407c1a2162c3cc220f54a286f3c43ae5f0e7 (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 439ec8b79f5a430e4850d4620287e7774b4fb1e4
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Mon Dec 5 10:06:10 2016 -0600

    71513: Card tokenization [v4 test db handling]

diff --git a/FS/t/suite/13-tokenization.t b/FS/t/suite/13-tokenization.t
index 0a965aa..edb0f38 100755
--- a/FS/t/suite/13-tokenization.t
+++ b/FS/t/suite/13-tokenization.t
@@ -8,7 +8,7 @@ use FS::cust_main;
 use Business::CreditCard qw(generate_last_digit);
 use DateTime;
 if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
-  plan tests => 18;
+  plan tests => 20;
 } else {
   plan skip_all => 'CardFortress test encryption key is not installed.';
 }
@@ -23,7 +23,23 @@ my $bopconf;
 
 like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');
 
-# test db no longer contains cardtype overrides
+# some pre-upgrade cleanup, upgrade will fail if these are still configured
+foreach my $cust_main ( $fs->qsearch('cust_main') ) {
+  my @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
+  if (@count > 1) {
+    note("DELETING CARDTYPE GATEWAYS");
+    foreach my $apg (@count) {
+      $err = $apg->delete if $apg->cardtype;
+      last if $err;
+    }
+    @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
+    if (@count > 1) {
+      $err = "Still found ". at count." gateways for custnum ".$cust_main->custnum;
+      last;
+    }
+  }
+}
+ok( !$err, "remove obsolete payment gateways" ) or BAIL_OUT($err);
 
 $bopconf = 
 'IPPay
@@ -121,6 +137,24 @@ private_key
 $conf->set('business-onlinepayment' => $bopconf);
 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting tokenizable default gateway" ) or BAIL_OUT('');
 
+foreach my $pg ($fs->qsearch('payment_gateway')) {
+  unless ($pg->gateway_module eq 'CardFortress') {
+    note('UPGRADING NON-CF PAYMENT GATEWAY');
+    my %pgopts = (
+      gateway          => $pg->gateway_module,
+      gateway_login    => $pg->gateway_username,
+      gateway_password => $pg->gateway_password,
+      private_key      => '/usr/local/etc/freeside/cardfortresstest.txt',
+    );
+    $pg->gateway_module('CardFortress');
+    $pg->gateway_username('cardfortresstest');
+    $pg->gateway_password('(TEST54)');
+    $err = $pg->replace(\%pgopts);
+    last if $err;
+  }
+}
+ok( !$err, "remove non-CF payment gateways" ) or BAIL_OUT($err);
+
 # create a payment using a non-tokenized card. this should immediately
 # trigger tokenization.
 ok( $payby[1]->payby eq 'CARD' && ! $payby[1]->tokenized,

commit 6bf73cace9d0cc630f54ec8b2cdb2d0fb6132cf5
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Mon Dec 5 08:53:13 2016 -0600

    71513: Card tokenization [minor test tweaks]

diff --git a/FS/t/suite/13-tokenization.t b/FS/t/suite/13-tokenization.t
index b4d204f..0a965aa 100755
--- a/FS/t/suite/13-tokenization.t
+++ b/FS/t/suite/13-tokenization.t
@@ -137,27 +137,8 @@ ok( $payment[1]->tokenized, "payment is tokenized" );
 $payby[1] = $payby[1]->replace_old;
 ok( $payby[1]->tokenized, "card is now tokenized" );
 
-# test db doesn't have this
-#foreach my $pg ($fs->qsearch('payment_gateway')) {
-#  unless ($pg->gateway_module eq 'CardFortress') {
-#    note('UPGRADING NON-CF PAYMENT GATEWAY');
-#    my %pgopts = (
-#      gateway          => $pg->gateway_module,
-#      gateway_login    => $pg->gateway_username,
-#      gateway_password => $pg->gateway_password,
-#      private_key      => '/usr/local/etc/freeside/cardfortresstest.txt',
-#    );
-#    $pg->gateway_module('CardFortress');
-#    $pg->gateway_username('cardfortresstest');
-#    $pg->gateway_password('(TEST54)');
-#    $err = $pg->replace(\%pgopts);
-#    last if $err;
-#  }
-#}
-#ok( !$err, "remove non-CF payment gateways" ) or BAIL_OUT($err);
-
 # invoke the part of freeside-upgrade that tokenizes
-FS::cust_main->queueable_upgrade( quiet => 1 );
+FS::cust_main->queueable_upgrade();
 #$err = system('freeside-upgrade','admin');
 #ok( !$err, 'tokenizable upgrade' ) or BAIL_OUT('Error string: '.$!);
 

commit b1fac2d6e401888a496ca1ac48fae1f46498333b
Author: Mark Wells <mark at freeside.biz>
Date:   Sun Dec 4 23:13:04 2016 -0800

    rework card tokenization test

diff --git a/FS/t/suite/13-tokenization.t b/FS/t/suite/13-tokenization.t
index 9a3ef3f..b4d204f 100755
--- a/FS/t/suite/13-tokenization.t
+++ b/FS/t/suite/13-tokenization.t
@@ -1,36 +1,29 @@
 #!/usr/bin/perl
 
+use strict;
 use FS::Test;
-use Test::More tests => 9;
+use Test::More;
 use FS::Conf;
+use FS::cust_main;
+use Business::CreditCard qw(generate_last_digit);
+use DateTime;
+if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
+  plan tests => 18;
+} else {
+  plan skip_all => 'CardFortress test encryption key is not installed.';
+}
 
 ### can only run on test database (company name "Freeside Test")
 ### will run upgrade, which uses lots of prints & warns beyond regular test output
 
 my $fs = FS::Test->new( user => 'admin' );
-my $conf = new_ok('FS::Conf');
+my $conf = FS::Conf->new;
 my $err;
 my $bopconf;
 
 like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');
 
-# some pre-upgrade cleanup, upgrade will fail if these are still configured
-foreach my $cust_main ( $fs->qsearch('cust_main') ) {
-  my @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
-  if (@count > 1) {
-    note("DELETING CARDTYPE GATEWAYS");
-    foreach my $apg (@count) {
-      $err = $apg->delete if $apg->cardtype;
-      last if $err;
-    }
-    @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
-    if (@count > 1) {
-      $err = "Still found ". at count." gateways for custnum ".$cust_main->custnum;
-      last;
-    }
-  }
-}
-ok( !$err, "remove obsolete payment gateways" ) or BAIL_OUT($err);
+# test db no longer contains cardtype overrides
 
 $bopconf = 
 'IPPay
@@ -78,9 +71,40 @@ foreach my $cust_pay ( $fs->qsearch('cust_pay',{ payby => 'CARD' }) ) {
 }
 ok( !$err, "create some refunds and voids" ) or BAIL_OUT($err);
 
+# also, just to test behavior in this case, create a record for an aborted
+# verification payment. this will have no customer number.
+
+my $pending_failed = FS::cust_pay_pending->new({
+  'custnum_pending' => 1,
+  'paid'    => '1.00',
+  '_date'   => time - 86400,
+  random_card(),
+  'status'  => 'failed',
+  'statustext' => 'Tokenization upgrade test',
+});
+$err = $pending_failed->insert;
+ok( !$err, "create a failed payment attempt" ) 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;
+my @payment;
+
+ok( $payby[0]->payby eq 'CARD' && !$payby[0]->tokenized,
+  "first customer has a non-tokenized card"
+  ) or BAIL_OUT();
+
+$err = $cust[0]->realtime_cust_payby(amount => '2.00');
+ok( !$err, "create a payment through IPPay" )
+  or BAIL_OUT($err);
+$payment[0] = $fs->qsearchs('cust_pay', { custnum => $cust[0]->custnum,
+                                     paid => '2.00' })
+  or BAIL_OUT("can't find payment record");
+
 $err = system('freeside-upgrade','admin');
 ok( !$err, 'initial upgrade' ) or BAIL_OUT('Error string: '.$!);
 
+# switch to CardFortress
 $bopconf =
 'CardFortress
 cardfortresstest
@@ -97,26 +121,78 @@ private_key
 $conf->set('business-onlinepayment' => $bopconf);
 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting tokenizable default gateway" ) or BAIL_OUT('');
 
-foreach my $pg ($fs->qsearch('payment_gateway')) {
-  unless ($pg->gateway_module eq 'CardFortress') {
-    note('UPGRADING NON-CF PAYMENT GATEWAY');
-    my %pgopts = (
-      gateway          => $pg->gateway_module,
-      gateway_login    => $pg->gateway_username,
-      gateway_password => $pg->gateway_password,
-      private_key      => '/usr/local/etc/freeside/cardfortresstest.txt',
-    );
-    $pg->gateway_module('CardFortress');
-    $pg->gateway_username('cardfortresstest');
-    $pg->gateway_password('(TEST54)');
-    $err = $pg->replace(\%pgopts);
-    last if $err;
-  }
+# create a payment using a non-tokenized card. this should immediately
+# trigger tokenization.
+ok( $payby[1]->payby eq 'CARD' && ! $payby[1]->tokenized,
+  "second customer has a non-tokenized card"
+  ) or BAIL_OUT();
+
+$err = $cust[1]->realtime_cust_payby(amount => '3.00');
+ok( !$err, "tokenize a card when it's first used for payment" )
+  or BAIL_OUT($err);
+$payment[1] = $fs->qsearchs('cust_pay', { custnum => $cust[1]->custnum,
+                                     paid => '3.00' })
+  or BAIL_OUT("can't find payment record");
+ok( $payment[1]->tokenized, "payment is tokenized" );
+$payby[1] = $payby[1]->replace_old;
+ok( $payby[1]->tokenized, "card is now tokenized" );
+
+# test db doesn't have this
+#foreach my $pg ($fs->qsearch('payment_gateway')) {
+#  unless ($pg->gateway_module eq 'CardFortress') {
+#    note('UPGRADING NON-CF PAYMENT GATEWAY');
+#    my %pgopts = (
+#      gateway          => $pg->gateway_module,
+#      gateway_login    => $pg->gateway_username,
+#      gateway_password => $pg->gateway_password,
+#      private_key      => '/usr/local/etc/freeside/cardfortresstest.txt',
+#    );
+#    $pg->gateway_module('CardFortress');
+#    $pg->gateway_username('cardfortresstest');
+#    $pg->gateway_password('(TEST54)');
+#    $err = $pg->replace(\%pgopts);
+#    last if $err;
+#  }
+#}
+#ok( !$err, "remove non-CF payment gateways" ) or BAIL_OUT($err);
+
+# invoke the part of freeside-upgrade that tokenizes
+FS::cust_main->queueable_upgrade( quiet => 1 );
+#$err = system('freeside-upgrade','admin');
+#ok( !$err, 'tokenizable upgrade' ) or BAIL_OUT('Error string: '.$!);
+
+$payby[0] = $payby[0]->replace_old;
+ok( $payby[0]->tokenized, "old card was tokenized during upgrade" );
+$payment[0] = $payment[0]->replace_old;
+ok( $payment[0]->tokenized, "old payment was tokenized during upgrade" );
+ok( ($payment[0]->cust_pay_pending)[0]->tokenized, "old cust_pay_pending was tokenized during upgrade" );
+
+$pending_failed = $pending_failed->replace_old;
+ok( $pending_failed->tokenized, "cust_pay_pending with no customer was tokenized" );
+
+# add a new payment card to one customer
+$payby[2] = FS::cust_payby->new({
+  custnum => $cust[0]->custnum,
+  random_card(),
+});
+$err = $payby[2]->insert;
+ok( !$err, "new card was saved" );
+ok($payby[2]->tokenized, "new card is tokenized" );
+
+sub random_card {
+  my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
+  $payinfo .= generate_last_digit($payinfo);
+  my $paydate = DateTime->now
+                ->add('years' => 1)
+                ->truncate(to => 'month')
+                ->strftime('%F');
+  return ( 'payby'    => 'CARD',
+           'payinfo'  => $payinfo,
+           'paydate'  => $paydate,
+           'payname'  => 'Tokenize Me',
+  );
 }
-ok( !$err, "remove non-CF payment gateways" ) or BAIL_OUT($err);
 
-$err = system('freeside-upgrade','admin');
-ok( !$err, 'tokenizable upgrade' ) or BAIL_OUT('Error string: '.$!);
 
 1;
 

commit 998d7aa2fd1394d70a4043fac9eac8fbec8cc41d
Author: Mark Wells <mark at freeside.biz>
Date:   Sun Dec 4 21:35:52 2016 -0800

    minor fix for custnum-less cust_pay_pending, #71513

diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index 0ea94c8..0ef423b 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -2552,7 +2552,11 @@ CUSTLOOP:
         # might happen for cust_pay_pending from failed verify records,
         #   in which case we attempt tokenization without cust_main
         # everything else should absolutely have a cust_main
-        unless ($table eq 'cust_pay_pending' && $record->{'custnum_pending'}) {
+        if ($table eq 'cust_pay_pending' and !$record->custnum ) {
+          # override the usual safety check and allow the record to be
+          # updated even without a custnum.
+          $record->set('custnum_pending', 1);
+        } else {
           my $error = "Could not load cust_main for $table ".$record->get($record->primary_key);
           if ($opt{'queue'}) {
             $log->critical($error);
@@ -2634,7 +2638,7 @@ CUSTLOOP:
       warn "ATTEMPTING GATEWAY-ONLY TOKENIZE" if $debug && !$cust_main;
 
       # if we got this far, time to mutex
-      $record = $record->select_for_update;
+      $record->select_for_update;
 
       # no clear record of name/address/etc used for transaction,
       # but will load name/phone/id from customer if run as an object method,

commit 32d2dd6ca5cbf5fd557583502211cbc4c036e22e
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Sat Dec 3 10:36:03 2016 -0600

    71513: Card tokenization [refund gateway choice]

diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index 68431fc..0ea94c8 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -1480,10 +1480,12 @@ sub realtime_refund_bop {
       @bop_options = $payment_gateway->gatewaynum
                        ? $payment_gateway->options
                        : @{ $payment_gateway->get('options') };
+      my %bop_options = @bop_options;
 
       return "processor of payment $options{'paynum'} $processor does not".
              " match default processor $conf_processor"
-        unless $processor eq $conf_processor;
+        unless ($processor eq $conf_processor)
+            || (($conf_processor eq 'CardFortress') && ($processor eq $bop_options{'gateway'}));
 
     }
 
@@ -1492,9 +1494,7 @@ sub realtime_refund_bop {
            # like a normal transaction 
  
     my $payment_gateway =
-      $self->agent->payment_gateway( 'method'  => $options{method},
-                                     #'payinfo' => $payinfo,
-                                   );
+      $self->agent->payment_gateway( 'method'  => $options{method} );
     my( $processor, $login, $password, $namespace ) =
       map { my $method = "gateway_$_"; $payment_gateway->$method }
         qw( module username password namespace );
@@ -1627,18 +1627,22 @@ sub realtime_refund_bop {
     if length($payip);
 
   my $payinfo = '';
+  my $paymask = ''; # for refund record
   if ( $options{method} eq 'CC' ) {
 
     if ( $cust_pay ) {
       $content{card_number} = $payinfo = $cust_pay->payinfo;
+      $paymask = $cust_pay->paymask;
       (exists($options{'paydate'}) ? $options{'paydate'} : $cust_pay->paydate)
         =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/ &&
         ($content{expiration} = "$2/$1");  # where available
     } else {
-      $content{card_number} = $payinfo = $self->payinfo;
-      (exists($options{'paydate'}) ? $options{'paydate'} : $self->paydate)
-        =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
-      $content{expiration} = "$2/$1";
+      # this really needs a better cleanup
+      die "Refund without paynum not supported";
+#      $content{card_number} = $payinfo = $self->payinfo;
+#      (exists($options{'paydate'}) ? $options{'paydate'} : $self->paydate)
+#        =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+#      $content{expiration} = "$2/$1";
     }
 
   } elsif ( $options{method} eq 'ECHECK' ) {
@@ -1702,6 +1706,7 @@ sub realtime_refund_bop {
     '_date'    => '',
     'payby'    => $bop_method2payby{$options{method}},
     'payinfo'  => $payinfo,
+    'paymask'  => $paymask,
     'reasonnum'     => $options{'reasonnum'},
     'gatewaynum'    => $gatewaynum, # may be null
     'processor'     => $processor,
@@ -2442,7 +2447,7 @@ CUSTLOOP:
       }
 
       # only load gateway if we need to, and only need to load it once
-      my $payment_gateway ||= $cust_main->_payment_gateway({
+      $payment_gateway ||= $cust_main->_payment_gateway({
         'method'  => 'CC',
         'conf'    => $conf,
         'nofatal' => 1, # handle lack of gateway smoothly below
@@ -2542,15 +2547,72 @@ CUSTLOOP:
         $dbh->commit or die $dbh->errstr; # commit log message
       }
 
-      # don't use customer agent gateway here, use the gatewaynum specified by the record
-      my $gateway = FS::payment_gateway->by_key_or_default( 
-        'method'     => 'CC',
-        'conf'       => $conf,
-        'nofatal'    => 1,
-        'gatewaynum' => $record->gatewaynum || '',
-      );
+      my $cust_main = $record->cust_main;
+      if (!$cust_main) {
+        # might happen for cust_pay_pending from failed verify records,
+        #   in which case we attempt tokenization without cust_main
+        # everything else should absolutely have a cust_main
+        unless ($table eq 'cust_pay_pending' && $record->{'custnum_pending'}) {
+          my $error = "Could not load cust_main for $table ".$record->get($record->primary_key);
+          if ($opt{'queue'}) {
+            $log->critical($error);
+            $dbh->commit or die $dbh->errstr; # commit log message
+            next;
+          }
+          $dbh->rollback if $oldAutoCommit;
+          die $error;
+        }
+      }
+
+      my $gateway;
+
+      # use the gatewaynum specified by the record if possible
+      $gateway = FS::payment_gateway->by_key_with_namespace(
+        'gatewaynum' => $record->gatewaynum,
+      ) if $record->gateway;
+
+      # otherwise use the cust agent gateway if possible (which realtime_refund_bop would do)
+      # otherwise just use default gateway
+      unless ($gateway) {
+
+        $gateway = $cust_main 
+                 ? $cust_main->agent->payment_gateway
+                 : FS::payment_gateway->default_gateway;
+
+        # check for processor mismatch
+        unless ($table eq 'cust_pay_pending') { # has no processor table
+          if (my $processor = $record->processor) {
+
+            my $conf_processor = $gateway->gateway_module;
+            my %bop_options = $gateway->gatewaynum
+                            ? $gateway->options
+                            : @{ $gateway->get('options') };
+
+            # this is the same standard used by realtime_refund_bop
+            unless (
+              ($processor eq $conf_processor) ||
+              (($conf_processor eq 'CardFortress') && ($processor eq $bop_options{'gateway'}))
+            ) {
+
+              # processors don't match, so refund already cannot be run on this object,
+              # regardless of what we do now...
+              # but unless we gotta tokenize everything, just leave well enough alone
+              unless ($require_tokenized) {
+                warn "Skipping mismatched processor for $table ".$record->get($record->primary_key) if $debug;
+                next;
+              }
+              ### no error--we'll tokenize using the new gateway, just to remove stored payinfo,
+              ### because refunds are already impossible for this record, anyway
+
+            } # end processor mismatch
+
+          } # end record has processor
+        } # end not cust_pay_pending
+
+      }
+
+      # means no default gateway, no promise to tokenize, can skip
       unless ($gateway) {
-        # means no default gateway, no promise to tokenize, can skip
         warn "Skipping missing gateway for $table ".$record->get($record->primary_key) if $debug;
         next;
       }
@@ -2569,24 +2631,7 @@ CUSTLOOP:
         next;
       }
 
-      my $cust_main = $record->cust_main;
-      if (!$cust_main) {
-        # might happen for cust_pay_pending from failed verify records,
-        #   in which case we attempt tokenization without cust_main
-        # everything else should absolutely have a cust_main
-        if ($table eq 'cust_pay_pending' && $record->{'custnum_pending'}) {
-          warn "ATTEMPTING GATEWAY-ONLY TOKENIZE" if $debug;
-        } else {
-          my $error = "Could not load cust_main for $table ".$record->get($record->primary_key);
-          if ($opt{'queue'}) {
-            $log->critical($error);
-            $dbh->commit or die $dbh->errstr; # commit log message
-            next;
-          }
-          $dbh->rollback if $oldAutoCommit;
-          die $error;
-        }
-      }
+      warn "ATTEMPTING GATEWAY-ONLY TOKENIZE" if $debug && !$cust_main;
 
       # if we got this far, time to mutex
       $record = $record->select_for_update;
diff --git a/FS/FS/payinfo_Mixin.pm b/FS/FS/payinfo_Mixin.pm
index be37568..3a51022 100644
--- a/FS/FS/payinfo_Mixin.pm
+++ b/FS/FS/payinfo_Mixin.pm
@@ -195,8 +195,6 @@ sub payinfo_check {
   FS::payby->can_payby($self->table, $self->payby)
     or return "Illegal payby: ". $self->payby;
 
-  my $conf = new FS::Conf;
-
   if ( $self->payby eq 'CARD' && ! $self->is_encrypted($self->payinfo) ) {
 
     my $payinfo = $self->payinfo;
diff --git a/FS/FS/payinfo_transaction_Mixin.pm b/FS/FS/payinfo_transaction_Mixin.pm
index c27d049..1b5a0cd 100644
--- a/FS/FS/payinfo_transaction_Mixin.pm
+++ b/FS/FS/payinfo_transaction_Mixin.pm
@@ -102,8 +102,6 @@ auth, and order_number) as well as payby and payinfo
 sub payinfo_check {
   my $self = shift;
 
-  my $conf = new FS::Conf;
-
   $self->SUPER::payinfo_check()
   || $self->ut_numbern('gatewaynum')
   # not ut_foreign_keyn, it causes upgrades to fail
diff --git a/FS/FS/payment_gateway.pm b/FS/FS/payment_gateway.pm
index 170d37a..3500bf9 100644
--- a/FS/FS/payment_gateway.pm
+++ b/FS/FS/payment_gateway.pm
@@ -385,6 +385,23 @@ sub default_gateway {
   return $payment_gateway;
 }
 
+=item by_key_with_namespace GATEWAYNUM
+
+Like usual by_key, but makes sure namespace is set,
+and dies if not found.
+
+=cut
+
+sub by_key_with_namespace {
+  my $self = shift;
+  my $payment_gateway = $self->by_key(@_);
+  die "payment_gateway not found"
+    unless $payment_gateway;
+  $payment_gateway->gateway_namespace('Business::OnlinePayment')
+    unless $payment_gateway->gateway_namespace;
+  return $payment_gateway;
+}
+
 =item by_key_or_default OPTIONS
 
 Either returns the gateway specified by option gatewaynum, or the default gateway.
@@ -399,13 +416,7 @@ sub by_key_or_default {
   my ($self,%options) = @_;
 
   if ($options{'gatewaynum'}) {
-    my $payment_gateway = $self->by_key($options{'gatewaynum'});
-    # regardless of nofatal, which is only meant for handling lack of default gateway
-    die "payment_gateway ".$options{'gatewaynum'}." not found"
-      unless $payment_gateway;
-    $payment_gateway->gateway_namespace('Business::OnlinePayment')
-      unless $payment_gateway->gateway_namespace;
-    return $payment_gateway;
+    return $self->by_key_with_namespace($options{'gatewaynum'});
   } else {
     return $self->default_gateway(%options);
   }

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

Summary of changes:
 FS/FS/cust_main/Billing_Realtime.pm |  121 ++++++++++++++++++++++++-----------
 FS/FS/payinfo_Mixin.pm              |    2 -
 FS/FS/payinfo_transaction_Mixin.pm  |    2 -
 FS/FS/payment_gateway.pm            |   25 ++++++--
 FS/t/suite/13-tokenization.t        |   99 ++++++++++++++++++++++++++--
 5 files changed, 198 insertions(+), 51 deletions(-)




More information about the freeside-commits mailing list