##### 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<< >> =head1 BUGS The documentation needs to be finished. Or started. Sorry about that. Please report any bugs or feature requests to C, or through the web interface at L. 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 =item * CPAN Ratings L =item * RT: CPAN's request tracker L =item * Search CPAN L =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