1 package Business::OnlinePayment::TransFirsteLink;
4 use vars qw($VERSION $DEBUG %error_messages);
5 use Carp qw(carp croak);
8 use base qw(Business::OnlinePayment::HTTPS);
11 $VERSION = eval $VERSION;
16 '001' => 'Call Issuer',
17 '002' => 'Referral special',
18 '003' => 'Invalid merchant number',
21 '006' => 'General error',
22 '007' => 'Pick up special',
23 '008' => 'Honor with ID',
24 '009' => 'General Decline',
25 '010' => 'Network Error',
27 '012' => 'Invalid transaction type',
28 '013' => 'Invalid amount field',
29 '014' => 'Invalid card number',
30 '015' => 'Invalid issuer',
31 '016' => 'General Decline',
32 '017' => 'General Decline',
33 '018' => 'General Decline',
35 '020' => 'General Decline',
36 '021' => 'No action taken',
37 '022' => 'General Decline',
38 '023' => 'General Decline',
39 '024' => 'General Decline',
40 '025' => 'Acct num miss',
41 '026' => 'General Decline',
42 '027' => 'General Decline',
43 '028' => 'File unavailable',
44 '029' => 'General Decline',
45 '030' => 'Format Error - Decline',
46 '031' => 'General Decline',
47 '032' => 'General Decline',
48 '033' => 'General Decline',
49 '034' => 'General Decline',
50 '036' => 'General Decline',
51 '037' => 'General Decline',
52 '038' => 'General Decline',
53 '039' => 'No card acct',
54 '040' => 'General Decline',
56 '042' => 'General Decline',
57 '043' => 'Stolen card',
58 '044' => 'General Decline',
59 '045' => 'General Decline',
60 '046' => 'General Decline',
61 '048' => 'General Decline',
62 '049' => 'General Decline',
63 '050' => 'General Decline',
64 '051' => 'Over limit',
65 '052' => 'No checking acct',
66 '053' => 'No saving acct',
67 '054' => 'Expired card',
68 '055' => 'Invalid pin',
69 '056' => 'General Decline',
70 '057' => 'TXN not allowed',
71 '058' => 'TXN not allowed term',
72 '059' => 'TXN not allowed - Merchant',
73 '060' => 'General Decline',
74 '061' => 'Over cash limit',
75 '062' => 'Restricted card',
76 '063' => 'Security violate',
77 '064' => 'General Decline',
78 '065' => 'Excessive authorizations',
79 '066' => 'General Decline',
80 '067' => 'General Decline',
81 '069' => 'General Decline',
82 '070' => 'General Decline',
83 '071' => 'General Decline',
84 '072' => 'General Decline',
85 '073' => 'General Decline',
86 '074' => 'General Decline',
87 '075' => 'Excessive pin entry tries',
88 '076' => 'Unable locate previous msg (ref# not found)',
89 '077' => 'Mismatched info',
90 '078' => 'No account',
91 '079' => 'Already reversed',
92 '080' => 'Invalid date',
93 '081' => 'Crypto error',
94 '082' => 'CVV failure',
95 '083' => 'Unable verify pin',
96 '084' => 'Duplicate trans',
97 '085' => 'No reason 2 decline',
98 '086' => 'Cannot verify pin',
99 '088' => 'General Decline',
100 '089' => 'General Decline',
101 '090' => 'General Decline',
102 '091' => 'Issuer unavailable',
103 '092' => 'Destination route not found',
104 '093' => 'Law violation',
105 '094' => 'Duplicate trans',
106 '096' => 'System malfunction',
107 '098' => 'General Decline',
108 '099' => 'General Decline',
109 '0B1' => 'Surcharge amount not permitted on Visa cards or EBT food stamps',
110 '0B2' => 'Surcharge amount not supported by debit network issuer',
111 '0EB' => 'Check digit error',
112 '0EC' => 'Cid format error',
113 '0N0' => 'FORCE STIP',
114 '0N3' => 'Service not available',
115 '0N4' => 'Exceeds limit issuer',
116 '0N5' => 'Ineligible for resubmission',
117 '0N7' => 'CVV2 failure',
118 '0N8' => 'Trans amount exceeds preauth amt',
119 '0P0' => 'Approved pvid miss',
120 '0P1' => 'Declined pvid miss',
121 '0P2' => 'Invalid bill info',
122 '0Q1' => 'Card auth failed',
123 '0R0' => 'Multipay stopped',
124 '0R1' => 'Multipay stopped merch',
125 '0R3' => 'Revocation of all authorizations order',
126 '0XA' => 'Forward to issue1',
127 '0XD' => 'Forward to issue2',
128 '0VD' => 'General Decline',
129 '0T0' => 'First Time Check',
130 '0T1' => 'Check is OK, but cannot be converted',
131 '0T2' => 'Invalid routing transit number or check belongs to a category that is not eligible for conversion',
132 '0T3' => 'Amount greater than established service limit',
133 '0T4' => 'Unpaid items, failed negative check',
134 '0T5' => 'Duplicate check number',
135 '0T6' => 'MICR Error',
136 '0T7' => 'Too many checks (over merchant or bank limit)',
137 '203' => 'Invalid merchant number',
138 '212' => 'Invalid transaction type',
139 '213' => 'Invalid amount field',
140 '214' => 'Invalid card number',
141 '254' => 'Expired card',
142 '257' => 'Txn not allowed',
143 '276' => 'Unable to locate prvious msg (ref # not found)',
144 '278' => 'No account',
145 '284' => 'General Decline',
146 '296' => 'System malfunction',
147 '2Q1' => 'Card authorization failed',
148 '300' => 'Invalid request format',
149 '301' => 'Missing file header',
150 '303' => 'Invalid sender ID',
151 '306' => 'Duplicate file number',
152 '307' => 'General Decline',
153 '309' => 'Comm link down',
154 '310' => 'Missing batch header',
155 '317' => 'Invalid MOTO ID',
156 '338' => 'General Decline',
157 '380' => 'Missing batch trailer',
158 '382' => 'Record count does not match number records in batch',
159 '383' => 'Net amount does not match file amount',
160 '384' => 'Duplicate transaction',
161 '385' => 'Invalid request format',
162 '394' => 'Record count does not match records in file',
163 '395' => 'Net amount does not match file amount',
164 '396' => 'Declined post - reauthorization attempt',
165 '318' => 'Invalid account data source',
166 '319' => 'Invalid POS entry mode',
167 '320' => 'Auth date invalid (transaction date)',
168 '321' => 'Invalid auth source code',
169 '322' => 'Invalid ACI code',
170 'REJ' => 'Rejected transaction that has been re-keyed',
171 '3AC' => 'Invalid authorization code (must be uppercase, no special chars)',
172 '3TI' => 'Invalid tax indicator',
173 '3VD' => 'Voided transaction',
174 '3AD' => 'AVS response code declined',
175 '3AR' => 'AVS required/address information not provided',
176 '3BD' => 'AVS and CVV2 response Code Declined',
177 '3BR' => 'AVS and CVV2 required/information not provided',
178 '3CD' => 'CVV2 response code declined',
179 '3CR' => 'CVV2 required/inrormation not provided',
180 '3L5' => 'No data sent',
181 '3L6' => 'Order number missing',
182 '3M1' => 'Auth date blank',
183 '3M2' => 'Auth amount blank',
184 '3MT' => 'Managed transaction',
185 '3RV' => 'Reversed transaction',
187 '600' => 'General Decline',
198 'XXX' => 'General Decline',
205 my $level = shift || 0;
207 $self->{"__DEBUG"} = $level;
212 $Business::OnlinePayment::HTTPS::DEBUG = $level;
214 return ref($self) ? ( $self->{"__DEBUG"} || $DEBUG ) : $DEBUG;
221 # standard B::OP methods/data
222 $self->server("epaysecure1.transfirst.com");
226 $self->build_subs(qw(
228 order_number avs_code cvv2_response
229 response_page response_code response_headers
233 # module specific data
234 if ( $opts{debug} ) {
235 $self->debug( $opts{debug} );
239 if ( $opts{merchantcustservnum} ) {
240 $self->merchantcustservnum( $opts{merchantcustservnum} );
241 delete $opts{merchantcustservnum};
249 my %content = $self->content();
253 'normal authorization' => 32, # Authorization/Settle transaction
254 'credit' => 20, # Credit (refund)
255 'authorization only' => 30, # Authorization only
256 'post authorization' => 40, # Settlement
260 $content{'TransactionCode'} = $actions{ lc( $content{'action'} ) }
261 || $content{'action'};
266 'mastercard' => 'CC',
267 'american express' => 'CC',
274 $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
276 $self->transaction_type( $content{'type'} );
278 # stuff it back into %content
279 $self->content(%content);
283 my ( $self, %map ) = @_;
284 my %content = $self->content();
285 foreach ( keys %map ) {
289 : $content{ $map{$_} };
291 $self->content(%content);
296 my $expiration = shift;
298 if ( defined($expiration) and $expiration =~ /^(\d+)\D+\d*(\d{2})$/ ) {
299 my ( $month, $year ) = ( $1, $2 );
300 $expdate_mmyy = sprintf( "%02d", $month ) . $year;
302 return defined($expdate_mmyy) ? $expdate_mmyy : $expiration;
305 sub required_fields {
306 my($self,@fields) = @_;
309 my %content = $self->content();
312 if (exists $content{$_} && defined $content{$_} && $content{$_}=~/\S+/);
316 Carp::croak("missing required field(s): " . join(", ", @missing) . "\n")
324 $self->_map_fields();
326 my %content = $self->content;
329 $required{CC_20} = [ qw( ePayAccountNum Password OrderNum
330 TransactionAmount CardAccountNum ExpirationDate
331 MerchantCustServNum ) ];
332 $required{CC_30} = [ qw( ePayAccountNum Password TransactionCode OrderNum
333 TransactionAmount CardAccountNum ExpirationDate
334 CardHolderZip MerchantCustServNum ) ];
335 $required{CC_32} = $required{CC_30};
336 $required{CC_61} = [ qw( ePayAccountNum Password TransactionCode
338 $required{ECHECK_20} = [ qw( ePayAccountNum Password AccountNumber
339 RoutingNumber DollarAmount OrderNumber
340 CustomerNumber CustomerName ) ];
341 $required{ECHECK_32} = [ qw( ePayAccountNum Password OrderNumber
342 AccountNumber RoutingNumber CheckNumber
343 DollarAmount CustomerName CustomerAddress
344 CustomerCity CustomerState CustomerZip
348 $optional{CC_20} = [ qw( CardHolderName CardHolderAddress CardHolderCity
349 CardHolderState CardHolderZip CardHolderEmail
350 CardHolderPhone CustomerNum Misc1 Misc2 CVV2
351 Ecommerce DuplicateChecking AuthorizedAmount
352 AutorizedDate AuthorizedTime FulfillmentDate
353 CardHolderCountry POSEntryMode MerchantStoreNum
354 CardHolderIDSource SICCATCode MerchantZipCode
355 AccountDataSource AuthResponseCode AuthSourceCode
356 AuthACICode AuthValidationCode AuthAVSResponse
357 MerchantCustServNum CrossReferenceNum
358 PaymentDescription ReferenceNum ) ];
359 $optional{CC_32} = $optional{CC_30};
360 $optional{CC_30} = [ qw( CardHolderName CardHolderAddress CardHolderCity
361 CardHolderState CardHolderEmail CardHolderPhone
362 CustomerNum Misc1 Misc2 CVV2 Ecommerce
363 DuplicateChecking MessageSequenceNum
364 CardHolderCountry POSEntryMode MerchantStoreNum
365 CardHolderIDSource SICCATCode MerchantZipCode
366 PaymenntDiscriptor CAVVCode ECIValue XID
367 TaxIndicator TotalTaxAmount ) ];
368 $optional{CC_32} = $optional{CC_30};
369 $optional{CC_61} = [ qw( MessageSequenceNum CrossReferenceNum OrderNum
371 $optional{ECHECK_20} = ();
372 $optional{ECHECK_32} = [ qw( CustomerNumber Misc1 Misc2 CustomerEmail
373 DriversLicense DriversLicenseState
374 BirthDate SocSecNum ) ];
376 my $type_action = $self->transaction_type(). '_'. $content{TransactionCode};
377 unless ( exists($required{$type_action}) ) {
378 # croak( "TransFirst eLink can't (yet?) handle transaction type: ".
379 # "$content{action} on " . $self->transaction_type() );
380 $self->error_message("TransFirst eLink can't handle transaction type: ".
381 "$content{action} on " . $self->transaction_type() );
382 $self->is_success(0);
386 my $expdate_mmyy = $self->expdate_mmyy( $content{"expiration"} );
387 my $zip = $content{'zip'};
388 $zip =~ s/[^[:alnum:]]//g;
390 my $merchantcustservnum = $self->merchantcustservnum;
391 my $account_number = $self->transaction_type() eq 'CC'
392 ? $content{card_number}
393 : $content{account_number} ;
395 my $invoice_number = $content{invoice_number} || "PAYMENT"; # make one up
396 my $check_number = $content{check_number} || "100"; # make one up
398 $self->_revmap_fields(
400 ePayAccountNum => 'login',
401 Password => 'password',
402 OrderNum => \$invoice_number,
403 OrderNumber => \$invoice_number,
404 MerchantCustServNum => \$merchantcustservnum,
406 TransactionAmount => 'amount',
407 DollarAmount => 'amount',
408 CardAccountNum => 'card_number',
409 ExpirationDate => \$expdate_mmyy, # MMYY from 'expiration'
412 RoutingNumber => 'routing_code',
413 AccountNumber => \$account_number,
414 AccountNum => \$account_number,
415 CheckNumber => \$check_number,
417 CardHolderName => 'name',
418 CustomerName => 'account_name',
419 CardHolderAddress => 'address',
420 CustomerAddress => 'address',
421 CardHolderCity => 'city',
422 CustomerCity => 'city',
423 CardHolderState => 'state',
424 CustomerState => 'state',
425 CardHolderZip => \$zip, # 'zip' with non-alnums removed
426 CustomerZip => \$zip, # 'zip' with non-alnums removed
427 CardHolderEmail => 'email',
428 CustomerEmail => 'email',
429 CardHolderPhone => 'phone',
430 CustomerPhone => 'phone',
431 CustomerNum => 'customer_id',
432 CustomerNumber => 'customer_id',
433 CardHolderCountry => 'country',
435 PaymentDescriptor => 'description',
437 ReferenceNum => 'order_number'
440 tie my %params, 'Tie::IxHash',
441 $self->get_fields( @{$required{$type_action}},
442 @{$optional{$type_action}},
445 $params{TestTransaction}='Y' if $self->test_transaction;
447 $params{InstallmentNum} = $params{InstallmentOf} = '01'
448 unless ($params{InstallmentNum} && $params{InstallmentOf});
450 if ($self->transaction_type() eq 'ECHECK') {
451 delete $params{InstallmentNum};
452 delete $params{InstallmentOf};
455 if ( $type_action eq "CC_30" || $type_action eq "CC_32" ) {
456 $self->path($self->path."elink/authpd.asp");
457 } elsif ( $type_action eq "CC_61" ) {
458 $self->path($self->path."eLink/voidpd.asp");
459 } elsif ( $type_action eq "CC_20" ) {
460 $self->path($self->path."eLink/creditpd.asp");
461 } elsif ( $type_action eq "ECHECK_32" ) {
462 $self->path($self->path."eLink/checkPD.asp");
463 } elsif ( $type_action eq "ECHECK_20" ) {
464 $self->path($self->path."eLink/checkcreditPD.asp");
466 croak "don't know path for unexpected type and action $type_action";
469 warn join("\n", map{ "$_ => $params{$_}" } keys(%params)) if $DEBUG > 1;
470 my ( $page, $resp, %resp_headers ) =
471 $self->https_post( %params );
473 $self->response_code( $resp );
474 $self->response_page( $page );
475 $self->response_headers( \%resp_headers );
477 warn "$page\n" if $DEBUG > 1;
478 # $page should contain | separated values
480 $self->required_fields(@{$required{$type_action}});
485 if ( $type_action eq "CC_30" || $type_action eq "CC_32" ) {
486 my ($format,$account,$tcode,$seq,$moi,$cardnum,$exp,$authamt,$authdate,
487 $authtime,$tstat,$custnum,$ordernum,$refnum,$rcode,$authsrc,$achar,
488 $transid,$vcode,$sic,$country,$avscode,$storenum,$cvv2resp,$cavvcode,
489 $crossrefnum,$etstat,$cavvresponse,$xid,$eci,@junk)
492 # AVS and CVS values may be set on success or failure
493 $self->avs_code($avscode);
494 $self->cvv2_response( $cvv2resp );
495 $self->result_code( $status = $etstat );
496 $self->order_number( $refnum );
497 $self->authorization( $rcode );
498 $self->junk( \@junk );
499 $self->error_message($error_messages{$status});
502 } elsif ( $type_action eq "CC_61" ) {
504 $self->cvv2_response('');
505 my ($format,$account,$tcode,$seq,$voiddate,$voidtime,$tstat, # flaky docs
506 $refnum,$filler1,$filler2,$filler3,$etstat,@junk)
508 $self->result_code( $status = $etstat );
509 $self->order_number( $refnum );
510 $self->authorization('');
511 $self->junk( \@junk );
512 $self->error_message($error_messages{$status});
514 } elsif ( $type_action eq "CC_20" ) {
516 $self->cvv2_response('');
517 my ($format,$account,$tcode,$seq,$moi,$authamt,$authdate,$authtime,
518 $tstat,$refnum,$crossrefnum,$custnum,$ordernum,$etstat,@junk)
520 $self->result_code( $status = $etstat );
521 $self->order_number( $refnum );
522 $self->authorization('');
523 $self->junk( \@junk );
524 $self->error_message($error_messages{$status});
526 } elsif ( $type_action eq "ECHECK_32" ) {
527 my ($responsecode,$response,$transactionid,$note,$errors,@junk)
530 $self->cvv2_response('');
531 $self->result_code( $status = $responsecode );
532 $self->order_number( $transactionid );
533 $self->authorization('');
534 $errors = $errors ? $errors : '';
535 $self->error_message("$response $errors");
536 $self->junk( \@junk );
538 } elsif ( $type_action eq "ECHECK_20" ) {
539 my ($response,$transactionid,$note,$errors,@junk) # very flaky docs
542 $self->cvv2_response('');
543 $self->result_code( $status = $response );
544 $self->order_number( $transactionid );
545 $self->authorization('');
546 $errors = $errors ? $errors : '';
547 $self->error_message("$response $errors");
548 $self->junk( \@junk );
551 croak "can't interpret response for unexpected type and action $type_action";
554 if ( $resp =~ /^(HTTP\S+ )?200/ && ($status eq "000" || $status eq "011" || $status eq "085" || $status eq "0P0" || $status eq "P00" || $status eq 'ACCEPTED') ) {
555 $self->is_success(1);
558 $self->is_success(0);
568 Business::OnlinePayment::TransFirsteLink - Transfirst eLink backend for Business::OnlinePayment
572 use Business::OnlinePayment;
574 my $tx = new Business::OnlinePayment(
576 'merchantcustservnum' => "8005551212",
579 # See the module documentation for details of content()
582 action => 'Normal Authorization',
583 description => 'Business::OnlinePayment::TransFirsteLink test',
585 invoice_number => '100100',
586 customer_id => 'jef',
587 name => 'Jeff Finucane',
588 address => '123 Anystreet',
592 email => 'transfirst@weasellips.com',
593 card_number => '4111111111111111',
594 expiration => '12/09',
596 order_number => 'string',
601 if ( $tx->is_success() ) {
603 "Card processed successfully: ", $tx->authorization, "\n",
604 "order number: ", $tx->order_number, "\n",
605 "CVV2 response: ", $tx->cvv2_response, "\n",
606 "AVS code: ", $tx->avs_code, "\n",
611 $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
614 "Card was rejected: ", $tx->error_message, $info, "\n",
615 "order number: ", $tx->order_number, "\n",
621 This module is a back end driver that implements the interface
622 specified by L<Business::OnlinePayment> to support payment handling
623 via TransFirst's eLink Internet payment solution.
625 See L<Business::OnlinePayment> for details on the interface this
628 =head1 Standard methods
634 This method sets the 'server' attribute to 'epaysecure1.transfirst.com' and
635 the port attribute to '443'. This method also sets up the
636 L</Module specific methods> described below.
642 =head1 Unofficial methods
644 This module provides the following methods which are not officially part of the
645 standard Business::OnlinePayment interface (as of 3.00_06) but are nevertheless
646 supported by multiple gateways modules and expected to be standardized soon:
650 =item L<order_number()|/order_number()>
652 =item L<avs_code()|/avs_code()>
654 =item L<cvv2_response()|/cvv2_response()>
658 =head1 Module specific methods
660 This module provides the following methods which are not currently
661 part of the standard Business::OnlinePayment interface:
665 =item L<expdate_mmyy()|/expdate_mmyy()>
667 =item L<debug()|/debug()>
673 The following default settings exist:
679 epaysecure1.transfirst.com
687 =head1 Handling of content(%content)
689 The following rules apply to content(%content) data:
693 If 'type' matches one of the following keys it is replaced by the
694 right hand side value:
697 'mastercard' => 'CC',
698 'american express' => 'CC',
702 The value of 'type' is used to set transaction_type(). Currently this
703 module only supports the above values.
705 =head1 Setting TransFirst eLink parameters from content(%content)
707 The following rules are applied to map data to TransFirst eLink parameters
708 from content(%content):
710 # eLink param => $content{<key>}
711 ePayAccountNum => 'login',
712 Password => 'password',
713 OrderNum => 'invoice_number',
714 OrderNumber => 'invoice_number',
716 TransactionAmount => 'amount',
717 DollarAmount => 'amount',
718 CardAccountNum => 'card_number',
719 ExpirationDate => \( $month.$year ), # MM/YY from 'expiration'
722 RoutingNumber => 'routing_code',
723 AccountNumber => \( $type eq 'CC' ? $card_number : $account_number ),
724 CheckNumber => 'check_number',
726 CardHolderName => 'name',
727 CardHolderAddress => 'address',
728 CardHolderCity => 'city',
729 CardHolderState => 'state',
730 CardHolderZip => \$zip, # 'zip' with non-alphanumerics removed
731 CardHolderEmail => 'email',
732 CardHolderPhone => 'phone',
733 CardHolderCountry => 'country',
735 CustomerName => 'name',
736 CustomerAddress => 'address',
737 CustomerCity => 'city',
738 CustomerState => 'state',
739 CustomerZip => \$zip, # 'zip' with non-alphanumerics removed
740 CustomerEmail => 'email',
741 CustomerPhone => 'phone',
743 PaymentDescriptor => 'description',
745 =head1 Mapping TransFirst eLink transaction responses to object methods
747 The following methods provides access to the transaction response data
748 resulting from a Payflow Pro request (after submit()) is called:
750 =head2 order_number()
752 This order_number() method returns the ReferenceNum field for card transactions
753 and TransactionId for check transactions to uniquely identify the transaction.
757 The result_code() method returns the Extended Transaction Status field for
758 card transactions and the Result Code field for check transactions. It is the
759 numeric return code indicating the outcome of the attempted
762 =head2 error_message()
764 The error_message() method returns the Errors field for check
765 transactions. This provides more details about the transaction result.
767 =head2 authorization()
769 The authorization() method returns the Authorization Response Code field,
770 which is the approval code obtained from the card processing network.
774 The avs_code() method returns the AVS Response Code field from the
777 =head2 cvv2_response()
779 The cvv2_response() method returns the CVV2 Response Code field, which is a
780 response message returned with the transaction result.
782 =head2 expdate_mmyy()
784 The expdate_mmyy() method takes a single scalar argument (typically
785 the value in $content{expiration}) and attempts to parse and format
786 and put the date in MMYY format as required by PayflowPro
787 specification. If unable to parse the expiration date simply leave it
788 as is and let the PayflowPro system attempt to handle it as-is.
792 Enable or disble debugging. The value specified here will also set
793 $Business::OnlinePayment::HTTPS::DEBUG in submit() to aid in
794 troubleshooting problems.
798 This module implements an interface to the TransFirst eLink API version
803 Jeff Finucane <transfirst@weasellips.com>
805 Based on Business::OnlinePayment::PayflowPro written by Ivan Kohler
810 perl(1), L<Business::OnlinePayment>, L<Carp>, and the TransFirst
811 e Payment Services Card Not Present eLink User Guide.