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