1 package Business::OnlinePayment::PayflowPro;
4 use vars qw($VERSION $DEBUG);
5 use Carp qw(carp croak);
9 use base qw(Business::OnlinePayment::HTTPS);
12 $VERSION = eval $VERSION;
17 my $md5 = Digest::MD5->new();
18 $md5->add( $$, time(), rand(time) );
19 return $md5->hexdigest();
26 my $level = shift || 0;
28 $self->{"__DEBUG"} = $level;
33 $Business::OnlinePayment::HTTPS::DEBUG = $level;
35 return ref($self) ? ( $self->{"__DEBUG"} || $DEBUG ) : $DEBUG;
42 # standard B::OP methods/data
43 #$self->server("payflow.verisign.com");
44 $self->server("payflowpro.verisign.com");
46 $self->path("/transaction");
49 partner vendor client_certification_id
52 order_number avs_code cvv2_response
53 response_page response_code response_headers
56 # module specific data
58 $self->debug( $opts{debug} );
62 $self->test_server( "pilot-payflowpro.verisign.com" );
69 my %content = $self->content();
73 'normal authorization' => 'S', # Sale transaction
74 'credit' => 'C', # Credit (refund)
75 'authorization only' => 'A', # Authorization
76 'post authorization' => 'D', # Delayed Capture
80 $content{'action'} = $actions{ lc( $content{'action'} ) }
81 || $content{'action'};
87 'american express' => 'C',
94 $content{'type'} = $types{ lc( $content{'type'} ) } || $content{'type'};
96 $self->transaction_type( $content{'type'} );
98 # stuff it back into %content
99 $self->content(%content);
103 my ( $self, %map ) = @_;
104 my %content = $self->content();
105 foreach ( keys %map ) {
109 : $content{ $map{$_} };
111 $self->content(%content);
116 my $expiration = shift;
118 if ( defined($expiration) and $expiration =~ /^(\d+)\D+\d*(\d{2})$/ ) {
119 my ( $month, $year ) = ( $1, $2 );
120 $expdate_mmyy = sprintf( "%02d", $month ) . $year;
122 return defined($expdate_mmyy) ? $expdate_mmyy : $expiration;
128 $self->_map_fields();
130 my %content = $self->content;
132 if ( $self->transaction_type() ne 'C' ) {
133 croak( "PayflowPro can't (yet?) handle transaction type: "
134 . $self->transaction_type() );
137 my $expdate_mmyy = $self->expdate_mmyy( $content{"expiration"} );
138 my $zip = $content{'zip'};
139 $zip =~ s/[^[:alnum:]]//g;
141 $self->server( $self->test_server ) if $self->test_transaction;
143 my $vendor = $self->vendor;
144 my $partner = $self->partner;
146 $self->_revmap_fields(
148 # BUG?: VENDOR B::OP:PayflowPro < 0.05 backward compatibility. If
149 # vendor not set use login although test indicate undef vendor is ok
150 VENDOR => $vendor ? \$vendor : 'login',
151 PARTNER => \$partner,
156 ORIGID => 'order_number',
157 COMMENT1 => 'description',
158 COMMENT2 => 'invoice_number',
160 ACCT => 'card_number',
162 EXPDATE => \$expdate_mmyy, # MM/YY from 'expiration'
165 FIRSTNAME => 'first_name',
166 LASTNAME => 'last_name',
169 COMPANYNAME => 'company',
173 ZIP => \$zip, # 'zip' with non-alnums removed
174 COUNTRY => 'country',
177 my @required = qw( TRXTYPE TENDER PARTNER VENDOR USER PWD );
178 if ( $self->transaction_type() eq 'C' ) { # credit card
179 if ( $content{'action'} =~ /^[CDV]$/
180 && defined( $content{'ORIGID'} )
181 && length( $content{'ORIGID'} ) )
183 push @required, qw(ORIGID);
187 # never get here, we croak above if transaction_type ne 'C'
188 push @required, qw(AMT ACCT EXPDATE);
191 $self->required_fields(@required);
193 my %params = $self->get_fields(
195 VENDOR PARTNER USER PWD TRXTYPE TENDER ORIGID COMMENT1 COMMENT2
196 ACCT CVV2 EXPDATE AMT
197 FIRSTNAME LASTNAME NAME EMAIL COMPANYNAME
198 STREET CITY STATE ZIP COUNTRY
202 # get header data, get request_id from %content if defined for ease of use
203 my %req_headers = %{ $self->headers || {} };
204 if ( defined $content{"request_id"} ) {
205 $req_headers{"X-VPS-Request-ID"} = $content{"request_id"};
207 unless ( defined( $req_headers{"X-VPS-Request-ID"} ) ) {
208 $req_headers{"X-VPS-Request-ID"} = $self->request_id();
211 unless ( defined( $req_headers{"X-VPS-VIT-Client-Certification-Id"} ) ) {
212 $req_headers{"X-VPS-VIT-Client-Certification-Id"} =
213 $self->client_certification_id;
217 "Content-Type" => "text/namevalue",
218 "headers" => \%req_headers,
221 my ( $page, $resp, %resp_headers ) =
222 $self->https_post( \%options, \%params );
224 $self->response_code( $resp );
225 $self->response_page( $page );
226 $self->response_headers( \%resp_headers );
228 # $page should contain name=value[[&name=value]...] pairs
229 my $cgi = CGI->new("$page");
231 # AVS and CVS values may be set on success or failure
233 if ( defined $cgi->param("AVSADDR") or defined $cgi->param("AVSZIP") ) {
234 if ( $cgi->param("AVSADDR") eq "Y" && $cgi->param("AVSZIP") eq "Y" ) {
237 elsif ( $cgi->param("AVSADDR") eq "Y" ) {
240 elsif ( $cgi->param("AVSZIP") eq "Y" ) {
243 elsif ( $cgi->param("AVSADDR") eq "N" or $cgi->param("AVSZIP") eq "N" )
252 $self->avs_code($avs_code);
253 $self->cvv2_response( $cgi->param("CVV2MATCH") );
254 $self->result_code( $cgi->param("RESULT") );
255 $self->order_number( $cgi->param("PNREF") );
256 $self->error_message( $cgi->param("RESPMSG") );
257 $self->authorization( $cgi->param("AUTHCODE") );
259 # RESULT must be an explicit zero, not just numerically equal
260 if ( $cgi->param("RESULT") eq "0" ) {
261 $self->is_success(1);
264 $self->is_success(0);
274 Business::OnlinePayment::PayflowPro - Payflow Pro backend for Business::OnlinePayment
278 use Business::OnlinePayment;
280 my $tx = new Business::OnlinePayment(
282 'vendor' => 'your_vendor',
283 'partner' => 'your_partner',
284 'client_certification_id' => 'assigned_certification_id',
287 # See the module documentation for details of content()
290 action => 'Normal Authorization',
291 description => 'Business::OnlinePayment::PayflowPro test',
293 invoice_number => '100100',
294 customer_id => 'jsk',
295 name => 'Jason Kohles',
296 address => '123 Anystreet',
300 email => 'ivan-payflowpro@420.am',
301 card_number => '4111111111111111',
302 expiration => '12/09',
304 order_number => 'string',
305 request_id => 'unique_identifier_for_transaction',
310 if ( $tx->is_success() ) {
312 "Card processed successfully: ", $tx->authorization, "\n",
313 "order number: ", $tx->order_number, "\n",
314 "CVV2 response: ", $tx->cvv2_response, "\n",
315 "AVS code: ", $tx->avs_code, "\n",
320 $info = " (CVV2 mismatch)" if ( $tx->result_code == 114 );
323 "Card was rejected: ", $tx->error_message, $info, "\n",
324 "order number: ", $tx->order_number, "\n",
330 This module is a back end driver that implements the interface
331 specified by L<Business::OnlinePayment> to support payment handling
332 via the PayPal's Payflow Pro Internet payment solution.
334 See L<Business::OnlinePayment> for details on the interface this
337 =head1 Standard methods
343 This method sets the 'server' attribute to 'payflowpro.verisign.com' and
344 the port attribute to '443'. This method also sets up the
345 L</Module specific methods> described below.
351 =head1 Unofficial methods
353 This module provides the following methods which are not officially part of the
354 standard Business::OnlinePayment interface (as of 3.00_06) but are nevertheless
355 supported by multiple gateways modules and expected to be standardized soon:
359 =item L<order_number()|/order_number()>
361 =item L<avs_code()|/avs_code()>
363 =item L<cvv2_response()|/cvv2_response()>
367 =head1 Module specific methods
369 This module provides the following methods which are not currently
370 part of the standard Business::OnlinePayment interface:
374 =item L<expdate_mmyy()|/expdate_mmyy()>
376 =item L<requeset_id()/request_id()>
378 =item L<debug()|/debug()>
382 =head2 Deprecated methods
384 The following methods are deprecated and may be removed in a future
385 release. Values for vendor and partner should now be set as arguments to
386 Business::OnlinePayment->new(). The value for cert_path was used to support
387 passing a path to PFProAPI.pm (a Perl module/SDK from Verisign/Paypal) which is
402 The following default settings exist:
408 payflowpro.verisign.com or pilot-payflowpro.verisign.com if
409 test_transaction() is TRUE
417 =head1 Handling of content(%content)
419 The following rules apply to content(%content) data:
423 If 'action' matches one of the following keys it is replaced by the
424 right hand side value:
426 'normal authorization' => 'S', # Sale transaction
427 'credit' => 'C', # Credit (refund)
428 'authorization only' => 'A', # Authorization
429 'post authorization' => 'D', # Delayed Capture
432 If 'action' is 'C', 'D' or 'V' and 'order_number' is not set then
433 'amount', 'card_number' and 'expiration' must be set.
437 If 'type' matches one of the following keys it is replaced by the
438 right hand side value:
442 'american express' => 'C',
446 The value of 'type' is used to set transaction_type(). Currently this
447 module only supports a transaction_type() of 'C' any other values will
448 cause Carp::croak() to be called in submit().
450 Note: Payflow Pro supports multiple credit card types, including:
451 American Express/Optima, Diners Club, Discover/Novus, Enroute, JCB,
454 =head1 Setting Payflow Pro parameters from content(%content)
456 The following rules are applied to map data to Payflow Pro parameters
457 from content(%content):
459 # PFP param => $content{<key>}
460 VENDOR => $self->vendor ? \( $self->vendor ) : 'login',
461 PARTNER => \( $self->partner ),
466 ORIGID => 'order_number',
467 COMMENT1 => 'description',
468 COMMENT2 => 'invoice_number',
470 ACCT => 'card_number',
472 EXPDATE => \( $month.$year ), # MM/YY from 'expiration'
475 FIRSTNAME => 'first_name',
476 LASTNAME => 'last_name',
479 COMPANYNAME => 'company',
483 ZIP => \$zip, # 'zip' with non-alphanumerics removed
484 COUNTRY => 'country',
486 The required Payflow Pro parameters for credit card transactions are:
488 TRXTYPE TENDER PARTNER VENDOR USER PWD ORIGID
490 =head1 Mapping Payflow Pro transaction responses to object methods
492 The following methods provides access to the transaction response data
493 resulting from a Payflow Pro request (after submit()) is called:
495 =head2 order_number()
497 This order_number() method returns the PNREF field, also known as the
498 PayPal Reference ID, which is a unique number that identifies the
503 The result_code() method returns the RESULT field, which is the
504 numeric return code indicating the outcome of the attempted
507 A RESULT of 0 (zero) indicates the transaction was approved and
508 is_success() will return '1' (one/TRUE). Any other RESULT value
509 indicates a decline or error and is_success() will return '0'
512 =head2 error_message()
514 The error_message() method returns the RESPMSG field, which is a
515 response message returned with the transaction result.
517 =head2 authorization()
519 The authorization() method returns the AUTHCODE field, which is the
520 approval code obtained from the processing network.
524 The avs_code() method returns a combination of the AVSADDR and AVSZIP
525 fields from the transaction result. The value in avs_code is as
528 Y - Address and ZIP match
529 A - Address matches but not ZIP
530 Z - ZIP matches but not address
532 undef - AVS values not available
534 =head2 cvv2_response()
536 The cvv2_response() method returns the CVV2MATCH field, which is a
537 response message returned with the transaction result.
539 =head2 expdate_mmyy()
541 The expdate_mmyy() method takes a single scalar argument (typically
542 the value in $content{expiration}) and attempts to parse and format
543 and put the date in MMYY format as required by PayflowPro
544 specification. If unable to parse the expiration date simply leave it
545 as is and let the PayflowPro system attempt to handle it as-is.
549 The request_id() method uses Digest::MD5 to attempt to generate a
550 request_id for a transaction. It is recommended that you specify your
551 own unique request_id for each transaction in %content. A request_id
552 is REQUIRED by the PayflowPro processor.
556 Enable or disble debugging. The value specified here will also set
557 $Business::OnlinePayment::HTTPS::DEBUG in submit() to aid in
558 troubleshooting problems.
562 This module implements an interface to the Payflow Pro Perl API, which
563 can be downloaded at https://manager.paypal.com/ with a valid login.
567 Ivan Kohler <ivan-payflowpro@420.am>
569 Phil Lobbes E<lt>phil at perkpartners.comE<gt>
571 Based on Business::OnlinePayment::AuthorizeNet written by Jason Kohles.
575 perl(1), L<Business::OnlinePayment>, L<Carp>, and the PayPal
576 Integration Center Payflow Pro resources at
577 L<https://www.paypal.com/IntegrationCenter/ic_payflowpro.html>