diff options
author | mark <mark> | 2010-12-21 09:12:45 +0000 |
---|---|---|
committer | mark <mark> | 2010-12-21 09:12:45 +0000 |
commit | d6741df87df9e3352d7ae47a02d0e3f46154fef9 (patch) | |
tree | 1c26d9fe83c3c1af9c207a9e9e56ab9d766ff070 /FS | |
parent | 46e04077cc22ff9d31e8e9896cbf97e31f1b0e7d (diff) |
changes to support eWay third-party payment, #10208
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/ClientAPI/MyAccount.pm | 76 | ||||
-rw-r--r-- | FS/FS/ClientAPI/Signup.pm | 102 | ||||
-rw-r--r-- | FS/FS/Conf.pm | 29 | ||||
-rw-r--r-- | FS/FS/agent.pm | 2 | ||||
-rw-r--r-- | FS/FS/cust_main/Billing_Realtime.pm | 43 | ||||
-rw-r--r-- | FS/FS/cust_pay_pending.pm | 13 |
6 files changed, 184 insertions, 81 deletions
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index e41fe7d56..ecabe31c7 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -237,6 +237,28 @@ sub logout { } } +sub payment_gateway { + # internal use only + # takes a cust_main and a cust_payby entry, returns the payment_gateway + my $conf = new FS::Conf; + my $cust_main = shift; + my $cust_payby = shift; + my $gatewaynum = $conf->config('selfservice-payment_gateway'); + if ( $gatewaynum ) { + my $pg = qsearchs('payment_gateway', { gatewaynum => $gatewaynum }); + die "configured gatewaynum $gatewaynum not found!" if !$pg; + return $pg; + } + else { + return '' if ! FS::payby->realtime($cust_payby); + my $pg = $cust_main->agent->payment_gateway( + 'method' => FS::payby->payby2bop($cust_payby), + 'nofatal' => 1 + ); + return $pg; + } +} + sub access_info { my $p = shift; @@ -262,18 +284,11 @@ sub access_info { my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) or return { 'error' => "unknown custnum $custnum" }; - $info->{hide_payment_fields} = - [ - map { my $pg = ''; - if ( FS::payby->realtime($_) ) { - $pg = $cust_main->agent->payment_gateway( - 'method' => FS::payby->payby2bop($_), - 'nofatal' => 1, - ); - } - $pg && $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; - } - @{ $info->{cust_paybys} } + $info->{'hide_payment_fields'} = [ + map { + my $pg = payment_gateway($cust_main, $_); + $pg && $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; + } @{ $info->{cust_paybys} } ]; $info->{'self_suspend_reason'} = @@ -532,18 +547,11 @@ sub payment_info { my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) or return { 'error' => "unknown custnum $custnum" }; - $return{hide_payment_fields} = - [ - map { my $pg = ''; - if ( FS::payby->realtime($_) ) { - $pg = $cust_main->agent->payment_gateway( - 'method' => FS::payby->payby2bop($_), - 'nofatal' => 1, - ); - } - $pg && $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; - } - @{ $return{cust_paybys} } + $return{'hide_payment_fields'} = [ + map { + my $pg = payment_gateway($cust_main, $_); + $pg && $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; + } @{ $return{cust_paybys} } ]; $return{balance} = $cust_main->balance; #XXX pkg-balances? @@ -692,6 +700,7 @@ sub process_payment { 'paycvv' => $paycvv, 'pkgnum' => $session->{'pkgnum'}, 'discount_term' => $discount_term, + 'selfservice' => 1, map { $_ => $p->{$_} } @{ $payby2fields{$payby} } ); return { 'error' => $error } if $error; @@ -746,18 +755,27 @@ sub realtime_collect { my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) or return { 'error' => "unknown custnum $custnum" }; + my $amount; + if ( $p->{'amount'} ) { + $amount = $p->{'amount'}; + } + elsif ( $session->{'pkgnum'} ) { + $amount = $cust_main->balance_pkgnum( $session->{'pkgnum'} ); + } + else { + $amount = $cust_main->balance; + } + my $error = $cust_main->realtime_collect( 'method' => $p->{'method'}, + 'amount' => $amount, 'pkgnum' => $session->{'pkgnum'}, 'session_id' => $p->{'session_id'}, 'apply' => 1, + 'selfservice'=> 1, ); return { 'error' => $error } unless ref( $error ); - my $amount = $session->{'pkgnum'} - ? $cust_main->balance_pkgnum( $session->{'pkgnum'} ) - : $cust_main->balance; - return { 'error' => '', amount => $amount, %$error }; } @@ -1410,7 +1428,7 @@ sub _do_bop_realtime { my $bill_error = $cust_main->bill || $cust_main->apply_payments_and_credits - || $cust_main->realtime_collect; + || $cust_main->realtime_collect('selfservice' => 1); if ( $cust_main->balance > $old_balance && $cust_main->balance > 0 diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm index 65bb4e3d5..757dd47f1 100644 --- a/FS/FS/ClientAPI/Signup.pm +++ b/FS/FS/ClientAPI/Signup.pm @@ -321,23 +321,34 @@ sub signup_info { warn "$me has agent $agent\n" if $DEBUG > 1; if ( $agent ) { #else complain loudly? $signup_info->{'hide_payment_fields'} = []; - foreach my $payby (@{$signup_info->{payby}}) { - warn "$me checking $payby payment fields\n" if $DEBUG > 1; - my $hide = 0; - if ( FS::payby->realtime($payby) ) { - my $payment_gateway = - $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), - 'nofatal' => 1, - ); - if ( $payment_gateway - && $payment_gateway->gateway_namespace - eq 'Business::OnlineThirdPartyPayment' - ) { - warn "$me hiding $payby payment fields\n" if $DEBUG > 1; - $hide = 1; + my $gatewaynum = $conf->config('selfservice-payment_gateway'); + if ( $gatewaynum ) { + my $pg = qsearchs('payment_gateway', { gatewaynum => $gatewaynum }); + die "configured gatewaynum $gatewaynum not found!" if !$pg; + my $hide = $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; + $signup_info->{'hide_payment_fields'} = [ + map { $hide } @{$signup_info->{'payby'}} + ]; + } + else { + foreach my $payby (@{$signup_info->{payby}}) { + warn "$me checking $payby payment fields\n" if $DEBUG > 1; + my $hide = 0; + if ( FS::payby->realtime($payby) ) { + my $payment_gateway = + $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), + 'nofatal' => 1, + ); + if ( $payment_gateway + && $payment_gateway->gateway_namespace + eq 'Business::OnlineThirdPartyPayment' + ) { + warn "$me hiding $payby payment fields\n" if $DEBUG > 1; + $hide = 1; + } } - } - push @{$signup_info->{'hide_payment_fields'}}, $hide; + push @{$signup_info->{'hide_payment_fields'}}, $hide; + } # foreach $payby } } warn "$me done setting agent-specific payment flag\n" if $DEBUG > 1; @@ -698,6 +709,7 @@ sub new_customer { $bill_error = $cust_main->realtime_collect( method => FS::payby->payby2bop( $packet->{payby} ), depend_jobnum => $placeholder->jobnum, + selfservice => 1, ); #warn "$me error collecting from new customer: $bill_error" # if $bill_error; @@ -787,29 +799,36 @@ sub capture_payment { my $conf = new FS::Conf; - my $url = $packet->{url}; - my $payment_gateway = - qsearchs('payment_gateway', { 'gateway_callback_url' => popurl(0, $url) } ); - - unless ($payment_gateway) { - - my ( $processor, $login, $password, $action, @bop_options ) = - $conf->config('business-onlinepayment'); - $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; - - $payment_gateway = new FS::payment_gateway( { - gateway_namespace => $conf->config('business-onlinepayment-namespace'), - gateway_module => $processor, - gateway_username => $login, - gateway_password => $password, - gateway_action => $action, - options => [ ( @bop_options ) ], - }); - + my $payment_gateway; + if ( my $gwnum = $conf->config('selfservice-payment_gateway') ) { + $payment_gateway = qsearchs('payment_gateway', { 'gatewaynum' => $gwnum }) + or die "configured gatewaynum $gwnum not found!"; + } + else { + my $url = $packet->{url}; + + $payment_gateway = qsearchs('payment_gateway', + { 'gateway_callback_url' => popurl(0, $url) } + ); + if (!$payment_gateway) { + + my ( $processor, $login, $password, $action, @bop_options ) = + $conf->config('business-onlinepayment'); + $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; + + $payment_gateway = new FS::payment_gateway( { + gateway_namespace => $conf->config('business-onlinepayment-namespace'), + gateway_module => $processor, + gateway_username => $login, + gateway_password => $password, + gateway_action => $action, + options => [ ( @bop_options ) ], + }); + } } die "No real-time third party processor is enabled - ". @@ -847,7 +866,10 @@ sub capture_payment { my $cust_main = $cust_pay_pending->cust_main; my $bill_error = - $cust_main->realtime_botpp_capture( $cust_pay_pending, %{$packet->{data}} ); + $cust_main->realtime_botpp_capture( $cust_pay_pending, + %{$packet->{data}}, + apply => 1, + ); return { 'error' => ( $bill_error->{bill_error} ? '_decline' : '' ), %$bill_error, diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 22e761f4a..5fb5a272f 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -584,6 +584,26 @@ my %msg_template_options = ( 'per_agent' => 1, ); +my $_gateway_name = sub { + my $g = shift; + return '' if !$g; + ($g->gateway_username . '@' . $g->gateway_module); +}; + +my %payment_gateway_options = ( + 'type' => 'select-sub', + 'options_sub' => sub { + my @gateways = qsearch({ + 'table' => 'payment_gateway', + 'hashref' => { 'disabled' => '' }, + }); + map { $_->gatewaynum, $_gateway_name->($_) } @gateways; + }, + 'option_sub' => sub { + my $gateway = FS::payment_gateway->by_key(shift); + $_gateway_name->($gateway); + }, +); #Billing (81 items) #Invoicing (50 items) @@ -1726,6 +1746,13 @@ and customer address. Include units.', }, { + 'key' => 'selfservice-payment_gateway', + 'section' => 'self-service', + 'description' => 'Force the use of this payment gateway for self-service.', + %payment_gateway_options, + }, + + { 'key' => 'selfservice-save_unchecked', 'section' => 'self-service', 'description' => 'In self-service, uncheck "Remember information" checkboxes by default (normally, they are checked by default).', @@ -2971,7 +2998,7 @@ and customer address. Include units.', # { # 'key' => 'batch-manual_approval', # 'section' => 'billing', -# 'description' => 'Allow manual batch closure, which will approve all payments that do not yet have a status. This is dangerous, but may be needed if your processor does not provide a list of approved payments.', +# 'description' => 'Allow manual batch closure, which will approve all payments that do not yet have a status. This is very dangerous.', # 'type' => 'checkbox', # }, # diff --git a/FS/FS/agent.pm b/FS/FS/agent.pm index d291ca070..3794d3f1d 100644 --- a/FS/FS/agent.pm +++ b/FS/FS/agent.pm @@ -276,7 +276,7 @@ sub payment_gateway { $payment_gateway = $override->payment_gateway; $payment_gateway->gateway_namespace('Business::OnlinePayment') - unless $payment_gateway->gateway_name; + unless $payment_gateway->gateway_namespace; } else { #use the standard settings from the config diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index ba1e9c86e..10b898db0 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -178,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}); @@ -467,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." @@ -511,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 ); @@ -1016,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, @@ -1080,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, } diff --git a/FS/FS/cust_pay_pending.pm b/FS/FS/cust_pay_pending.pm index f48e1a8c1..e54690e4b 100644 --- a/FS/FS/cust_pay_pending.pm +++ b/FS/FS/cust_pay_pending.pm @@ -120,9 +120,9 @@ Transaction recorded in database Additional status information. -=cut +=item gatewaynum -#=item cust_balance - +L<FS::payment_gateway> id. =item paynum - @@ -292,10 +292,10 @@ sub insert_cust_pay { } -=item decline +=item decline [ STATUSTEXT ] -Sets the status of this pending pament to "done" (with statustext -"declined (manual)"). +Sets the status of this pending payment to "done" (with statustext +"declined (manual)" unless otherwise specified). Currently only used when resolving pending payments manually. @@ -303,11 +303,12 @@ Currently only used when resolving pending payments manually. sub decline { my $self = shift; + my $statustext = shift || "declined (manual)"; #could send decline email too? doesn't seem useful in manual resolution $self->status('done'); - $self->statustext("declined (manual)"); + $self->statustext($statustext); $self->replace; } |