X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main%2FBilling_Realtime.pm;h=364d4a7d8accfc8b72d061e4e619e87dbf30ed54;hp=500d2c2898337ce1ec97b024ff759ed3233106d9;hb=c5e31619e5a3071506cff19578e9e377753a96f4;hpb=69aeadc496d86a86bb87cf56c629c85684dcce29 diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index 500d2c289..364d4a7d8 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -4,7 +4,6 @@ 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 ); @@ -13,6 +12,7 @@ use FS::payby; use FS::cust_pay; use FS::cust_pay_pending; use FS::cust_refund; +use FS::banned_pay; $realtime_bop_decline_quiet = 0; @@ -138,7 +138,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 +I attempts to take a discount by prepaying for discount_term. +The payment will fail if I is incorrect for this discount term. A direct (Business::OnlinePayment) transaction will return nothing on success, or an error message on failure. @@ -306,6 +307,10 @@ sub _bop_content { $content{referer} = 'http://cleanwhisker.420.am/'; #XXX fix referer :/ $content{phone} = $self->daytime || $self->night; + my $currency = $conf->exists('business-onlinepayment-currency') + && $conf->config('business-onlinepayment-currency'); + $content{currency} = $currency if $currency; + \%content; } @@ -401,11 +406,29 @@ sub realtime_bop { # check for banned credit card/ACH ### - my $ban = qsearchs('banned_pay', { + my $ban = FS::banned_pay->ban_search( 'payby' => $bop_method2payby{$options{method}}, - 'payinfo' => md5_base64($options{payinfo}), - } ); - return "Banned credit card" if $ban; + 'payinfo' => $options{payinfo}, + ); + return "Banned credit card" if $ban && $ban->bantype ne 'warn'; + + ### + # check for term discount validity + ### + + my $discount_term = $options{discount_term}; + if ( $discount_term ) { + my $bill = ($self->cust_bill)[-1] + or return "Can't apply a term discount to an unbilled customer"; + my $plan = FS::discount_plan->new( + cust_bill => $bill, + months => $discount_term + ) or return "No discount available for term '$discount_term'"; + + if ( $plan->discounted_total != $options{amount} ) { + return "Incorrect term prepayment amount (term $discount_term, amount $options{amount}, requires ".$plan->discounted_total.")"; + } + } ### # massage data @@ -522,23 +545,18 @@ 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 ) { + + #for third-party payments only, remove pending payments if they're in the + #'thirdparty' (waiting for customer action) state. + if ( $namespace eq 'Business::OnlineThirdPartyPayment' ) { + foreach ( grep { $_->status eq 'thirdparty' } @pending ) { my $error = $_->delete; - warn "error deleting stale pending payment ".$_->paypendingnum.": $error" - if $error; # not fatal, it will fail anyway - } - else { - push @still_pending, $_; + warn "error deleting unfinished third-party payment ". + $_->paypendingnum . ": $error\n" + if $error; } + @pending = grep { $_->status ne 'thirdparty' } @pending; } - @pending = @still_pending; return "A payment is already being processed for this customer (". join(', ', map 'paypendingnum '. $_->paypendingnum, @pending ). @@ -612,6 +630,9 @@ sub realtime_bop { if ( $transaction->is_success() && $namespace eq 'Business::OnlineThirdPartyPayment' ) { + $cust_pay_pending->status('thirdparty'); + my $cpp_err = $cust_pay_pending->replace; + return { error => $cpp_err } if $cpp_err; return { reference => $cust_pay_pending->paypendingnum, map { $_ => $transaction->$_ } qw ( popup_url collectitems ) }; @@ -1287,7 +1308,8 @@ Implementation note: If I is unspecified or equal to the amount of the orignal payment, first an attempt is made to "void" the transaction via the gateway (to cancel a not-yet settled transaction) and then if that fails, the normal attempt is made to "refund" ("credit") the transaction via the -gateway is attempted. +gateway is attempted. No attempt to "void" the transaction is made if the +gateway has introspection data and doesn't support void. #The additional options I, I, I, I, I, #I, I and I are also available. Any of these options, @@ -1417,14 +1439,28 @@ sub realtime_refund_bop { } #first try void if applicable + my $void = new Business::OnlinePayment( $processor, @bop_options ); + + my $tryvoid = 1; + if ($void->can('info')) { + my $paytype = ''; + $paytype = 'ECHECK' if $cust_pay && $cust_pay->payby eq 'CHEK'; + $paytype = 'CC' if $cust_pay && $cust_pay->payby eq 'CARD'; + my %supported_actions = $void->info('supported_actions'); + $tryvoid = 0 + if ( %supported_actions && $paytype + && defined($supported_actions{$paytype}) + && !grep{ $_ eq 'Void' } @{$supported_actions{$paytype}} ); + } + if ( $cust_pay && $cust_pay->paid == $amount && ( ( not defined($disable_void_after) ) || ( time < ($cust_pay->_date + $disable_void_after ) ) ) + && $tryvoid ) { warn " attempting void\n" if $DEBUG > 1; - my $void = new Business::OnlinePayment( $processor, @bop_options ); if ( $void->can('info') ) { if ( $cust_pay->payby eq 'CARD' && $void->info('CC_void_requires_card') )