1 package Business::OnlinePayment::PayflowPro;
4 use vars qw($VERSION $DEBUG);
5 use Carp qw(carp croak);
8 use Business::OnlinePayment::HTTPS 0.06;
10 use base qw(Business::OnlinePayment::HTTPS);
13 $VERSION = eval $VERSION;
18 my $md5 = Digest::MD5->new();
19 $md5->add( $$, time(), rand(time) );
20 return $md5->hexdigest();
27 my $level = shift || 0;
29 $self->{"__DEBUG"} = $level;
34 $Business::OnlinePayment::HTTPS::DEBUG = $level;
36 return ref($self) ? ( $self->{"__DEBUG"} || $DEBUG ) : $DEBUG;
43 # standard B::OP methods/data
44 #$self->server("payflow.verisign.com");
45 $self->server("payflowpro.verisign.com");
47 $self->path("/transaction");
50 partner vendor client_certification_id
53 order_number avs_code cvv2_response
54 response_page response_code response_headers
57 # module specific data
59 $self->debug( $opts{debug} );
63 $self->test_server( "pilot-payflowpro.verisign.com" );
70 my %content = $self->content();
74 'normal authorization' => 'S', # Sale transaction
75 'credit' => 'C', # Credit (refund)
76 'authorization only' => 'A', # Authorization
77 'post authorization' => 'D', # Delayed Capture
81 $content{'action'} = $actions{ lc( $content{'action'} ) }
82 || $content{'action'};
88 'american express' => 'C',
95 $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
97 $self->transaction_type( $content{'type'} );
99 # stuff it back into %content
100 $self->content(%content);
104 my ( $self, %map ) = @_;
105 my %content = $self->content();
106 foreach ( keys %map ) {
110 : $content{ $map{$_} };
112 $self->content(%content);
117 my $expiration = shift;
119 if ( defined($expiration) and $expiration =~ /^(\d+)\D+\d*(\d{2})$/ ) {
120 my ( $month, $year ) = ( $1, $2 );
121 $expdate_mmyy = sprintf( "%02d", $month ) . $year;
123 return defined($expdate_mmyy) ? $expdate_mmyy : $expiration;
129 $self->_map_fields();
131 my %content = $self->content;
133 if ( $self->transaction_type() ne 'C' ) {
134 croak( "PayflowPro can't (yet?) handle transaction type: "
135 . $self->transaction_type() );
138 my $expdate_mmyy = $self->expdate_mmyy( $content{"expiration"} );
139 my $zip = $content{'zip'};
140 $zip =~ s/[^[:alnum:]]//g;
142 $self->server( $self->test_server ) if $self->test_transaction;
144 my $vendor = $self->vendor;
145 my $partner = $self->partner;
147 $self->_revmap_fields(
149 # BUG?: VENDOR B::OP:PayflowPro < 0.05 backward compatibility. If
150 # vendor not set use login although test indicate undef vendor is ok
151 VENDOR => $vendor ? \$vendor : 'login',
152 PARTNER => \$partner,
157 ORIGID => 'order_number',
158 COMMENT1 => 'description',
159 COMMENT2 => 'invoice_number',
161 ACCT => 'card_number',
163 EXPDATE => \$expdate_mmyy, # MM/YY from 'expiration'
166 FIRSTNAME => 'first_name',
167 LASTNAME => 'last_name',
170 COMPANYNAME => 'company',
174 ZIP => \$zip, # 'zip' with non-alnums removed
175 COUNTRY => 'country',
178 my @required = qw( TRXTYPE TENDER PARTNER VENDOR USER PWD );
179 if ( $self->transaction_type() eq 'C' ) { # credit card
180 if ( $content{'action'} =~ /^[CDV]$/
181 && defined( $content{'ORIGID'} )
182 && length( $content{'ORIGID'} ) )
184 push @required, qw(ORIGID);
188 # never get here, we croak above if transaction_type ne 'C'
189 push @required, qw(AMT ACCT EXPDATE);
192 $self->required_fields(@required);
194 my %params = $self->get_fields(
196 VENDOR PARTNER USER PWD TRXTYPE TENDER ORIGID COMMENT1 COMMENT2
197 ACCT CVV2 EXPDATE AMT
198 FIRSTNAME LASTNAME NAME EMAIL COMPANYNAME
199 STREET CITY STATE ZIP COUNTRY
203 # get header data, get request_id from %content if defined for ease of use
204 my %req_headers = %{ $self->headers || {} };
205 if ( defined $content{"request_id"} ) {
206 $req_headers{"X-VPS-Request-ID"} = $content{"request_id"};
208 unless ( defined( $req_headers{"X-VPS-Request-ID"} ) ) {
209 $req_headers{"X-VPS-Request-ID"} = $self->request_id();
212 unless ( defined( $req_headers{"X-VPS-VIT-Client-Certification-Id"} ) ) {
213 $req_headers{"X-VPS-VIT-Client-Certification-Id"} =
214 $self->client_certification_id;
218 "Content-Type" => "text/namevalue",
219 "headers" => \%req_headers,
222 my ( $page, $resp, %resp_headers ) =
223 $self->https_post( \%options, \%params );
225 $self->response_code( $resp );
226 $self->response_page( $page );
227 $self->response_headers( \%resp_headers );
229 # $page should contain name=value[[&name=value]...] pairs
230 my $cgi = CGI->new("$page");
232 # AVS and CVS values may be set on success or failure
234 if ( defined $cgi->param("AVSADDR") or defined $cgi->param("AVSZIP") ) {
235 if ( $cgi->param("AVSADDR") eq "Y" && $cgi->param("AVSZIP") eq "Y" ) {
238 elsif ( $cgi->param("AVSADDR") eq "Y" ) {
241 elsif ( $cgi->param("AVSZIP") eq "Y" ) {
244 elsif ( $cgi->param("AVSADDR") eq "N" or $cgi->param("AVSZIP") eq "N" )
253 $self->avs_code($avs_code);
254 $self->cvv2_response( $cgi->param("CVV2MATCH") );
255 $self->result_code( $cgi->param("RESULT") );
256 $self->order_number( $cgi->param("PNREF") );
257 $self->error_message( $cgi->param("RESPMSG") );
258 $self->authorization( $cgi->param("AUTHCODE") );
260 # RESULT must be an explicit zero, not just numerically equal
261 if ( $cgi->param("RESULT") eq "0" ) {
262 $self->is_success(1);
265 $self->is_success(0);
275 Business::OnlinePayment::PayflowPro - Payflow Pro backend for Business::OnlinePayment
279 use Business::OnlinePayment;
281 my $tx = new Business::OnlinePayment(
283 'vendor' => 'your_vendor',
284 'partner' => 'your_partner',
285 'client_certification_id' => 'assigned_certification_id',
288 # See the module documentation for details of content()
291 action => 'Normal Authorization',
292 description => 'Business::OnlinePayment::PayflowPro test',
294 invoice_number => '100100',
295 customer_id => 'jsk',
296 name => 'Jason Kohles',
297 address => '123 Anystreet',
301 email => 'ivan-payflowpro@420.am',
302 card_number => '4111111111111111',
303 expiration => '12/09',
305 order_number => 'string',
306 request_id => 'unique_identifier_for_transaction',
311 if ( $tx->is_success() ) {
313 "Card processed successfully: ", $tx->authorization, "\n",
314 "order number: ", $tx->order_number, "\n",
315 "CVV2 response: ", $tx->cvv2_response, "\n",
316 "AVS code: ", $tx->avs_code, "\n",
321 $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
324 "Card was rejected: ", $tx->error_message, $info, "\n",
325 "order number: ", $tx->order_number, "\n",
331 This module is a back end driver that implements the interface
332 specified by L<Business::OnlinePayment> to support payment handling
333 via the PayPal's Payflow Pro Internet payment solution.
335 See L<Business::OnlinePayment> for details on the interface this
338 =head1 Standard methods
344 This method sets the 'server' attribute to 'payflowpro.verisign.com' and
345 the port attribute to '443'. This method also sets up the
346 L</Module specific methods> described below.
352 =head1 Unofficial methods
354 This module provides the following methods which are not officially part of the
355 standard Business::OnlinePayment interface (as of 3.00_06) but are nevertheless
356 supported by multiple gateways modules and expected to be standardized soon:
360 =item L<order_number()|/order_number()>
362 =item L<avs_code()|/avs_code()>
364 =item L<cvv2_response()|/cvv2_response()>
368 =head1 Module specific methods
370 This module provides the following methods which are not currently
371 part of the standard Business::OnlinePayment interface:
375 =item L<expdate_mmyy()|/expdate_mmyy()>
377 =item L<requeset_id()/request_id()>
379 =item L<debug()|/debug()>
383 =head2 Deprecated methods
385 The following methods are deprecated and may be removed in a future
386 release. Values for vendor and partner should now be set as arguments to
387 Business::OnlinePayment->new(). The value for cert_path was used to support
388 passing a path to PFProAPI.pm (a Perl module/SDK from Verisign/Paypal) which is
403 The following default settings exist:
409 payflowpro.verisign.com or pilot-payflowpro.verisign.com if
410 test_transaction() is TRUE
418 =head1 Handling of content(%content)
420 The following rules apply to content(%content) data:
424 If 'action' matches one of the following keys it is replaced by the
425 right hand side value:
427 'normal authorization' => 'S', # Sale transaction
428 'credit' => 'C', # Credit (refund)
429 'authorization only' => 'A', # Authorization
430 'post authorization' => 'D', # Delayed Capture
433 If 'action' is 'C', 'D' or 'V' and 'order_number' is not set then
434 'amount', 'card_number' and 'expiration' must be set.
438 If 'type' matches one of the following keys it is replaced by the
439 right hand side value:
443 'american express' => 'C',
447 The value of 'type' is used to set transaction_type(). Currently this
448 module only supports a transaction_type() of 'C' any other values will
449 cause Carp::croak() to be called in submit().
451 Note: Payflow Pro supports multiple credit card types, including:
452 American Express/Optima, Diners Club, Discover/Novus, Enroute, JCB,
455 =head1 Setting Payflow Pro parameters from content(%content)
457 The following rules are applied to map data to Payflow Pro parameters
458 from content(%content):
460 # PFP param => $content{<key>}
461 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
462 PARTNER => \( $self->partner ),
467 ORIGID => 'order_number',
468 COMMENT1 => 'description',
469 COMMENT2 => 'invoice_number',
471 ACCT => 'card_number',
473 EXPDATE => \( $month.$year ), # MM/YY from 'expiration'
476 FIRSTNAME => 'first_name',
477 LASTNAME => 'last_name',
480 COMPANYNAME => 'company',
484 ZIP => \$zip, # 'zip' with non-alphanumerics removed
485 COUNTRY => 'country',
487 The required Payflow Pro parameters for credit card transactions are:
489 TRXTYPE TENDER PARTNER VENDOR USER PWD ORIGID
491 =head1 Mapping Payflow Pro transaction responses to object methods
493 The following methods provides access to the transaction response data
494 resulting from a Payflow Pro request (after submit()) is called:
496 =head2 order_number()
498 This order_number() method returns the PNREF field, also known as the
499 PayPal Reference ID, which is a unique number that identifies the
504 The result_code() method returns the RESULT field, which is the
505 numeric return code indicating the outcome of the attempted
508 A RESULT of 0 (zero) indicates the transaction was approved and
509 is_success() will return '1' (one/TRUE). Any other RESULT value
510 indicates a decline or error and is_success() will return '0'
513 =head2 error_message()
515 The error_message() method returns the RESPMSG field, which is a
516 response message returned with the transaction result.
518 =head2 authorization()
520 The authorization() method returns the AUTHCODE field, which is the
521 approval code obtained from the processing network.
525 The avs_code() method returns a combination of the AVSADDR and AVSZIP
526 fields from the transaction result. The value in avs_code is as
529 Y - Address and ZIP match
530 A - Address matches but not ZIP
531 Z - ZIP matches but not address
533 undef - AVS values not available
535 =head2 cvv2_response()
537 The cvv2_response() method returns the CVV2MATCH field, which is a
538 response message returned with the transaction result.
540 =head2 expdate_mmyy()
542 The expdate_mmyy() method takes a single scalar argument (typically
543 the value in $content{expiration}) and attempts to parse and format
544 and put the date in MMYY format as required by PayflowPro
545 specification. If unable to parse the expiration date simply leave it
546 as is and let the PayflowPro system attempt to handle it as-is.
550 The request_id() method uses Digest::MD5 to attempt to generate a
551 request_id for a transaction. It is recommended that you specify your
552 own unique request_id for each transaction in %content. A request_id
553 is REQUIRED by the PayflowPro processor.
557 Enable or disble debugging. The value specified here will also set
558 $Business::OnlinePayment::HTTPS::DEBUG in submit() to aid in
559 troubleshooting problems.
563 This module implements an interface to the Payflow Pro Perl API, which
564 can be downloaded at https://manager.paypal.com/ with a valid login.
568 Ivan Kohler <ivan-payflowpro@420.am>
570 Phil Lobbes E<lt>phil at perkpartners.comE<gt>
572 Based on Business::OnlinePayment::AuthorizeNet written by Jason Kohles.
576 perl(1), L<Business::OnlinePayment>, L<Carp>, and the PayPal
577 Integration Center Payflow Pro resources at
578 L<https://www.paypal.com/IntegrationCenter/ic_payflowpro.html>