1 package Business::OnlinePayment::PayflowPro;
6 use base qw(Business::OnlinePayment);
9 use PFProAPI qw( pfpro );
12 $VERSION = eval $VERSION;
17 $self->server('payflow.verisign.com');
21 vendor partner cert_path order_number avs_code cvv2_code
28 my %content = $self->content();
31 my %actions = ('normal authorization' => 'S', # Sale transaction
32 'credit' => 'C', # Credit (refund)
33 'authorization only' => 'A', # Authorization
34 'post authorization' => 'D', # Delayed Capture
37 $content{'action'} = $actions{lc($content{'action'})} || $content{'action'};
40 my %types = ('visa' => 'C',
42 'american express' => 'C',
47 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
48 $self->transaction_type($content{'type'});
50 # stuff it back into %content
51 $self->content(%content);
57 my %content = $self->content();
59 $content{$map{$_}} = $content{$_};
61 $self->content(%content);
66 my %content = $self->content();
68 # warn "$_ = ". ( ref($map{$_})
70 # : $content{$map{$_}} ). "\n";
71 $content{$_} = ref($map{$_})
75 $self->content(%content);
83 my %content = $self->content;
85 my($month, $year, $zip);
87 if ( $self->transaction_type() ne 'C' ) {
88 croak("PayflowPro can't (yet?) handle transaction type: " .
89 $self->transaction_type());
92 if ( defined($content{'expiration'}) && length($content{'expiration'}) ) {
93 $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
94 or croak "unparsable expiration $content{expiration}";
96 ( $month, $year ) = ( $1, $2 );
97 $month = '0'. $month if $month =~ /^\d$/;
100 ( $zip = $content{'zip'} ) =~ s/\D//g;
102 $self->server('test-payflow.verisign.com') if $self->test_transaction;
104 $self->revmap_fields(
105 # (BUG?) VENDOR B::OP:PayflowPro < 0.05 backward compatibility. If
106 # vendor not set use login (although test indicate undef vendor is ok)
107 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
108 PARTNER => \( $self->partner ),
113 ORIGID => 'order_number',
114 COMMENT1 => 'description',
115 COMMENT2 => 'invoice_number',
117 ACCT => 'card_number',
119 EXPDATE => \( $month.$year ), # MM/YY from 'expiration'
122 FIRSTNAME => 'first_name',
123 LASTNAME => 'last_name',
126 COMPANYNAME => 'company',
130 ZIP => \$zip, # 'zip' with non-numbers removed
131 COUNTRY => 'country',
134 my @required = qw( TRXTYPE TENDER PARTNER VENDOR USER PWD );
135 if ( $self->transaction_type() eq 'C' ) { #credit card
136 if ( $content{'action'} =~ /^[CDV]$/
137 && defined($content{'ORIGID'})
138 && length($content{'ORIGID'})
141 push @required, qw(ORIGID);
143 # not currently supported, we croak above if transaction_type ne 'C'
144 push @required, qw(AMT ACCT EXPDATE);
147 $self->required_fields(@required);
149 my %params = $self->get_fields(qw(
150 VENDOR PARTNER USER PWD TRXTYPE TENDER ORIGID COMMENT1 COMMENT2
151 ACCT CVV2 EXPDATE AMT
152 FIRSTNAME LASTNAME NAME EMAIL COMPANYNAME STREET CITY STATE ZIP COUNTRY
155 $ENV{'PFPRO_CERT_PATH'} = $self->cert_path;
156 my( $response, $resultstr ) = pfpro( \%params, $self->server, $self->port );
157 # PNREF (aka transaction id) is set on success and failure
158 $self->order_number( $response->{'PNREF'} );
160 # AVS and CVS values may be set on success or failure
162 if ( exists $response->{AVSADDR} || exists $response->{AVSZIP} ) {
163 if ( $response->{AVSADDR} eq 'Y' && $response->{AVSZIP} eq 'Y' ) {
165 } elsif ( $response->{AVSADDR} eq 'Y' ) {
167 } elsif ( $response->{AVSZIP} eq 'Y' ) {
169 } elsif ( $response->{AVSADDR} eq 'N' || $response->{AVSZIP} eq 'N' ) {
176 $self->avs_code($avs_code);
177 $self->cvv2_code($response->{'CVV2MATCH'});
178 $self->result_code($response->{'RESULT'});
179 $self->error_message($response->{'RESPMSG'});
180 $self->authorization($response->{'AUTHCODE'});
182 # RESULT must be an explicit zero, not just numerically equal
183 if ( $response->{'RESULT'} eq '0' ) {
184 $self->is_success(1);
186 $self->is_success(0);
196 Business::OnlinePayment::PayflowPro - Payflow Pro backend for Business::OnlinePayment
200 use Business::OnlinePayment;
202 my $tx = new Business::OnlinePayment(
204 'vendor' => 'your_vendor',
205 'partner' => 'your_partner',
206 'cert_path' => '/path/to/your/certificate/file/', # just the dir
209 # See the module documentation for details of content()
212 action => 'Normal Authorization',
213 description => 'Business::OnlinePayment::PayflowPro test',
215 invoice_number => '100100',
216 customer_id => 'jsk',
217 name => 'Jason Kohles',
218 address => '123 Anystreet',
222 email => 'ivan-payflowpro@420.am',
223 card_number => '4111111111111111',
224 expiration => '12/09',
226 order_number => 'string',
231 if ( $tx->is_success() ) {
233 "Card processed successfully: ", $tx->authorization, "\n",
234 "order number: ", $tx->order_number, "\n",
235 "CVV2 code: ", $tx->cvv2_code, "\n",
236 "AVS code: ", $tx->avs_code, "\n",
241 $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
244 "Card was rejected: ", $tx->error_message, $info, "\n",
245 "order number: ", $tx->order_number, "\n",
251 This module is a back end driver that implements the interface
252 specified by L<Business::OnlinePayment> to support payment handling
253 via the PayPal's Payflow Pro Internet payment solution.
255 See L<Business::OnlinePayment> for details on the interface this
258 =head1 Module specific methods
260 This module provides the following methods which are not currently
261 part of the standard Business::OnlinePayment interface:
271 =item L<order_number()|/order_number()>
273 =item L<avs_code()|/avs_code()>
275 =item L<cvv2_code()|/cvv2_code()>
281 The following default settings exist:
287 payflow.verisign.com or test-payflow.verisign.com if
288 test_transaction() is TRUE
296 =head1 Handling of content(%content)
298 The following rules apply to content(%content) data:
302 If 'action' matches one of the following keys it is replaced by the
303 right hand side value:
305 'normal authorization' => 'S', # Sale transaction
306 'credit' => 'C', # Credit (refund)
307 'authorization only' => 'A', # Authorization
308 'post authorization' => 'D', # Delayed Capture
311 If 'action' is 'C', 'D' or 'V' and 'order_number' is not set then
312 'amount', 'card_number' and 'expiration' must be set.
316 If 'type' matches one of the following keys it is replaced by the
317 right hand side value:
321 'american express' => 'C',
325 The value of 'type' is used to set transaction_type(). Currently this
326 module only supports a transaction_type() of 'C' any other values will
327 cause Carp::croak() to be called in submit().
329 Note: Payflow Pro supports multiple credit card types, including:
330 American Express/Optima, Diners Club, Discover/Novus, Enroute, JCB,
333 =head1 Setting Payflow Pro parameters from content(%content)
335 The following rules are applied to map data to Payflow Pro parameters
336 from content(%content):
338 # PFP param => $content{<key>}
339 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
340 PARTNER => \( $self->partner ),
345 ORIGID => 'order_number',
346 COMMENT1 => 'description',
347 COMMENT2 => 'invoice_number',
349 ACCT => 'card_number',
351 EXPDATE => \( $month.$year ), # MM/YY from 'expiration'
354 FIRSTNAME => 'first_name',
355 LASTNAME => 'last_name',
358 COMPANYNAME => 'company',
362 ZIP => \$zip, # 'zip' with non-numbers removed
363 COUNTRY => 'country',
365 The required Payflow Pro parameters for credit card transactions are:
367 TRXTYPE TENDER PARTNER VENDOR USER PWD ORIGID
369 =head1 Mapping Payflow Pro transaction responses to object methods
371 The following methods provides access to the transaction response data
372 resulting from a Payflow Pro request (after submit()) is called:
374 =head2 order_number()
376 This order_number() method returns the PNREF field, also known as the
377 PayPal Reference ID, which is a unique number that identifies the
382 The result_code() method returns the RESULT field, which is the
383 numeric return code indicating the outcome of the attempted
386 A RESULT of 0 (zero) indicates the transaction was approved and
387 is_success() will return '1' (one/TRUE). Any other RESULT value
388 indicates a decline or error and is_success() will return '0'
391 =head2 error_message()
393 The error_message() method returns the RESPMSG field, which is a
394 response message returned with the transaction result.
396 =head2 authorization()
398 The authorization() method returns the AUTHCODE field, which is the
399 approval code obtained from the processing network.
403 The avs_code() method returns a combination of the AVSADDR and AVSZIP
404 fields from the transaction result. The value in avs_code is as
407 Y - Address and ZIP match
408 A - Address matches but not ZIP
409 Z - ZIP matches but not address
411 undef - AVS values not available
415 The cvv2_code() method returns the CVV2MATCH field, which is a
416 response message returned with the transaction result.
420 This module implements an interface to the Payflow Pro Perl API, which
421 can be downloaded at https://manager.paypal.com/ with a valid login.
425 Ivan Kohler <ivan-payflowpro@420.am>
427 Based on Business::OnlinePayment::AuthorizeNet written by Jason Kohles.
431 perl(1), L<Business::OnlinePayment>, L<Carp>, and the PayPal
432 Integration Center Payflow Pro resources at
433 L<https://www.paypal.com/IntegrationCenter/ic_payflowpro.html>