1 package FS::pay_batch::paymentech;
4 use vars qw(@ISA %import_info %export_info $name);
5 use FS::Record 'qsearchs';
7 use Date::Format 'time2str';
8 use Date::Parse 'str2time';
11 use Unicode::Truncate 'truncate_egc';
14 my ($bin, $merchantID, $terminalID, $username, $password, $with_recurringInd);
21 xmlrow => [ qw(transResponse newOrderResp) ],
44 # find a gateway configuration that has the same merchantID
45 # as the batch config, if there is one. If not, leave
46 # gateway out entirely.
47 my $merchant = (FS::Conf->new->config('batchconfig-paymentech'))[2];
49 'table' => 'payment_gateway',
50 'addl_from' => ' JOIN payment_gateway_option USING (gatewaynum) ',
51 'hashref' => { disabled => '',
52 optionname => 'merchant_id',
53 optionvalue => $merchant,
57 my ($hash, $oldhash) = @_;
58 $hash->{'gatewaynum'} = $gateway->gatewaynum if $gateway;
59 $hash->{'processor'} = 'PaymenTech';
60 my ($mon, $day, $year, $hour, $min, $sec) =
61 $hash->{'_date'} =~ /^(..)(..)(....)(..)(..)(..)$/;
62 $hash->{'_date'} = timelocal($sec, $min, $hour, $day, $mon-1, $year);
63 $hash->{'paid'} = $oldhash->{'amount'};
64 if ( $hash->{'procStatus'} == 0 ) {
65 $hash->{'error_message'} = $hash->{'respCodeMessage'};
67 $hash->{'error_message'} = $hash->{'procStatusMessage'};
70 'approved' => sub { shift->{'approvalStatus'} == 1 },
71 'declined' => sub { shift->{'approvalStatus'} != 1 },
75 'personal checking' => 'C',
76 'personal savings' => 'S',
77 'business checking' => 'X',
78 'business savings' => 'X',
81 my %paymentech_countries = map { $_ => 1 } qw( US CA GB UK );
85 # Load this at run time
86 eval "use XML::Writer";
89 ($bin, $terminalID, $merchantID, $username, $password, $with_recurringInd) =
90 $conf->config('batchconfig-paymentech');
92 # Here we do all the work in the header function.
94 my $pay_batch = shift;
95 my @cust_pay_batch = @{(shift)};
98 my $xml = XML::Writer->new(
104 $xml->xmlDecl(); # it is in the spec
105 $xml->startTag('transRequest', RequestCount => scalar(@cust_pay_batch) + 1);
106 $xml->startTag('batchFileID');
107 $xml->dataElement(userID => $username);
108 $xml->dataElement(fileDateTime => time2str('%Y%m%d%H%M%S', time));
109 $xml->dataElement(fileID => 'FILEID');
110 $xml->endTag('batchFileID');
112 foreach (@cust_pay_batch) {
113 $xml->startTag('newOrder', BatchRequestNo => $count++);
114 my $status = $_->cust_main->status;
115 tie my %order, 'Tie::IxHash', (
116 industryType => 'EC',
119 merchantID => $merchantID,
120 terminalID => $terminalID,
121 ($_->payby eq 'CARD') ? (
122 ccAccountNum => $_->payinfo,
123 ccExp => $_->expmmyy,
125 ecpCheckRT => ($_->payinfo =~ /@(\d+)/),
126 ecpCheckDDA => ($_->payinfo =~ /(\d+)@/),
127 ecpBankAcctType => $paytype{lc($_->paytype)},
128 ecpDelvMethod => 'A',
130 # truncate_egc will die() on empty string
131 avsZip => $_->zip ? truncate_egc($_->zip, 10, '') : undef,
132 avsAddress1 => $_->address1 ? truncate_egc($_->address1, 30, '') : undef,
133 avsAddress2 => $_->address2 ? truncate_egc($_->address2, 30, '') : undef,
134 avsCity => $_->city ? truncate_egc($_->city, 20, '') : undef,
135 avsState => $_->state ? truncate_egc($_->state, 2, '') : undef,
136 avsName => ($_->first || $_->last)
137 ? truncate_egc($_->first. ' '. $_->last, 30, '') : undef,
138 ( $paymentech_countries{ $_->country }
139 ? ( avsCountryCode => $_->country )
142 orderID => $_->paybatchnum,
143 amount => $_->amount * 100,
145 # only do this if recurringInd is enabled in config,
146 # and the customer has at least one non-canceled recurring package
147 if ( $with_recurringInd and $status =~ /^active|suspended|ordered$/ ) {
148 # then send RF if this is the first payment on this payinfo,
150 $order{'recurringInd'} = $_->payinfo_used ? 'RS' : 'RF';
152 foreach my $key (keys %order) {
153 $xml->dataElement($key, $order{$key})
155 $xml->endTag('newOrder');
157 $xml->startTag('endOfDay', BatchRequestNo => $count);
158 $xml->dataElement(bin => $bin);
159 $xml->dataElement(merchantID => $merchantID);
160 $xml->dataElement(terminalID => $terminalID);
161 $xml->endTag('endOfDay');
162 $xml->endTag('transRequest');
168 # Including this means that there is a Business::BatchPayment module for
169 # this gateway and we want to upgrade it.
170 # Must return the name of the module, followed by a hash of options.
172 sub _upgrade_gateway {
173 my $conf = FS::Conf->new;
174 my @batchconfig = $conf->config('batchconfig-paymentech');
184 $options{'industryType'} = 'EC';
185 ( 'Paymentech', %options );