local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ $self->select_for_update; #mutex
+
# find the packages which are due for billing, find out how much they are
# & generate invoice database.
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ $self->select_for_update; #mutex
+
my $balance = $self->balance;
warn "collect customer". $self->custnum. ": balance $balance" if $DEBUG;
unless ( $balance > 0 ) { #redundant?????
#overrides
$self->set( $_ => $options{$_} )
foreach grep { exists($options{$_}) }
- qw( payname address1 address2 city state zip payinfo paydate );
+ qw( payname address1 address2 city state zip payinfo paydate paycvv);
#load up config
my $bop_config = 'business-onlinepayment';
$conf->config($bop_config);
$action ||= 'normal authorization';
pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
+ die "No real-time processor is enabled - ".
+ "did you set the business-onlinepayment configuration value?\n"
+ unless $processor;
#massage data
}
#remove paycvv after initial transaction
- #make this disable-able via a config option if anyone insists?
- # (though that probably violates cardholder agreements)
+ #false laziness w/misc/process/payment.cgi - check both to make sure working
+ # correctly
if ( defined $self->dbdef_table->column('paycvv')
&& length($self->paycvv)
&& ! grep { $_ eq cardtype($self->payinfo) } $conf->config('cvv-save')
+ && ! length($options{'paycvv'})
) {
my $new = new FS::cust_main { $self->hash };
$new->paycvv('');
} );
my $error = $cust_pay->insert;
if ( $error ) {
- # gah, even with transactions.
- my $e = 'WARNING: Card/ACH debited but database not updated - '.
- 'error applying payment, invnum #' . $self->invnum.
- " ($processor): $error";
- warn $e;
- return $e;
- } else {
- return '';
+ $cust_pay->invnum(''); #try again with no specific invnum
+ my $error2 = $cust_pay->insert;
+ if ( $error2 ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card/ACH debited but database not updated - '.
+ "error inserting payment ($processor): $error2".
+ " (previously tried insert with invnum #$options{'invnum'}" .
+ ": $error )";
+ warn $e;
+ return $e;
+ }
}
+ return ''; #no error
} else {
if ( !$options{'quiet'} && !$realtime_bop_decline_quiet
&& $conf->exists('emaildecline')
&& grep { $_ ne 'POST' } $self->invoicing_list
- && ! grep { $_ eq $transaction->error_message }
+ && ! grep { $transaction->error_message =~ /$_/ }
$conf->config('emaildecline-exclude')
) {
my @templ = $conf->config('declinetemplate');
);
}
+=item paydate_monthyear
+
+Returns a two-element list consisting of the month and year of this customer's
+paydate (credit card expiration date for CARD customers)
+
+=cut
+
+sub paydate_monthyear {
+ my $self = shift;
+ if ( $self->paydate =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #Pg date format
+ ( $2, $1 );
+ } elsif ( $self->paydate =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+ ( $1, $3 );
+ } else {
+ ('', '');
+ }
+}
+
=item invoicing_list [ ARRAYREF ]
If an arguement is given, sets these email addresses as invoice recipients
qsearch( 'cust_refund', { 'custnum' => $self->custnum } )
}
+=item select_for_update
+
+Selects this record with the SQL "FOR UPDATE" command. This can be useful as
+a mutex.
+
+=cut
+
+sub select_for_update {
+ my $self = shift;
+ qsearch('cust_main', { 'custnum' => $self->custnum }, '*', 'FOR UPDATE' );
+}
+
=back
=head1 SUBROUTINES