[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