use 5.006;
use strict;
use warnings;
-our $VERSION = '0.03';
+our $VERSION = '0.08';
+
+use Unicode::Truncate 'truncate_egc';
=head1 NAME
industryType => 'EC'
login => 'TESTUSER',
password => 'MYPASS',
+ with_recurringInd => 1,
);
my $result = $processor->submit(@items);
=item industryType - your 2-letter industry type code
+=item with_recurringInd - enable the recurring charge indicator field
+
=back
=cut
with 'Business::BatchPayment::Processor';
with 'Business::BatchPayment::TestMode';
+use Encode;
+
# could have some validation on all of these
has [ qw(merchantID terminalID bin industryType login password) ] => (
is => 'ro',
required => 1,
);
+has 'with_recurringInd' => (
+ is => 'ro',
+ isa => 'Bool',
+ default => 0,
+);
+
has 'fileDateTime' => (
is => 'ro',
isa => 'Str',
OUTPUT => \$output,
DATA_MODE => 1,
DATA_INDENT => 2,
+ ENCODING => 'utf-8',
);
$self->format_header($batch, $xml);
my $count = 1;
my ($self, $batch, $xml) = @_;
my $num_items = $batch->count;
+ $xml->xmlDecl();
$xml->startTag('transRequest', RequestCount => $num_items + 1);
$xml->startTag('batchFileID');
$xml->dataElement(userID => $self->login);
terminalID => $self->terminalID,
);
if ($item->payment_type eq 'CC') {
+ my $expiration = $item->expiration;
+ $expiration =~ s/\D//g;
push @order, (
ccAccountNum => $item->card_number,
- ccExp => $item->expiration,
+ ccExp => $expiration,
);
- }
- elsif ( $item->payment_type eq 'ECHECK' ) {
+ } elsif ( $item->payment_type eq 'ECHECK' ) {
push @order, (
cardBrand => 'EC',
ecpCheckRT => $item->routing_code,
ecpBankAcctType => $BankAcctType{ $item->account_type },
ecpDelvMethod => 'A',
);
- }
- else {
+ } else {
die "payment type ".$item->type." not supported";
}
- push @order, (
- avsZip => $item->zip,
- avsAddress1 => substr($item->address, 0, 30),
- avsAddress2 => substr($item->address2, 0, 30),
- avsCity => substr($item->city, 0, 20),
- avsState => substr($item->state, 0, 2),
- avsName => substr($item->first_name. ' '. $item->last_name, 0, 30),
- avsCountryCode => ( $paymentech_countries{ $item->country }
- ? $_->country
- : ''
- ),
+ if ( $self->with_recurringInd ) {
+ if ( $item->recurring_billing eq 'F' ) {
+ push @order, ( recurringInd => 'RF' );
+ } elsif ( $item->recurring_billing eq 'S' ) {
+ push @order, ( recurringInd => 'RS' );
+ }
+ } # else don't send recurringInd at all
+
+ push @order, ( # truncate_egc will die() on empty string
+ avsZip => $item->zip,
+ avsAddress1 => $item->address ? truncate_egc($item->address, 30, '') : undef,
+ avsAddress2 => $item->address2 ? truncate_egc($item->address2, 30, '') : undef,
+ avsCity => $item->city ? truncate_egc($item->city, 20, '') : undef,
+ avsState => $item->state ? truncate_egc($item->state, 2, '') : undef,
+ avsName => ($item->first_name || $item->last_name)
+ ? truncate_egc($item->first_name.' '.$item->last_name, 30, '')
+ : undef,
+ ( $paymentech_countries{ $item->country }
+ ? ( avsCountryCode => $item->country )
+ : ()
+ ),
orderID => $item->tid,
amount => int( $item->amount * 100 ),
);
second => $sec,
);
+ my %failure_status = (
+ # API version 2.6, April 2013
+ '00' => undef, # Approved
+ '04' => 'pickup',
+ '33' => 'expired',
+ '41' => 'stolen',
+ '42' => 'inactive',
+ '43' => 'stolen',
+ '44' => 'inactive',
+ 'B7' => 'blacklisted', # Fraud
+ 'B9' => 'blacklisted', # On Negative File
+ 'BB' => 'stolen', # Possible Compromise
+ 'BG' => 'blacklisted', # Blocked Account
+ 'BQ' => 'blacklisted', # Issuer has Flagged Account as Suspected Fraud
+ 'C4' => 'nsf', # Over Credit Limit
+ 'D5' => 'blacklisted', # On Negative File
+ 'D7' => 'nsf', # Insufficient Funds
+ 'F3' => 'inactive', # Account Closed
+ 'K6' => 'nsf', # NSF
+ ); # all others are "decline"
+
+ my $failure_status = undef;
+ my $error_message;
+
+ if ( $resp->{procStatus} ) {
+ $error_message = $resp->{procStatusMessage};
+ } elsif ( $resp->{respCode} ) {
+ $error_message = $resp->{respCodeMessage};
+ $failure_status = $failure_status{ $resp->{respCode} } || 'decline';
+ } else {
+ $error_message = '';
+ }
+
my $item = Business::BatchPayment->create(Item =>
tid => $resp->{orderID},
process_date => $dt,
authorization => $resp->{authorizationCode},
order_number => $resp->{txRefNum},
approved => ($resp->{approvalStatus} == 1),
- error_message => $resp->{procStatusMessage},
+ error_message => $error_message,
+ failure_status => $failure_status,
);
$item;
}
+# DEPRECATED
+
+# sub bytes_substr {
+# my ($string, $offset, $length, $repl) = @_;
+# my $bytes = substr(
+# Encode::encode('utf8', $string || ''),
+# $offset,
+# $length,
+# Encode::encode('utf8', $repl || '')
+# );
+# return Encode::decode('utf8', $bytes, Encode::FB_QUIET);
+# }
+
+
package Business::BatchPayment::Paymentech::Transport;
use File::Temp qw( tempdir );