record and show batch payment status info, #21117
[freeside.git] / FS / FS / pay_batch / paymentech.pm
1 package FS::pay_batch::paymentech;
2
3 use strict;
4 use vars qw(@ISA %import_info %export_info $name);
5 use FS::Record 'qsearchs';
6 use Time::Local;
7 use Date::Format 'time2str';
8 use Date::Parse 'str2time';
9 use Tie::IxHash;
10 use FS::Conf;
11
12 my $conf;
13 my ($bin, $merchantID, $terminalID, $username, $password, $with_recurringInd);
14 $name = 'paymentech';
15
16 my $gateway;
17
18 %import_info = (
19   filetype    => 'XML',
20   xmlrow         => [ qw(transResponse newOrderResp) ],
21   fields      => [
22     'paybatchnum',
23     '_date',
24     'approvalStatus',
25     'order_number',
26     'auth',
27     'procStatus',
28     'procStatusMessage',
29     'respCodeMessage',
30     ],
31   xmlkeys     => [
32     'orderID',
33     'respDateTime',
34     'approvalStatus',
35     'txRefNum',
36     'authorizationCode',
37     'procStatus',
38     'procStatusMessage',
39     'respCodeMessage',
40     ],
41   'hook'        => sub {
42       if ( !$gateway ) {
43         # find a gateway configuration that has the same merchantID 
44         # as the batch config, if there is one.  If not, leave 
45         # gateway out entirely.
46         my $merchant = (FS::Conf->new->config('batchconfig-paymentech'))[2];
47         $gateway = qsearchs({
48               'table'     => 'payment_gateway',
49               'addl_from' => ' JOIN payment_gateway_option USING (gatewaynum) ',
50               'hashref'   => {  disabled    => '',
51                                 optionname  => 'merchant_id',
52                                 optionvalue => $merchant,
53                               },
54               });
55       }
56       my ($hash, $oldhash) = @_;
57       $hash->{'gatewaynum'} = $gateway->gatewaynum if $gateway;
58       $hash->{'processor'} = 'PaymenTech';
59       my ($mon, $day, $year, $hour, $min, $sec) = 
60         $hash->{'_date'} =~ /^(..)(..)(....)(..)(..)(..)$/;
61       $hash->{'_date'} = timelocal($sec, $min, $hour, $day, $mon-1, $year);
62       $hash->{'paid'} = $oldhash->{'amount'};
63       if ( $hash->{'procStatus'} == 0 ) {
64         $hash->{'error_message'} = $hash->{'respCodeMessage'};
65       } else {
66         $hash->{'error_message'} = $hash->{'procStatusMessage'};
67       }
68     },
69   'approved'    => sub { my $hash = shift;
70                             $hash->{'approvalStatus'} 
71     },
72   'declined'    => sub { my $hash = shift;
73                             ! $hash->{'approvalStatus'} 
74     },
75 );
76
77 my %paytype = (
78   'personal checking' => 'C',
79   'personal savings'  => 'S',
80   'business checking' => 'X',
81   'business savings'  => 'X',
82 );
83
84 my %paymentech_countries = map { $_ => 1 } qw( US CA GB UK );
85
86 %export_info = (
87   init  => sub {
88 # Load this at run time
89     eval "use XML::Writer";
90     die $@ if $@;
91     my $conf = shift;
92     ($bin, $terminalID, $merchantID, $username, $password, $with_recurringInd) =
93        $conf->config('batchconfig-paymentech');
94     },
95 # Here we do all the work in the header function.
96   header => sub {
97     my $pay_batch = shift;
98     my @cust_pay_batch = @{(shift)};
99     my $count = 1;
100     my $output;
101     my $xml = new XML::Writer(OUTPUT => \$output, DATA_MODE => 1, DATA_INDENT => 2);
102     $xml->startTag('transRequest', RequestCount => scalar(@cust_pay_batch) + 1);
103     $xml->startTag('batchFileID');
104     $xml->dataElement(userID => $username);
105     $xml->dataElement(fileDateTime => time2str('%Y%m%d%H%M%S', time));
106     $xml->dataElement(fileID => 'FILEID');
107     $xml->endTag('batchFileID');
108
109     foreach (@cust_pay_batch) {
110       $xml->startTag('newOrder', BatchRequestNo => $count++);
111       my $status = $_->cust_main->status;
112       tie my %order, 'Tie::IxHash', (
113         industryType    => 'EC',
114         transType       => 'AC',
115         bin             => $bin,
116         merchantID      => $merchantID,
117         terminalID      => $terminalID,
118         ($_->payby eq 'CARD') ? (
119           ccAccountNum    => $_->payinfo,
120           ccExp           => $_->expmmyy,
121         ) : (
122           ecpCheckRT      => ($_->payinfo =~ /@(\d+)/),
123           ecpCheckDDA     => ($_->payinfo =~ /(\d+)@/),
124           ecpBankAcctType => $paytype{lc($_->cust_main->paytype)},
125           ecpDelvMethod   => 'A',
126         ),
127         avsZip          => substr($_->zip,      0, 10),
128         avsAddress1     => substr($_->address1, 0, 30),
129         avsAddress2     => substr($_->address2, 0, 30),
130         avsCity         => substr($_->city,     0, 20),
131         avsState        => substr($_->state,    0, 2),
132         avsName         => substr($_->first. ' '. $_->last, 0, 30),
133         avsCountryCode  => ( $paymentech_countries{ $_->country }
134                                  ? $_->country
135                                  : ''
136                              ),
137         orderID           => $_->paybatchnum,
138         amount            => $_->amount * 100,
139         );
140       # only do this if recurringInd is enabled in config, 
141       # and the customer has at least one non-canceled recurring package
142       if ( $with_recurringInd and $status =~ /^active|suspended|ordered$/ ) {
143         # then send RF if this is the first payment on this payinfo,
144         # RS otherwise.
145         $order{'recurringInd'} = $_->payinfo_used ? 'RS' : 'RF';
146       }
147       foreach my $key (keys %order) {
148         $xml->dataElement($key, $order{$key})
149       }
150       $xml->endTag('newOrder');
151     }
152     $xml->startTag('endOfDay', BatchRequestNo => $count);
153     $xml->dataElement(bin => $bin);
154     $xml->dataElement(merchantID => $merchantID);
155     $xml->dataElement(terminalID => $terminalID);
156     $xml->endTag('endOfDay');
157     $xml->endTag('transRequest');
158     return $output;
159   },
160   row => sub {},
161 );
162
163 # Including this means that there is a Business::BatchPayment module for
164 # this gateway and we want to upgrade it.
165 # Must return the name of the module, followed by a hash of options.
166
167 sub _upgrade_gateway {
168   my $conf = FS::Conf->new;
169   my @batchconfig = $conf->config('batchconfig-paymentech');
170   my %options;
171   @options{ qw(bin terminalID merchantID login password ) } = @batchconfig;
172   $options{'industryType'} = 'EC';
173   ( 'Paymentech', %options );
174 }
175
176 1;
177