[freeside-commits] freeside/FS/FS/pay_batch ach_spiritone.pm, NONE, 1.1 BoM.pm, NONE, 1.1 chase_canada.pm, NONE, 1.1 PAP.pm, NONE, 1.1 paymentech.pm, NONE, 1.1 td_canada_trust.pm, NONE, 1.1
Mark Wells
mark at wavetail.420.am
Thu Sep 24 19:30:22 PDT 2009
Update of /home/cvs/cvsroot/freeside/FS/FS/pay_batch
In directory wavetail.420.am:/tmp/cvs-serv27311/FS/FS/pay_batch
Added Files:
ach_spiritone.pm BoM.pm chase_canada.pm PAP.pm paymentech.pm
td_canada_trust.pm
Log Message:
Batch payment refactoring
--- NEW FILE: PAP.pm ---
package FS::pay_batch::PAP;
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local 'timelocal';
use FS::Conf;
my $conf;
my ($origid, $datacenter, $typecode, $shortname, $longname, $mybank, $myacct);
$name = 'PAP';
%import_info = (
'filetype' => 'fixed',
'formatre' => '^(.).{19}(.{4})(.{3})(.{10})(.{6})(.{9})(.{12}).{110}(.{19}).{71}$',
'fields' => [
'recordtype',
'batchnum',
'datacenter',
'paid',
'_date',
'bank',
'payinfo',
'paybatchnum',
],
'hook' => sub {
my $hash = shift;
$hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
my $tmpdate = timelocal( 0,0,1,1,0,substr($hash->{'_date'}, 0, 3)+2000);
$tmpdate += 86400*(substr($hash->{'_date'}, 3, 3)-1) ;
$hash->{'_date'} = $tmpdate;
$hash->{'payinfo'} = $hash->{'payinfo'} . '@' . $hash->{'bank'};
},
'approved' => sub { 1 },
'declined' => sub { 0 },
# Why does pay_batch.pm have approved_condition and declined_condition?
# It doesn't even try to handle the case of neither condition being met.
'end_hook' => sub {
my( $hash, $total) = @_;
$total = sprintf("%.2f", $total);
my $batch_total = $hash->{'datacenter'}.$hash->{'paid'}.
substr($hash->{'_date'},0,1); # YUCK!
$batch_total = sprintf("%.2f", $batch_total / 100 );
return "Our total $total does not match bank total $batch_total!"
if $total != $batch_total;
'';
},
'end_condition' => sub {
my $hash = shift;
$hash->{recordtype} eq 'W';
},
);
%export_info = (
init => sub {
$conf = shift;
($origid,
$datacenter,
$typecode,
$shortname,
$longname,
$mybank,
$myacct) = $conf->config("batchconfig-PAP");
},
header => sub {
my $pay_batch = shift;
sprintf( "H%10sD%3s%06u%-15s%09u%-12s%04u%19s\n",
$origid,
$typecode,
cdate($pay_batch->download),
$shortname,
$mybank,
$myacct,
$pay_batch->batchnum,
"" )
},
row => sub {
my ($cust_pay_batch, $pay_batch) = @_;
my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
sprintf( "D%-23s%06u%-19s%09u%-12s%010.0f\n",
$cust_pay_batch->payname,
cdate($pay_batch->download),
$cust_pay_batch->paybatchnum,
$aba,
$account,
$cust_pay_batch->amount*100 );
},
footer => sub {
my ($pay_batch, $batchcount, $batchtotal) = @_;
sprintf( "T%08u%014.0f%57s\n",
$batchcount,
$batchtotal*100,
"" );
},
);
sub cdate {
my (@date) = localtime(shift);
sprintf("%02d%02d%02d", $date[3], $date[4] + 1, $date[5] % 100);
}
1;
--- NEW FILE: td_canada_trust.pm ---
package FS::pay_batch::td_canada_trust;
# Formerly known as csv-td_canada_trust-merchant_pc_batch,
# which I'm sure we can all agree is both a terrible name
# and an illegal Perl identifier.
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local 'timelocal';
use FS::Conf;
my $conf;
my ($origid, $datacenter, $typecode, $shortname, $longname, $mybank, $myacct);
$name = 'csv-td_canada_trust-merchant_pc_batch';
%import_info = (
'filetype' => 'CSV',
'fields' => [
'paybatchnum',
'paid',
'', # card type
'_date',
'time',
'payinfo',
'', # expiry date
'', # auth number
'type', # transaction type
'result', # processing result
'', # terminal ID
],
'hook' => sub {
my $hash = shift;
my $date = $hash->{'_date'};
my $time = $hash->{'time'};
$hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100);
$hash->{'_date'} = timelocal( substr($time, 4, 2),
substr($time, 2, 2),
substr($time, 0, 2),
substr($date, 6, 2),
substr($date, 4, 2)-1,
substr($date, 0, 4)-1900 );
},
'approved' => sub {
my $hash = shift;
$hash->{'type'} eq '0' && $hash->{'result'} == 3
},
'declined' => sub {
my $hash = shift;
$hash->{'type'} eq '0' && ( $hash->{'result'} == 4
|| $hash->{'result'} == 5 )
},
'end_condition' => sub {
my $hash = shift;
$hash->{'type'} eq '0BC';
},
'end_hook' => sub {
my ($hash, $total) = @_;
$total = sprintf("%.2f", $total);
my $batch_total = sprintf("%.2f", $hash->{'paybatchnum'} / 100);
return "Our total $total does not match bank total $batch_total!"
if $total != $batch_total;
},
);
%export_info = (
init => sub {
$conf = shift;
},
# no header
row => sub {
my ($cust_pay_batch, $pay_batch) = @_;
return join(',',
'',
'',
'',
'',
$cust_pay_batch->payinfo,
expdate($cust_pay_batch->exp),
$cust_pay_batch->amount,
$cust_pay_batch->paybatchnum
);
},
# no footer
);
sub expdate {
my $exp = shift;
$exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
my ($mon, $y) = ($2, $1);
if($conf->exists('batch-increment_expiration')) {
my ($curmon, $curyear) = (localtime(time))[4,5];
$curmon++;
$curyear -= 100;
$y++ while $y < $curyear || ($y == $curyear && $mon < $curmon);
}
$mon = "0$mon" if $mon =~ /^\d$/;
$y = "0$y" if $y =~ /^\d$/;
return "$mon$y";
}
1;
--- NEW FILE: paymentech.pm ---
package FS::pay_batch::paymentech;
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local;
use Date::Format 'time2str';
use Date::Parse 'str2time';
use FS::Conf;
use XML::Simple qw(XMLin XMLout);
my $conf;
my ($bin, $merchantID, $terminalID, $username);
$name = 'paymentech';
%import_info = (
filetype => 'XML',
xmlrow => [ qw(transResponse newOrderResp) ],
fields => [
'paybatchnum',
'_date',
'approvalStatus',
],
xmlkeys => [
'orderID',
'respDateTime',
'approvalStatus',
],
'hook' => sub {
my ($hash, $oldhash) = @_;
my ($mon, $day, $year, $hour, $min, $sec) =
$hash->{'_date'} =~ /^(..)(..)(....)(..)(..)(..)$/;
$hash->{'_date'} = timelocal($sec, $min, $hour, $day, $mon-1, $year);
$hash->{'paid'} = $oldhash->{'amount'};
},
'approved' => sub { my $hash = shift;
$hash->{'approvalStatus'}
},
'declined' => sub { my $hash = shift;
! $hash->{'approvalStatus'}
},
);
my %paytype = (
'personal checking' => 'C',
'personal savings' => 'S',
'business checking' => 'X',
'business savings' => 'X',
);
%export_info = (
init => sub {
my $conf = shift;
($bin, $terminalID, $merchantID, $username) =
$conf->config('batchconfig-paymentech');
},
# Here we do all the work in the header function.
header => sub {
my $pay_batch = shift;
my @cust_pay_batch = @{(shift)};
my $count = 0;
XMLout( {
transRequest => {
RequestCount => scalar(@cust_pay_batch),
batchFileID => {
userID => $username,
fileDateTime => time2str('%Y%m%d%H%M%s',time),
fileID => 'batch'.time2str('%Y%m%d',time),
},
newOrder => [ map { {
# $_ here refers to a cust_pay_batch record.
BatchRequestNo => $count++,
industryType => 'EC',
transType => 'AC',
bin => $bin,
merchantID => $merchantID,
terminalID => $terminalID,
($_->payby eq 'CARD') ? (
# Credit card stuff
ccAccountNum => $_->payinfo,
ccExp => time2str('%y%m',str2time($_->exp)),
) : (
# ECP (electronic check) stuff
ecpCheckRT => ($_->payinfo =~ /@(\d+)/),
ecpCheckDDA => ($_->payinfo =~ /(\d+)@/),
ecpBankAcctType => $paytype{lc($_->cust_main->paytype)},
ecpDelvMethod => 'B'
),
avsZip => $_->zip,
avsAddress1 => $_->address1,
avsAddress2 => $_->address2,
avsCity => $_->city,
avsState => $_->state,
avsName => $_->first . ' ' . $_->last,
avsCountryCode => $_->country,
orderID => $_->paybatchnum,
amount => $_->amount * 100,
} } @cust_pay_batch
],
endOfDay => {
BatchRequestNo => $count++,
bin => $bin,
merchantID => $merchantID,
terminalID => $terminalID
},
}
}, KeepRoot => 1, NoAttr => 1);
},
row => sub {},
);
1;
--- NEW FILE: BoM.pm ---
package FS::pay_batch::BoM;
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local 'timelocal';
use FS::Conf;
my $conf;
my ($origid, $datacenter, $typecode, $shortname, $longname, $mybank, $myacct);
$name = 'BoM';
%import_info = (
'filetype' => 'CSV',
'fields' => [],
'hook' => sub { die "Can't import BoM" },
'approved' => sub { 1 },
'declined' => sub { 0 },
);
%export_info = (
init => sub {
$conf = shift;
($origid,
$datacenter,
$typecode,
$shortname,
$longname,
$mybank,
$myacct) = $conf->config("batchconfig-BoM");
},
header => sub {
my $pay_batch = shift;
sprintf( "A%10s%04u%06u%05u%54s\n",
$origid,
$pay_batch->batchnum,
jdate($pay_batch->download),
$datacenter,
"") .
sprintf( "XD%03u%06u%-15s%-30s%09u%-12s \n",
$typecode,
jdate($pay_batch->download),
$shortname,
$longname,
$mybank,
$myacct);
},
row => sub {
my ($cust_pay_batch, $pay_batch) = @_;
my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
sprintf( "D%010.0f%09u%-12s%-29s%-19s\n",
$cust_pay_batch->amount * 100,
$aba,
$account,
$cust_pay_batch->payname,
$cust_pay_batch->paybatchnum
);
},
footer => sub {
my ($pay_batch, $batchcount, $batchtotal) = @_;
sprintf( "YD%08u%014.0f%56s\n", $batchcount, $batchtotal*100, "").
sprintf( "Z%014u%04u%014u%05u%41s\n",
$batchtotal*100, $batchcount, "0", "0", "");
},
);
sub jdate {
my (@date) = localtime(shift);
sprintf("%03d%03d", $date[5] % 100, $date[7] + 1);
}
1;
--- NEW FILE: chase_canada.pm ---
package FS::pay_batch::chase_canada;
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local 'timelocal';
use FS::Conf;
my $conf;
my $origid;
$name = 'csv-chase_canada-E-xactBatch';
%import_info = (
'filetype' => 'CSV',
'fields' => [
'',
'',
'',
'paid',
'auth',
'payinfo',
'',
'',
'bankcode',
'bankmess',
'etgcode',
'etgmess',
'',
'paybatchnum',
'',
'result',
],
'hook' => sub {
my $hash = shift;
my $cpb = shift;
$hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} );
$hash->{'_date'} = time;
$hash->{'payinfo'} = $cpb->{'payinfo'}
if( substr($hash->{'payinfo'}, -4) eq substr($cpb->{'payinfo'}, -4) );
},
'approved' => sub {
my $hash = shift;
$hash->{'etgcode'} eq '00' && $hash->{'result'} eq 'Approved';
},
'declined' => sub {
my $hash = shift;
$hash->{'etgcode'} ne '00' || $hash->{'result'} eq 'Declined';
},
);
%export_info = (
init => sub {
$conf = shift;
($origid) = $conf->config("batchconfig-$name");
},
header => sub {
my $pay_batch = shift;
sprintf( '$$E-xactBatchFileV1.0$$%s:%03u$$%s',
sdate($pay_batch->download),
$pay_batch->batchnum,
$origid );
},
row => sub {
my ($cust_pay_batch, $pay_batch) = @_;
my $payname = $cust_pay_batch->payname;
$payname =~ tr/",/ /;
join(',',
$cust_pay_batch->paybatchnum,
$cust_pay_batch->custnum,
$cust_pay_batch->invnum,
qq!"$payname"!,
'00',
$cust_pay_batch->payinfo,
$cust_pay_batch->amount,
expdate($cust_pay_batch->exp),
'',
''
);
},
# no footer
);
sub sdate {
my (@date) = localtime(shift);
sprintf('%02d/%02d/%02d', $date[5] % 100, $date[4] + 1, $date[3]);
}
sub expdate {
my $exp = shift;
$exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
my ($mon, $y) = ($2, $1);
if($conf->exists('batch-increment_expiration')) {
my ($curmon, $curyear) = (localtime(time))[4,5];
$curmon++;
$curyear -= 100;
$y++ while $y < $curyear || ($y == $curyear && $mon < $curmon);
}
$mon = "0$mon" if $mon =~ /^\d$/;
$y = "0$y" if $y =~ /^\d$/;
return "$mon$y";
}
1;
--- NEW FILE: ach_spiritone.pm ---
package FS::pay_batch::ach_spiritone;
use strict;
use vars qw(@ISA %import_info %export_info $name);
use Time::Local 'timelocal';
use FS::Conf;
use File::Temp;
my $conf;
my ($origid, $datacenter, $typecode, $shortname, $longname, $mybank, $myacct);
$name = 'ach-spiritone'; # note spelling
%import_info = (
'filetype' => 'CSV',
'fields' => [
'', #name
'paybatchnum',
'aba',
'payinfo',
'', #transaction type
'paid',
'', #default transaction type
'', #default amount
],
'hook' => sub {
my $hash = shift;
$hash->{'_date'} = time;
$hash->{'payinfo'} = $hash->{'payinfo'} . '@' . $hash->{'aba'};
},
'approved' => sub { 1 },
'declined' => sub { 0 },
);
%export_info = (
# This is the simplest case.
row => sub {
my ($cust_pay_batch, $pay_batch) = @_;
my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
my $payname = $cust_pay_batch->first . ' ' . $cust_pay_batch->last;
$payname =~ tr/",/ /;
qq!"$payname","!.$cust_pay_batch->paybatchnum.
qq!","$aba","$account","27","!.$cust_pay_batch->amount.
qq!","27","0.00"!; #"
},
autopost => sub {
my ($pay_batch, $batch) = @_;
my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
my $fh = new File::Temp(
TEMPLATE => 'paybatch.'. $pay_batch->batchnum .'.XXXXXXXX',
DIR => $dir,
) or return "can't open temp file: $!\n";
print $fh $batch;
seek $fh, 0, 0;
my $error = $pay_batch->import_results( 'filehandle' => $fh,
'format' => $name,
);
return $error if $error;
},
);
1;
More information about the freeside-commits
mailing list