diff options
Diffstat (limited to 'FS/FS/pay_batch')
-rw-r--r-- | FS/FS/pay_batch/BoM.pm | 73 | ||||
-rw-r--r-- | FS/FS/pay_batch/PAP.pm | 103 | ||||
-rw-r--r-- | FS/FS/pay_batch/ach_spiritone.pm | 65 | ||||
-rw-r--r-- | FS/FS/pay_batch/chase_canada.pm | 104 | ||||
-rw-r--r-- | FS/FS/pay_batch/paymentech.pm | 114 | ||||
-rw-r--r-- | FS/FS/pay_batch/td_canada_trust.pm | 104 |
6 files changed, 563 insertions, 0 deletions
diff --git a/FS/FS/pay_batch/BoM.pm b/FS/FS/pay_batch/BoM.pm new file mode 100644 index 0000000..7bfc22a --- /dev/null +++ b/FS/FS/pay_batch/BoM.pm @@ -0,0 +1,73 @@ +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; + diff --git a/FS/FS/pay_batch/PAP.pm b/FS/FS/pay_batch/PAP.pm new file mode 100644 index 0000000..432ef07 --- /dev/null +++ b/FS/FS/pay_batch/PAP.pm @@ -0,0 +1,103 @@ +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; + diff --git a/FS/FS/pay_batch/ach_spiritone.pm b/FS/FS/pay_batch/ach_spiritone.pm new file mode 100644 index 0000000..bd3bb14 --- /dev/null +++ b/FS/FS/pay_batch/ach_spiritone.pm @@ -0,0 +1,65 @@ +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; + diff --git a/FS/FS/pay_batch/chase_canada.pm b/FS/FS/pay_batch/chase_canada.pm new file mode 100644 index 0000000..909e4ae --- /dev/null +++ b/FS/FS/pay_batch/chase_canada.pm @@ -0,0 +1,104 @@ +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; diff --git a/FS/FS/pay_batch/paymentech.pm b/FS/FS/pay_batch/paymentech.pm new file mode 100644 index 0000000..44fa78a --- /dev/null +++ b/FS/FS/pay_batch/paymentech.pm @@ -0,0 +1,114 @@ +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; + +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 { +# Load this at run time + eval "use XML::Simple"; + die $@ if $@; + 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; + XML::Simple::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; + diff --git a/FS/FS/pay_batch/td_canada_trust.pm b/FS/FS/pay_batch/td_canada_trust.pm new file mode 100644 index 0000000..43b9237 --- /dev/null +++ b/FS/FS/pay_batch/td_canada_trust.pm @@ -0,0 +1,104 @@ +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; + |