X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2Fcust_main%2FBilling_Realtime.pm;h=714a2e6879b5f2a06f891ec19d23d54d4d1daeb6;hp=0623fbb0db88e816f324d055f36ae9f8614d69f4;hb=829aa888318799d2ff4871c92b0d457abda49714;hpb=bd5c864f75fd5d4aaddd60d2ba1ebc15f14a9aa8 diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index 0623fbb0d..714a2e687 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -6,6 +6,7 @@ use vars qw( $realtime_bop_decline_quiet ); #ugh use Carp; use Data::Dumper; use Business::CreditCard 0.35; +use Business::OnlinePayment; use FS::UID qw( dbh myconnect ); use FS::Record qw( qsearch qsearchs ); use FS::payby; @@ -15,6 +16,7 @@ use FS::cust_bill_pay; use FS::cust_refund; use FS::banned_pay; use FS::payment_gateway; +use FS::Misc::Savepoint; $realtime_bop_decline_quiet = 0; @@ -26,6 +28,7 @@ $me = '[FS::cust_main::Billing_Realtime]'; our $BOP_TESTING = 0; our $BOP_TESTING_SUCCESS = 1; +our $BOP_TESTING_TIMESTAMP = ''; install_callback FS::UID sub { $conf = new FS::Conf; @@ -126,6 +129,13 @@ sub realtime_collect { $options{amount} = $self->balance unless exists( $options{amount} ); return '' unless $options{amount} > 0; + #huh, in v4, realtime_bop no longer will just process a card without passing + # payinfo or cust_payby... + if ( ! $options{'payinfo'} && ! $options{'cust_payby'} && $self->has_cust_payby_auto ) { + my @cust_payby = $self->cust_payby; + $options{'cust_payby'} = $cust_payby[0]; + } + return $self->realtime_bop({%options}); } @@ -397,7 +407,7 @@ sub realtime_bop { confess "Can't call realtime_bop within another transaction ". '($FS::UID::AutoCommit is false)' - unless $FS::UID::AutoCommit; + unless $FS::UID::AutoCommit || $BOP_TESTING; local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG; @@ -413,6 +423,8 @@ sub realtime_bop { $options{amount} = $amount; } + return '' unless $options{amount} > 0; + # set fields from passed cust_payby _bop_cust_payby_options(\%options); @@ -446,16 +458,24 @@ sub realtime_bop { if $conf->config('credit-card-surcharge-percentage', $self->agentnum) && $options{method} eq 'CC'; + my $cc_surcharge_flat = 0; + $cc_surcharge_flat = $conf->config('credit-card-surcharge-flatfee', $self->agentnum) + if $conf->config('credit-card-surcharge-flatfee', $self->agentnum) + && $options{method} eq 'CC'; + # always add cc surcharge if called from event - if($options{'cc_surcharge_from_event'} && $cc_surcharge_pct > 0) { - $cc_surcharge = $options{'amount'} * $cc_surcharge_pct / 100; + if($options{'cc_surcharge_from_event'} && ($cc_surcharge_pct > 0 || $cc_surcharge_flat > 0)) { + if ($options{'amount'} > 0) { + $cc_surcharge = ($options{'amount'} * ($cc_surcharge_pct / 100)) + $cc_surcharge_flat; $options{'amount'} += $cc_surcharge; $options{'amount'} = sprintf("%.2f", $options{'amount'}); # round (again)? + } } - elsif($cc_surcharge_pct > 0) { # we're called not from event (i.e. from a - # payment screen), so consider the given - # amount as post-surcharge - $cc_surcharge = $options{'amount'} - ($options{'amount'} / ( 1 + $cc_surcharge_pct/100 )); + elsif($cc_surcharge_pct > 0 || $cc_surcharge_flat > 0) { + # we're called not from event (i.e. from a + # payment screen), so consider the given + # amount as post-surcharge + $cc_surcharge = $options{'amount'} - (($options{'amount'} - $cc_surcharge_flat) / ( 1 + $cc_surcharge_pct/100 )) if $options{'amount'} > 0; } $cc_surcharge = sprintf("%.2f",$cc_surcharge) if $cc_surcharge > 0; @@ -664,7 +684,7 @@ sub realtime_bop { my $cust_pay_pending = new FS::cust_pay_pending { 'custnum' => $self->custnum, 'paid' => $options{amount}, - '_date' => '', + '_date' => $BOP_TESTING ? $BOP_TESTING_TIMESTAMP : '', 'payby' => $bop_method2payby{$options{method}}, 'payinfo' => $options{payinfo}, 'paymask' => $options{paymask}, @@ -739,7 +759,7 @@ sub realtime_bop { return { reference => $cust_pay_pending->paypendingnum, map { $_ => $transaction->$_ } qw ( popup_url collectitems ) }; - } elsif ( $transaction->is_success() && $action2 ) { + } elsif ( !$BOP_TESTING && $transaction->is_success() && $action2 ) { $cust_pay_pending->status('authorized'); my $cpp_authorized_err = $cust_pay_pending->replace; @@ -928,7 +948,7 @@ sub _realtime_bop_result { 'custnum' => $self->custnum, 'invnum' => $options{'invnum'}, 'paid' => $cust_pay_pending->paid, - '_date' => '', + '_date' => $BOP_TESTING ? $BOP_TESTING_TIMESTAMP : '', 'payby' => $cust_pay_pending->payby, 'payinfo' => $options{'payinfo'}, 'paymask' => $options{'paymask'} || $cust_pay_pending->paymask, @@ -949,12 +969,16 @@ sub _realtime_bop_result { local $FS::UID::AutoCommit = 0; my $dbh = dbh; + my $savepoint_label = '_realtime_bop_result'; + savepoint_create( $savepoint_label ); + #start a transaction, insert the cust_pay and set cust_pay_pending.status to done in a single transction my $error = $cust_pay->insert($options{'manual'} ? ( 'manual' => 1 ) : () ); if ( $error ) { - $dbh->rollback or die $dbh->errstr if $oldAutoCommit; + savepoint_rollback( $savepoint_label ); + $cust_pay->invnum(''); #try again with no specific invnum $cust_pay->paynum(''); my $error2 = $cust_pay->insert( $options{'manual'} ? @@ -963,7 +987,8 @@ sub _realtime_bop_result { if ( $error2 ) { # gah. but at least we have a record of the state we had to abort in # from cust_pay_pending now. - $dbh->rollback or die $dbh->errstr if $oldAutoCommit; + savepoint_rollback_and_release( $savepoint_label ); + my $e = "WARNING: $options{method} captured but payment not recorded -". " error inserting payment (". $payment_gateway->gateway_module. "): $error2". @@ -978,9 +1003,10 @@ sub _realtime_bop_result { my $jobnum = $cust_pay_pending->jobnum; if ( $jobnum ) { my $placeholder = qsearchs( 'queue', { 'jobnum' => $jobnum } ); - + unless ( $placeholder ) { - $dbh->rollback or die $dbh->errstr if $oldAutoCommit; + savepoint_rollback_and_release( $savepoint_label ); + my $e = "WARNING: $options{method} captured but job $jobnum not ". "found for paypendingnum ". $cust_pay_pending->paypendingnum. "\n"; warn $e; @@ -990,7 +1016,8 @@ sub _realtime_bop_result { $error = $placeholder->delete; if ( $error ) { - $dbh->rollback or die $dbh->errstr if $oldAutoCommit; + savepoint_rollback_and_release( $savepoint_label ); + my $e = "WARNING: $options{method} captured but could not delete ". "job $jobnum for paypendingnum ". $cust_pay_pending->paypendingnum. ": $error\n"; @@ -1012,8 +1039,8 @@ sub _realtime_bop_result { my $cpp_done_err = $cust_pay_pending->replace; if ( $cpp_done_err ) { + savepoint_rollback_and_release( $savepoint_label ); - $dbh->rollback or die $dbh->errstr if $oldAutoCommit; my $e = "WARNING: $options{method} captured but payment not recorded - ". "error updating status for paypendingnum ". $cust_pay_pending->paypendingnum. ": $cpp_done_err \n"; @@ -1021,7 +1048,7 @@ sub _realtime_bop_result { return $e; } else { - + savepoint_release( $savepoint_label ); $dbh->commit or die $dbh->errstr if $oldAutoCommit; if ( $options{'apply'} ) { @@ -1060,9 +1087,11 @@ sub _realtime_bop_result { } my $cust_pkg; + my $cc_surcharge_text = 'Credit Card Surcharge'; + $cc_surcharge_text = $conf->config('credit-card-surcharge-text', $self->agentnum) if $conf->exists('credit-card-surcharge-text', $self->agentnum); my $charge_error = $self->charge({ 'amount' => $options{'cc_surcharge'}, - 'pkg' => 'Credit Card Surcharge', + 'pkg' => $cc_surcharge_text, 'setuptax' => 'Y', 'cust_pkg_ref' => \$cust_pkg, }); @@ -1185,6 +1214,7 @@ sub _realtime_bop_result { "resolved - error updating status for paypendingnum ". $cust_pay_pending->paypendingnum. ": $cpp_done_err \n"; warn $e; + #XXX internal system log $e (what's going on?) $perror = "$e ($perror)"; } @@ -1512,7 +1542,7 @@ sub realtime_refund_bop { my $payment_gateway = $self->agent->payment_gateway( 'method' => $options{method} ); - my( $processor, $login, $password, $namespace ) = + ( $processor, $login, $password, $namespace ) = map { my $method = "gateway_$_"; $payment_gateway->$method } qw( module username password namespace );