fix a "false success" problem caused by discrepancy between test and live systems...
[Business-OnlinePayment-TransactionCentral.git] / lib / Business / OnlinePayment / TransactionCentral.pm
1 package Business::OnlinePayment::TransactionCentral;
2
3 use 5.005;
4 use strict;
5 use Carp;
6 use Business::OnlinePayment 3;
7 use Business::OnlinePayment::HTTPS 0.02;
8 use vars qw($VERSION @ISA $DEBUG);
9
10 @ISA = qw(Business::OnlinePayment::HTTPS);
11 $VERSION = '0.04';
12 $DEBUG = 0;
13
14 sub set_defaults {
15     my $self = shift;
16
17     $self->server('webservices.primerchants.com');
18     $self->port('443');
19     $self->path('/billing/TransactionCentral/');
20
21     $self->build_subs(qw( order_number avs_code cvv2_response
22                           response_page response_code response_headers
23                      ));
24 }
25
26 sub submit {
27   my($self) = @_;
28
29   $self->revmap_fields(
30     'MerchantID'    => 'login',
31     'RegKey'        => 'password',
32     'Amount'        => 'amount',
33 #    'CreditAmount'  => 'amount',
34     'AccountNo'     => 'card_number',
35     'NameonAccount' => 'name',
36     'AVSADDR'       => 'address',
37     'AVSZIP'        => 'zip',
38     'CCRURL'        => \'',
39     'CVV2'          => 'cvv2',
40     'TransID'       => 'order_number',
41     'TRANSROUTE'    => 'routing_code',
42   );
43
44   #XXX also set required fields here...
45
46   my @required_fields = qw(login password);
47   my %content = $self->content();
48   my $action = $content{'action'};
49   my $url = $self->path;
50   if (
51     $content{'type'} =~ /^(cc|visa|mastercard|american express|discover)$/i
52   ) {
53
54     if ( $action =~ /^\s*normal\s*authorization\s*$/i ) {
55       $url .= 'processCC.asp';
56
57       #REFID
58       $content{'REFID'} = int(rand(2**31));
59
60       #CCMonth & CCYear
61       $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
62         or croak "unparsable expiration ". $content{'expiration'};
63       my( $month, $year ) = ( $1, $2 );
64       $month = '0'. $month if $month =~ /^\d$/;
65       $content{'CCMonth'} = $month;
66       $content{'CCYear'} = $year;
67
68       push @required_fields, qw( amount card_number expiration
69                                  name address zip
70                                );
71
72     } elsif ( $action =~ /^\s*authorization\s*only\s*$/i ) {
73       croak "Authorizaiton Only is not supported by Transaction Central";
74     } elsif ( $action =~ /^\s*post\s*authorization\s*$/i ) {
75       croak "Post Authorizaiton is not supported by Transaction Central";
76     } elsif ( $action =~ /^\s*(void|credit)\s*$/i ) {
77       $url .= 'voidcreditcconline.asp';
78
79       $content{'CreditAmount'} = delete $content{'Amount'};
80
81     } else {
82       croak "Unknown action $action";
83     }
84
85   } elsif ( $content{'type'} =~ /^e?check$/i ) {
86
87     if ( $action =~ /^\s*normal\s*authorization\s*$/i ) {
88       $url .= 'processcheckonline.asp';
89       $content{'AccountNo'} = $content{'account_number'};
90       $content{'TRANSTYPE'} =
91         ( exists($content{account_type}) && $content{account_type} =~ /^s/i )
92         ? 'SA'
93         : 'CK';
94
95       push @required_fields, qw( amount account_number routing_code
96                                  name
97                                );
98
99     } elsif ( $action =~ /^\s*authorization\s*only\s*$/i ) {
100       croak "Authorizaiton Only is not supported by Transaction Central";
101     } elsif ( $action =~ /^\s*post\s*authorization\s*$/i ) {
102       croak "Post Authorizaiton is not supported by Transaction Central";
103     } elsif ( $action =~ /^\s*(void|credit)\s*$/i ) {
104       $url .= 'addckcreditupdtonline.asp';
105     } else {
106       croak "Unknown action $action";
107     }
108
109   } else {
110     croak 'Unknown type: '. $content{'type'};
111   }
112   $self->path($url);
113   $self->content(%content);
114
115   $self->required_fields(@required_fields);
116
117   my @fields = qw(
118     MerchantID RegKey Amount REFID AccountNo CCMonth CCYear NameonAccount
119     AVSADDR AVSZIP CCRURL CVV2 USER1 USER2 USER3 USER4 TrackData
120     TransID CreditAmount
121     DESCRIPTION DESCDATE TRANSTYPE TRANSROUTE
122   );
123
124   my( $page, $response, %reply_headers ) =
125     $self->https_post( $self->get_fields( @fields ) );
126
127   $self->response_code( $response );
128   $self->response_page( $page );
129   $self->response_headers( \%reply_headers );
130
131   #trim off <html><body> </body></html> around the response we want
132   $page =~ s/^[\s\n]*<html>[\s\n]*<body>[\s\n]*//;
133   $page =~ s/[\s\n]*<\/body>[\s\n]*<\/html>[\s\n]*$//;
134
135   my %return = map { /^(\w+)=(.*)$/ ? ( $1 => $2 ) : () } split(/&/, $page);
136
137   if ( $DEBUG ) { warn "$_ => $return{$_}\n" foreach keys %return; }
138
139   #$self->result_code(   $return{'AVSCode'} );
140   $self->avs_code(      $return{'AVSCode'} );
141   $self->cvv2_response( $return{'CVV2ResponseMsg'} );
142
143   if ( $return{'Auth'} =~ /^\s*(\w+)\s*$/ && lc($1) ne 'declined' ) {
144
145     $self->is_success(1);
146     $self->authorization( $return{'Auth'}   );
147     $self->order_number(  $return{'TransID'} );
148
149   } else {
150
151     $self->is_success(0);
152     $self->error_message( $return{'Notes'} );
153
154   }
155
156 }
157
158 sub revmap_fields {
159     my($self, %map) = @_;
160     my %content = $self->content();
161     foreach(keys %map) {
162 #    warn "$_ = ". ( ref($map{$_})
163 #                         ? ${ $map{$_} }
164 #                         : $content{$map{$_}} ). "\n";
165         $content{$_} = ref($map{$_})
166                          ? ${ $map{$_} }
167                          : $content{$map{$_}};
168     }
169     $self->content(%content);
170 }
171
172 1;
173
174 __END__
175
176 =head1 NAME
177
178 Business::OnlinePayment::TransactionCentral - Transaction Central backend module for Business::OnlinePayment
179
180 =head1 SYNOPSIS
181
182   use Business::OnlinePayment;
183
184   ####
185   # One step transaction, the simple case.
186   ####
187
188   my $tx = new Business::OnlinePayment("TransactionCentral");
189   $tx->content(
190       type           => 'CC',
191       login          => '10011', #MerchantID
192       password       => 'KK48NPYEJHMAH6DK', #Regkey
193       action         => 'Normal Authorization',
194       description    => 'Business::OnlinePayment test',
195       amount         => '49.95',
196       name           => 'Tofu Beast',
197       address        => '123 Anystreet',
198       city           => 'Anywhere',
199       state          => 'UT',
200       zip            => '84058',
201       phone          => '420-867-5309',
202       email          => 'tofu.beast@example.com',
203       card_number    => '4012000000001',
204       expiration     => '08/06',
205       cvv2           => '1234', #optional
206   );
207   $tx->submit();
208
209   if($tx->is_success()) {
210       print "Card processed successfully: ".$tx->authorization."\n";
211   } else {
212       print "Card was rejected: ".$tx->error_message."\n";
213   }
214 =head1 DESCRIPTION
215
216 This is a Business::OnlinePayment backend module for the Transaction Central
217 (MerchantAnywhere, PRIMerchants) gateway.  It is only useful if you have a
218 merchant account with MerchantAnywhere / PRIMerchants:
219
220 http://www.merchantanywhere.com/
221 http://www.merchantanywhere.com/ecshop/TC_elink.htm
222
223 http://www.primerchants.com/
224 http://www.primerchants.com/info/transactioncentral.asp
225
226 =head1 SUPPORTED TRANSACTION TYPES
227
228 =head2 CC, Visa, MasterCard, American Express, Discover
229
230 Content required: type, login, password, action, amount, card_number, expiration, name, address, zip.
231
232 =head2 ECHECK
233
234 Content required: type, login, password, action, amount, account_number, routing_code, name
235
236 =head1 PREREQUISITES
237
238   URI::Escape
239
240   Net::SSLeay _or_ ( Crypt::SSLeay and LWP )
241
242 =head1 DESCRIPTION
243
244 For detailed information see L<Business::OnlinePayment>.
245
246 =head1 NOTE
247
248 The newest publicly available documentation is available at:
249
250 http://www.merchantanywhere.com/ecshop/TC%20Interface%20NEW.pdf
251
252 It is somewhat out-of-date and contains a few discrepancies.  Google
253 "TCInterfaceGuide" for current documentation.
254
255 =head1 AUTHOR
256
257 Ivan Kohler <ivan-transactioncentral@420.am>
258
259 =head1 COPYRIGHT AND LICENSE
260
261 Copyright (C) 2006 by Ivan Kohler
262 Copyright (C) 2007 Freeside Internet Services, Inc.
263
264 This library is free software; you can redistribute it and/or modify
265 it under the same terms as Perl itself.
266
267 =head1 SEE ALSO
268
269 perl(1). L<Business::OnlinePayment>.
270
271 =cut