X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main%2FBilling_Realtime.pm;h=10b898db08805c7bc5359ad333cb96e6e9b3bed8;hp=ece07b6a3d5a6629fd3da718916635b79c505498;hb=d6741df87df9e3352d7ae47a02d0e3f46154fef9;hpb=28da97099bf6b01659718a0c6a1086c9a0f22729 diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index ece07b6a3..10b898db0 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -3,6 +3,9 @@ package FS::cust_main::Billing_Realtime; use strict; use vars qw( $conf $DEBUG $me ); use vars qw( $realtime_bop_decline_quiet ); #ugh +use Data::Dumper; +use Digest::MD5 qw(md5_base64); +use Business::CreditCard 0.28; use FS::UID qw( dbh ); use FS::Record qw( qsearch qsearchs ); use FS::Misc qw( send_email ); @@ -30,7 +33,7 @@ FS::cust_main::Billing_Realtime - Realtime billing mixin for cust_main =head1 SYNOPSIS -=head1 DESCRIPTIONS +=head1 DESCRIPTION These methods are available on FS::cust_main objects. @@ -86,6 +89,8 @@ I allows payment capture to unlock export jobs sub realtime_collect { my( $self, %options ) = @_; + local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; + if ( $DEBUG ) { warn "$me realtime_collect:\n"; warn " $_ => $options{$_}\n" foreach keys %options; @@ -136,6 +141,8 @@ I is a session identifier associated with this payment. I allows payment capture to unlock export jobs +I attempts to take a discount by prepaying for discount_term + (moved from cust_bill) (probably should get realtime_{card,ach,lec} here too) =cut @@ -171,6 +178,14 @@ sub _bop_recurring_billing { sub _payment_gateway { my ($self, $options) = @_; + if ( $options->{'selfservice'} ) { + my $gatewaynum = FS::Conf->new->config('selfservice-payment_gateway'); + if ( $gatewaynum ) { + return $options->{payment_gateway} ||= + qsearchs('payment_gateway', { gatewaynum => $gatewaynum }); + } + } + $options->{payment_gateway} = $self->agent->payment_gateway( %$options ) unless exists($options->{payment_gateway}); @@ -282,6 +297,8 @@ my %bop_method2payby = ( sub realtime_bop { my $self = shift; + local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; + my %options = (); if (ref($_[0]) eq 'HASH') { %options = %{$_[0]}; @@ -458,6 +475,24 @@ sub realtime_bop { 'custnum' => $self->custnum, 'status' => { op=>'!=', value=>'done' } }); + # This is a problem. A self-service third party payment that fails somehow + # can't be retried, EVER, until someone manually clears it. Totally + # arbitrary fix: if the existing payment is more than two minutes old, + # kill it. This doesn't limit how long it can take the pending payment + # to complete, only how long it will obstruct new payments. + my @still_pending; + foreach (@pending) { + if ( time - $_->_date > 120 ) { + my $error = $_->delete; + warn "error deleting stale pending payment ".$_->paypendingnum.": $error" + if $error; # not fatal, it will fail anyway + } + else { + push @still_pending, $_; + } + } + @pending = @still_pending; + return "A payment is already being processed for this customer (". join(', ', map 'paypendingnum '. $_->paypendingnum, @pending ). "); $options{method} transaction aborted." @@ -502,6 +537,7 @@ sub realtime_bop { 'customer_id' => $self->custnum, %$bop_content, 'reference' => $cust_pay_pending->paypendingnum, #for now + 'callback_url' => $payment_gateway->gateway_callback_url, 'email' => $email, %content, #after ); @@ -707,6 +743,9 @@ sub fake_bop { sub _realtime_bop_result { my( $self, $cust_pay_pending, $transaction, %options ) = @_; + + local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; + if ( $DEBUG ) { warn "$me _realtime_bop_result: pending transaction ". $cust_pay_pending->paypendingnum. "\n"; @@ -744,6 +783,7 @@ sub _realtime_bop_result { 'paybatch' => $paybatch, 'paydate' => $cust_pay_pending->paydate, 'pkgnum' => $cust_pay_pending->pkgnum, + 'discount_term' => $options{'discount_term'}, } ); #doesn't hurt to know, even though the dup check is in cust_pay_pending now $cust_pay->payunique( $options{payunique} ) @@ -889,10 +929,10 @@ sub _realtime_bop_result { } if ( !$options{'quiet'} && !$realtime_bop_decline_quiet - && $conf->exists('emaildecline') + && $conf->exists('emaildecline', $self->agentnum) && grep { $_ ne 'POST' } $self->invoicing_list && ! grep { $transaction->error_message =~ /$_/ } - $conf->config('emaildecline-exclude') + $conf->config('emaildecline-exclude', $self->agentnum) ) { # Send a decline alert to the customer. @@ -986,6 +1026,9 @@ upon success) and session_id of any associated session. sub realtime_botpp_capture { my( $self, $cust_pay_pending, %options ) = @_; + + local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; + if ( $DEBUG ) { warn "$me realtime_botpp_capture: pending transaction $cust_pay_pending\n"; warn " $_ => $options{$_}\n" foreach keys %options; @@ -1000,9 +1043,10 @@ sub realtime_botpp_capture { my $method = FS::payby->payby2bop($cust_pay_pending->payby); - my $payment_gateway = $cust_pay_pending->gatewaynum - ? qsearchs( 'payment_gateway', - { gatewaynum => $cust_pay_pending->gatewaynum } + my $payment_gateway; + my $gatewaynum = $cust_pay_pending->getfield('gatewaynum'); + $payment_gateway = $gatewaynum ? qsearchs( 'payment_gateway', + { gatewaynum => $gatewaynum } ) : $self->agent->payment_gateway( 'method' => $method, # 'invnum' => $cust_pay_pending->invnum, @@ -1064,7 +1108,14 @@ sub realtime_botpp_capture { my $error = $self->_realtime_bop_result( $cust_pay_pending, $transaction, %options ); - { + if ( $options{'apply'} ) { + my $apply_error = $self->apply_payments_and_credits; + if ( $apply_error ) { + warn "WARNING: error applying payment: $apply_error\n"; + } + } + + return { bill_error => $error, session_id => $cust_pay_pending->session_id, } @@ -1141,6 +1192,8 @@ gateway is attempted. sub realtime_refund_bop { my $self = shift; + local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; + my %options = (); if (ref($_[0]) eq 'HASH') { %options = %{$_[0]};