0.01
[Business-OnlinePayment-PayflowPro.git] / PayflowPro.pm
1 package Business::OnlinePayment::PayflowPro;
2
3 use strict;
4 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
5 use Carp qw(croak);
6 use AutoLoader;
7 use Business::OnlinePayment;
8
9 #PayflowPRO SDK from Verisign
10 use PFProAPI qw( pfpro );
11
12 require Exporter;
13
14 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
15 @EXPORT = qw();
16 @EXPORT_OK = qw();
17 $VERSION = '0.01';
18
19 sub set_defaults {
20     my $self = shift;
21
22     #$self->server('staging.linkpt.net');
23     $self->server('payflow.verisign.com');
24     $self->port('443');
25
26     $self->build_subs(qw( vendor partner order_number cert_path ));
27
28 }
29
30 sub map_fields {
31     my($self) = @_;
32
33     my %content = $self->content();
34
35     my $action = lc($content{'action'});
36     if ( $action eq 'normal authorization' ) {
37     } else {
38       croak "$action not (yet) supported";
39     }
40
41     #ACTION MAP
42     my %actions = ('normal authorization' => 'S', #Sale
43                    'authorization only'   => 'A', #Authorization
44                    'credit'               => 'C', #Credit (refund)
45                    'post authorization'   => 'D', #Delayed Capture
46                   );
47     $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
48
49     # TYPE MAP
50     my %types = ('visa'               => 'C',
51                  'mastercard'         => 'C',
52                  'american express'   => 'C',
53                  'discover'           => 'C',
54                  'cc'                 => 'C'
55                  #'check'              => 'ECHECK',
56                 );
57     $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
58     $self->transaction_type($content{'type'});
59
60     # stuff it back into %content
61     $self->content(%content);
62 }
63
64 sub build_subs {
65     my $self = shift;
66     foreach(@_) {
67         #no warnings; #not 5.005
68         local($^W)=0;
69         eval "sub $_ { my \$self = shift; if(\@_) { \$self->{$_} = shift; } return \$self->{$_}; }";
70     }
71 }
72
73 sub remap_fields {
74     my($self,%map) = @_;
75
76     my %content = $self->content();
77     foreach(keys %map) {
78         $content{$map{$_}} = $content{$_};
79     }
80     $self->content(%content);
81 }
82
83 sub revmap_fields {
84     my($self, %map) = @_;
85     my %content = $self->content();
86     foreach(keys %map) {
87 #    warn "$_ = ". ( ref($map{$_})
88 #                         ? ${ $map{$_} }
89 #                         : $content{$map{$_}} ). "\n";
90         $content{$_} = ref($map{$_})
91                          ? ${ $map{$_} }
92                          : $content{$map{$_}};
93     }
94     $self->content(%content);
95 }
96
97 sub get_fields {
98     my($self,@fields) = @_;
99
100     my %content = $self->content();
101     my %new = ();
102     foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
103     return %new;
104 }
105
106 sub submit {
107     my($self) = @_;
108
109
110     
111
112     $self->map_fields();
113
114     my %content = $self->content;
115
116     my($month, $year, $zip);
117
118     #unless ( $content{action} eq 'BillOrders' ) {
119
120         if (  $self->transaction_type() eq 'C' ) {
121         } else {
122             Carp::croak("PayflowPro can't (yet?) 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       $zip = $content{'zip'} =~ s/\D//;
133     #}
134
135     #$content{'address'} =~ /^(\S+)\s/;
136     #my $addrnum = $1;
137
138     $self->server('test-payflow.verisign.com') if $self->test_transaction;
139
140     $self->revmap_fields(
141       ACCT       => 'card_number',
142       EXPDATE     => \( $month.$year ),
143       AMT         => 'amount',
144       USER        => 'login',
145       #VENDOR      => \( $self->vendor ),
146       VENDOR      => 'login',
147       PARTNER     => \( $self->partner ),
148       PWD         => 'password',
149       TRXTYPE     => 'action',
150       TENDER      => 'type',
151
152       STREET      => 'address',
153       ZIP         => \$zip,
154
155       CITY        => 'city',
156       COMMENT1    => 'description',
157       COMMENT2    => 'invoice_number',
158       COMPANYNAME => 'company',
159       COUNTRY     => 'country',
160       FIRSTNAME   => 'first_name',
161       LASTNAME    => 'last_name',
162       NAME        => 'name',
163       EMAIL       => 'email',
164       STATE       => 'state',
165
166     );
167
168     $self->required_fields(qw(
169       ACCT EXPDATE AMT USER VENDOR PARTNER PWD TRXTYPE TENDER ));
170
171     my %params = $self->get_fields(qw(
172       ACCT EXPDATE AMT USER VENDOR PARTNER PWD TRXTYPE TENDER
173       STREET ZIP
174       CITY COMMENT1 COMMENT2 COMPANYNAME COUNTRY FIRSTNAME LASTNAME NAME EMAIL
175         STATE
176     ));
177
178     #print "$_ => $params{$_}\n" foreach keys %params;
179
180     $ENV{'PFPRO_CERT_PATH'} = $self->cert_path;
181     my( $response, $resultstr ) = pfpro( \%params, $self->server, $self->port );
182
183     #if ( $response->{'RESULT'} == 0 ) {
184     if ( $response->{'RESULT'} eq '0' ) { #want an explicit zero, not just
185                                           #numerically equal
186       $self->is_success(1);
187       $self->result_code(   $response->{'RESULT'}   );
188       $self->error_message( $response->{'RESPMSG'}  );
189       $self->authorization( $response->{'AUTHCODE'} );
190       $self->order_number(  $response->{'PNREF'}    );
191     } else {
192       $self->is_success(0);
193       $self->result_code(   $response->{'RESULT'}  );
194       $self->error_message( $response->{'RESPMSG'} );
195     }
196
197 }
198
199 1;
200 __END__
201
202 =head1 NAME
203
204 Business::OnlinePayment::PayflowPro - Verisign PayflowPro backend for Business::OnlinePayment
205
206 =head1 SYNOPSIS
207
208   use Business::OnlinePayment;
209
210   my $tx = new Business::OnlinePayment( 'PayflowPro',
211     'vendor'    => 'your_vendor',
212     'partner'   => 'your_partner',
213   );
214
215   $tx->content(
216       type           => 'VISA',
217       action         => 'Normal Authorization',
218       description    => 'Business::OnlinePayment test',
219       amount         => '49.95',
220       invoice_number => '100100',
221       customer_id    => 'jsk',
222       name           => 'Jason Kohles',
223       address        => '123 Anystreet',
224       city           => 'Anywhere',
225       state          => 'UT',
226       zip            => '84058',
227       email          => 'ivan-payflowpro@420.am',
228       card_number    => '4007000000027',
229       expiration     => '09/04',
230   );
231   $tx->submit();
232
233   if($tx->is_success()) {
234       print "Card processed successfully: ".$tx->authorization."\n";
235   } else {
236       print "Card was rejected: ".$tx->error_message."\n";
237   }
238
239 =head1 SUPPORTED TRANSACTION TYPES
240
241 =head2 Visa, MasterCard, American Express, JCB, Discover/Novus, Carte blanche/Diners Club
242
243 =head1 DESCRIPTION
244
245 For detailed information see L<Business::OnlinePayment>.
246
247 =head1 COMPATIBILITY
248
249 This module implements an interface to the PayflowPro Perl API, which can
250 be downloaded at https://manager.verisign.com/ with a valid login.
251
252 =head1 BUGS
253
254 =head1 AUTHOR
255
256 Ivan Kohler <ivan-payflowpro@420.am>
257
258 Based on Busienss::OnlinePayment::AuthorizeNet written by Jason Kohles.
259
260 =head1 SEE ALSO
261
262 perl(1), L<Business::OnlinePayment>.
263
264 =cut
265