coverage checking exists
[Business-OnlinePayment-TransFirsteLink.git] / TransFirsteLink.pm
1 package Business::OnlinePayment::TransFirsteLink;
2
3 use strict;
4 use vars qw($VERSION $DEBUG %error_messages);
5 use Carp qw(carp croak);
6
7 use base qw(Business::OnlinePayment::HTTPS);
8
9 $VERSION = '0.01';
10 $VERSION = eval $VERSION;
11 $DEBUG   = 0;
12
13 %error_messages = (
14   '000' => 'Approval',
15   '001' => 'Call Issuer',
16   '002' => 'Referral special',
17   '003' => 'Invalid merchant number',
18   '004' => 'Pick up',
19   '005' => 'Declined',
20   '006' => 'General error',
21   '007' => 'Pick up special',
22   '008' => 'Honor with ID',
23   '009' => 'General Decline',
24   '010' => 'Network Error',
25   '011' => 'Approval',
26   '012' => 'Invalid transaction type',
27   '013' => 'Invalid amount field',
28   '014' => 'Invalid card number',
29   '015' => 'Invalid issuer',
30   '016' => 'General Decline',
31   '017' => 'General Decline',
32   '018' => 'General Decline',
33   '019' => 'Re-enter',
34   '020' => 'General Decline',
35   '021' => 'No action taken',
36   '022' => 'General Decline',
37   '023' => 'General Decline',
38   '024' => 'General Decline',
39   '025' => 'Acct num miss',
40   '026' => 'General Decline',
41   '027' => 'General Decline',
42   '028' => 'File unavailable',
43   '029' => 'General Decline',
44   '030' => 'Format Error - Decline',
45   '031' => 'General Decline',
46   '032' => 'General Decline',
47   '033' => 'General Decline',
48   '034' => 'General Decline',
49   '036' => 'General Decline',
50   '037' => 'General Decline',
51   '038' => 'General Decline',
52   '039' => 'No card acct',
53   '040' => 'General Decline',
54   '041' => 'Lost card',
55   '042' => 'General Decline',
56   '043' => 'Stolen card',
57   '044' => 'General Decline',
58   '045' => 'General Decline',
59   '046' => 'General Decline',
60   '048' => 'General Decline',
61   '049' => 'General Decline',
62   '050' => 'General Decline',
63   '051' => 'Over limit',
64   '052' => 'No checking acct',
65   '053' => 'No saving acct',
66   '054' => 'Expired card',
67   '055' => 'Invalid pin',
68   '056' => 'General Decline',
69   '057' => 'TXN not allowed',
70   '058' => 'TXN not allowed term',
71   '059' => 'TXN not allowed - Merchant',
72   '060' => 'General Decline',
73   '061' => 'Over cash limit',
74   '062' => 'Restricted card',
75   '063' => 'Security violate',
76   '064' => 'General Decline',
77   '065' => 'Excessive authorizations',
78   '066' => 'General Decline',
79   '067' => 'General Decline',
80   '069' => 'General Decline',
81   '070' => 'General Decline',
82   '071' => 'General Decline',
83   '072' => 'General Decline',
84   '073' => 'General Decline',
85   '074' => 'General Decline',
86   '075' => 'Excessive pin entry tries',
87   '076' => 'Unable locate previous msg (ref# not found)',
88   '077' => 'Mismatched info',
89   '078' => 'No account',
90   '079' => 'Already reversed',
91   '080' => 'Invalid date',
92   '081' => 'Crypto error',
93   '082' => 'CVV failure',
94   '083' => 'Unable verify pin',
95   '084' => 'Duplicate trans',
96   '085' => 'No reason 2 decline',
97   '086' => 'Cannot verify pin',
98   '088' => 'General Decline',
99   '089' => 'General Decline',
100   '090' => 'General Decline',
101   '091' => 'Issuer unavailable',
102   '092' => 'Destination route not found',
103   '093' => 'Law violation',
104   '094' => 'Duplicate trans',
105   '096' => 'System malfunction',
106   '098' => 'General Decline',
107   '099' => 'General Decline',
108   '0B1' => 'Surcharge amount not permitted on Visa cards or EBT food stamps',
109   '0B2' => 'Surcharge amount not supported by debit network issuer',
110   '0EB' => 'Check digit error',
111   '0EC' => 'Cid format error',
112   '0N0' => 'FORCE STIP',
113   '0N3' => 'Service not available',
114   '0N4' => 'Exceeds limit issuer',
115   '0N5' => 'Ineligible for resubmission',
116   '0N7' => 'CVV2 failure',
117   '0N8' => 'Trans amount exceeds preauth amt',
118   '0P0' => 'Approved pvid miss',
119   '0P1' => 'Declined pvid miss',
120   '0P2' => 'Invalid bill info',
121   '0Q1' => 'Card auth failed',
122   '0R0' => 'Multipay stopped',
123   '0R1' => 'Multipay stopped merch',
124   '0R3' => 'Revocation of all authorizations order',
125   '0XA' => 'Forward to issue1',
126   '0XD' => 'Forward to issue2',
127   '0VD' => 'General Decline',
128   '0T0' => 'First Time Check',
129   '0T1' => 'Check is OK, but cannot be converted',
130   '0T2' => 'Invalid routing transit number or check belongs to a category that is not eligible for conversion',
131   '0T3' => 'Amount greater than established service limit',
132   '0T4' => 'Unpaid items, failed negative check',
133   '0T5' => 'Duplicate check number',
134   '0T6' => 'MICR Error',
135   '0T7' => 'Too many checks (over merchant or bank limit)',
136   '203' => 'Invalid merchant number',
137   '212' => 'Invalid transaction type',
138   '213' => 'Invalid amount field',
139   '214' => 'Invalid card number',
140   '254' => 'Expired card',
141   '257' => 'Txn not allowed',
142   '276' => 'Unable to locate prvious msg (ref # not found)',
143   '278' => 'No account',
144   '284' => 'General Decline',
145   '296' => 'System malfunction',
146   '2Q1' => 'Card authorization failed',
147   '300' => 'Invalid request format',
148   '301' => 'Missing file header',
149   '303' => 'Invalid sender ID',
150   '306' => 'Duplicate file number',
151   '307' => 'General Decline',
152   '309' => 'Comm link down',
153   '310' => 'Missing batch header',
154   '317' => 'Invalid MOTO ID',
155   '338' => 'General Decline',
156   '380' => 'Missing batch trailer',
157   '382' => 'Record count does not match number records in batch',
158   '383' => 'Net amount does not match file amount',
159   '384' => 'Duplicate transaction',
160   '385' => 'Invalid request format',
161   '394' => 'Record count does not match records in file',
162   '395' => 'Net amount does not match file amount',
163   '396' => 'Declined post - reauthorization attempt',
164   '318' => 'Invalid account data source',
165   '319' => 'Invalid POS entry mode',
166   '320' => 'Auth date invalid (transaction date)',
167   '321' => 'Invalid auth source code',
168   '322' => 'Invalid ACI code',
169   'REJ' => 'Rejected transaction that has been re-keyed',
170   '3AC' => 'Invalid authorization code (must be uppercase, no special chars)',
171   '3TI' => 'Invalid tax indicator',
172   '3VD' => 'Voided transaction',
173   '3AD' => 'AVS response code declined',
174   '3AR' => 'AVS required/address information not provided',
175   '3BD' => 'AVS and CVV2 response Code Declined',
176   '3BR' => 'AVS and CVV2 required/information not provided',
177   '3CD' => 'CVV2 response code declined',
178   '3CR' => 'CVV2 required/inrormation not provided',
179   '3L5' => 'No data sent',
180   '3L6' => 'Order number missing',
181   '3M1' => 'Auth date blank',
182   '3M2' => 'Auth amount blank',
183   '3MT' => 'Managed transaction',
184   '3RV' => 'Reversed transaction',
185   '3TO' => 'Timeout',
186   '600' => 'General Decline',
187   '990' => 'Voided',
188   '991' => 'Voided',
189   '992' => 'Voided',
190   '993' => 'Voided',
191   '994' => 'Voided',
192   '995' => 'Voided',
193   '996' => 'Voided',
194   '997' => 'Voided',
195   '998' => 'Voided',
196   '999' => 'Voided',
197   'XXX' => 'General Decline',
198 );
199
200 sub debug {
201     my $self = shift;
202
203     if (@_) {
204         my $level = shift || 0;
205         if ( ref($self) ) {
206             $self->{"__DEBUG"} = $level;
207         }
208         else {
209             $DEBUG = $level;
210         }
211         $Business::OnlinePayment::HTTPS::DEBUG = $level;
212     }
213     return ref($self) ? ( $self->{"__DEBUG"} || $DEBUG ) : $DEBUG;
214 }
215
216 sub set_defaults {
217     my $self = shift;
218     my %opts = @_;
219
220     # standard B::OP methods/data
221     $self->server("epaysecure1.transfirst.com");
222     $self->port("443");
223     $self->path("/");
224
225     $self->build_subs(qw( 
226                           merchantcustservnum
227                           order_number avs_code cvv2_response
228                           response_page response_code response_headers
229                           junk
230                      ));
231
232     # module specific data
233     if ( $opts{debug} ) {
234         $self->debug( $opts{debug} );
235         delete $opts{debug};
236     }
237
238     if ( $opts{merchantcustservnum} ) {
239         $self->merchantcustservnum( $opts{merchantcustservnum} );
240         delete $opts{merchantcustservnum};
241     }
242
243 }
244
245 sub _map_fields {
246     my ($self) = @_;
247
248     my %content = $self->content();
249
250     #ACTION MAP
251     my %actions = (
252         'normal authorization' => 32,    # Authorization/Settle transaction
253         'credit'               => 20,    # Credit (refund)
254         'authorization only'   => 30,    # Authorization only
255         'post authorization'   => 40,    # Settlement
256         'void'                 => 61,    # Void
257     );
258
259     $content{'TransactionCode'} = $actions{ lc( $content{'action'} ) }
260       || $content{'action'};
261
262     # TYPE MAP
263     my %types = (
264         'visa'             => 'CC',
265         'mastercard'       => 'CC',
266         'american express' => 'CC',
267         'discover'         => 'CC',
268         'cc'               => 'CC',
269
270         'check'            => 'ECHECK',
271     );
272
273     $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
274
275     $self->transaction_type( $content{'type'} );
276
277     # stuff it back into %content
278     $self->content(%content);
279 }
280
281 sub _revmap_fields {
282     my ( $self, %map ) = @_;
283     my %content = $self->content();
284     foreach ( keys %map ) {
285         $content{$_} =
286           ref( $map{$_} )
287           ? ${ $map{$_} }
288           : $content{ $map{$_} };
289     }
290     $self->content(%content);
291 }
292
293 sub expdate_mmyy {
294     my $self       = shift;
295     my $expiration = shift;
296     my $expdate_mmyy;
297     if ( defined($expiration) and $expiration =~ /^(\d+)\D+\d*(\d{2})$/ ) {
298         my ( $month, $year ) = ( $1, $2 );
299         $expdate_mmyy = sprintf( "%02d", $month ) . $year;
300     }
301     return defined($expdate_mmyy) ? $expdate_mmyy : $expiration;
302 }
303
304 sub required_fields {
305     my($self,@fields) = @_;
306
307     my @missing;
308     my %content = $self->content();
309     foreach(@fields) {
310       next
311         if (exists $content{$_} && defined $content{$_} && $content{$_}=~/\S+/);
312       push(@missing, $_);
313     }
314
315     Carp::croak("missing required field(s): " . join(", ", @missing) . "\n")
316       if(@missing);
317
318 }
319
320 sub submit {
321     my ($self) = @_;
322
323     $self->_map_fields();
324
325     my %content = $self->content;
326
327     my %required;
328     $required{CC_20} = [ qw( ePayAccountNum Password OrderNumber
329                              TransactionAmount AccountNumber ExpirationDate
330                              MerchantCustServNum ) ];
331     $required{CC_30} = [ qw( ePayAccountNum Password TransactionCode OrderNum
332                              TransactionAmount CardAccountNum ExpirationDate
333                              CardHolderZip MerchantCustServNum ) ];
334     $required{CC_32} = $required{CC_30};
335     $required{CC_61} = [ qw( ePayAccountNum Password TransactionCode
336                              ReferenceNum ) ];
337     $required{ECHECK_20} = [ qw( ePayAccountNum Password AccountNumber
338                                  RoutingNumber DollarAmount OrderNumber
339                                  CustomerNumber ) ];
340     $required{ECHECK_32} = [ qw( ePayAccountNum Password OrderNumber
341                                  AccountNumber RoutingNumber CheckNumber
342                                  DollarAmount CustomerName CustomerAddress
343                                  CustomerCity CustomerState CustomerZip
344                                  CustomerPhone ) ];
345
346     my %optional;
347     $optional{CC_20} = [ qw( CardHolderName CardHolderAddress CardHolderCity
348                              CardHolderState CardHolderZip CardHolderEmail
349                              CardHolderPhone CustomerNum Misc1 Misc2 CVV2
350                              Ecommerce DuplicateChecking AuthorizedAmount
351                              AutorizedDate AuthorizedTime FulfillmentDate
352                              CardHolderCountry POSEntryMode MerchantStoreNum
353                              CardHolderIDSource SICCATCode MerchantZipCode
354                              AccountDataSource AuthResponseCode AuthSourceCode
355                              AuthACICode AuthValidationCode AuthAVSResponse
356                              MerchantCustServNum CrossReferenceNum
357                              PaymentDescription ReferenceNum ) ];
358     $optional{CC_32} = $optional{CC_30};
359     $optional{CC_30} = [ qw( CardHolderName CardHolderAddress CardHolderCity
360                              CardHolderState CardHolderEmail CardHolderPhone
361                              CustomerNum Misc1 Misc2 CVV2 Ecommerce
362                              DuplicateChecking MessageSequenceNum
363                              CardHolderCountry POSEntryMode MerchantStoreNum
364                              CardHolderIDSource SICCATCode MerchantZipCode
365                              PaymenntDiscriptor CAVVCode ECIValue XID
366                              TaxIndicator TotalTaxAmount ) ];
367     $optional{CC_32} = $optional{CC_30};
368     $optional{CC_61} = [ qw( MessageSequenceNum CrossReferenceNum OrderNum
369                              CustomerNum ) ];
370     $optional{ECHECK_20} = ();
371     $optional{ECHECK_32} = [ qw( CustomerNumber Misc1 Misc2 CustomerEmail
372                                  DriversLicense DriversLicenseState
373                                  BirthDate SocSecNum ) ];
374
375     my $type_action = $self->transaction_type(). '_'. $content{TransactionCode};
376     unless ( exists($required{$type_action}) ) {
377         croak( "TransFirst eLink can't (yet?) handle transaction type: ".
378               "$content{action} on " . $self->transaction_type() );
379     }
380
381     my $expdate_mmyy = $self->expdate_mmyy( $content{"expiration"} );
382     my $zip          = $content{'zip'};
383     $zip =~ s/[^[:alnum:]]//g;
384
385     my $merchantcustservnum = $self->merchantcustservnum;
386     my $account_number = $self->transaction_type() eq 'CC'
387                            ? $content{card_number}
388                            : $content{account_number} ;
389
390     my $invoice_number = $content{invoice_number} || "PAYMENT";  # make one up
391     my $check_number = $content{check_number} || "100";  # make one up
392
393     $self->_revmap_fields(
394
395         ePayAccountNum      => 'login',
396         Password            => 'password',
397         OrderNum            => \$invoice_number,
398         OrderNumber         => \$invoice_number,
399         MerchantCustServNum => \$merchantcustservnum,
400
401         TransactionAmount   => 'amount',
402         DollarAmount        => 'amount',
403         CardAccountNum      => 'card_number',
404         ExpirationDate      => \$expdate_mmyy,    # MMYY from 'expiration'
405         CVV2                => 'cvv2',
406
407         RoutingNumber       => 'routing_code',
408         AccountNumber       => \$account_number,
409         CheckNumber         => \$check_number,
410
411         CardHolderName      => 'name',
412         CustomerName        => 'account_name',
413         CardHolderAddress   => 'address',
414         CustomerAddress     => 'address',
415         CardHolderCity      => 'city',
416         CustomerCity        => 'city',
417         CardHolderState     => 'state',
418         CustomerState       => 'state',
419         CardHolderZip       => \$zip,          # 'zip' with non-alnums removed
420         CustomerZip         => \$zip,          # 'zip' with non-alnums removed
421         CardHolderEmail     => 'email',
422         CustomerEmail       => 'email',
423         CardHolderPhone     => 'phone',
424         CustomerPhone       => 'phone',
425         CustomerNum         => 'customer_id',
426         CustomerNumber      => 'customer_id',
427         CardHolderCountry   => 'country',
428
429         PaymentDescriptor   => 'description',
430
431         ReferenceNum        => 'order_number'
432     );
433
434     my %params = $self->get_fields( @{$required{$type_action}},
435                                     @{$optional{$type_action}},
436                                   );
437
438     $params{TestTransaction}='Y' if $self->test_transaction;
439
440     $params{InstallmentNum} = $params{InstallmentOf} = '01'
441       unless ($params{InstallmentNum} && $params{InstallmentOf}); 
442
443     if ( $type_action eq "CC_30" || $type_action eq "CC_32" ) {
444       $self->path($self->path."elink/authpd.asp");
445     } elsif ( $type_action eq "CC_61" ) {
446       $self->path($self->path."eLink/voidpd.asp");
447     } elsif ( $type_action eq "CC_20" ) {
448       $self->path($self->path."eLink/creditpd.asp");
449     } elsif ( $type_action eq "ECHECK_32" ) {
450       $self->path($self->path."eLink/checkPD.asp");
451     } elsif ( $type_action eq "ECHECK_20" ) {
452       $self->path($self->path."eLink/checkcreditPD.asp");
453     } else {
454       croak "don't know path for unexpected type and action $type_action";
455     }
456
457     warn join("\n", map{ "$_ => $params{$_}" } keys(%params)) if $DEBUG > 1;
458     my ( $page, $resp, %resp_headers ) =
459       $self->https_post( %params );
460
461     $self->response_code( $resp );
462     $self->response_page( $page );
463     $self->response_headers( \%resp_headers );
464
465     warn "$page\n" if $DEBUG > 1;
466     # $page should contain | separated values
467
468     $self->required_fields(@{$required{$type_action}});
469
470     my $status ='';
471
472     if ( $type_action eq "CC_30" || $type_action eq "CC_32" ) {
473       my ($format,$account,$tcode,$seq,$moi,$cardnum,$exp,$authamt,$authdate,
474           $authtime,$tstat,$custnum,$ordernum,$refnum,$rcode,$authsrc,$achar,
475           $transid,$vcode,$sic,$country,$avscode,$storenum,$cvv2resp,$cavvcode,
476           $crossrefnum,$etstat,$cavvresponse,$xid,$eci,@junk)
477          = split '\|', $page;
478
479       # AVS and CVS values may be set on success or failure
480       $self->avs_code($avscode);
481       $self->cvv2_response( $cvv2resp );
482       $self->result_code( $status = $etstat );
483       $self->order_number( $refnum );
484       $self->authorization( $rcode );
485       $self->junk( \@junk );
486       $self->error_message($error_messages{$status});
487
488     } elsif ( $type_action eq "CC_61" ) {
489       my ($format,$account,$tcode,$voidamt,$seq,$voiddate,$voidtime,$tstat,
490           $refnum,$filler1,$filler2,$filler3,$etstat,@junk)
491          = split '\|', $page;
492       $self->result_code( $status = $etstat );
493       $self->order_number( $refnum );
494       $self->junk( \@junk );
495       $self->error_message($error_messages{$status});
496
497     } elsif ( $type_action eq "CC_20" ) {
498       my ($format,$account,$tcode,$seq,$moi,$authamt,$authdate,$authtime,
499           $tstat,$refnum,$crossrefnum,$custnum,$ordernum,$etstat,@junk)
500          = split '\|', $page;
501       $self->result_code( $status = $etstat );
502       $self->order_number( $refnum );
503       $self->junk( \@junk );
504       $self->error_message($error_messages{$status});
505
506     } elsif ( $type_action eq "ECHECK_32" || $type_action eq "ECHECK_20" ) {
507       my ($responsecode,$response,$transactionid,$note,$errors,@junk)
508          = split '\|', $page;
509       # AVS and CVS values may be set on success or failure
510       $self->result_code( $status = $responsecode );
511       $self->order_number( $transactionid );
512       $self->error_message("$response $errors");
513       $self->junk( \@junk );
514
515     } else {
516       croak "can't interpret response for unexpected type and action $type_action";
517     }
518
519     if ( $resp eq "200" && ($status eq "000" || $status eq "011" || $status eq "085" || $status eq "0P0" || $status eq "P00") ) {
520         $self->is_success(1);
521     }
522     else {
523         $self->is_success(0);
524     }
525 }
526
527 1;
528
529 __END__
530
531 =head1 NAME
532
533 Business::OnlinePayment::TransFirsteLink - Transfirst eLink backend for Business::OnlinePayment
534
535 =head1 SYNOPSIS
536
537   use Business::OnlinePayment;
538   
539   my $tx = new Business::OnlinePayment(
540       'TransFirsteLink',
541       'merchantcustservnum' => "8005551212",
542   );
543   
544   # See the module documentation for details of content()
545   $tx->content(
546       type           => 'CC',
547       action         => 'Normal Authorization',
548       description    => 'Business::OnlinePayment::TransFirsteLink test',
549       amount         => '49.95',
550       invoice_number => '100100',
551       customer_id    => 'jef',
552       name           => 'Jeff Finucane',
553       address        => '123 Anystreet',
554       city           => 'Anywhere',
555       state          => 'GA',
556       zip            => '30004',
557       email          => 'transfirst@weasellips.com',
558       card_number    => '4111111111111111',
559       expiration     => '12/09',
560       cvv2           => '123',
561       order_number   => 'string',
562   );
563   
564   $tx->submit();
565   
566   if ( $tx->is_success() ) {
567       print(
568           "Card processed successfully: ", $tx->authorization, "\n",
569           "order number: ",                $tx->order_number,  "\n",
570           "CVV2 response: ",               $tx->cvv2_response, "\n",
571           "AVS code: ",                    $tx->avs_code,      "\n",
572       );
573   }
574   else {
575       my $info = "";
576       $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
577       
578       print(
579           "Card was rejected: ", $tx->error_message, $info, "\n",
580           "order number: ",      $tx->order_number,         "\n",
581       );
582   }
583
584 =head1 DESCRIPTION
585
586 This module is a back end driver that implements the interface
587 specified by L<Business::OnlinePayment> to support payment handling
588 via TransFirst's eLink Internet payment solution.
589
590 See L<Business::OnlinePayment> for details on the interface this
591 modules supports.
592
593 =head1 Standard methods
594
595 =over 4
596
597 =item set_defaults()
598
599 This method sets the 'server' attribute to 'epaysecure1.transfirst.com' and
600 the port attribute to '443'.  This method also sets up the
601 L</Module specific methods> described below.
602
603 =item submit()
604
605 =back
606
607 =head1 Unofficial methods
608
609 This module provides the following methods which are not officially part of the
610 standard Business::OnlinePayment interface (as of 3.00_06) but are nevertheless
611 supported by multiple gateways modules and expected to be standardized soon:
612
613 =over 4
614
615 =item L<order_number()|/order_number()>
616
617 =item L<avs_code()|/avs_code()>
618
619 =item L<cvv2_response()|/cvv2_response()>
620
621 =back
622
623 =head1 Module specific methods
624
625 This module provides the following methods which are not currently
626 part of the standard Business::OnlinePayment interface:
627
628 =over 4
629
630 =item L<expdate_mmyy()|/expdate_mmyy()>
631
632 =item L<debug()|/debug()>
633
634 =back
635
636 =head1 Settings
637
638 The following default settings exist:
639
640 =over 4
641
642 =item server
643
644 epaysecure1.transfirst.com
645
646 =item port
647
648 443
649
650 =back
651
652 =head1 Handling of content(%content)
653
654 The following rules apply to content(%content) data:
655
656 =head2 type
657
658 If 'type' matches one of the following keys it is replaced by the
659 right hand side value:
660
661   'visa'               => 'CC',
662   'mastercard'         => 'CC',
663   'american express'   => 'CC',
664   'discover'           => 'CC',
665   'check'              => 'ECHECK',
666
667 The value of 'type' is used to set transaction_type().  Currently this
668 module only supports the above values.
669
670 =head1 Setting TransFirst eLink parameters from content(%content)
671
672 The following rules are applied to map data to TransFirst eLink parameters
673 from content(%content):
674
675     # eLink param       => $content{<key>}
676       ePayAccountNum    => 'login',
677       Password          => 'password',
678       OrderNum          => 'invoice_number',
679       OrderNumber       => 'invoice_number',
680
681       TransactionAmount => 'amount',
682       DollarAmount      => 'amount',
683       CardAccountNum    => 'card_number',
684       ExpirationDate    => \( $month.$year ), # MM/YY from 'expiration'
685       CVV2              => 'cvv2',
686
687       RoutingNumber     => 'routing_code',
688       AccountNumber     => \( $type eq 'CC' ? $card_number : $account_number ),
689       CheckNumber       => 'check_number',
690
691       CardHolderName    => 'name',
692       CardHolderAddress => 'address',
693       CardHolderCity    => 'city',
694       CardHolderState   => 'state',
695       CardHolderZip     => \$zip,       # 'zip' with non-alphanumerics removed
696       CardHolderEmail   => 'email',
697       CardHolderPhone   => 'phone',
698       CardHolderCountry => 'country',
699
700       CustomerName      => 'name',
701       CustomerAddress   => 'address',
702       CustomerCity      => 'city',
703       CustomerState     => 'state',
704       CustomerZip       => \$zip,       # 'zip' with non-alphanumerics removed
705       CustomerEmail     => 'email',
706       CustomerPhone     => 'phone',
707
708       PaymentDescriptor => 'description',
709
710 =head1 Mapping TransFirst eLink transaction responses to object methods
711
712 The following methods provides access to the transaction response data
713 resulting from a Payflow Pro request (after submit()) is called:
714
715 =head2 order_number()
716
717 This order_number() method returns the ReferenceNum field for card transactions
718 and TransactionId for check transactions to uniquely identify the transaction.
719
720 =head2 result_code()
721
722 The result_code() method returns the Extended Transaction Status field for
723 card transactions and the Result Code field for check transactions.  It is the
724 numeric return code indicating the outcome of the attempted
725 transaction.
726
727 =head2 error_message()
728
729 The error_message() method returns the Errors field for check
730 transactions.  This provides more details about the transaction result.
731
732 =head2 authorization()
733
734 The authorization() method returns the Authorization Response Code field,
735 which is the approval code obtained from the card processing network.
736
737 =head2 avs_code()
738
739 The avs_code() method returns the AVS Response Code field from the
740 transaction result.
741
742 =head2 cvv2_response()
743
744 The cvv2_response() method returns the CVV2 Response Code field, which is a
745 response message returned with the transaction result.
746
747 =head2 expdate_mmyy()
748
749 The expdate_mmyy() method takes a single scalar argument (typically
750 the value in $content{expiration}) and attempts to parse and format
751 and put the date in MMYY format as required by PayflowPro
752 specification.  If unable to parse the expiration date simply leave it
753 as is and let the PayflowPro system attempt to handle it as-is.
754
755 =head2 debug()
756
757 Enable or disble debugging.  The value specified here will also set
758 $Business::OnlinePayment::HTTPS::DEBUG in submit() to aid in
759 troubleshooting problems.
760
761 =head1 COMPATIBILITY
762
763 This module implements an interface to the TransFirst eLink API version
764 3.4
765
766 =head1 AUTHORS
767
768 Jeff Finucane <transfirst@weasellips.com>
769
770 Based on Business::OnlinePayment::PayflowPro written by Ivan Kohler
771 and Phil Lobbes.
772
773 =head1 SEE ALSO
774
775 perl(1), L<Business::OnlinePayment>, L<Carp>, and the TransFirst
776 e Payment Services Card Not Present eLink User Guide.
777
778 =cut