- my $search = FS::Cursor->new({
- table => $table,
- hashref => { 'payby' => 'CARD' },
- },$dbh);
- while (my $record = $search->fetch) {
- next if $record->tokenized;
- next if !$record->payinfo; #shouldn't happen, but at least it's not a card number
- next if $record->payinfo =~ /N\/A/; # ??? Not sure why we do this, but it's not a card number
-
- # don't use customer agent gateway here, use the gatewaynum specified by the record
- my $gatewaynum = $record->gatewaynum || '';
- my $gateway = FS::agent->payment_gateway( load_gatewaynum => $gatewaynum );
- unless ($gateway) { # already died if $gatewaynum
- # only fatal if we expected everyone to tokenize
- next unless $disallow_untokenized;
- $search->DESTROY;
- $dbh->rollback if $oldAutoCommit;
- return "No gateway found for $table ".$record->get($record->primary_key);
+ warn "Checking $table" if $debug;
+
+ # FS::Cursor does not seem to work over multiple commits (gives cursor not found errors)
+ # loading only record ids, then loading individual records one at a time
+ my $tclass = 'FS::'.$table;
+ $offset = 0;
+ @recnums = ();
+
+ 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;
+ }
+ if (!$record->payinfo) { #shouldn't happen, but at least it's not a card number
+ warn "Skipping blank payinfo for $table ".$record->get($record->primary_key) if $debug;
+ next;
+ }
+ if ($record->payinfo =~ /N\/A/) { # ??? Not sure why we do this, but it's not a card number
+ warn "Skipping NA payinfo for $table ".$record->get($record->primary_key) if $debug;
+ next;
+ }
+
+ if ($require_tokenized && $opt{'daily'}) {
+ $log->info("Untokenized card number detected in $table ".$record->get($record->primary_key). ';tokenizing');
+ $dbh->commit or die $dbh->errstr; # commit log message
+ }
+
+ 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' 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'}) {
+ $hascritical = 1;
+ $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
+