##### TODO: expand grammar map
package ICS::Simple;
use strict;
no strict "refs";
eval "use ICS";
die qq{ICS::Simple requires the CyberSource ICS module.\nSee http://www.cybersource.com/support_center/implementation/downloads/scmp_api/.\n} if $@;
=head1 NAME
ICS::Simple - Simple interface to CyberSource ICS2
=head1 VERSION
Version 0.06
=cut
our $VERSION = '0.06';
=head1 SYNOPSIS
Here is some basic code. Hopefully I'll come back through soon to document it properly.
use ICS::Simple;
my $ics = ICS::Simple->new(
ICSPath => '/opt/ics',
MerchantId => 'v0123456789', # CyberSource supplies this number to you
Mode => 'test',
Currency => 'USD',
Grammar => 'UpperCamel', # defaults to raw ICS responses, so you might want to set this
#ErrorsTo => 'all-errors@some.fun.place.com',
CriticalErrorsTo => 'only-critical-errors@some.fun.place.com',
);
my $request = {
OrderId => 'order19857219',
FirstName => 'Fred',
LastName => 'Smith',
Email => 'fred.smith@buyer-of-stuff.com',
CardNumber => '4111111111111111',
CardCVV => '123',
CardExpYear => '2008',
CardExpMonth => '12',
BillingAddress => '123 Main St',
BillingCity => 'Olympia',
BillingRegion => 'WA',
BillingPostalCode => '98501',
BillingCountryCode => 'US',
ShippingAddress1 => '6789 Industrial Pl',
ShippingAddress2 => 'Floor 83, Room 11415',
ShippingCity => 'Olympia',
ShippingRegion => 'WA',
ShippingPostalCode => '98506',
ShippingCountryCode => 'US',
ShippingFee => '25.05',
HandlingFee => '5.00',
Items => [
{ Description => 'Mega Lizard Monster RC',
Price => '25.00',
SKU => 'prod15185' },
{ Description => 'Super Racer Parts Kit',
Price => '15.30',
SKU => 'prod23523' },
{ Description => 'Uber Space Jacket',
Price => '72.24',
SKU => 'prod18718' },
],
};
my $response = $ics->requestBill($request);
if ($response->{success}) {
print "Woo! Success!\n";
$response = $response->{response};
print "Thanks for your payment of \$$response->{BillAmount}.\n";
}
else {
print "Boo! Failure!\n";
print "Error: $response->{error}->{description}\n";
}
=head1 FUNCTIONS
=head2 new
=cut
sub new {
my $class = shift;
my %args = @_;
my $self = {};
bless($self, $class);
foreach my $arg (keys(%args)) {
$self->set($arg, $args{$arg}, $self);
}
$self->{server_mode} ||= 'test'; # default to test mode
$self->{icspath} ||= '/opt/ics';
$self->{cvv_accepted} ||= $ICS::Simple::cvvAcceptedDefault;
return $self;
}
=head2 set
=cut
sub set {
my ($self, $key, $val, $namespace, $mapRequired) = @_;
my $lookupKey = lc($key);
$lookupKey =~ s|_||g;
return undef if ($mapRequired && !$ICS::Simple::fieldMap->{$lookupKey}); # if we require that the name is mapped but it isn't, fail
$key = $ICS::Simple::fieldMap->{$lookupKey} || $key; # if it has a mapped name, use it instead
$namespace->{$key} = $val;
return $key;
}
=head2 requestIcs
=cut
sub requestIcs {
my $self = shift;
my $request = shift;
$request->{server_mode} ||= $self->{server_mode};
$request->{server_host} ||= $self->{server_host};
unless ($request->{server_host}) {
if ($request->{server_mode} =~ m|^\s*prod|i) {
$request->{server_host} = 'ics2.ic3.com';
$request->{server_port} = '80';
} else {
$request->{server_host} = 'ics2test.ic3.com';
$request->{server_port} = '80';
}
}
$request->{currency} ||= 'USD';
$request->{merchant_id} ||= $self->{merchant_id};
$request->{decline_avs_flags} ||= $self->{decline_avs_flags} || $ICS::Simple::avsRejectedDefault;
my $response = {};
%{$response} = ICS::ics_send(%{$request});
$ICS::Simple::response->{rmsg} = $response->{auth_rmsg} || $response->{ics_rmsg};
$response->{success} = ($response->{ics_rcode} == 1 ? 1 : 0);
if (!$response->{success}) {
$response->{rmsg} = $ICS::Simple::vitalErrorMap->{$response->{ics_rcode}} || $response->{rmsg};
$response->{auth_auth_error} = $ICS::Simple::vitalErrorMap->{$response->{auth_auth_response}} || $ICS::Simple::vitalErrorMap->{0};
}
my $error = {};
if ($response->{ics_rcode} != 1) { # error
$error = $self->_handleError($request, $response);
}
return {
success => $response->{success},
request => $request,
response => $self->_translateResponse($response),
error => $error
};
}
=head2 _translateResponse
=cut
sub _translateResponse {
my $self = shift;
my $response = shift;
my $grammar = shift || $self->{x_grammar};
my $gMap = (ref($grammar) eq 'HASH' ? $grammar : ($ICS::Simple::grammarMap->{lc($grammar)} || $ICS::Simple::grammarMap->{stock} || {}));
my $mapRequired = ($gMap->{_mapRequired} ? 1 : 0);
foreach my $key (keys(%{$response})) {
my $val = $response->{$key};
delete($response->{$key}); # remove old key
return undef if ($mapRequired && !$gMap->{$key}); # if we require that the name is mapped but it isn't, fail
$key = $gMap->{$key} || $key; # if it has a mapped name, use it instead
$response->{$key} = $val; # insert new key
}
return $response;
}
=head2 _resolveApp
=cut
sub _resolveApp {
my $self = shift;
my $appVal = shift;
my @apps = (ref($appVal) eq 'ARRAY' ? @{$appVal} : split(/\s*[,;]+\s*/, $appVal)); # turn it into an array if it isn't one already
my @appsResolved;
foreach my $app (@apps) {
next unless $app; # in case we grabbed an empty one (two delimiters seperated by spaces would cause this)
my $lookupKey = lc($app);
$lookupKey =~ s|_||g;
$app = $ICS::Simple::appMap->{$lookupKey} || $app; # if it has a mapped name, use it instead
push(@appsResolved, $app);
}
return join(',', @appsResolved);
}
=head2 _constructOffers
=cut
sub _constructOffers {
my $self = shift;
my $itemsKey = shift;
my $namespace = shift;
my $items = $namespace->{$itemsKey};
my @items = (ref($items) eq 'ARRAY' ? @{$items} : [$items]);
my @offers;
my $i = 0;
foreach my $item (@items) {
my @offerAttrs;
foreach my $attr (keys(%{$item})) {
my $val = $item->{$attr};
my $lookupKey = lc($attr);
$lookupKey =~ s|_||g;
$attr = $ICS::Simple::offerAttrMap->{$lookupKey} || $attr; # if it has a mapped name, use it instead
push(@offerAttrs, $attr.':'.$val);
}
my $offer = join('^', 'offerid:'.$i, @offerAttrs);
$namespace->{'offer'.$i} = $offer;
$i++;
}
delete($namespace->{$itemsKey});
}
=head2 request
=cut
sub request { # convert the request into ICS format and then send it to requestIcs
my $self = shift;
my $request = shift;
# combine input namespaces into one
my $item = {};
for my $key (keys(%{$self})) {
$self->set($key, $self->{$key}, $item, 1);
}
for my $key (keys(%{$request})) {
$self->set($key, $request->{$key}, $item, 1);
}
@{$item->{x_items}} = (ref($item->{x_items}) eq 'ARRAY' ? @{$item->{x_items}} : [$item->{x_items}]);
if ($item->{x_shipping_handling_fee}) {
my $offer = {
Amount => $item->{x_shipping_handling_fee},
ProductCode => 'shipping_and_handling',
ProductName => 'Shipping & Handling',
};
delete($item->{x_shipping_handling_fee});
unshift(@{$item->{x_items}}, $offer);
}
if ($item->{x_shipping_fee}) {
my $offer = {
Amount => $item->{x_shipping_fee},
ProductCode => 'shipping_only',
ProductName => 'Shipping',
};
delete($item->{x_shipping_fee});
unshift(@{$item->{x_items}}, $offer);
}
if ($item->{x_handling_fee}) {
my $offer = {
Amount => $item->{x_handling_fee},
ProductCode => 'handling_only',
ProductName => 'Handling',
};
delete($item->{x_handling_fee});
unshift(@{$item->{x_items}}, $offer);
}
$self->_constructOffers('x_items', $item);
$item->{ics_applications} = $self->_resolveApp($item->{ics_applications});
$item->{decline_avs_flags} = $item->{decline_avs_flags} || $self->{decline_avs_flags} || $ICS::Simple::avsRejectedDefault;
return $self->requestIcs($item);
}
=head2 requestBill
=cut
sub requestBill {
my $self = shift;
my $request = shift;
$request->{action} = ['auth', 'bill'];
return $self->request($request);
}
=head2 requestAuth
=cut
sub requestAuth {
my $self = shift;
my $request = shift;
$request->{action} = ['auth'];
return $self->request($request);
}
=head2 requestAuthReversal
=cut
sub requestAuthReversal {
my $self = shift;
my $request = shift;
$request->{action} = ['authreversal'];
return $self->request($request);
}
=head2 requestSettle
=cut
sub requestSettle {
my $self = shift;
my $request = shift;
$request->{action} = ['bill'];
return $self->request($request);
}
=head2 requestCredit
=cut
sub requestCredit {
my $self = shift;
my $request = shift;
$request->{action} = ['credit'];
return $self->request($request);
}
=head2 _handleError
=cut
sub _handleError { # it failed, but in a normal kind of way
my $self = shift;
my $request = shift; # just so we can pass it through to _handleCriticalError
my $response = shift;
my $statusCode = lc($response->{ics_rflag});
$statusCode =~ s|^\s+||;
$statusCode =~ s|\s+$||;
my $error = $ICS::Simple::errorMap->{$statusCode} || $ICS::Simple::errorMap->{default};
$error->{statuscode} = $statusCode;
$error->{data} = $response->{ics_rmsg};
# mail the error, if we can
my $sendErrorsTo = $self->{x_errors_to};
if ($error->{critical} || $response->{ics_rcode} < 0) { # critical!
$sendErrorsTo = $self->{x_critical_errors_to} || $sendErrorsTo;
}
$self->_sendError('CyberSource Error: '.($error->{name} || $statusCode), $sendErrorsTo, $request, $response, $error);
return $error;
}
=head2 _sendError
=cut
sub _sendError {
my $self = shift;
my $subject = shift || 'ICS::Simple Error Mail';
my $sendTo = shift or return undef;
my $request = shift;
my $response = shift;
my $error = shift;
use Data::Dumper;
use Mail::Send;
my @mailTo = (ref($sendTo) eq 'ARRAY' ? @{$sendTo} : split(/\s*[,;]+\s*/, $sendTo));
my $msg = Mail::Send->new;
$msg->to(@mailTo);
$msg->subject($subject);
$msg->add('X-Site', $self->{x_site});
$msg->add('X-IP', $ENV{REMOTE_ADDR});
my $body = $msg->open;
my $bodyContent = join("\n",
'['.gmtime().' UTC]',
'ERROR:', Dumper($error),
'RESPONSE:', Dumper($response),
'REQUEST:', Dumper($request),
'ENV:', Dumper(\%ENV),
'ICS::Simple:', Dumper($self),
);
# filter things before we send it out
$bodyContent =~ s|(customer_cc_number'.*?'[^']{4})([^']+?)([^']{4}')|$1.('x' x length($2)).$3|ge; # keep the first/last 4 characters, replace all else
$bodyContent =~ s|(customer_cc_cv_number'.*?')([^']+?)(')|$1.('x' x length($2)).$3|ge; # replace all the characters
print $body $bodyContent;
$body->close;
return $error;
}
=head1 AUTHOR
Dusty Wilson, C<< <cpan-ics-simple at dusty.hey.nu> >>
=head1 BUGS
The documentation needs to be finished. Or started. Sorry about that.
Please report any bugs or feature requests to
C<bug-ics-simple at rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=ICS-Simple>.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc ICS::Simple
You can also look for information at:
=over 4
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/ICS-Simple>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/ICS-Simple>
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ICS-Simple>
=item * Search CPAN
L<http://search.cpan.org/dist/ICS-Simple>
=back
=head1 ACKNOWLEDGEMENTS
=head1 COPYRIGHT & LICENSE
Copyright 2006 Dusty Wilson, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
$ICS::Simple::fieldMap = { # not case-sens on the key-side
# forced lowercase
# stripped underscores
host => 'server_host',
serverhost => 'server_host',
port => 'server_port',
serverport => 'server_port',
mode => 'server_mode',
servermode => 'server_mode',
icspath => 'icspath',
hostid => 'host_id',
merchantid => 'merchant_id',
orderid => 'merchant_ref_number',
merchantorderid => 'merchant_ref_number',
ordernumber => 'merchant_ref_number',
merchantordernumber => 'merchant_ref_number',
orderrefnumber => 'merchant_ref_number',
merchantorderrefnumber => 'merchant_ref_number',
refnumber => 'merchant_ref_number',
merchantrefnumber => 'merchant_ref_number',
merchantdescriptor => 'merchant_descriptor',
merchantdesc => 'merchant_descriptor',
merchantdescriptorcontact => 'merchant_descriptor_contact',
merchantdesccontact => 'merchant_descriptor_contact',
currency => 'currency',
action => 'ics_applications',
actions => 'ics_applications',
icsapplications => 'ics_applications',
declineavs => 'decline_avs_flags',
cvvaccepted => 'cvv_accepted',
firstname => 'customer_firstname',
givenname => 'customer_firstname',
customergivenname => 'customer_firstname',
customerfirstname => 'customer_firstname',
lastname => 'customer_lastname',
familyname => 'customer_lastname',
customerfamilyname => 'customer_lastname',
customerlastname => 'customer_lastname',
email => 'customer_email',
customeremail => 'customer_email',
billingaddress => 'bill_address1',
billingaddress1 => 'bill_address1',
billingaddress2 => 'bill_address2',
billaddress => 'bill_address1',
billaddress1 => 'bill_address1',
billaddress2 => 'bill_address2',
billingcity => 'bill_city',
billcity => 'bill_city',
billingregion => 'bill_state',
billregion => 'bill_state',
billingstateprov => 'bill_state',
billstateprov => 'bill_state',
billingprov => 'bill_state',
billprov => 'bill_state',
billingprovince => 'bill_state',
billprovince => 'bill_state',
billingstate => 'bill_state',
billstate => 'bill_state',
billingpostalcode => 'bill_zip',
billingpostal => 'bill_zip',
billpostalcode => 'bill_zip',
billpostal => 'bill_zip',
billingcountrycode => 'bill_country',
billingcountry => 'bill_country',
billcountrycode => 'bill_country',
billcountry => 'bill_country',
shippingaddress => 'ship_to_address1',
shippingaddress1 => 'ship_to_address1',
shippingaddress2 => 'ship_to_address2',
shipaddress => 'ship_to_address1',
shipaddress1 => 'ship_to_address1',
shipaddress2 => 'ship_to_address2',
shiptoaddress => 'ship_to_address1',
shiptoaddress1 => 'ship_to_address1',
shiptoaddress2 => 'ship_to_address2',
shippingcity => 'ship_to_city',
shipcity => 'ship_to_city',
shiptocity => 'ship_to_city',
shippingregion => 'ship_to_state',
shipregion => 'ship_to_state',
shiptoregion => 'ship_to_state',
shippingstateprov => 'ship_to_state',
shipstateprov => 'ship_to_state',
shiptostateprov => 'ship_to_state',
shippingprov => 'ship_to_state',
shipprov => 'ship_to_state',
shiptoprov => 'ship_to_state',
shippingprovince => 'ship_to_state',
shipprovince => 'ship_to_state',
shiptoprovince => 'ship_to_state',
shippingstate => 'ship_to_state',
shipstate => 'ship_to_state',
shiptostate => 'ship_to_state',
shippingpostalcode => 'ship_to_zip',
shippingpostal => 'ship_to_zip',
shippostalcode => 'ship_to_zip',
shippostal => 'ship_to_zip',
shiptopostalcode => 'ship_to_zip',
shiptopostal => 'ship_to_zip',
shippingcountrycode => 'ship_to_country',
shippingcountry => 'ship_to_country',
shipcountrycode => 'ship_to_country',
shipcountry => 'ship_to_country',
shiptocountrycode => 'ship_to_country',
shiptocountry => 'ship_to_country',
cardexpmonth => 'customer_cc_expmo',
ccexpmo => 'customer_cc_expmo',
customerccexpmo => 'customer_cc_expmo',
cardexpyear => 'customer_cc_expyr',
ccexpyr => 'customer_cc_expyr',
customerccexpyr => 'customer_cc_expyr',
cardnumber => 'customer_cc_number',
ccnumber => 'customer_cc_number',
customerccnumber => 'customer_cc_number',
cardcvnumber => 'customer_cc_cv_number',
cardcv => 'customer_cc_cv_number',
cardcvvnumber => 'customer_cc_cv_number',
cardcvv => 'customer_cc_cv_number',
cardcvv2 => 'customer_cc_cv_number',
cardcvnnumber => 'customer_cc_cv_number',
cardcvn => 'customer_cc_cv_number',
cccvnnumber => 'customer_cc_cv_number',
cccv => 'customer_cc_cv_number',
cccvv => 'customer_cc_cv_number',
cccvn => 'customer_cc_cv_number',
customercccvnnumber => 'customer_cc_cv_number',
customercccvvnumber => 'customer_cc_cv_number',
customercccvv2number => 'customer_cc_cv_number',
customercccvnumber => 'customer_cc_cv_number',
ignoreavs => 'ignore_avs',
ignorebadcv => 'ignore_bad_cv',
timeout => 'timeout',
# x_* items are custom (not ICS) fields that are processed before sending
appname => 'x_site',
app => 'x_site',
sitename => 'x_site',
site => 'x_site',
xsite => 'x_site',
errto => 'x_errors_to',
errorto => 'x_errors_to',
errorsto => 'x_errors_to',
xerrorsto => 'x_errors_to',
criterrto => 'x_critical_errors_to',
criterrorto => 'x_critical_errors_to',
criterrorsto => 'x_critical_errors_to',
criticalerrto => 'x_critical_errors_to',
criticalerrorto => 'x_critical_errors_to',
criticalerrorsto => 'x_critical_errors_to',
xcriticalerrorsto => 'x_critical_errors_to',
grammar => 'x_grammar',
shippingfee => 'x_shipping_fee',
xshippingfee => 'x_shipping_fee',
handlingfee => 'x_handling_fee',
xhandlingfee => 'x_handling_fee',
shippinghandlingfee => 'x_shipping_handling_fee',
xshippinghandlingfee => 'x_shipping_handling_fee',
merch => 'x_items',
offer => 'x_items',
offers => 'x_items',
item => 'x_items',
items => 'x_items',
xitem => 'x_items',
xitems => 'x_items',
};
$ICS::Simple::appMap = { # not case-sens on the key-side
# forced lowercase
# stripped underscores
auth => 'ics_auth',
icsauth => 'ics_auth',
bill => 'ics_bill',
icsbill => 'ics_bill',
credit => 'ics_credit',
icscredit => 'ics_credit',
authreversal => 'ics_auth_reversal',
icsauthreversal => 'ics_auth_reversal',
authrev => 'ics_auth_reversal',
icsauthrev => 'ics_auth_reversal',
revauth => 'ics_auth_reversal',
icsrevauth => 'ics_auth_reversal',
reverseauth => 'ics_auth_reversal',
icsreverseauth => 'ics_auth_reversal',
score => 'ics_score',
icsscore => 'ics_score',
export => 'ics_export',
icsexport => 'ics_export',
dav => 'ics_dav',
icsdav => 'ics_dav',
};
$ICS::Simple::offerAttrMap = { # not case-sens on the key-side
# forced lowercase
# stripped underscores
description => 'product_name',
productname => 'product_name',
sku => 'merchant_product_sku',
itemsku => 'merchant_product_sku',
productid => 'merchant_product_sku',
itemid => 'merchant_product_sku',
merchantproductsku => 'merchant_product_sku',
type => 'product_code',
producttype => 'product_code',
code => 'product_code',
productcode => 'product_code',
each => 'amount',
price => 'amount',
amount => 'amount',
count => 'quantity',
qty => 'quantity',
quantity => 'quantity',
tax => 'tax_amount',
taxamount => 'tax_amount',
};
$ICS::Simple::grammarMap = {
stock => {}, # no change -- raw response
lowercamel => {
success => 'success',
request_token => 'requestToken',
request_id => 'requestId',
merchant_ref_number => 'orderNumber',
rmsg => 'responseMessage',
ics_rflag => 'icsResponseFlag',
ics_rcode => 'icsResponseCode',
ics_rmsg => 'icsResponseMessage',
auth_auth_time => 'authTime',
auth_rflag => 'authResponseFlag',
auth_rcode => 'authResponseCode',
auth_rmsg => 'authResponseMessage',
auth_auth_response => 'authResponse',
auth_auth_error => 'authError',
auth_auth_amount => 'authAmount',
auth_auth_code => 'authCode',
auth_cv_result => 'authCvResult',
auth_cv_result_raw => 'authCvRaw',
auth_auth_avs => 'authAvsResult',
auth_avs_raw => 'authAvsRaw',
auth_factor_code => 'authFactorCode',
auth_vital_error => 'authVitalError',
bill_bill_request_time => 'billRequestTime',
bill_rflag => 'billResponseFlag',
bill_rcode => 'billResponseCode',
bill_rmsg => 'billResponseMessage',
bill_trans_ref_no => 'billTransactionNumber',
bill_bill_amount => 'billAmount',
currency => 'currency',
},
uppercamel => {
success => 'Success',
request_token => 'RequestToken',
request_id => 'RequestId',
merchant_ref_number => 'OrderNumber',
rmsg => 'ResponseMessage',
ics_rflag => 'IcsResponseFlag',
ics_rcode => 'IcsResponseCode',
ics_rmsg => 'IcsResponseMessage',
auth_auth_time => 'AuthTime',
auth_rflag => 'AuthResponseFlag',
auth_rcode => 'AuthResponseCode',
auth_rmsg => 'AuthResponseMessage',
auth_auth_response => 'AuthResponse',
auth_auth_error => 'AuthError',
auth_auth_amount => 'AuthAmount',
auth_auth_code => 'AuthCode',
auth_cv_result => 'AuthCvResult',
auth_cv_result_raw => 'AuthCvRaw',
auth_auth_avs => 'AuthAvsResult',
auth_avs_raw => 'AuthAvsRaw',
auth_factor_code => 'AuthFactorCode',
bill_bill_request_time => 'BillRequestTime',
bill_rflag => 'BillResponseFlag',
bill_rcode => 'BillResponseCode',
bill_rmsg => 'BillResponseMessage',
bill_trans_ref_no => 'BillTransactionNumber',
bill_bill_amount => 'BillAmount',
currency => 'Currency',
},
};
$ICS::Simple::errorMap = {
default => {
name => 'UNKNOWN',
description => 'Unable to process order.',
},
esystem => {
name => 'SYSTEM',
description => 'System error.',
critical => 1,
},
etimeout => {
name => 'TIMEOUT',
description => 'Communications timeout. Please wait a few moments, then try again.',
},
dduplicate => {
name => 'DUPLICATE',
description => 'Duplicate transaction refused.',
},
dinvalid => {
name => 'INVALIDDATA',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dinvaliddata => {
name => 'INVALIDDATA',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dinvalidcard => {
name => 'INVALIDCARD',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dinvalidaddress => {
name => 'INVALIDADDRESS',
description => 'Invalid address data provided. Please reenter the necessary data and try again.',
},
dmissingfield => {
name => 'MISSINGFIELD',
description => 'Required field data missing. Please reenter the necessary data and try again.',
},
drestricted => {
name => 'RESTRICTED',
description => 'Unable to process order.',
},
dcardrefused => {
name => 'CARDREFUSED',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dcall => {
name => 'CALL',
description => 'Unable to process order.',
},
dcardexpired => {
name => 'CARDEXPIRED',
description => 'The provided credit card is expired. Please use another card and try again.',
},
davsno => {
name => 'AVSFAILED',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dcv => {
name => 'CV',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
dnoauth => {
name => 'NOAUTH',
description => 'The requested transaction does not match a valid, existing authorization transaction.',
},
dscore => {
name => 'SCORE',
description => 'Score exceeds limit.',
},
dsettings => {
name => 'SETTINGS',
description => 'The provided credit card was declined. Check your billing address, credit card number, and CVV and try again or use a different card.',
},
};
$ICS::Simple::cvvAcceptedDefault = 'M,P,U,X,1';
$ICS::Simple::cvvMap = {
I => {
note => 'Card verification number failed processor\'s data validation check.',
},
M => {
note => 'Card verification number matched.',
},
N => {
note => 'Card verification number not matched.',
},
P => {
note => 'Card verification number not processed.',
},
S => {
note => 'Card verification number is on the card but was not included in the request.',
},
U => {
note => 'Card verification is not supported by the issuing bank.',
},
X => {
note => 'Card verification is not supported by the card association.',
},
1 => {
note => 'CyberSource does not support card verification for this processor or card type.',
},
2 => {
note => 'The processor returned an unrecognized value for the card verification response.',
},
3 => {
note => 'The processor did not return a card verification result code.',
},
};
$ICS::Simple::avsRejectedDefault = 'A,C,E,I,N,R,S,U,W,1,2';
$ICS::Simple::avsMap = {
A => {
note => 'Street address matches, but both 5-digit ZIP code and 9-digit ZIP code do not match.',
},
B => {
note => 'Street address matches, but postal code not verified. Returned only for non-U.S.-issued Visa cards.',
},
C => {
note => 'Street address and postal code do not match. Returned only for non U.S.-issued Visa cards.',
},
D => {
note => 'Street address and postal code both match. Returned only for non-U.S.-issued Visa cards.',
},
E => {
note => 'AVS data is invalid.',
},
G => {
note => 'Non-U.S. issuing bank does not support AVS.',
},
I => {
note => 'Address information not verified. Returned only for non-U.S.-issued Visa cards.',
},
J => {
note => 'Card member name, billing address, and postal code all match. Ship-to information verified and chargeback protection guaranteed through the Fraud Protection Program. This code is returned only if you are signed up to use AAV+ with the American Express Phoenix processor.',
},
K => {
note => 'Card member\'s name matches. Both billing address and billing postal code do not match. This code is returned only if you are signed up to use Enhanced AVS or AAV+ with the American Express Phoenix processor.',
},
L => {
note => 'Card member\'s name matches. Billing postal code matches, but billing address does not match. This code is returned only if you are signed up to use Enhanced AVS or AAV+ with the American Express Phoenix processor.',
},
M => {
note => 'Street address and postal code both match. Returned only for non-U.S.-issued Visa cards.',
},
N => {
note => 'Street address and postal code do not match. Returned only for U.S.-issued Visa cards.',
},
O => {
note => 'Card member name matches. Billing address matches, but billing postal code does not match. This code is returned only if you are signed up to use Enhanced AVS or AAV+ with the American Express Phoenix processor.',
},
P => {
note => 'Postal code matches, but street address not verified. Returned only for non-U.S.-issued Visa cards.',
},
Q => {
note => 'Card member name, billing address, and postal code all match. Ship-to information verified but chargeback protection not guaranteed (Standard program). This code is returned only if you are signed up to use AAV+ with the American Express Phoenix processor.',
},
R => {
note => 'System unavailable.',
},
S => {
note => 'U.S. issuing bank does not support AVS.',
},
U => {
note => 'Address information unavailable. Returned if non-U.S. AVS is not available or if the AVS in a U.S. bank is not functioning properly.',
},
V => {
note => 'Card member name matches. Both billing address and billing postal code match. This code is returned only if you are signed up to use Enhanced AVS or AAV+ with the American Express Phoenix processor.',
},
W => {
note => 'Street address does not match, but 9-digit ZIP code matches.',
},
X => {
note => 'Exact match. Street address and 9-digit ZIP code both match.',
},
Y => {
note => 'Street address and 5-digit ZIP code both match.',
},
Z => {
note => 'Street address does not match, but 5-digit ZIP code matches.',
},
1 => {
note => 'CyberSource AVS code. AVS is not supported for this processor or card type.',
},
2 => {
note => 'CyberSource AVS code. The processor returned an unrecognized value for the AVS response.',
},
};
$ICS::Simple::vitalErrorMap = {
'01' => {
action => 'decline',
note => 'Refer to Issuer',
description => 'Please call your card issuer.',
},
'02' => {
action => 'decline',
note => 'Refer to Issuer - Special Condition',
description => 'Please call your card issuer.',
},
'03' => {
action => 'error',
note => 'Invalid Merchant ID',
description => 'Please call customer service to complete order.',
},
'04' => {
action => 'decline',
note => 'Pick up card',
description => 'Authorization has been declined.',
},
'05' => {
action => 'decline',
note => 'Do Not Honor',
description => 'Authorization has been declined.',
},
'06' => {
action => 'error',
note => 'General Error',
description => 'Please call customer service to complete order.',
},
'07' => {
action => 'decline',
note => 'Pick up card - Special Condition',
description => 'Authorization has been declined.',
},
'12' => {
action => 'error',
note => 'Unknown system error',
description => 'Please call customer service to complete order.',
},
'13' => {
action => 'error',
note => 'Invalid Amount',
description => 'Invalid amount.',
},
'14' => {
action => 'error',
note => 'Invalid Card Number',
description => 'Invalid card number.',
},
'15' => {
action => 'error',
note => 'No such issuer',
description => 'Invalid card number.',
},
'19' => {
action => 'decline',
note => 'Unknown Decline Error',
description => 'Authorization has been declined.',
},
'28' => {
action => 'error',
note => 'File is temporarily unavailable',
description => 'Please call customer service to complete order.',
},
'39' => {
action => 'error',
note => 'Invalid Card Number',
description => 'Invalid card number.',
},
'41' => {
action => 'decline',
note => 'Pick up card - Lost',
description => 'Authorization has been declined.',
},
'43' => {
action => 'decline',
note => 'Pick up card - Stolen',
description => 'Authorization has been declined.',
},
'51' => {
action => 'decline',
note => 'Insufficient Funds',
description => 'Authorization has been declined.',
},
'52' => {
action => 'error',
note => 'Unknown Card Number Error',
description => 'Invalid Card Number',
},
'53' => {
action => 'error',
note => 'Unknown Card Number Error',
description => 'Invalid Card Number',
},
'54' => {
action => 'expired',
note => 'Expired Card',
description => 'Expired card.',
},
'55' => {
action => 'error',
note => 'Incorrect PIN',
description => 'Incorrect Card/PIN combination.', # is it safe to let them know the PIN is wrong?
},
'57' => {
action => 'decline',
note => 'Transaction Disallowed - Card',
description => 'Authorization has been declined.',
},
'58' => {
action => 'decline',
note => 'Transaction Disallowed - Term',
description => 'Authorization has been declined.',
},
'61' => {
action => 'decline',
note => 'Withdrawal Limit Exceeded',
description => 'Withdrawal limit exceeded.',
},
'62' => {
action => 'decline',
note => 'Invalid Service Code, Restricted',
description => 'Authorization has been declined.',
},
'63' => {
action => 'decline',
note => 'Unknown Decline Error',
description => 'Authorization has been declined.',
},
'65' => {
action => 'decline',
note => 'Activity Limit Exceeded',
description => 'Authorization has been declined.',
},
'75' => {
action => 'decline',
note => 'PIN Attempts Exceeded',
description => 'Authorization has been declined.',
},
'78' => {
action => 'error',
note => 'Unknown Invalid Card Number Error',
description => 'Invalid Card Number',
},
'79' => {
action => 'call',
note => 'Unknown Error',
description => 'Please call customer service to complete order.',
},
'80' => {
action => 'error',
note => 'Invalid Expiration Date',
description => 'Invalid expiration date.',
},
'82' => {
action => 'decline',
note => 'Cashback Limit Exceeded',
description => 'Cashback limit exceeded.',
},
'83' => {
action => 'decline',
note => 'Unknown PIN Verification Error',
description => 'Can not verify PIN.',
},
'86' => {
action => 'decline',
note => 'Unknown PIN Verification Error',
description => 'Can not verify PIN.',
},
'91' => {
action => 'unavailable',
note => 'Issuer or switch is unavailable',
description => 'Please call customer service to complete order.',
},
'92' => {
action => 'call',
note => 'Unknown Error',
description => 'Please call customer service to complete order.',
},
'93' => {
action => 'decline',
note => 'Violation, Cannot Complete',
description => 'Authorization has been declined.',
},
'96' => {
action => 'error',
note => 'System Malfunction',
description => 'Please call customer service to complete order.',
},
'EA' => {
action => 'error',
note => 'Verification Error',
description => 'Please call customer service to complete order.',
},
'EB' => {
action => 'call',
note => 'Unknown Error',
description => 'Please call customer service to complete order.',
},
'EC' => {
action => 'error',
note => 'Verification Error',
description => 'Please call customer service to complete order.',
},
'N3' => {
action => 'error',
note => 'Cashback Service Not Available',
description => 'Cashback service is not available.',
},
'N4' => {
action => 'decline',
note => 'Issuer Withdrawal Limit Exceeded',
description => 'Amount exceeds issuer withdrawal limit.',
},
'N7' => {
action => 'error',
note => 'Invalid CVV2 Data Supplied',
description => 'Invalid card security code.', # should we let the enduser know this?
},
};
1; # End of ICS::Simple