summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authormark <mark>2010-12-21 09:12:45 +0000
committermark <mark>2010-12-21 09:12:45 +0000
commitd6741df87df9e3352d7ae47a02d0e3f46154fef9 (patch)
tree1c26d9fe83c3c1af9c207a9e9e56ab9d766ff070 /FS
parent46e04077cc22ff9d31e8e9896cbf97e31f1b0e7d (diff)
changes to support eWay third-party payment, #10208
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm76
-rw-r--r--FS/FS/ClientAPI/Signup.pm102
-rw-r--r--FS/FS/Conf.pm29
-rw-r--r--FS/FS/agent.pm2
-rw-r--r--FS/FS/cust_main/Billing_Realtime.pm43
-rw-r--r--FS/FS/cust_pay_pending.pm13
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;
}