File Coverage

blib/lib/Business/OnlinePayment/OpenECHO.pm
Criterion Covered Total %
statement 58 103 56.3
branch 8 38 21.0
condition 1 9 11.1
subroutine 12 13 92.3
pod 1 7 14.2
total 80 170 47.0


line stmt bran cond sub pod time code
1             ##############################################################################
2             # Business::OnlinePayment::OpenECHO
3             #
4             # Credit card transactions via SSL to
5             # Electronic Clearing House (ECHO) Systems
6             #
7             # Refer to ECHO's documentation for more info
8             # http://www.openecho.com/echo_gateway_guide.html
9             #
10             # AUTHOR
11             # Michael Lehmkuhl
12             #
13             # SPECIAL THANKS
14             # Jim Darden
15             # Dan Browning
16             #
17             # BUSINESS::ONLINEPAYMENT IMPLEMENTATION
18             # Ivan Kohler
19             #
20             # VERSION HISTORY
21             # + v1.2 08/17/2002 Corrected problem with certain string comparisons.
22             # + v1.3 08/23/2002 Converted Interchange GlobalSub to Vend::Payment module.
23             # + v1.3.1 11/12/2002 Updated the OpenECHO_example.perl test script.
24             # + v1.4 11/18/2002 Corrected a problem with Submit method when using LWP.
25             # + v1.5 03/29/2003 Updated for additional status and avs_result codes.
26             # + v1.6 08/26/2003 Cleaned up code (salim qadeer sqadeer@echo-inc.com)
27             # + v1.6.2 09/23/2003 Added DH transaction type (salim qadeer sqadeer@echo-inc.com)
28             # --------
29             # + v0.1 08/26/2004 Business::OnlinePayment implementation
30             # + v0.2 09/13/2004
31             # + v0.3 03/25/2006
32             #
33             # Copyright (C) 2002 Electric Pulp.
34             # Copyright (C) 2004-2006 Ivan Kohler
35             #
36             # This program is free software; you can redistribute it and/or modify
37             # it under the terms of the GNU General Public License as published by
38             # the Free Software Foundation; either version 2 of the License, or
39             # (at your option) any later version.
40             #
41             # This program is distributed in the hope that it will be useful,
42             # but WITHOUT ANY WARRANTY; without even the implied warranty of
43             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44             # GNU General Public License for more details.
45             #
46             # You should have received a copy of the GNU General Public
47             # License along with this program; if not, write to the Free
48             # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
49             # MA 02111-1307 USA.
50             #
51             ################################################################################
52            
53             package Business::OnlinePayment::OpenECHO;
54            
55 3     3   58871 use strict;
  3         6  
  3         107  
56 3     3   15 use Carp;
  3         6  
  3         205  
57 3     3   1013 use Business::OnlinePayment 3;
  3         3766  
  3         68  
58 3     3   2620 use Business::OnlinePayment::HTTPS;
  3         33878  
  3         105  
59 3     3   26 use vars qw($VERSION @ISA $DEBUG);
  3         5  
  3         4882  
60            
61             @ISA = qw(Business::OnlinePayment::HTTPS);
62             $VERSION = '0.03';
63             $DEBUG = 0;
64            
65             sub set_defaults {
66 2     2 0 97 my $self = shift;
67            
68 2         61 $self->server('wwws.echo-inc.com');
69 2         79 $self->port('443');
70 2         64 $self->path('/scripts/INR200.EXE');
71            
72 2         26 $self->build_subs(qw(
73             order_number avs_code
74             ));
75             # order_type
76             # md5 cvv2_response cavv_response
77            
78             }
79            
80             #map_fileds originally from AuthorizeNet.pm
81            
82             sub map_fields {
83 2     2 0 2 my($self) = @_;
84            
85             ##AD (Address Verification) - 0.18 cents
86             #AS (Authorization) - 0.18 cents
87             #AV (Authorization with Address Verification) - 0.18 cents
88             #CR (Credit) - 0.18 cents + 0.12 cents (discount fee returned to merchant)
89             #DS (Deposit) - 0.18 cents + 0.12 cents
90             #ES (Authorization and Deposit) - 0.18 cents + 0.12 cents + an extra 0.17%
91             #EV (Authorization and Deposit with Address Verification) - 0.18 cents + 0.12 cents
92             ##CK (System check) - 0.18 cents
93             #DV (Electronic Check Verification) - check your contract
94             #DD (Electronic Check Debit with Verification) - check your contract
95             #DH (Electronic Check Debit ACH Only) - check your contract
96             #DC (Electronic Check Credit) - check your contract
97            
98 2         8 my %content = $self->content();
99            
100 2 50       43 if ( lc($content{'action'}) eq 'void' ) {
101 0         0 $self->is_success(0);
102 0         0 $self->error_message( 'OpenECHO gateway does not support voids; '.
103             'try action => "Credit" '
104             );
105 0         0 return;
106             }
107            
108 2         54 my $avs = $self->require_avs;
109 2 50 33     25 $avs = 1 unless defined($avs) && length($avs); #default AVS on unless explicitly turned off
110            
111 2         3 my %map;
112 2 50       28 if (
    0          
113             $content{'type'} =~ /^(cc|visa|mastercard|american express|discover)$/i
114             ) {
115 2 50       6 if ( $avs ) {
116 2         10 %map = ( 'normal authorization' => 'EV',
117             'authorization only' => 'AV',
118             'credit' => 'CR',
119             'post authorization' => 'DS',
120             #'void' => 'VOID',
121             );
122             } else {
123 0         0 %map = ( 'normal authorization' => 'ES',
124             'authorization only' => 'AS',
125             'credit' => 'CR',
126             'post authorization' => 'DS',
127             #'void' => 'VOID',
128             );
129             }
130             } elsif ( $content{'type'} =~ /^e?check$/i ) {
131 0         0 %map = ( 'normal authorization' => 'DD',
132             'authorization only' => 'DV',
133             'credit' => 'DC',
134             'post authorization' => 'DH',
135             #'void' => 'VOID',
136             );
137             } else {
138 0         0 croak 'Unknown type: '. $content{'type'};
139             }
140            
141 2 50       9 $content{'type'} = $map{lc($content{'action'})}
142             or croak 'Unknown action: '. $content{'action'};
143            
144 2         46 $self->transaction_type($content{'type'});
145            
146             # stuff it back into %content
147 2         20 $self->content(%content);
148            
149             }
150            
151             sub submit {
152 0     0 1   my($self) = @_;
153            
154 0           $self->map_fields();
155 0           $self->remap_fields(
156             # => 'order_type',
157             type => 'transaction_type',
158             #action =>
159             login => 'merchant_echo_id',
160             password => 'merchant_pin',
161             # => 'isp_echo_id',
162             # => 'isp_pin',
163             #transaction_key =>
164             authorization => 'authorization', # auth_code => 'authorization',
165             customer_ip => 'billing_ip_address',
166             # 'billing_prefix',
167             name => 'billing_name',
168             first_name => 'billing_first_name',
169             last_name => 'billing_last_name',
170             company => 'billing_company_name',
171             address => 'billing_address_1',
172             # => 'billing_address_2',
173             city => 'billing_city',
174             state => 'billing_state',
175             zip => 'billing_zip',
176             country => 'billing_country',
177             phone => 'billing_phone',
178             fax => 'billing_fax',
179             email => 'billing_email',
180             card_number => 'cc_number',
181             # => 'ccexp_month',
182             # => 'ccexp_year',
183             # => 'counter',
184             # => 'debug',
185            
186             #XXX# => 'ec_*',
187            
188             'amount' => 'grand_total',
189             # => 'merchant_email',
190             #invoice_number =>
191             customer_id => 'merchant_trace_nbr',
192             # => 'original_amount',
193             # => 'original_trandate_mm',
194             # => 'original_trandate_dd',
195             # => 'original_trandate_yyyy',
196             # => 'original_reference',
197             order_number => 'order_number',
198             # => 'shipping_flag',
199            
200             #description =>
201             #currency =>
202            
203             #ship_last_name =>
204             #ship_first_name =>
205             #ship_company =>
206             #ship_address =>
207             #ship_city =>
208             #ship_state =>
209             #ship_zip =>
210             #ship_country =>
211            
212             #expiration =>
213             cvv2 => 'cnp_security',
214            
215             #check_type =>
216             #account_name => 'ec_last_name' & 'ec_first_name',
217             account_number => 'ec_account',
218             #account_type =>
219             bank_name => 'ec_bank_name',
220             routing_code => 'ec_rt',
221             #customer_org =>
222             #customer_ssn =>
223             license_num => 'ec_id_number',
224             license_state => 'ec_id_state',
225             #license_dob =>
226             #get from new() args instead# payee => 'ec_payee',
227             check_number => 'ec_serial_number',
228            
229             #recurring_billing => 'cnp_recurring',
230             );
231            
232             #XXX hosted order_type?
233 0           $self->{_content}{order_type} = 'S';
234            
235             #XXX counter field shouldn't be just a random integer (but it does need a
236             #default this way i guess...
237 0           $self->{_content}{counter} = int(rand(2**31));
238            
239 0 0         if ( $self->transaction_type =~ /^[EA][VS]$/ ) {
240             #ccexp_month & ccexp_year
241 0 0         $self->{_content}{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
242             or croak "unparsable expiration ". $self->{_content}{expiration};
243 0           my( $month, $year ) = ( $1, $2 );
244 0 0         $month = '0'. $month if $month =~ /^\d$/;
245 0           $self->{_content}{ccexp_month} = $month;
246 0           $self->{_content}{ccexp_year} = $year;
247             }
248            
249 0 0         if ( $self->transaction_type =~ /^D[DVCH]$/ ) { #echeck
250            
251             #check number kludge... "periodic bill payments" don't have check #s!
252             #$self->{_content}{ec_serial_number} = 'RECURRIN'
253 0 0 0       $self->{_content}{ec_serial_number} = '00000000'
254             if ! length($self->{_content}{ec_serial_number})
255             && $self->{_content}{ec_payment_type} =~ /^(PPD)?$/i;
256            
257 0 0         ( $self->{_content}{ec_payee} = $self->payee )
258             or croak "'payee' option required when instantiating new ".
259             "Business::OnlinePayment::OpenECHO object\n";
260             }
261            
262 0 0 0       $self->{_content}{cnp_recurring} = 'Y'
263             if exists($self->{_content}{recurring_billing})
264             && $self->{_content}{recurring_billing} =~ /^y/i;
265            
266             #XXX echeck use customer_org and account_type to generate ec_account_type
267            
268             #XXX set required fields
269             # https://wwws.echo-inc.com/ISPGuide-Fields2.asp
270 0           $self->required_fields();
271            
272 0           my( $page, $response, %reply_headers) =
273             $self->https_post( $self->get_fields( $self->fields ) );
274            
275 0 0         warn "raw echo response: $page" if $DEBUG;
276            
277             #XXX check $response and die if not 200?
278            
279 0           my $echotype1 = $self->GetEchoReturn($page, 1);
280 0           my $echotype2 = $self->GetEchoReturn($page, 2);
281 0           my $echotype3 = $self->GetEchoReturn($page, 3);
282 0           my $openecho = $self->GetEchoReturn($page, 'OPEN');
283            
284             # server_response
285             # avs_code
286             # order_number
287             # is_success
288             # result_code
289             # authorization
290             #md5 cvv2_response cavv_response ...?
291            
292             # Get all the metadata.
293 0           $self->server_response($page);
294 0           $self->authorization( $self->GetEchoProp($echotype3, 'auth_code') );
295 0           $self->order_number( $self->GetEchoProp($echotype3, 'order_number') );
296            
297             #XXX ???
298             #$self->reference( $this->GetEchoProp($echotype3, "echo_reference");
299            
300 0           $self->result_code( $self->GetEchoProp($echotype3, 'status') );
301 0           $self->avs_code( $self->GetEchoProp($echotype3, 'avs_result') );
302            
303             #XXX ???
304             #$self->security_result( $self->GetEchoProp($echotype3, 'security_result');
305             #$self->mac( $self->GetEchoProp($echotype3, 'mac') );
306             #$self->decline_code( $self->GetEchoProp($echotype3, 'decline_code') );
307            
308 0 0         if ($self->result_code =~ /^[GR]$/ ) { #success
309            
310             #XXX special case for AVS-only transactions we don't handle yet
311             #if ($self->transaction_type eq "AD") {
312             # if ($self->avs_code =~ /^[XYDM]$/ ) {
313             # $self->is_success(1);
314             # } else {
315             # $self->is_success(0);
316             # }
317             #} else {
318 0           $self->is_success(1);
319             #}
320            
321             } else {
322 0           $self->is_success(0);
323            
324 0           my $decline_code = $self->GetEchoProp($echotype3, 'decline_code');
325 0           my $error_message = $self->error($decline_code);
326 0 0         if ( $decline_code =~ /^(00)?30$/ ) {
327 0           $echotype2 =~ s/
/\n/ig;
328 0           $echotype2 =~ s'''ig;
329 0           $error_message .= ": $echotype2";
330             }
331 0           $self->error_message( $error_message );
332            
333             }
334            
335 0 0         $self->is_success(0) if $page eq '';
336            
337             }
338            
339            
340             sub fields {
341 2     2 0 3 my $self = shift;
342            
343 2         34 my @fields = qw(
344             order_type
345             transaction_type
346             merchant_echo_id
347             merchant_pin
348             isp_echo_id
349             isp_pin
350             authorization
351             billing_ip_address
352             billing_prefix
353             billing_name
354             billing_first_name
355             billing_last_name
356             billing_company_name
357             billing_address1
358             billing_address2
359             billing_city
360             billing_state
361             billing_zip
362             billing_country
363             billing_phone
364             billing_fax
365             billing_email
366             cc_number
367             ccexp_month
368             ccexp_year
369             counter
370             debug
371             );
372            
373 2 50       45 if ($self->transaction_type =~ /^D[DCVH]$/) {
374 0         0 push @fields, qw(
375             ec_account
376             ec_account_type
377             ec_payment_type
378             ec_address1
379             ec_address2
380             ec_bank_name
381             ec_business_acct
382             ec_city
383             ec_email
384             ec_first_name
385             ec_id_country
386             ec_id_exp_mm
387             ec_id_exp_dd
388             ec_id_exp_yy
389             ec_id_number
390             ec_id_state
391             ec_id_type
392             ec_last_name
393             ec_merchant_ref
394             ec_nbds_code
395             ec_other_name
396             ec_payee
397             ec_rt
398             ec_serial_number
399             ec_state
400             ec_transaction_dt
401             ec_zip
402             );
403             }
404            
405 2         25 push @fields, qw(
406             grand_total
407             merchant_email
408             merchant_trace_nbr
409             original_amount
410             original_trandate_mm
411             original_trandate_dd
412             original_trandate_yyyy
413             original_reference
414             order_number
415             shipping_flag
416             shipping_prefix
417             shipping_name
418             shipping_address1
419             shipping_address2
420             shipping_city
421             shipping_state
422             shipping_zip
423             shipping_comments
424             shipping_country
425             shipping_phone
426             shipping_fax
427             shipper
428             shipper_tracking_nbr
429             track1
430             track2
431             cnp_security
432             cnp_recurring
433             );
434            
435 2         27 return @fields;
436             }
437            
438             sub GetEchoProp {
439 10     10 0 20 my( $self, $raw, $prop ) = @_;
440 10         26 local $^W=0;
441            
442 10         14 my $data;
443 10         141 ($data) = $raw =~ m"<$prop>(.*?)"gsi;
444 10         19 $data =~ s/<.*?>/ /gs;
445 10         15 chomp $data;
446 10         281 return $data;
447             }
448            
449             # Get's a given Echo return type and strips all HTML style tags from it.
450             # It also strips any new line characters from the returned string.
451             #
452             # This function based on Ben Reser's Echo::Process
453             # module.
454             sub GetEchoReturn {
455 8     8 0 17 my( $self, $page, $type ) = @_;
456 8         34 local $^W=0;
457            
458 8         17 my $data;
459 8 100       32 if ($type eq 'OPEN') {
460 2         13 ($data) = $page =~ m"(.*?)"gsi;
461             }
462             else {
463 6         144 ($data) = $page =~ m"(.*?)"gsi;
464             }
465             # $data =~ s"<.*?>" "g;
466            
467             #unless (length($data)) {
468             # warn "$self $page $type";
469             #}
470            
471 8         18 chomp $data;
472 8         69 return $data;
473             }
474            
475 3     3   20 use vars qw(%error);
  3         4  
  3         3422  
476             %error = (
477             "01" => [ "Refer to card issuer", "The merchant must call the issuer before the transaction can be approved." ],
478             "02" => [ "Refer to card issuer, special condition", "The merchant must call the issuer before the transaction can be approved." ],
479             "03" => [ "Invalid merchant number", "The merchant ID is not valid." ],
480             "04" => [ "Pick-up card. Capture for reward", "The card is listed on the Warning Bulletin. Merchant may receive reward money by capturing the card." ],
481             "05" => [ "Do not honor. The transaction was declined by the issuer without definition or reason", "The transaction was declined without explanation by the card issuer." ],
482             "06" => [ "Error", "The card issuer returned an error without further explanation." ],
483             "07" => [ "Pick-up card, special condition", "The card is listed on the Warning Bulletin. Merchant may receive reward money by capturing the card." ],
484             "08" => [ "Honor with identification", "Honor with identification." ],
485             "09" => [ "Request in progress", "Request in progress." ],
486             "10" => [ "Approved for partial amount", "Approved for partial amount." ],
487             "11" => [ "Approved, VIP", "Approved, VIP program." ],
488             "12" => [ "Invalid transaction", "The requested transaction is not supported or is not valid for the card number presented." ],
489             "13" => [ "Invalid amount", "The amount exceeds the limits established by the issuer for this type of transaction." ],
490             "14" => [ "Invalid card #", "The issuer indicates that this card is not valid." ],
491             "15" => [ "No such issuer", "The card issuer number is not valid." ],
492             "16" => [ "Approved, update track 3", "Approved, update track 3." ],
493             "17" => [ "Customer cancellation", "Customer cancellation." ],
494             "18" => [ "Customer dispute", "Customer dispute." ],
495             "19" => [ "Re enter transaction", "Customer should resubmit transaction." ],
496             "20" => [ "Invalid response", "Invalid response." ],
497             "21" => [ "No action taken", "No action taken. The issuer declined with no other explanation." ],
498             "22" => [ "Suspected malfunction", "Suspected malfunction." ],
499             "23" => [ "Unacceptable transaction fee", "Unacceptable transaction fee." ],
500             "24" => [ "File update not supported", "File update not supported." ],
501             "25" => [ "Unable to locate record", "Unable to locate record." ],
502             "26" => [ "Duplicate record", "Duplicate record." ],
503             "27" => [ "File update edit error", "File update edit error." ],
504             "28" => [ "File update file locked", "File update file locked." ],
505             "30" => [ "Format error, call ECHO", "The host reported that the transaction was not formatted properly." ],
506             "31" => [ "Bank not supported", "Bank not supported by switch." ],
507             "32" => [ "Completed partially", "Completed partially." ],
508             "33" => [ "Expired card, pick-up", "The card is expired. Merchant may receive reward money by capturing the card." ],
509             "34" => [ "Issuer suspects fraud, pick-up card", "The card issuer suspects fraud. Merchant may receive reward money by capturing the card." ],
510             "35" => [ "Contact acquirer, pick-up", "Contact card issuer. Merchant may receive reward money by capturing the card." ],
511             "36" => [ "Restricted card, pick-up", "The card is restricted by the issuer. Merchant may receive reward money by capturing the card." ],
512             "37" => [ "Call ECHO security, pick-up", "Contact ECHO security. Merchant may receive reward money by capturing the card." ],
513             "38" => [ "PIN tries exceeded, pick-up", "PIN attempts exceed issuer limits. Merchant may receive reward money by capturing the card." ],
514             "39" => [ "No credit account", "No credit account." ],
515             "40" => [ "Function not supported", "Requested function not supported." ],
516             "41" => [ "Lost Card, capture for reward", "The card has been reported lost." ],
517             "42" => [ "No universal account", "No universal account." ],
518             "43" => [ "Stolen Card, capture for reward", "The card has been reported stolen." ],
519             "44" => [ "No investment account", "No investment account." ],
520             "51" => [ "Not sufficient funds", "The credit limit for this account has been exceeded." ],
521             "54" => [ "Expired card", "The card is expired." ],
522             "55" => [ "Incorrect PIN", "The cardholder PIN is incorrect." ],
523             "56" => [ "No card record", "No card record." ],
524             "57" => [ "Transaction not permitted to cardholder", "The card is not allowed the type of transaction requested." ],
525             "58" => [ "Transaction not permitted on terminal", "The Merchant is not allowed this type of transaction." ],
526             "59" => [ "Suspected fraud", "Suspected fraud." ],
527             "60" => [ "Contact ECHO", "Contact ECHO." ],
528             "61" => [ "Exceeds withdrawal limit", "The amount exceeds the allowed daily maximum." ],
529             "62" => [ "Restricted card", "The card has been restricted." ],
530             "63" => [ "Security violation.", "The card has been restricted." ],
531             "64" => [ "Original amount incorrect", "Original amount incorrect." ],
532             "65" => [ "Exceeds withdrawal frequency", "The allowable number of daily transactions has been exceeded." ],
533             "66" => [ "Call acquirer security, call ECHO", "Call acquirer security, call ECHO." ],
534             "68" => [ "Response received too late", "Response received too late." ],
535             "75" => [ "PIN tries exceeded", "The allowed number of PIN retries has been exceeded." ],
536             "76" => [ "Invalid \"to\" account", "The debit account does not exist." ],
537             "77" => [ "Invalid \"from\" account", "The credit account does not exist." ],
538             "78" => [ "Invalid account specified (general)", "The associated card number account is invalid or does not exist." ],
539             "79" => [ "Already reversed", "Already reversed." ],
540             "84" => [ "Invalid authorization life cycle", "The authorization life cycle is invalid." ],
541             "86" => [ "Cannot verify PIN", "Cannot verify PIN." ],
542             "87" => [ "Network Unavailable", "Network Unavailable." ],
543             "89" => [ "Ineligible to receive financial position information", "Ineligible to receive financial position information." ],
544             "90" => [ "Cut-off in progress", "Cut-off in progress." ],
545             "91" => [ "Issuer or switch inoperative", "The bank is not available to authorize this transaction." ],
546             "92" => [ "Routing error", "The transaction cannot be routed to the authorizing agency." ],
547             "93" => [ "Violation of law", "Violation of law." ],
548             "94" => [ "Duplicate transaction", "Duplicate transaction." ],
549             "95" => [ "Reconcile error", "Reconcile error." ],
550             "96" => [ "System malfunction", "A system error has occurred." ],
551             "98" => [ "Exceeds cash limit", "Exceeds cash limit." ],
552             "1000" => [ "Unrecoverable error.", "An unrecoverable error has occurred in the ECHONLINE processing." ],
553             "1001" => [ "Account closed", "The merchant account has been closed." ],
554             "1002" => [ "System closed", "Services for this system are not available. (Not used by ECHONLINE)" ],
555             "1003" => [ "E-Mail Down", "The e-mail function is not available. (Not used by ECHONLINE)" ],
556             "1012" => [ "Invalid trans code", "The host computer received an invalid transaction code." ],
557             "1013" => [ "Invalid term id", "The ECHO-ID is invalid." ],
558             "1015" => [ "Invalid card number", "The credit card number that was sent to the host computer was invalid" ],
559             "1016" => [ "Invalid expiry date", "The card has expired or the expiration date was invalid." ],
560             "1017" => [ "Invalid amount", "The dollar amount was less than 1.00 or greater than the maximum allowed for this card." ],
561             "1019" => [ "Invalid state", "The state code was invalid. (Not used by ECHONLINE)" ],
562             "1021" => [ "Invalid service", "The merchant or card holder is not allowed to perform that kind of transaction" ],
563             "1024" => [ "Invalid auth code", "The authorization number presented with this transaction is incorrect. (deposit transactions only)" ],
564             "1025" => [ "Invalid reference number", "The reference number presented with this transaction is incorrect or is not numeric." ],
565             "1029" => [ "Invalid contract number", "The contract number presented with this transaction is incorrect or is not numeric. (Not used by ECHONLINE)" ],
566             "1030" => [ "Invalid inventory data", "The inventory data presented with this transaction is not ASCII \"printable\". (Not used by ECHONLINE)" ],
567             "1508" => [ " ", "Invalid or missing order_type." ],
568             "1509" => [ " ", "The merchant is not approved to submit this order_type." ],
569             "1510" => [ " ", "The merchant is not approved to submit this transaction_type." ],
570             #"1511" => [ " ", "Duplicate transaction attempt (see counterin Part I of this Specification)." ],
571             "1511" => [ " ", "Duplicate transaction attempt (set counter field?)." ],
572             "1599" => [ " ", "An system error occurred while validating the transaction input." ],
573             "1801" => [ "Return Code \"A\"", "Address matches; ZIP does not match." ],
574             "1802" => [ "Return Code \"W\"", "9-digit ZIP matches; Address does not match." ],
575             "1803" => [ "Return Code \"Z\"", "5-digit ZIP matches; Address does not match." ],
576             "1804" => [ "Return Codes \"U\"", "Issuer unavailable; cannot verify." ],
577             "1805" => [ "Return Code \"R\"", "Retry; system is currently unable to process." ],
578             "1806" => [ "Return Code \"S\" or \"G\"", "Issuer does not support AVS." ],
579             "1807" => [ "Return Code \"N\"", "Nothing matches." ],
580             "1808" => [ "Return Code \"E\"", "Invalid AVS only response." ],
581             "1809" => [ "Return Code \"B\"", "Street address match. Postal code not verified because of incompatible formats." ],
582             "1810" => [ "Return Code \"C\"", "Street address and Postal code not verified because of incompatible formats." ],
583             "1811" => [ "Return Code \"D\"", "Street address match and Postal code match." ],
584             "1812" => [ "Return Code \"I\"", "Address information not verified for international transaction." ],
585             "1813" => [ "Return Code \"M\"", "Street address match and Postal code match." ],
586             "1814" => [ "Return Code \"P\"", "Postal code match. Street address not verified because of incompatible formats." ],
587             "1897" => [ "invalid response", "The host returned an invalid response." ],
588             "1898" => [ "disconnect", "The host unexpectedly disconnected." ],
589             "1899" => [ "timeout", "Timeout waiting for host response." ],
590             "2071" => [ "Call VISA", "An authorization number from the VISA Voice Center is required to approve this transaction." ],
591             "2072" => [ "Call Master Card", "An authorization number from the Master Card Voice Center is required to approve this transaction." ],
592             "2073" => [ "Call Carte Blanche", "An authorization number from the Carte Blanche Voice Center is required to approve this transaction." ],
593             "2074" => [ "Call Diners Club", "An authorization number from the Diners' Club Voice Center is required to approve this transaction." ],
594             "2075" => [ "Call AMEX", "An authorization number from the American Express Voice Center is required to approve this transaction." ],
595             "2076" => [ "Call Discover", "An authorization number from the Discover Voice Center is required to approve this transaction." ],
596             "2078" => [ "Call ECHO", "The merchant must call ECHOCustomer Support for approval.or because there is a problem with the merchant's account." ],
597             "2079" => [ "Call XpresscheX", "The merchant must call ECHOCustomer Support for approval.or because there is a problem with the merchant's account." ],
598             "3001" => [ "No ACK on Resp", "The host did not receive an ACK from the terminal after sending the transaction response." ],
599             "3002" => [ "POS NAK'd 3 Times", "The host disconnected after the terminal replied 3 times to the host response with a NAK." ],
600             "3003" => [ "Drop on Wait", "The line dropped before the host could send a response to the terminal." ],
601             "3005" => [ "Drop on Resp", "The line dropped while the host was sending the response to the terminal." ],
602             "3007" => [ "Drop Before EOT", "The host received an ACK from the terminal but the line dropped before the host could send the EOT." ],
603             "3011" => [ "No Resp to ENQ", "The line was up and carrier detected, but the terminal did not respond to the ENQ." ],
604             "3012" => [ "Drop on Input", "The line disconnected while the host was receiving data from the terminal." ],
605             "3013" => [ "FEP NAK'd 3 Times", "The host disconnected after receiving 3 transmissions with incorrect LRC from the terminal." ],
606             "3014" => [ "No Resp to ENQ", "The line disconnected during input data wait in Multi-Trans Mode." ],
607             "3015" => [ "Drop on Input", "The host encountered a full queue and discarded the input data." ],
608             );
609             for ( 9000..9999 ) {
610             $error{$_} = [ "Host Error", "The host encountered an internal error and was not able to process the transaction." ];
611             }
612             sub error {
613 2     2 0 6 my( $self, $num ) = @_;
614 2         6 $num =~ s/^00(\d\d)$/$1/;
615 2         16 return $num. ': '. $error{$num}[0]. ': '. $error{$num}[1];
616             }
617            
618             1;
619            
620             __END__