1 package Business::OnlinePayment::PayflowPro;
4 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
7 use Business::OnlinePayment;
9 #PayflowPRO SDK from Verisign
10 use PFProAPI qw( pfpro );
14 @ISA = qw(Exporter AutoLoader Business::OnlinePayment);
22 $self->server('payflow.verisign.com');
26 vendor partner order_number cert_path avs_code cvv2_code
34 my %content = $self->content();
37 my %actions = ('normal authorization' => 'S', #Sale
38 'authorization only' => 'A', #Authorization
39 'credit' => 'C', #Credit (refund)
40 'post authorization' => 'D', #Delayed Capture
43 $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
46 my %types = ('visa' => 'C',
48 'american express' => 'C',
53 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
54 $self->transaction_type($content{'type'});
56 # stuff it back into %content
57 $self->content(%content);
63 #no warnings; #not 5.005
65 eval "sub $_ { my \$self = shift; if(\@_) { \$self->{$_} = shift; } return \$self->{$_}; }";
72 my %content = $self->content();
74 $content{$map{$_}} = $content{$_};
76 $self->content(%content);
81 my %content = $self->content();
83 # warn "$_ = ". ( ref($map{$_})
85 # : $content{$map{$_}} ). "\n";
86 $content{$_} = ref($map{$_})
90 $self->content(%content);
94 my($self,@fields) = @_;
96 my %content = $self->content();
98 foreach( grep defined $content{$_}, @fields) { $new{$_} = $content{$_}; }
107 my %content = $self->content;
109 my($month, $year, $zip);
111 #unless ( $content{action} eq 'BillOrders' ) {
113 if ( $self->transaction_type() eq 'C' ) {
115 Carp::croak("PayflowPro can't (yet?) handle transaction type: ".
116 $self->transaction_type());
119 if ( exists($content{'expiration'}) && defined($content{'expiration'})
120 && length($content{'expiration'}) ) {
121 $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
122 or croak "unparsable expiration $content{expiration}";
124 ( $month, $year ) = ( $1, $2 );
125 $month = '0'. $month if $month =~ /^\d$/;
128 ( $zip = $content{'zip'} ) =~ s/\D//g;
131 #$content{'address'} =~ /^(\S+)\s/;
134 $self->server('test-payflow.verisign.com') if $self->test_transaction;
136 $self->revmap_fields(
137 ACCT => 'card_number',
138 EXPDATE => \( $month.$year ),
141 #VENDOR => \( $self->vendor ),
143 PARTNER => \( $self->partner ),
152 COMMENT1 => 'description',
153 COMMENT2 => 'invoice_number',
154 COMPANYNAME => 'company',
155 COUNTRY => 'country',
156 FIRSTNAME => 'first_name',
157 LASTNAME => 'last_name',
163 ORIGID => 'order_number'
167 my @required = qw( TRXTYPE TENDER PARTNER VENDOR USER PWD );
168 if ( $self->transaction_type() eq 'C' ) { #credit card
169 if ( $content{'action'} =~ /^[CDV]$/ && exists($content{'ORIGID'})
170 && defined($content{'ORIGID'}) && length($content{'ORIGID'}) ) {
171 push @required, qw(ORIGID);
173 push @required, qw(AMT ACCT EXPDATE);
176 $self->required_fields(@required);
178 my %params = $self->get_fields(qw(
179 ACCT EXPDATE AMT USER VENDOR PARTNER PWD TRXTYPE TENDER
181 CITY COMMENT1 COMMENT2 COMPANYNAME COUNTRY FIRSTNAME LASTNAME NAME EMAIL
186 #print "$_ => $params{$_}\n" foreach keys %params;
188 $ENV{'PFPRO_CERT_PATH'} = $self->cert_path;
189 my( $response, $resultstr ) = pfpro( \%params, $self->server, $self->port );
191 #if ( $response->{'RESULT'} == 0 ) {
192 if ( $response->{'RESULT'} eq '0' ) { #want an explicit zero, not just
194 $self->is_success(1);
195 $self->result_code( $response->{'RESULT'} );
196 $self->error_message( $response->{'RESPMSG'} );
197 $self->authorization( $response->{'AUTHCODE'} );
198 $self->order_number( $response->{'PNREF'} );
200 if ( exists $response->{AVSADDR} || exists $response->{AVSZIP} ) {
201 if ( $response->{AVSADDR} eq 'Y' && $response->{AVSZIP} eq 'Y' ) {
203 } elsif ( $response->{AVSADDR} eq 'Y' ) {
205 } elsif ( $response->{AVSZIP} eq 'Y' ) {
207 } elsif ( $response->{AVSADDR} eq 'N' || $response->{AVSZIP} eq 'N' ) {
211 $self->avs_code( $avs_code );
212 $self->cvv2_code( $response->{'CVV2MATCH'});
214 $self->is_success(0);
215 $self->result_code( $response->{'RESULT'} );
216 $self->error_message( $response->{'RESPMSG'} );
226 Business::OnlinePayment::PayflowPro - Verisign PayflowPro backend for Business::OnlinePayment
230 use Business::OnlinePayment;
232 my $tx = new Business::OnlinePayment( 'PayflowPro',
233 'vendor' => 'your_vendor',
234 'partner' => 'your_partner',
235 'cert_path' => '/path/to/your/certificate/file/', #just the dir
240 action => 'Normal Authorization',
241 description => 'Business::OnlinePayment test',
243 invoice_number => '100100',
244 customer_id => 'jsk',
245 name => 'Jason Kohles',
246 address => '123 Anystreet',
250 email => 'ivan-payflowpro@420.am',
251 card_number => '4007000000027',
252 expiration => '09/04',
256 order_number => 'string', # returned by $tx->order_number() from an
257 # "authorization only" or
258 # "normal authorization" action, used by a
259 # "credit", "void", or "post authorization"
263 if($tx->is_success()) {
264 print "Card processed successfully: ".$tx->authorization."\n";
265 print "order number: ". $tx->order_number. "\n";
266 print "AVS code: ". $tx->avs_code. "\n"; # Y - Address and ZIP match
267 # A - Address matches but not ZIP
268 # Z - ZIP matches bu tnot address
270 # E - AVS error or unsupported
272 print "CVV2 code: ". $tx->cvv2_code. "\n";
275 print "Card was rejected: ".$tx->error_message;
276 print " (CVV2 mismatch)" if $tx->result_code == 114;
280 =head1 SUPPORTED TRANSACTION TYPES
282 =head2 Visa, MasterCard, American Express, JCB, Discover/Novus, Carte blanche/Diners Club, CC
284 =head1 SUPPORTED ACTIONS
286 =head2 Normal Authorization, Authorization Only, Post Authorization, Credit, Void
290 For detailed information see L<Business::OnlinePayment>.
294 This module implements an interface to the PayflowPro Perl API, which can
295 be downloaded at https://manager.verisign.com/ with a valid login.
301 Ivan Kohler <ivan-payflowpro@420.am>
303 Based on Busienss::OnlinePayment::AuthorizeNet written by Jason Kohles.
307 perl(1), L<Business::OnlinePayment>.