use FS::payby;
use FS::cust_pay;
use FS::cust_pay_pending;
+use FS::cust_bill_pay;
use FS::cust_refund;
use FS::banned_pay;
$options{amount} = $self->balance unless exists( $options{amount} );
- my @cust_payby = qsearch({
- 'table' => 'cust_payby',
- 'hashref' => { 'custnum' => $self->custnum, },
- 'extra_sql' => " AND payby IN ( 'CARD', 'CHEK' ) ",
- 'order_by' => 'ORDER BY weight ASC',
- });
+ my @cust_payby = $self->cust_payby('CARD','CHEK');
my $error;
foreach my $cust_payby (@cust_payby) {
}
$options{amount} = $self->balance unless exists( $options{amount} );
+ return '' unless $options{amount} > 0;
+
$options{method} = FS::payby->payby2bop($self->payby)
unless exists( $options{method} );
invoice number will be assumed. If you don't specify an I<invnum> you might
want to call the B<apply_payments> method or set the I<apply> option.
-I<apply> can be set to true to apply a resulting payment.
+I<no_invnum> can be set to true to prevent that default invnum from being set.
+
+I<apply> can be set to true to run B<apply_payments_and_credits> on success.
+
+I<no_auto_apply> can be set to true to set that flag on the resulting payment
+(prevents payment from being applied by B<apply_payments> or B<apply_payments_and_credits>,
+but will still be applied if I<invnum> exists...use with I<no_invnum> for intended effect.)
I<quiet> can be set true to surpress email decline notices.
=cut
# some helper routines
+#
+# _bop_recurring_billing: Checks whether this payment should have the
+# recurring_billing flag used by some B:OP interfaces (IPPay, PlugnPay,
+# vSecure, etc.). This works in two different modes:
+# - actual_oncard (default): treat the payment as recurring if the customer
+# has made a payment using this card before.
+# - transaction_is_recur: treat the payment as recurring if the invoice
+# being paid has any recurring package charges.
+
sub _bop_recurring_billing {
my( $self, %opt ) = @_;
}
}
- $options->{payinfo} = $self->payinfo unless exists( $options->{payinfo} );
+ unless ( exists( $options->{'payinfo'} ) ) {
+ $options->{'payinfo'} = $self->payinfo;
+ $options->{'paymask'} = $self->paymask;
+ }
# Default invoice number if the customer has exactly one open invoice.
- if( ! $options->{'invnum'} ) {
+ unless ( $options->{'invnum'} || $options->{'no_invnum'} ) {
$options->{'invnum'} = '';
my @open = $self->open_cust_bill;
$options->{'invnum'} = $open[0]->invnum if scalar(@open) == 1;
'_date' => '',
'payby' => $bop_method2payby{$options{method}},
'payinfo' => $options{payinfo},
+ 'paymask' => $options{paymask},
'paydate' => $paydate,
'recurring_billing' => $content{recurring_billing},
'pkgnum' => $options{'pkgnum'},
# remove paycvv after initial transaction
###
- #false laziness w/misc/process/payment.cgi - check both to make sure working
- # correctly
+ # compare to FS::cust_main::save_cust_payby - check both to make sure working correctly
if ( length($self->paycvv)
&& ! grep { $_ eq cardtype($options{payinfo}) } $conf->config('cvv-save')
) {
'_date' => '',
'payby' => $cust_pay_pending->payby,
'payinfo' => $options{'payinfo'},
+ 'paymask' => $options{'paymask'} || $cust_pay_pending->paymask,
'paydate' => $cust_pay_pending->paydate,
'pkgnum' => $cust_pay_pending->pkgnum,
'discount_term' => $options{'discount_term'},
'processor' => $payment_gateway->gateway_module,
'auth' => $transaction->authorization,
'order_number' => $order_number || '',
-
+ 'no_auto_apply' => $options{'no_auto_apply'} ? 'Y' : '',
} );
#doesn't hurt to know, even though the dup check is in cust_pay_pending now
$cust_pay->payunique( $options{payunique} )
return $e;
}
+ $cust_pay_pending->set('jobnum','');
+
}
if ( $options{'paynum_ref'} ) {
if ( $placeholder ) {
my $error = $placeholder->depended_delete;
$error ||= $placeholder->delete;
+ $cust_pay_pending->set('jobnum','');
warn "error removing provisioning jobs after declined paypendingnum ".
- $cust_pay_pending->paypendingnum. ": $error\n";
+ $cust_pay_pending->paypendingnum. ": $error\n" if $error;
} else {
my $e = "error finding job $jobnum for declined paypendingnum ".
$cust_pay_pending->paypendingnum. "\n";
Available methods are: I<CC>, I<ECHECK> and I<LEC>
-Available options are: I<amount>, I<reason>, I<paynum>, I<paydate>
+Available options are: I<amount>, I<reasonnum>, I<paynum>, I<paydate>
Most gateways require a reference to an original payment transaction to refund,
so you probably need to specify a I<paynum>.
I<amount> defaults to the original amount of the payment if not specified.
-I<reason> specifies a reason for the refund.
+I<reasonnum> specified an existing refund reason for the refund
I<paydate> specifies the expiration date for a credit card overriding the
value from the customer record or the payment record. Specified as yyyy-mm-dd
warn " $_ => $options{$_}\n" foreach keys %options;
}
+ return "No reason specified" unless $options{'reasonnum'} =~ /^\d+$/;
+
+ my %content = ();
+
###
# look up the original payment and optionally a gateway for that payment
###
or return "Unknown paynum $options{'paynum'}";
$amount ||= $cust_pay->paid;
+ my @cust_bill_pay = qsearch('cust_bill_pay', { paynum=>$cust_pay->paynum });
+ $content{'invoice_number'} = $cust_bill_pay[0]->invnum if @cust_bill_pay;
+
if ( $cust_pay->get('processor') ) {
($gatewaynum, $processor, $auth, $order_number) =
(
eval "use $namespace";
die $@ if $@;
- my %content = (
+ %content = (
+ %content,
'type' => $options{method},
'login' => $login,
'password' => $password,
if $conf->exists('business-onlinepayment-test_transaction');
$void->submit();
if ( $void->is_success ) {
- my $error = $cust_pay->void($options{'reason'});
+ # specified as a refund reason, but now we want a payment void reason
+ # extract just the reason text, let cust_pay::void handle new_or_existing
+ my $reason = qsearchs('reason',{ 'reasonnum' => $options{'reasonnum'} });
+ my $error;
+ $error = 'Reason could not be loaded' unless $reason;
+ $error = $cust_pay->void($reason->reason) unless $error;
if ( $error ) {
# gah, even with transactions.
my $e = 'WARNING: Card/ACH voided but database not updated - '.
my $cust_refund = new FS::cust_refund ( {
'custnum' => $self->custnum,
'paynum' => $options{'paynum'},
+ 'source_paynum' => $options{'paynum'},
'refund' => $amount,
'_date' => '',
'payby' => $bop_method2payby{$options{method}},
'payinfo' => $payinfo,
- 'reason' => $options{'reason'} || 'card or ACH refund',
+ 'reasonnum' => $options{'reasonnum'},
'gatewaynum' => $gatewaynum, # may be null
'processor' => $processor,
'auth' => $refund->authorization,
my $error = $cust_refund->insert;
if ( $error ) {
$cust_refund->paynum(''); #try again with no specific paynum
+ $cust_refund->source_paynum('');
my $error2 = $cust_refund->insert;
if ( $error2 ) {
# gah, even with transactions.