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');
22 vendor partner cert_path order_number avs_code cvv2_code
30 my %content = $self->content();
34 'normal authorization' => 'S', # Sale transaction
35 'credit' => 'C', # Credit (refund)
36 'authorization only' => 'A', # Authorization
37 'post authorization' => 'D', # Delayed Capture
41 $content{'action'} = $actions{ lc( $content{'action'} ) }
42 || $content{'action'};
48 'american express' => 'C',
54 $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
56 $self->transaction_type( $content{'type'} );
58 # stuff it back into %content
59 $self->content(%content);
63 my ( $self, %map ) = @_;
65 my %content = $self->content();
66 foreach ( keys %map ) {
67 $content{ $map{$_} } = $content{$_};
69 $self->content(%content);
73 my ( $self, %map ) = @_;
74 my %content = $self->content();
75 foreach ( keys %map ) {
79 : $content{ $map{$_} };
81 $self->content(%content);
89 my %content = $self->content;
91 my ( $month, $year, $zip );
93 if ( $self->transaction_type() ne 'C' ) {
94 croak( "PayflowPro can't (yet?) handle transaction type: "
95 . $self->transaction_type() );
98 if ( defined( $content{'expiration'} ) && length( $content{'expiration'} ) )
100 $content{'expiration'} =~ /^(\d+)\D+\d*(\d{2})$/
101 or croak "unparsable expiration $content{expiration}";
103 ( $month, $year ) = ( $1, $2 );
104 $month = '0' . $month if $month =~ /^\d$/;
107 ( $zip = $content{'zip'} ) =~ s/[^[:alnum:]]//g;
109 $self->server('test-payflow.verisign.com') if $self->test_transaction;
111 $self->revmap_fields(
113 # (BUG?) VENDOR B::OP:PayflowPro < 0.05 backward compatibility. If
114 # vendor not set use login (although test indicate undef vendor is ok)
115 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
116 PARTNER => \( $self->partner ),
121 ORIGID => 'order_number',
122 COMMENT1 => 'description',
123 COMMENT2 => 'invoice_number',
125 ACCT => 'card_number',
127 EXPDATE => \( $month . $year ), # MM/YY from 'expiration'
130 FIRSTNAME => 'first_name',
131 LASTNAME => 'last_name',
134 COMPANYNAME => 'company',
138 ZIP => \$zip, # 'zip' with non-alnums removed
139 COUNTRY => 'country',
142 my @required = qw( TRXTYPE TENDER PARTNER VENDOR USER PWD );
143 if ( $self->transaction_type() eq 'C' ) { # credit card
144 if ( $content{'action'} =~ /^[CDV]$/
145 && defined( $content{'ORIGID'} )
146 && length( $content{'ORIGID'} ) )
148 push @required, qw(ORIGID);
151 # never get here, we croak above if transaction_type ne 'C'
152 push @required, qw(AMT ACCT EXPDATE);
155 $self->required_fields(@required);
157 my %params = $self->get_fields(
159 VENDOR PARTNER USER PWD TRXTYPE TENDER ORIGID COMMENT1 COMMENT2
160 ACCT CVV2 EXPDATE AMT
161 FIRSTNAME LASTNAME NAME EMAIL COMPANYNAME
162 STREET CITY STATE ZIP COUNTRY
166 $ENV{'PFPRO_CERT_PATH'} = $self->cert_path;
167 my ( $response, $resultstr ) =
168 pfpro( \%params, $self->server, $self->port );
170 # AVS and CVS values may be set on success or failure
172 if ( exists $response->{AVSADDR} || exists $response->{AVSZIP} ) {
173 if ( $response->{AVSADDR} eq 'Y' && $response->{AVSZIP} eq 'Y' ) {
176 elsif ( $response->{AVSADDR} eq 'Y' ) {
179 elsif ( $response->{AVSZIP} eq 'Y' ) {
182 elsif ( $response->{AVSADDR} eq 'N' || $response->{AVSZIP} eq 'N' ) {
190 $self->avs_code($avs_code);
191 $self->cvv2_code( $response->{'CVV2MATCH'} );
192 $self->result_code( $response->{'RESULT'} );
193 $self->order_number( $response->{'PNREF'} );
194 $self->error_message( $response->{'RESPMSG'} );
195 $self->authorization( $response->{'AUTHCODE'} );
197 # RESULT must be an explicit zero, not just numerically equal
198 if ( $response->{'RESULT'} eq '0' ) {
199 $self->is_success(1);
202 $self->is_success(0);
212 Business::OnlinePayment::PayflowPro - Payflow Pro backend for Business::OnlinePayment
216 use Business::OnlinePayment;
218 my $tx = new Business::OnlinePayment(
220 'vendor' => 'your_vendor',
221 'partner' => 'your_partner',
222 'cert_path' => '/path/to/your/certificate/file/', # just the dir
225 # See the module documentation for details of content()
228 action => 'Normal Authorization',
229 description => 'Business::OnlinePayment::PayflowPro test',
231 invoice_number => '100100',
232 customer_id => 'jsk',
233 name => 'Jason Kohles',
234 address => '123 Anystreet',
238 email => 'ivan-payflowpro@420.am',
239 card_number => '4111111111111111',
240 expiration => '12/09',
242 order_number => 'string',
247 if ( $tx->is_success() ) {
249 "Card processed successfully: ", $tx->authorization, "\n",
250 "order number: ", $tx->order_number, "\n",
251 "CVV2 code: ", $tx->cvv2_code, "\n",
252 "AVS code: ", $tx->avs_code, "\n",
257 $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
260 "Card was rejected: ", $tx->error_message, $info, "\n",
261 "order number: ", $tx->order_number, "\n",
267 This module is a back end driver that implements the interface
268 specified by L<Business::OnlinePayment> to support payment handling
269 via the PayPal's Payflow Pro Internet payment solution.
271 See L<Business::OnlinePayment> for details on the interface this
274 =head1 Module specific methods
276 This module provides the following methods which are not currently
277 part of the standard Business::OnlinePayment interface:
287 =item L<order_number()|/order_number()>
289 =item L<avs_code()|/avs_code()>
291 =item L<cvv2_code()|/cvv2_code()>
297 The following default settings exist:
303 payflow.verisign.com or test-payflow.verisign.com if
304 test_transaction() is TRUE
312 =head1 Handling of content(%content)
314 The following rules apply to content(%content) data:
318 If 'action' matches one of the following keys it is replaced by the
319 right hand side value:
321 'normal authorization' => 'S', # Sale transaction
322 'credit' => 'C', # Credit (refund)
323 'authorization only' => 'A', # Authorization
324 'post authorization' => 'D', # Delayed Capture
327 If 'action' is 'C', 'D' or 'V' and 'order_number' is not set then
328 'amount', 'card_number' and 'expiration' must be set.
332 If 'type' matches one of the following keys it is replaced by the
333 right hand side value:
337 'american express' => 'C',
341 The value of 'type' is used to set transaction_type(). Currently this
342 module only supports a transaction_type() of 'C' any other values will
343 cause Carp::croak() to be called in submit().
345 Note: Payflow Pro supports multiple credit card types, including:
346 American Express/Optima, Diners Club, Discover/Novus, Enroute, JCB,
349 =head1 Setting Payflow Pro parameters from content(%content)
351 The following rules are applied to map data to Payflow Pro parameters
352 from content(%content):
354 # PFP param => $content{<key>}
355 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
356 PARTNER => \( $self->partner ),
361 ORIGID => 'order_number',
362 COMMENT1 => 'description',
363 COMMENT2 => 'invoice_number',
365 ACCT => 'card_number',
367 EXPDATE => \( $month.$year ), # MM/YY from 'expiration'
370 FIRSTNAME => 'first_name',
371 LASTNAME => 'last_name',
374 COMPANYNAME => 'company',
378 ZIP => \$zip, # 'zip' with non-alphanumerics removed
379 COUNTRY => 'country',
381 The required Payflow Pro parameters for credit card transactions are:
383 TRXTYPE TENDER PARTNER VENDOR USER PWD ORIGID
385 =head1 Mapping Payflow Pro transaction responses to object methods
387 The following methods provides access to the transaction response data
388 resulting from a Payflow Pro request (after submit()) is called:
390 =head2 order_number()
392 This order_number() method returns the PNREF field, also known as the
393 PayPal Reference ID, which is a unique number that identifies the
398 The result_code() method returns the RESULT field, which is the
399 numeric return code indicating the outcome of the attempted
402 A RESULT of 0 (zero) indicates the transaction was approved and
403 is_success() will return '1' (one/TRUE). Any other RESULT value
404 indicates a decline or error and is_success() will return '0'
407 =head2 error_message()
409 The error_message() method returns the RESPMSG field, which is a
410 response message returned with the transaction result.
412 =head2 authorization()
414 The authorization() method returns the AUTHCODE field, which is the
415 approval code obtained from the processing network.
419 The avs_code() method returns a combination of the AVSADDR and AVSZIP
420 fields from the transaction result. The value in avs_code is as
423 Y - Address and ZIP match
424 A - Address matches but not ZIP
425 Z - ZIP matches but not address
427 undef - AVS values not available
431 The cvv2_code() method returns the CVV2MATCH field, which is a
432 response message returned with the transaction result.
436 This module implements an interface to the Payflow Pro Perl API, which
437 can be downloaded at https://manager.paypal.com/ with a valid login.
441 Ivan Kohler <ivan-payflowpro@420.am>
443 Phil Lobbes E<lt>phil at perkpartners.comE<gt>
445 Based on Business::OnlinePayment::AuthorizeNet written by Jason Kohles.
449 perl(1), L<Business::OnlinePayment>, L<Carp>, and the PayPal
450 Integration Center Payflow Pro resources at
451 L<https://www.paypal.com/IntegrationCenter/ic_payflowpro.html>