X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=Paymentech.pm;h=9eec93503c27007e5499606d1c6d812611d4317e;hb=1667d469317704ae542a9b1f6fdc8e498e0eff59;hp=7fa2e442745e65706341c05abd13d592b2a49f18;hpb=eabacc85da1879f947797698461efa97a27c7b50;p=Business-BatchPayment-Paymentech.git diff --git a/Paymentech.pm b/Paymentech.pm index 7fa2e44..9eec935 100644 --- a/Paymentech.pm +++ b/Paymentech.pm @@ -3,16 +3,12 @@ package Business::BatchPayment::Paymentech; use 5.006; use strict; use warnings; -our $VERSION = '0.01'; +our $VERSION = '0.03'; =head1 NAME Business::BatchPayment::Paymentech - Chase Paymentech XML batch format. -=head1 VERSION - -Version 0.01 - =head1 USAGE See L for general usage notes. @@ -69,16 +65,18 @@ use Moose; 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', - isa => 'Str', + is => 'ro', + isa => 'Str', required => 1, ); has 'fileDateTime' => ( - is => 'ro', - isa => 'Str', + is => 'ro', + isa => 'Str', default => sub { DateTime->now->strftime('%Y%m%d%H%M%S') }, @@ -91,12 +89,13 @@ my %BankAcctType = ( 'business savings' => 'X', ); +my %paymentech_countries = map { $_ => 1 } qw( US CA GB UK ); + sub default_transport { my $self = shift; Business::BatchPayment::Paymentech::Transport->new( login => $self->login, password => $self->password, - put_path => $self->fileDateTime, debug => $self->debug, test_mode => $self->test_mode, ); @@ -108,9 +107,10 @@ sub format_request { my $output; my $xml = XML::Writer->new( - OUTPUT => \$output, - DATA_MODE => 1, + OUTPUT => \$output, + DATA_MODE => 1, DATA_INDENT => 2, + ENCODING => 'utf-8', ); $self->format_header($batch, $xml); my $count = 1; @@ -130,11 +130,13 @@ sub format_header { 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); $xml->dataElement(fileDateTime => $self->fileDateTime); - $xml->dataElement(fileID => $self->fileDateTime); + $xml->dataElement(fileID => sprintf('%06d-', $batch->batch_id) . + $self->fileDateTime); $xml->endTag('batchFileID'); } @@ -143,40 +145,43 @@ sub format_item { if ( $item->action eq 'payment' ) { $xml->startTag('newOrder', BatchRequestNo => $count); my @order = ( - industryType => $self->industryType, - transType => 'AC', - bin => $self->bin, - merchantID => $self->merchantID, - terminalID => $self->terminalID, + industryType => $self->industryType, + transType => 'AC', + bin => $self->bin, + merchantID => $self->merchantID, + terminalID => $self->terminalID, ); if ($item->payment_type eq 'CC') { push @order, ( - ccAccountNum => $item->card_number, - ccExp => $item->expiration, + ccAccountNum => $item->card_number, + ccExp => $item->expiration, ); } elsif ( $item->payment_type eq 'ECHECK' ) { push @order, ( - cardBrand => 'EC', - ecpCheckRT => $item->routing_code, - ecpCheckDDA => $item->account_number, + cardBrand => 'EC', + ecpCheckRT => $item->routing_code, + ecpCheckDDA => $item->account_number, ecpBankAcctType => $BankAcctType{ $item->account_type }, - ecpDelvMethod => 'A', + ecpDelvMethod => 'A', ); } 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 => $item->state, - avsName => substr($item->first_name .' '. $item->last_name, 0, 30), - avsCountryCode => $item->country, - orderID => $item->tid, - amount => int( $item->amount * 100 ), + avsZip => $item->zip, + avsAddress1 => bytes_substr($item->address, 0, 30), + avsAddress2 => bytes_substr($item->address2, 0, 30), + avsCity => bytes_substr($item->city, 0, 20), + avsState => bytes_substr($item->state, 0, 2), + avsName => bytes_substr($item->first_name. ' '. $item->last_name, 0, 30), + ( $paymentech_countries{ $item->country } + ? ( avsCountryCode => $item->country ) + : () + ), + orderID => $item->tid, + amount => int( $item->amount * 100 ), ); while (@order) { my $key = shift @order; @@ -239,17 +244,65 @@ sub parse_item { 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; } +# internal use + +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 ); @@ -285,7 +338,8 @@ sub upload { my $self = shift; my $content = shift; my $tmpdir = tempdir( CLEANUP => 1 ); - my $filename = $self->put_path; # also the value of the fileId tag + $content =~ /(.*)<\/fileID>/; + my $filename = $1; my $archive_dir = $self->archive_to; warn "Writing temp file to $tmpdir/$filename.xml.\n" if $self->debug;