echeck imrovements
[Business-OnlinePayment-LinkPoint.git] / LinkPoint.pm
1 package Business::OnlinePayment::LinkPoint;
2
3 # $Id: LinkPoint.pm,v 1.26 2007-04-07 00:00:31 jeff Exp $
4
5 use strict;
6 use vars qw($VERSION @ISA $DEBUG @EXPORT @EXPORT_OK);
7 use Carp qw(croak);
8 use AutoLoader;
9 use Business::OnlinePayment;
10
11 require Exporter;
12
13 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
14 @EXPORT = qw();
15 @EXPORT_OK = qw();
16 $VERSION = '0.07';
17 $DEBUG = 1;
18
19 use lpperl; #3;  #lpperl.pm from LinkPoint
20 $LPPERL::VERSION =~ /^(\d+\.\d+)/
21   or die "can't parse lpperl.pm version: $LPPERL::VERSION";
22 die "lpperl.pm minimum version 3 required\n" unless $1 >= 3;
23
24 sub set_defaults {
25     my $self = shift;
26
27     #$self->server('staging.linkpt.net');
28     $self->server('secure.linkpt.net');
29     $self->port('1129');
30
31     $self->build_subs(qw(order_number avs_code));
32
33 }
34
35 sub map_fields {
36     my($self) = @_;
37
38     my %content = $self->content();
39
40     #ACTION MAP
41     my %actions = ('normal authorization' => 'SALE',
42                    'authorization only'   => 'PREAUTH',
43                    'credit'               => 'CREDIT',
44                    'post authorization'   => 'POSTAUTH',
45                    'void'                 => 'VOID',
46                   );
47     $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
48
49     #ACCOUNT TYPE MAP
50     my %account_types = ('personal checking' => 'pc',
51                          'personal savings'  => 'ps',
52                          'business checking' => 'bc',
53                          'business savings'  => 'bs',
54                         );
55     $content{'account_type'} = $account_types{lc($content{'account_type'})}
56                                || $content{'account_type'};
57
58     # stuff it back into %content
59     $self->content(%content);
60 }
61
62 sub build_subs {
63     my $self = shift;
64     foreach(@_) {
65         #no warnings; #not 5.005
66         local($^W)=0;
67         eval "sub $_ { my \$self = shift; if(\@_) { \$self->{$_} = shift; } return \$self->{$_}; }";
68     }
69 }
70
71 sub remap_fields {
72     my($self,%map) = @_;
73
74     my %content = $self->content();
75     foreach(keys %map) {
76         $content{$map{$_}} = $content{$_};
77     }
78     $self->content(%content);
79 }
80
81 sub revmap_fields {
82     my($self, %map) = @_;
83     my %content = $self->content();
84     foreach(keys %map) {
85 #    warn "$_ = ". ( ref($map{$_})
86 #                         ? ${ $map{$_} }
87 #                         : $content{$map{$_}} ). "\n";
88         $content{$_} = ref($map{$_})
89                          ? ${ $map{$_} }
90                          : $content{$map{$_}};
91     }
92     $self->content(%content);
93 }
94
95 sub get_fields {
96     my($self,@fields) = @_;
97
98     my %content = $self->content();
99     my %new = ();
100     foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
101     return %new;
102 }
103
104 sub submit {
105     my($self) = @_;
106
107     $self->map_fields();
108
109     my %content = $self->content;
110
111     my($month, $year);
112     unless ( $content{action} eq 'POSTAUTH'
113              || ( $content{'action'} =~ /^(CREDIT|VOID)$/
114                   && exists $content{'order_number'} )
115              || $self->transaction_type() =~ /^e?check$/i
116            ) {
117
118         if (  $self->transaction_type() =~
119                 /^(cc|visa|mastercard|american express|discover)$/i
120            ) {
121         } else {
122             Carp::croak("LinkPoint can't handle transaction type: ".
123                         $self->transaction_type());
124         }
125
126       $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
127         or croak "unparsable expiration $content{expiration}";
128
129       ( $month, $year ) = ( $1, $2 );
130       $month = '0'. $month if $month =~ /^\d$/;
131     }
132
133     $content{'address'} =~ /^(\S+)\s/;
134     my $addrnum = $1;
135
136     my $result = $content{'result'};
137     if ( $self->test_transaction) {
138       $result ||= 'GOOD';
139       #$self->server('staging.linkpt.net');
140     } else {
141       $result ||= 'LIVE';
142     }
143
144     #docs disagree with lpperl.pm here
145     $content{'voidcheck'} = 1       
146       if ($self->transaction_type() =~ /^e?check$/i
147           &&  $content{'action'} =~ /^VOID$/);
148
149     $self->revmap_fields(
150       host         => \( $self->server ),
151       port         => \( $self->port ),
152       #storename    => \( $self->storename ),
153       configfile   => \( $self->storename ),
154       keyfile      => \( $self->keyfile ),
155       addrnum      => \$addrnum,
156       result       => \$result,
157       cardnumber   => 'card_number',
158       cardexpmonth => \$month,
159       cardexpyear  => \$year,
160       chargetotal  => 'amount',
161       oid          => 'order_number',
162       bankname     => 'bank_name',
163       bankstate    => 'bank_state',
164       routing      => 'routing_code',
165       account      => 'account_number',
166       accounttype  => 'account_type',
167       name         => 'account_name',
168       dl           => 'state_id',
169       dlstate      => 'state_id_state',
170     );
171
172     my $lperl = new LPPERL;
173
174     my @required_fields = qw(host port configfile keyfile amount);
175     if ($self->transaction_type() =~ /^(cc|visa|mastercard|american express|discover)$/i) {
176       push @required_fields, qw(cardnumber cardexpmonth cardexpyear);
177     }elsif ($self->transaction_type() =~ /^e?check$/i) {
178       push @required_fields, qw(
179         dl dlstate routing account accounttype bankname bankstate name
180                                );
181     }
182     $self->required_fields(@required_fields);
183
184     my %post_data = $self->get_fields(qw/
185       host port configfile keyfile
186       result
187       chargetotal cardnumber cardexpmonth cardexpyear
188       name email phone addrnum city state zip country
189       oid
190       dl dlstate routing account accounttype bankname bankstate name void
191
192     /);
193
194     $post_data{'ordertype'} = $content{action};
195
196     if ( $content{'cvv2'} ) { 
197       $post_data{cvmindicator} = 'provided';
198       $post_data{cvmvalue} = $content{'cvv2'};
199     }
200
201     if ( $DEBUG ) {
202       warn "$_ => $post_data{$_}\n" foreach keys %post_data;
203     }
204
205     my %response;
206     #{
207     #  local($^W)=0;
208     #  %response = $lperl->$action(\%post_data);
209     #}
210     %response = $lperl->curl_process(\%post_data);
211
212     if ( $DEBUG ) {
213       warn "$_ => $response{$_}\n" for keys %response;
214     }
215
216     if ( $response{'r_approved'} eq 'APPROVED' ) {
217       $self->is_success(1);
218       $self->result_code($response{'r_code'});
219       $self->authorization($response{'r_ref'});
220       $self->order_number($response{'r_ordernum'});
221       $self->avs_code($response{'r_avs'});
222     } else {
223       $self->is_success(0);
224       $self->result_code('');
225       $self->error_message($response{'r_error'});
226     }
227
228 }
229
230 1;
231 __END__
232
233 =head1 NAME
234
235 Business::OnlinePayment::LinkPoint - LinkPoint (Cardservice) backend for Business::OnlinePayment
236
237 =head1 SYNOPSIS
238
239   use Business::OnlinePayment;
240
241   my $tx = new Business::OnlinePayment( 'LinkPoint',
242     'storename' => 'your_store_number',
243     'keyfile'   => '/path/to/keyfile.pem',
244   );
245
246   $tx->content(
247       type           => 'VISA',
248       action         => 'Normal Authorization',
249       description    => 'Business::OnlinePayment test',
250       amount         => '49.95',
251       invoice_number => '100100',
252       customer_id    => 'jsk',
253       name           => 'Jason Kohles',
254       address        => '123 Anystreet',
255       city           => 'Anywhere',
256       state          => 'UT',
257       zip            => '84058',
258       email          => 'ivan-linkpoint@420.am',
259       card_number    => '4007000000027',
260       expiration     => '09/99',
261   );
262   $tx->submit();
263
264   if($tx->is_success()) {
265       print "Card processed successfully: ".$tx->authorization."\n";
266   } else {
267       print "Card was rejected: ".$tx->error_message."\n";
268   }
269
270 =head1 SUPPORTED TRANSACTION TYPES
271
272 =head2 Visa, MasterCard, American Express, JCB, Discover/Novus, Carte blanche/Diners Club
273
274 =head1 DESCRIPTION
275
276 For detailed information see L<Business::OnlinePayment>.
277
278 =head1 COMPATIBILITY
279
280 This module implements an interface to the LinkPoint Perl Wrapper
281 http://www.linkpoint.com/product_solutions/internet/lperl/lperl_main.html
282
283 Versions 0.4 and on of this module support the LinkPoint Perl Wrapper version
284 3.5.
285
286 =head1 BUGS
287
288 =head1 AUTHOR
289
290 Ivan Kohler <ivan-linkpoint@420.am>
291
292 Based on Busienss::OnlinePayment::AuthorizeNet written by Jason Kohles.
293
294 =head1 SEE ALSO
295
296 perl(1), L<Business::OnlinePayment>.
297
298 =cut
299