use FS::cust_refund;
use FS::banned_pay;
use FS::payment_gateway;
+use FS::Misc::Savepoint;
$realtime_bop_decline_quiet = 0;
our $BOP_TESTING = 0;
our $BOP_TESTING_SUCCESS = 1;
+our $BOP_TESTING_TIMESTAMP = '';
install_callback FS::UID sub {
$conf = new FS::Conf;
if ( $cust_payby->locationnum ) {
my $cust_location = $cust_payby->cust_location;
- $options->{$_} = $cust_location->$_() for qw( address1 address2 city state zip );
+ $options->{$_} = $cust_location->$_()
+ for qw( address1 address2 city state zip country );
}
}
}
# can be called as class method,
# but can't load default name/phone fields as class method
+# (why was this added? ah, it might get called from realtime_tokenize in this
+# fashion "to tokenize old records on upgrade")
sub _bop_content {
my ($self, $options) = @_;
my %content = ();
$content{name} = $payname if $payname;
- $content{address} = $options->{'address1'};
- my $address2 = $options->{'address2'};
- $content{address} .= ", ". $address2 if length($address2);
+ if ( exists($options->{'address1'}) && length($options->{'address1'}) ) {
- $content{city} = $options->{'city'};
- $content{state} = $options->{'state'};
- $content{zip} = $options->{'zip'};
- $content{country} = $options->{'country'};
+ $content{address} = $options->{'address1'};
+ my $address2 = $options->{'address2'};
+ $content{address} .= ", ". $address2 if length($address2);
+
+ $content{$_} = $options->{$_} foreach qw( city state zip country );
+
+ } elsif ( ref($self) ) {
+
+ $content{address} = $self->address1;
+ my $address2 = $self->address2;
+ $content{address} .= ", ". $address2 if length($address2);
+
+ $content{$_} = $self->$_() foreach qw( city state zip country );
+
+ }
# can't set phone if called as class method
$content{phone} = $self->daytime || $self->night
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;
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;
+ # amount as post-surcharge-processing_fee
+ $cc_surcharge = $options{'amount'} - $options{'processing-fee'} - (($options{'amount'} - ($cc_surcharge_flat + $options{'processing-fee'})) / ( 1 + $cc_surcharge_pct/100 )) if $options{'amount'} > 0;
}
$cc_surcharge = sprintf("%.2f",$cc_surcharge) if $cc_surcharge > 0;
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},
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;
'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,
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'} ?
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".
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;
$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";
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";
return $e;
} else {
-
+ savepoint_release( $savepoint_label );
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
if ( $options{'apply'} ) {
}
# have a CC surcharge portion --> one-time charge
- if ( $options{'cc_surcharge'} > 0 ) {
+ if ( $options{'cc_surcharge'} > 0 || $options{'processing-fee'} > 0) {
# XXX: this whole block needs to be in a transaction?
my $invnum;
unless ( $invnum ) {
# XXX: unlikely case - pre-paying before any invoices generated
# what it should do is create a new invoice and pick it
- warn 'CC SURCHARGE AND NO INVOICES PICKED TO APPLY IT!';
+ warn 'CC SURCHARGE OR PROCESS FEE AND NO INVOICES PICKED TO APPLY IT!';
return '';
}
- 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({
+ if ($options{'cc_surcharge'} > 0) {
+ 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' => $cc_surcharge_text,
'setuptax' => 'Y',
'cust_pkg_ref' => \$cust_pkg,
- });
- if($charge_error) {
- warn 'Unable to add CC surcharge cust_pkg';
- return '';
- }
+ });
+
+ if($charge_error) {
+ warn 'Unable to add CC surcharge cust_pkg';
+ return '';
+ }
+
+ $cust_pkg->setup(time);
+ my $cp_error = $cust_pkg->replace;
+ if($cp_error) {
+ warn 'Unable to set setup time on cust_pkg for cc surcharge';
+ # but keep going...
+ }
- $cust_pkg->setup(time);
- my $cp_error = $cust_pkg->replace;
- if($cp_error) {
- warn 'Unable to set setup time on cust_pkg for cc surcharge';
- # but keep going...
- }
-
- my $cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum });
- unless ( $cust_bill ) {
- warn "race condition + invoice deletion just happened";
- return '';
- }
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum });
+ unless ( $cust_bill ) {
+ warn "race condition + invoice deletion just happened";
+ return '';
+ }
+
+ my $grand_error =
+ $cust_bill->add_cc_surcharge($cust_pkg->pkgnum,$options{'cc_surcharge'});
+
+ warn "cannot add CC surcharge to invoice #$invnum: $grand_error"
+ if $grand_error;
+ } # end if $options{'cc_surcharge'}
+
+ if ($options{'processing-fee'} > 0) {
+ my $pf_cust_pkg;
+ my $processing_fee_text = 'Payment Processing Fee';
+ my $pf_change_error = $self->charge({
+ 'amount' => $options{'processing-fee'},
+ 'pkg' => $processing_fee_text,
+ 'setuptax' => 'Y',
+ 'cust_pkg_ref' => \$pf_cust_pkg,
+ });
+
+ if($pf_change_error) {
+ warn 'Unable to add payment processing fee';
+ return '';
+ }
- my $grand_error =
- $cust_bill->add_cc_surcharge($cust_pkg->pkgnum,$options{'cc_surcharge'});
+ $pf_cust_pkg->setup(time);
+ my $pf_error = $pf_cust_pkg->replace;
+ if($pf_error) {
+ warn 'Unable to set setup time on cust_pkg for processing fee';
+ # but keep going...
+ }
- warn "cannot add CC surcharge to invoice #$invnum: $grand_error"
- if $grand_error;
+ my $cust_bill = qsearchs('cust_bill', { 'invnum' => $invnum });
+ unless ( $cust_bill ) {
+ warn "race condition + invoice deletion just happened";
+ return '';
}
+ my $grand_pf_error =
+ $cust_bill->add_cc_surcharge($pf_cust_pkg->pkgnum,$options{'processing-fee'});
+
+ warn "cannot add Processing fee to invoice #$invnum: $grand_pf_error"
+ if $grand_pf_error;
+ } #end if $options{'processing-fee'}
+
+ } #end if ( $options{'cc_surcharge'} > 0 || $options{'processing-fee'} > 0)
+
return ''; #no error
}