use strict;
use Data::Dumper;
use Business::CreditCard;
-use SOAP::Lite +trace => 'all';
+use SOAP::Lite; #+trace => 'all';
#SOAP::Lite->import(+trace=>'debug');
our $VERSION = '0.01';
our @alpha = ( 'a'..'z', 'A'..'Z', '0'..'9' );
+our %failure_status = (
+ 302 => 'nsf',
+ 501 => 'pickup',
+ 502 => 'stolen',
+ 503 => 'stolen', # "Fraud/Security violation"
+ 504 => 'blacklisted',
+ 509 => 'nsf',
+ 510 => 'nsf',
+ 519 => 'blacklisted',
+ 521 => 'nsf',
+ 522 => 'expired',
+ 530 => 'blacklisted',
+ 534 => 'blacklisted',
+ # others are all "declined"
+);
+
+
sub _info {
{
'info_compat' => '0.01',
'gateway_url' => 'https://www.firstdata.com/en_us/products/merchants/ecommerce/online-payment-processing.html',
'module_version' => $VERSION,
'supported_types' => [ 'CC' ], #, 'ECHECK' ],
- #'token_support' => 1,
- #'test_transaction' => 1,
+ #'token_support' => 1, # "Transarmor" is this, but not implemented yet
+ 'test_transaction' => 1,
'supported_actions' => [ 'Normal Authorization',
- #'Credit',
+ 'Authorization Only',
+ 'Post Authorization',
+ 'Credit',
+ 'Void',
],
};
}
my $self = shift;
#my %opts = @_;
- #$self->build_subs(qw( order_number avs_code cvv2_response
- # response_page response_code response_headers
- # ));
-
- $self->build_subs(qw( avs_code ));
-
+ $self->build_subs(qw( order_number avs_code cvv2_response
+ authorization failure_status result_code
+ ));
}
sub map_fields {
$content{'action'} = $actions{$action} || $action;
+ # make sure there's a combined name
+ $content{name} ||= $content{first_name} . ' ' . $content{last_name};
+
# stuff it back into %content
$self->content(%content);
'login' => 'ExactID',
'password' => 'Password',
- 'action' => 'TransactionType',
+ 'action' => 'Transaction_Type',
'amount' => 'DollarAmount',
'currency' => 'Currency',
my %content = $self->content();
- #$content{'mop'} = $mop{ cardtype($content{creditCardNum}) }
- # if $content{'type'} eq 'CC';
-
- #if ( $self->test_transaction ) {
- # $content{agentCode} = 'TEST88';
- # $content{password} = 'TEST88';
- #}
-
$content{Expiry_Date} =~ s/\///;
$content{country} ||= 'US';
}
my $base_uri;
- if ( $self->test_transaction ) {
- $base_uri =
- 'https://api.demo.globalgatewaye4.firstdata.com/transaction';
- } else {
- $base_uri =
- 'https://api.globalgatewaye4.firstdata.com/vplug-in/transaction';
- }
+ $base_uri = 'https://api.demo.globalgatewaye4.firstdata.com/transaction';
my $proxy = "$base_uri/v11";
- my $uri = "$base_uri/rpc-enc";
- my %transaction = map { $_ => $content{$_} } (qw(
+ my @transaction = map { SOAP::Data->name($_)->value( $content{$_} ) }
+ grep { defined($content{$_}) }
+ (qw(
ExactID Password Transaction_Type DollarAmount Card_Number Transaction_Tag
Track1 Track2 Authorization_Num Expiry_Date CardHoldersName
VerificationStr1 VerificationStr2 CVD_Presence_Ind Reference_No ZipCode
Tax1Amount Tax1Number Tax2Amount Tax2Number Customer_Ref Reference_3
- Language Client_IP Client_Email user_name Currency PartialRedemption
+ Language Client_IP Client_Email User_Name Currency PartialRedemption
CAVV XID Ecommerce_Flag
));
#TransarmorToken CardType EAN VirtualCard CardCost FraudSuspected
#CheckNumber CheckType BankAccountNumber BankRoutingNumber CustomerName
#CustomerIDType CustomerID
- #my @opts = map { SOAP::Data->name($_)->value( $data{$_} ) }
- # keys %data;
-
- my $result = SOAP::Lite
- ->proxy($proxy)
-
- ->default_ns($base_uri)
- ->uri($uri)
-
- ->on_action( sub { join '/', @_ } )
- #->on_action( sub { join '', @_ } )
- #->on_action(sub { qq("$_[0]") }) #? https://firstdata.zendesk.com/entries/407569-First-Data-Global-Gateway-e4-Web-Service-API-Sample-Code-Perl
- ->autotype(0)
-
- ->readable(1)
-
- ->ns($uri,'q1')
- ->SendAndCommit( SOAP::Data->name('Transaction')->value( \%transaction ) )
-
- ->result();
-
- die Dumper($result);
-
- die Dumper($result->result) if $result->fault;
- #die $result->fault->faultstring if $result->fault;
-
- die Dumper($result);
-
- #$self->is_success
- #$self->authorization
- #$self->avs_code
- #$self->error_message
- #$self->result_code
- ##$self->failure_status
+ my $wsdl = "$proxy/wsdl";
+ my $client = SOAP::Lite->service($wsdl)->proxy($proxy)->readable(1);
+ my $action_prefix = 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc';
+ my $type_prefix = $action_prefix . '/encodedTypes';
+ $client->on_action( sub { $action_prefix . '/' . $_[1] } );
+ my $source = SOAP::Data->name('SendAndCommitSource')
+ ->value(\@transaction)
+ ->type("$type_prefix:Transaction");
+ local $@;
+ my $som = eval { $client->call('SendAndCommit', $source) };
+ die $@ if $@;
+ if ($som->fault) { # indicates a protocol error
+ die $som->faultstring;
+ }
+ $DB::single = 1;
+ $som->match('/Envelope/Body/SendAndCommitResponse/SendAndCommitResult');
+ my $result = $som->valueof; # hashref of the result properties
+ $self->is_success( $result->{Transaction_Approved} );
+ $self->authorization( $result->{Authorization_Num} );
+ $self->order_number( $result->{SequenceNo} );
+ $self->avs_code( $result->{AVS} );
+ $self->cvv2_response( $result->{CVV2} );
+
+ if (!$self->is_success) {
+ # note spelling of "EXact_Resp_Code"
+ if ($result->{EXact_Resp_Code} ne '00') {
+ # then there's something wrong with the transaction inputs
+ # (invalid card number, malformed amount, attempt to refund a
+ # transaction that didn't happen, etc.)
+ $self->error_message($result->{EXact_Message});
+ $self->result_code($result->{EXact_Resp_Code});
+ $self->failure_status('');
+ # not a decline, as the transaction was never really detected
+ } else {
+ $self->error_message($result->{Bank_Message});
+ $self->result_code($result->{Bank_Resp_Code});
+ $self->failure_status(
+ $failure_status{$result->{Bank_Resp_Code}} || 'declined'
+ );
+ }
+ }
}
1;