'payname', 'varchar', 'NULL', $char_d, '', '',
'amount', @money_type, '', '',
'status', 'varchar', 'NULL', $char_d, '', '',
+ 'error_message', 'varchar', 'NULL', $char_d, '', '',
],
'primary_key' => 'paybatchnum',
'unique' => [],
use FS::cust_main;
use FS::cust_bill;
-@ISA = qw( FS::payinfo_Mixin FS::Record );
+@ISA = qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
# 1 is mostly method/subroutine entry and options
# 2 traces progress of some operations
=item country
-=item status
+=item status - 'Approved' or 'Declined'
+
+=item error_message - the error returned by the gateway if any
=back
'';
}
-=item approve PAYBATCH
+=item approve OPTIONS
Approve this payment. This will replace the existing record with the
same paybatchnum, set its status to 'Approved', and generate a payment
record (L<FS::cust_pay>). This should only be called from the batch
import process.
+OPTIONS may contain "gatewaynum", "processor", "auth", and "order_number".
+
=cut
sub approve {
# to break up the Big Wall of Code that is import_results
my $new = shift;
- my $paybatch = shift;
+ my %opt = @_;
my $paybatchnum = $new->paybatchnum;
my $old = qsearchs('cust_pay_batch', { paybatchnum => $paybatchnum })
or return "paybatchnum $paybatchnum not found";
my $cust_pay = new FS::cust_pay ( {
'custnum' => $new->custnum,
'payby' => $new->payby,
- 'paybatch' => $paybatch,
'payinfo' => $new->payinfo || $old->payinfo,
'paid' => $new->paid,
'_date' => $new->_date,
'usernum' => $new->usernum,
'batchnum' => $new->batchnum,
+ 'gatewaynum' => $opt{'gatewaynum'},
+ 'processor' => $opt{'processor'},
+ 'auth' => $opt{'auth'},
+ 'order_number' => $opt{'order_number'}
} );
+
$error = $cust_pay->insert;
if ( $error ) {
return "error inserting payment for paybatchnum $paybatchnum: $error\n";
}
} # !$old->status
$new->status('Declined');
+ $new->error_message($reason);
my $error = $new->replace($old);
if ( $error ) {
return "error updating status of paybatchnum $paybatchnum: $error\n";
foreach ('paid', '_date', 'payinfo') {
$new_cust_pay_batch->$_($hash{$_}) if $hash{$_};
}
- $error = $new_cust_pay_batch->approve($hash{'paybatch'} || $self->batchnum);
+ $error = $new_cust_pay_batch->approve(%hash);
$total += $hash{'paid'};
} elsif ( &{$declined_condition}(\%hash) ) {
- $error = $new_cust_pay_batch->decline;
+ $error = $new_cust_pay_batch->decline($hash{'error_message'});;
}
my $payby; # CARD or CHEK
my $error;
- # follow realtime gateway practice here
- # though eventually this stuff should go into separate fields...
my $paybatch = $gateway->gatewaynum . '-' . $gateway->gateway_module .
':' . $item->authorization . ':' . $item->order_number;
payby => $payby,
invnum => $item->invoice_number,
batchnum => $pay_batch->batchnum,
- paybatch => $paybatch,
payinfo => $payinfo,
+ gatewaynum => $gateway->gatewaynum,
+ processor => $gateway->gateway_module,
+ auth => $item->authorization,
+ order_number => $item->order_number,
}
);
$error ||= $cust_pay->insert;
# approval status
if ( $item->approved ) {
# follow Billing_Realtime format for paybatch
- $error = $cust_pay_batch->approve($paybatch);
+ $error = $cust_pay_batch->approve(
+ 'gatewaynum' => $gateway->gatewaynum,
+ 'processor' => $gateway->gateway_module,
+ 'auth' => $item->authorization,
+ 'order_number' => $item->order_number,
+ );
$total += $cust_pay_batch->paid;
}
else {
}
return $error if $error;
}
+ } elsif ( @unresolved ) {
+ # auto resolve is not enabled, and we're not ready to resolve
+ return;
}
$self->set_status('R');
my $self = shift;
my $date = time;
my %opt = @_;
- my $paybatch = $opt{'paybatch'} || $self->batchnum;
my $usernum = $opt{'usernum'} || die "manual approval requires a usernum";
my $conf = FS::Conf->new;
return 'manual batch approval disabled'
'_date' => $date,
'usernum' => $usernum,
};
- my $error = $new_cust_pay_batch->approve($paybatch);
+ my $error = $new_cust_pay_batch->approve();
+ # there are no approval options here (authorization, order_number, etc.)
+ # because the transaction wasn't really approved
if ( $error ) {
$dbh->rollback;
return 'paybatchnum '.$cust_pay_batch->paybatchnum.": $error";
'_date',
'approvalStatus',
'order_number',
- 'authorization',
+ 'auth',
+ 'procStatus',
+ 'procStatusMessage',
+ 'respCodeMessage',
],
xmlkeys => [
'orderID',
'approvalStatus',
'txRefNum',
'authorizationCode',
+ 'procStatus',
+ 'procStatusMessage',
+ 'respCodeMessage',
],
'hook' => sub {
if ( !$gateway ) {
# as the batch config, if there is one. If not, leave
# gateway out entirely.
my $merchant = (FS::Conf->new->config('batchconfig-paymentech'))[2];
- my $g = qsearchs({
+ $gateway = qsearchs({
'table' => 'payment_gateway',
'addl_from' => ' JOIN payment_gateway_option USING (gatewaynum) ',
'hashref' => { disabled => '',
optionvalue => $merchant,
},
});
- $gateway = ($g ? $g->gatewaynum . '-' : '') . 'PaymenTech';
}
my ($hash, $oldhash) = @_;
+ $hash->{'gatewaynum'} = $gateway->gatewaynum if $gateway;
+ $hash->{'processor'} = 'PaymenTech';
my ($mon, $day, $year, $hour, $min, $sec) =
$hash->{'_date'} =~ /^(..)(..)(....)(..)(..)(..)$/;
$hash->{'_date'} = timelocal($sec, $min, $hour, $day, $mon-1, $year);
$hash->{'paid'} = $oldhash->{'amount'};
- $hash->{'paybatch'} = join(':',
- $gateway,
- $hash->{'authorization'},
- $hash->{'order_number'},
- );
+ if ( $hash->{'procStatus'} == 0 ) {
+ $hash->{'error_message'} = $hash->{'respCodeMessage'};
+ } else {
+ $hash->{'error_message'} = $hash->{'procStatusMessage'};
+ }
},
'approved' => sub { my $hash = shift;
$hash->{'approvalStatus'}
'disable_download' => 1,
'header' => [ '#',
'Inv #',
- 'Customer',
+ 'Cust #',
'Customer',
'Card Name',
'Card',
'Exp',
'Amount',
'Status',
+ '', # error_message
],
- 'fields' => [ sub {
- shift->[0];
- },
- sub {
- shift->[1];
- },
- sub {
- shift->[2];
- },
- sub {
- my $cpb = shift;
- $cpb->[3] . ', ' . $cpb->[4];
- },
- sub {
- shift->[5];
- },
- sub {
- my $cardnum = shift->[6];
- 'x'x(length($cardnum)-4). substr($cardnum,(length($cardnum)-4));
- },
- sub {
- shift->[7] =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
- my( $mon, $year ) = ( $2, $1 );
- $mon = "0$mon" if length($mon) == 1;
- "$mon/$year";
- },
- sub {
- shift->[8];
- },
- sub {
- shift->[9];
- },
- ],
- 'align' => 'lllllllrl',
- 'links' => [ ['', sub{'#';}],
- ["${p}view/cust_bill.cgi?", sub{shift->[1];},],
- ["${p}view/cust_main.cgi?", sub{shift->[2];},],
- ["${p}view/cust_main.cgi?", sub{shift->[2];},],
+ 'fields' => [ 'paybatchnum',
+ 'invnum',
+ 'custnum',
+ sub { $_[0]->cust_main->name_short },
+ 'payname',
+ 'mask_payinfo',
+ sub {
+ return('') if $_[0]->payby ne 'CARD';
+ $_[0]->get('exp') =~ /^\d\d(\d\d)-(\d\d)/;
+ sprintf('%02d/%02d',$1,$2);
+ },
+ sub {
+ sprintf('%.02f', $_[0]->amount)
+ },
+ 'status',
+ 'error_message',
+ ],
+ 'align' => 'rrrlllcrll',
+ 'links' => [ '',
+ ["${p}view/cust_bill.cgi?", 'invnum'],
+ (["${p}view/cust_main.cgi?", 'custnum']) x 2,
],
+ 'link_onclicks' => [ ('') x 8,
+ $sub_receipt
+ ],
)
%>
<%init>
'LEFT JOIN pay_batch USING ( batchnum )' .
$search;
-#grr
-$sql_query = "SELECT paybatchnum,invnum,custnum,cpb.last,cpb.first," .
- "cpb.payname,cpb.payinfo,cpb.exp,amount,cpb.status " .
- "FROM cust_pay_batch AS cpb " .
- 'LEFT JOIN cust_main USING ( custnum ) ' .
- 'LEFT JOIN pay_batch USING ( batchnum ) ' .
- "$search ORDER BY $orderby";
+$sql_query = {
+ 'table' => 'cust_pay_batch',
+ 'select' => 'cust_pay_batch.*, cust_main.*, cust_pay.paynum',
+ 'hashref' => {},
+ 'addl_from' => 'LEFT JOIN pay_batch USING ( batchnum ) '.
+ 'LEFT JOIN cust_main USING ( custnum ) '.
+
+ 'LEFT JOIN cust_pay USING ( batchnum, custnum ) ',
+ 'extra_sql' => $search,
+ 'order_by' => "ORDER BY $orderby",
+};
+
+my $sub_receipt = sub {
+ my $paynum = shift->paynum or return '';
+ include('/elements/popup_link_onclick.html',
+ 'action' => $p.'view/cust_pay.html?link=popup;paynum='.$paynum,
+ 'actionlabel' => emt('Payment Receipt'),
+ );
+};
my $html_init = '';
if ( $pay_batch ) {
'sort_fields' => \@sort_fields,
'align' => $align,
'links' => \@links,
+ 'link_onclicks' => \@link_onclicks,
'color' => \@color,
'style' => \@style,
&>
}
}
-my @header = ();
-my @fields = ();
-my @sort_fields = ();
+my @header;
+my @fields;
+my @sort_fields;
my $align = '';
-my @links = ();
+my @links;
+my @link_onclicks;
if ( $opt{'pre_header'} ) {
push @header, @{ $opt{'pre_header'} };
$align .= 'c' x scalar(@{ $opt{'pre_header'} });
push @sort_fields, @{ $opt{'pre_fields'} };
}
+my $sub_receipt = sub {
+ my $obj = shift;
+ my $objnum = $obj->primary_key . '=' . $obj->get($obj->primary_key);
+
+ include('/elements/popup_link_onclick.html',
+ 'action' => $p.'view/cust_pay.html?link=popup;'.$objnum,
+ 'actionlabel' => emt('Payment Receipt'),
+ );
+};
+
push @header, "\u$name_singular",
'Amount',
;
push @fields, 'payby_payinfo_pretty',
sub { sprintf('$%.2f', shift->$amount_field() ) },
;
+push @link_onclicks, $sub_receipt, '',
push @sort_fields, '', $amount_field;
if ( $unapplied ) {