From f3c205158ec48799a7188bb3eb89a7230e49aee9 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 9 Aug 2016 11:05:04 -0400 Subject: [PATCH] JBAS-1503 PayPal Layout-A plus Silent POST Avoid iframe by sending patrons to PP to pay. PP posts results back to EG via Silent POST. Silent POSTs that fail result in a voided payment on the PP side. Signed-off-by: Bill Erickson --- .../var/templates_kcls/opac/biblio/main_fines.tt2 | 28 ++- .../opac/biblio/main_refund_policy.tt2 | 4 +- .../var/templates_kcls/opac/payflow/errors.tt2 | 12 +- .../var/templates_kcls/opac/payflow/form1.tt2 | 109 +--------- .../var/templates_kcls/opac/payflow/form2.tt2 | 32 --- .../var/templates_kcls/opac/payflow/pay_form.tt2 | 80 +++---- .../templates_kcls/opac/payflow/silent_post.tt2 | 1 + .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm | 7 +- .../lib/OpenILS/WWW/EGCatLoader/Account.pm | 242 ++++++++++----------- .../lib/OpenILS/WWW/EGCatLoader/PayflowHosted.pm | 56 +++-- Open-ILS/src/templates/opac/css/style.css.tt2 | 8 + Open-ILS/web/css/skin/default/opac/style.css | 8 + 12 files changed, 259 insertions(+), 328 deletions(-) delete mode 100644 KCLS/openils/var/templates_kcls/opac/payflow/form2.tt2 create mode 100644 KCLS/openils/var/templates_kcls/opac/payflow/silent_post.tt2 diff --git a/KCLS/openils/var/templates_kcls/opac/biblio/main_fines.tt2 b/KCLS/openils/var/templates_kcls/opac/biblio/main_fines.tt2 index 3bad65b5ca..649c08ae38 100644 --- a/KCLS/openils/var/templates_kcls/opac/biblio/main_fines.tt2 +++ b/KCLS/openils/var/templates_kcls/opac/biblio/main_fines.tt2 @@ -22,6 +22,14 @@ pay_form_url = ctx.opac_root _ '/payflow/pay_form'; END; %] + +[% IF ctx.payflow_hosted_ctx.pay_result_code %] + +
+ [% INCLUDE 'opac/payflow/errors.tt2' %] +
+[% END %] +
[% IF ctx.fines.circulation.size > 0 %]
@@ -40,7 +48,8 @@ [% l("Date Returned") %] [% l("Balance Owed") %] - @@ -92,7 +101,10 @@ - @@ -126,7 +138,8 @@ [% l("Billing Type") %] - @@ -159,9 +172,12 @@ [% f.xact.last_billing_type %] - + [% checked = NOT ctx.selected_xacts + || ctx.selected_xacts.grep(f.xact.id).0 %] + [% END %] diff --git a/KCLS/openils/var/templates_kcls/opac/biblio/main_refund_policy.tt2 b/KCLS/openils/var/templates_kcls/opac/biblio/main_refund_policy.tt2 index 74755a31c9..10976fe4cf 100644 --- a/KCLS/openils/var/templates_kcls/opac/biblio/main_refund_policy.tt2 +++ b/KCLS/openils/var/templates_kcls/opac/biblio/main_refund_policy.tt2 @@ -19,8 +19,6 @@ non-refundable items, visit http://kcls.org/faq/borrowing/#faq_1766 -

- This site uses VeriSign SSL encryption to ensure your - privacy. +
diff --git a/KCLS/openils/var/templates_kcls/opac/payflow/errors.tt2 b/KCLS/openils/var/templates_kcls/opac/payflow/errors.tt2 index 15dc23e848..7e04c590be 100644 --- a/KCLS/openils/var/templates_kcls/opac/payflow/errors.tt2 +++ b/KCLS/openils/var/templates_kcls/opac/payflow/errors.tt2 @@ -1,4 +1,10 @@ -
+
+ +There was a problem processing the credit card payment: +
+
+ +
[% # Map PayFlow POST response codes to patron messages. @@ -36,6 +42,8 @@ SWITCH ctx.payflow_hosted_ctx.RESULT; CASE DEFAULT; l('An unkown error occurred attempting credit card payment.'); END; - %]
+
+Click the 'Pay Fines' button to try the payment again. +
diff --git a/KCLS/openils/var/templates_kcls/opac/payflow/form1.tt2 b/KCLS/openils/var/templates_kcls/opac/payflow/form1.tt2 index 93906cbeba..15102afa58 100644 --- a/KCLS/openils/var/templates_kcls/opac/payflow/form1.tt2 +++ b/KCLS/openils/var/templates_kcls/opac/payflow/form1.tt2 @@ -1,105 +1,20 @@ - + - + - [% FOR xact IN CGI.param('xact') %] - - [% END %] - - [% FOR xact IN CGI.param('xact_misc') %] - - [% END %] + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + [% l('Cancel') %] + + +
[% l('First Name *') %]
[% l('Last Name *') %]
[% l('Email Address') %] - -
- [% l('Please use the address that is on your bank/credit card statement.') %] -
[% l('Street Address *') %]
[% l('City *')%]
[% l('State or Province *') %]
[% l('ZIP or Postal Code *') %]
- - - [% l('Cancel') %] - -
- - - diff --git a/KCLS/openils/var/templates_kcls/opac/payflow/form2.tt2 b/KCLS/openils/var/templates_kcls/opac/payflow/form2.tt2 deleted file mode 100644 index 3c89cbd1f4..0000000000 --- a/KCLS/openils/var/templates_kcls/opac/payflow/form2.tt2 +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - -
- - - [% IF ctx.payflow_hosted_error %] - - An error occurred communicating with PayPal. - Unable to process payment at this time. - - [% STOP %] - [% END %] - - [% - token = ctx.payflow_hosted_ctx.secure_token | uri; - token_id = ctx.payflow_hosted_ctx.secure_token_id | uri; - iframe_url = ctx.payflow_hosted_ctx.hosted_server _ - '?SECURETOKEN=' _ token _ '&SECURETOKENID=' _ token_id; - IF ctx.payflow_hosted_ctx.test_mode; - iframe_url = iframe_url _ '&MODE=TEST'; - END; - %] - - -
- diff --git a/KCLS/openils/var/templates_kcls/opac/payflow/pay_form.tt2 b/KCLS/openils/var/templates_kcls/opac/payflow/pay_form.tt2 index e5d539ce1d..303c9052a6 100644 --- a/KCLS/openils/var/templates_kcls/opac/payflow/pay_form.tt2 +++ b/KCLS/openils/var/templates_kcls/opac/payflow/pay_form.tt2 @@ -18,68 +18,58 @@
- -
-

[% l('KCLS only accepts Visa or MasterCard') %]

-
- [% l('Billing Information') %] -
-
+

[% l('KCLS only accepts Visa or MasterCard') %]

+
- [% IF ctx.payflow_hosted_ctx.secure_token_id - AND NOT ctx.payflow_hosted_ctx.pay_result_code %] - - - [% INCLUDE 'opac/payflow/form2.tt2' %] + [% IF ctx.payflow_hosted_ctx.init_error %] +
+ [% l('Error initializing credit card payments. Unable to make payments at this time.') %] +
[% ELSE %] +
+ - [% IF ctx.payflow_hosted_ctx.pay_result_code %] - - [% INCLUDE 'opac/payflow/errors.tt2' %] - [% ELSIF ctx.payflow_hosted_ctx.init_error %] -
- [% l('Error initializing credit card payments. Unable to make payments at this time.') %] -
- [% END %] - - - [% INCLUDE 'opac/payflow/form1.tt2' %] + + +
[% END %]
+ +
+
+ Click + + + [% l('Cancel') %] + + + to go back and change your selection. +
+
[% INCLUDE "opac/parts/myopac/payment_xacts.tt2" %] -
- - Click - - - [% l('Cancel') %] - - - to go back and change your selection. - -

diff --git a/KCLS/openils/var/templates_kcls/opac/payflow/silent_post.tt2 b/KCLS/openils/var/templates_kcls/opac/payflow/silent_post.tt2 new file mode 100644 index 0000000000..9f3458ace8 --- /dev/null +++ b/KCLS/openils/var/templates_kcls/opac/payflow/silent_post.tt2 @@ -0,0 +1 @@ +Payflow Silent POST Placeholder Page diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index 22d3361c11..dca6eb25e3 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -206,6 +206,11 @@ sub load { return $self->load_sms_cn if $skip_sms_auth; } + # Payflow silent post responses do not require a login, + # because they use a secondary secure token that acts + # as an auth token proxy. + return $self->load_myopac_payflow_silent_post if $path =~ m|opac/payflow/silent_post|; + # ---------------------------------------------------------------- # Everything below here requires authentication # ---------------------------------------------------------------- @@ -230,7 +235,6 @@ sub load { return $self->load_myopac_payments if $path =~ m|opac/myopac/main_payments|; return $self->load_myopac_pay_init if $path =~ m|opac/myopac/main_pay_init|; return $self->load_myopac_pay if $path =~ m|opac/myopac/main_pay|; - return $self->load_myopac_pay_response if $path =~ m|opac/myopac/pay_response|; return $self->load_myopac_main if $path =~ m|opac/myopac/main|; return $self->load_myopac_receipt_email if $path =~ m|opac/myopac/receipt_email|; return $self->load_myopac_receipt_print if $path =~ m|opac/myopac/receipt_print|; @@ -251,7 +255,6 @@ sub load { # PayflowHosted E-Com pages. return $self->load_myopac_payflow_form if $path =~ m|opac/payflow/pay_form|; - return $self->load_myopac_payflow_response if $path =~ m|opac/payflow/pay_response|; return $self->load_myopac_payflow_receipt if $path =~ m|opac/payflow/pay_receipt|; #BiblioCommons E-Commerce Screens diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index d9eb379822..2ac8f23f07 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -1,6 +1,6 @@ package OpenILS::WWW::EGCatLoader; use strict; use warnings; -use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST); +use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST HTTP_REQUEST_TIME_OUT); use OpenSRF::Utils::Logger qw/$logger/; use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Utils::Fieldmapper; @@ -1806,11 +1806,10 @@ sub load_myopac_payment_form { return Apache2::Const::OK; } -# UI for entering billing address, etc. data. +# UI for showing summary of fines to pay and form that directs +# patrons to the PP site for payment. sub load_myopac_payflow_form { my $self = shift; - my $token_id = $self->ctx->{page_args}->[0]; - my $tokens; my $stat = $self->prepare_extended_user_info; return $stat if $stat; @@ -1818,43 +1817,10 @@ sub load_myopac_payflow_form { # If this is a new payment, the transactions will come from GET params. my $xacts = [$self->cgi->param('xact'), $self->cgi->param('xact_misc')]; - if ($token_id) { - # We were directed back to the form1 page after a payment attempt - # at PP was rejected with a non-zero status. - # Re-display form1 with error info. - - my $cache = OpenSRF::Utils::Cache->new('global'); - $tokens = $cache->get_cache($token_id); - - if (!$tokens) { - - $logger->error("PayflowHosted payment was rejected, but ". - "we were unable to retrieve the payment context data ". - "from the cache. Unable to continue payment!"); - - $self->ctx->{payflow_hosted_ctx} = {error => 1}; - return Apache2::Const::OK; - } - - # After we render this page, this payment context is no longer valid. - $self->ctx->{payflow_hosted_ctx} = $tokens; - - # If this is an existing payment, pull the transactions from - # the payment context blob. - $xacts = $tokens->{xacts}; - } - $stat = $self->prepare_fines(undef, undef, $xacts); return $stat if $stat; - - if ($self->cgi->param('BILLTOLASTNAME')) { - # We received form1 POST data from the patron. - # Request a secure token from PP en route to form 2. - # TODO: check for reasonable values for all required fields. - $self->generate_payflow_secure_token($xacts); - } - + $self->generate_payflow_secure_token($xacts); return Apache2::Const::OK; } @@ -1889,27 +1855,15 @@ sub generate_payflow_secure_token { return unless $self->payflow_hosted_enabled; - # Collect all BILLTO* params from form1. - my %payflow_params = - map {$_ => $cgi->param($_)} - grep /^BILLTO/, $cgi->param; - - # amount comes from prepare_fines() - $payflow_params{AMT} = sprintf("%.2f", $ctx->{fines}->{balance_owed}); - - $payflow_params{COMMENT1} = - $ctx->{user}->card->barcode if $ctx->{user}->card; - # Generate the PayPal secure token. my $tokens = OpenILS::WWW::EGCatLoader::PayflowHosted::create_xact_token( + authtoken => $self->editor->authtoken, + user => $ctx->{user}, + amount => $ctx->{fines}->{balance_owed}, # from prepare_fines() billing_org => $org, - response_host => "https://" . $self->ctx->{hostname}, - payflow_params => \%payflow_params + response_host => "https://" . $self->ctx->{hostname} ); - # The payment form needs these when re-rendering values. - $tokens->{payflow_params} = \%payflow_params; - unless ($tokens && $tokens->{secure_token}) { # Let the template gracefully warn the user. $ctx->{payflow_hosted_ctx} = {init_error => 1}; @@ -1930,38 +1884,49 @@ sub generate_payflow_secure_token { $cache->put_cache($tokens->{secure_token_id}, $tokens, 1800); } -# Called from 3rd-party credit card processors to POST payment results data. -# Caller will not have an authentication token. -# This happens within an iframe. -# Either we process the PP POST response data and redirect the user to -# an intermidiate 'Processing...' page, or if that's already happened, -# create the payment internally, then let the iframe redirect the -# browser to the receipts page. -sub load_myopac_payflow_response { + +# See if the provided authtoken is still valid. If so, use it. +# Otherwise, create a temp auth token using open-ils.auth_internal. +# Returns undef on succcess, server error on failure. +sub create_tmp_auth { my $self = shift; - my $cgi = $self->cgi; - my $ctx = $self->ctx; + my $authtoken = shift; + my $user_id = shift; - my $token_id = $self->ctx->{page_args}->[0]; + my $e = $self->editor; - if ($token_id) { - # Payflow response has already been received. - # Create the payment internally now. - - $logger->info("PayflowHosted creating payment for token: $token_id"); - return $self->payflow_create_payment($token_id); + $e->authtoken($authtoken); + if ($e->checkauth) { # test token and set $e->requestor + $logger->info( + "PayflowHosted existing authtoken still valid for $user_id"); + return undef; + } - } else { - # Handling PayFlow POST response. Collect data, put - # it into the cache, then redirect the caller back to - # this page to finalize processing. + $logger->info("PayflowHosted generating temp auth token for $user_id"); + + my $evt = $U->simplereq( + 'open-ils.auth_internal', + 'open-ils.auth_internal.session.create', { + user_id => $user_id, + login_type => "temp" + } + ); - $logger->info("PayflowHosted processing POST results"); - return $self->handle_payflow_response; + if ($evt && $evt->{payload} && + ($authtoken = $evt->{payload}->{authtoken})) { + $e->authtoken($authtoken); + $e->checkauth; # sets $e->requestor + return undef; } + + $logger->error("PayflowHosted unable to generate temp auth ". + "session for user $user_id to complete credit card payment!"); + + return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; } -sub handle_payflow_response { + +sub load_myopac_payflow_silent_post { my $self = shift; my $cgi = $self->cgi; my $ctx = $self->ctx; @@ -1971,6 +1936,7 @@ sub handle_payflow_response { my $appr_code = $cgi->param('AUTHCODE'); my $token_id = $cgi->param('SECURETOKENID'); my $result = $cgi->param('RESULT'); + my $authtoken = $cgi->param('USER1'); # Toss the CGI params into a string for easier error logging. my $log_params = ''; @@ -1981,31 +1947,24 @@ sub handle_payflow_response { if (!$token_id) { $logger->error("PayflowHosted processor responsded with success but ". "failed to return a SECURETOKENID. Cannot complete payment!"); - $ctx->{payflow_hosted_ctx} = {error => 1}; - return Apache2::Const::OK; + return Apache2::Const::HTTP_BAD_REQUEST; } - my $cache = OpenSRF::Utils::Cache->new('global'); - my $tokens = $cache->get_cache($token_id); + my $tokens = $self->load_payflow_tokens($token_id); if (!$tokens) { - $logger->error("PayflowHosted payment succeeded, but no matching ". "transaction data found in memcache. Cannot complete payment!"); - $ctx->{payflow_hosted_ctx} = { - error => 1, - cc_args => {order_number => $order_number} - }; - - return Apache2::Const::OK; + # Token data was not found in the cache. + # Presumably the cache entry expired. + return Apache2::Const::HTTP_REQUEST_TIME_OUT; } $tokens->{$_} = $cgi->param($_) for (qw/RESULT PNREF AVSADDR AVSZIP PROCCVV2/); $tokens->{pay_result_code} = $result; - $ctx->{payflow_hosted_ctx} = $tokens; if ($result eq '0') { # Payment processed successfully at PP. Track the payment locally. @@ -2019,36 +1978,35 @@ sub handle_payflow_response { processor => 'PayflowHosted' }; - # Redirect to intermediate 'Processing...' page. - $self->ctx->{refresh} = "1; url=pay_response/$token_id"; - $ctx->{on_processing_page} = 1; + # This API is called directly from PP with no session cookie. + # Create a new temp auth session for the paying patron via + # open-ils.auth_internal. + my $stat = $self->create_tmp_auth($authtoken, $tokens->{user}); + return $stat if $stat; + + return $self->payflow_create_payment($token_id, $tokens); + } + # Payment failed. + if ($result < 0) { + $logger->error("PayflowHosted processor returned a". + "communication error response code=$result : $respmsg"); } else { - # Payment failed. Iframe will automatically redirect - # back to the form1 page to display the message allow - # for a re-pay attempt. - if ($result < 0) { - $logger->error("PayflowHosted processor returned a". - "communication error response code=$result : $respmsg"); - } else { - $logger->warn("PayflowHosted processor returned a non-success ". - "(but recoverable) response code=$result : $respmsg"); - } + $logger->warn("PayflowHosted processor returned a non-success ". + "(but recoverable) response code=$result : $respmsg"); } + # Cache the tokens once again so we can report errors + my $cache = OpenSRF::Utils::Cache->new('global'); $cache->put_cache($token_id, $tokens, 1800); return Apache2::Const::OK; } sub payflow_create_payment { - my ($self, $token_id) = @_; - + my ($self, $token_id, $tokens) = @_; my $cgi = $self->cgi; - my $ctx = $self->ctx; - my $cache = OpenSRF::Utils::Cache->new('global'); - my $tokens = $cache->get_cache($token_id); # Must be called before prepare_fines_for_payment(); $self->prepare_fines(undef, undef, $tokens->{xacts}); @@ -2066,25 +2024,22 @@ sub payflow_create_payment { "open-ils.circ", "open-ils.circ.money.payment", $self->editor->authtoken, $args, - $self->ctx->{user}->last_xact_id + $self->editor->requestor->last_xact_id ); - $self->ctx->{payment_response} = $resp; - $self->ctx->{token_id} = $token_id; - if ($resp->{textcode}) { - $logger->error("PayflowHosted CC internal payment tracking failed"); - } else { - $logger->info("PayflowHosted CC internal payment tracking succeeded"); - $logger->info("PayflowHosted created payments: ".Dumper($resp->{payments})); - $tokens->{payments} = $resp->{payments}; - } + $logger->error("PayflowHosted CC internal payment tracking ". + "failed with event code " . $resp->{textcode}); + # Tell PP to void the payment. + return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + } - $ctx->{payflow_hosted_ctx} = $tokens; + $logger->info("PayflowHosted CC internal payment tracking succeeded"); + $logger->info("PayflowHosted created payments: ".Dumper($resp->{payments})); + $tokens->{payments} = $resp->{payments}; - # Cache the payment info so we can generate a receipt on the receipts - # page, but only cache it for a minute so the token can expire. - $cache->put_cache($token_id, $tokens, 60); + # Cache the payment info to generate a receipt on the receipts page. + $cache->put_cache($token_id, $tokens, 1800); return Apache2::Const::OK; } @@ -2096,8 +2051,7 @@ sub load_myopac_payflow_receipt { my $token_id = $self->ctx->{page_args}->[0]; return Apache2::Const::HTTP_BAD_REQUEST unless $token_id; - my $cache = OpenSRF::Utils::Cache->new('global'); - my $tokens = $cache->get_cache($token_id); + my $tokens = $self->load_payflow_tokens($token_id); # this page is loaded immediately after the token is created. # if the cached data is not there, it's because of an invalid @@ -2111,10 +2065,6 @@ sub load_myopac_payflow_receipt { ); $self->ctx->{payflow_hosted_ctx} = $tokens; - - # NOTE: not deleting the cache data since it's set to expire - # within 60 seconds above. - return Apache2::Const::OK; } @@ -2463,8 +2413,10 @@ sub prepare_fines_for_payment { sub load_myopac_main { my $self = shift; + my $token_id = $self->ctx->{page_args}->[0]; my $limit = $self->cgi->param('limit') || 0; my $offset = $self->cgi->param('offset') || 0; + $self->ctx->{search_ou} = $self->_get_search_lib(); $self->ctx->{user}->notes( $self->editor->search_actor_usr_note({ @@ -2476,9 +2428,43 @@ sub load_myopac_main { # determines which payment form page the user is directed to. $self->ctx->{using_payflow} = $self->payflow_hosted_is_default(); + if ($self->ctx->{using_payflow} && $token_id) { + # If we have a token_id, it means a payment attempt failed. + # Load the cached token data so the template can decide what + # to do next. + my $tokens = $self->load_payflow_tokens($token_id); + + if ($tokens) { + # Select the transactions in the transaction list that were + # used for the failed payment attempt. + $self->ctx->{selected_xacts} = $tokens->{xacts}; + + } else { + + $logger->error("PayflowHosted payment failed, but we were ". + "unable to retrieve the payment context data from the ". + "cache. This may be due to the patron taking too long to ". + "complete the payment. Unable to resume payment!"); + + $self->ctx->{payflow_hosted_ctx} = {error => 1}; + } + } + return $self->prepare_fines($limit, $offset) || Apache2::Const::OK; } +sub load_payflow_tokens { + my ($self, $token_id) = @_; + + $logger->info( + "PayflowHosted loading cached payment info for token ID $token_id"); + + my $cache = OpenSRF::Utils::Cache->new('global'); + my $tokens = $cache->get_cache($token_id); + + return $self->ctx->{payflow_hosted_ctx} = $tokens; +} + sub load_myopac_update_email { my $self = shift; my $e = $self->editor; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/PayflowHosted.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/PayflowHosted.pm index b88139928d..a80b646429 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/PayflowHosted.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/PayflowHosted.pm @@ -7,11 +7,13 @@ use UUID::Tiny qw/:std/; use OpenSRF::Utils::Logger qw/$logger/; my $U = 'OpenILS::Application::AppUtils'; -my $test_server = 'https://pilot-payflowpro.paypal.com'; -my $live_server = 'https://payflowpro.paypal.com'; +# API servers +my $live_api_server = 'https://payflowpro.paypal.com'; +my $test_api_server = 'https://pilot-payflowpro.paypal.com'; -# Hosted Pages server. This is the same for test or live mode. -my $hosted_server = 'https://payflowlink.paypal.com'; +# Hosted Pages servers +my $live_forms_server = 'https://payflowlink.paypal.com'; +my $test_forms_server = 'https://pilot-payflowlink.paypal.com'; # Creates a transaction token so the calling code can send the user # to the hosted pages site. @@ -36,39 +38,62 @@ sub create_xact_token { my %settings = get_settings($params{billing_org}); return undef unless %settings; - my %pf_params = %{$params{payflow_params}}; - # Per-transaction unique token (my $tokenid = create_uuid_as_string(UUID_V4)) =~ s/-//g; + my %pf_params; $pf_params{PARTNER} = $settings{partner}; $pf_params{VENDOR} = $settings{vendor}; $pf_params{TRXTYPE} = 'S'; # sale - $pf_params{TEMPLATE} = 'MINLAYOUT'; # or MOBILE (AKA "Layout C") $pf_params{URLMETHOD} = 'POST'; + $pf_params{TEMPLATE} = 'TEMPLATEA'; # TODO: just for testing + $pf_params{AMT} = sprintf("%.2f", $params{amount}); + $pf_params{USER1} = $params{authtoken}; $pf_params{SECURETOKENID} = $tokenid; $pf_params{CREATESECURETOKEN} = 'Y'; + my $user = $params{user}; + + $pf_params{COMMENT1} = $user->card->barcode if $user->card; + $pf_params{BILLTOFIRSTNAME} = $user->first_given_name; + $pf_params{BILLTOLASTNAME} = $user->family_name; + $pf_params{BILLTOEMAIL} = $user->email; + $pf_params{BILLTOPHONE} = $user->day_phone; + + if (my $addr = $user->billing_address) { + $pf_params{BILLTOSTREET} = $addr->street1; + $pf_params{BILLTOSTREE2} = $addr->street2; + $pf_params{BILLTOCITY} = $addr->city; + $pf_params{BILLTOSTATE} = $addr->state; + $pf_params{BILLTOZIP} = $addr->post_code; + } + if ($settings{autohosts}) { # Tell PP to send POST response data to this host, # regardless of what's configured within PayPal. my $host = $params{response_host}; - $pf_params{RETURNURL} = "$host/eg/opac/payflow/pay_response"; $pf_params{CANCELURL} = "$host/eg/opac/biblio/main_fines"; - $pf_params{ERRORURL} = $pf_params{RETURNURL}; + $pf_params{RETURNURL} = "$host/eg/opac/payflow/pay_receipt/$tokenid"; + $pf_params{ERRORURL} = "$host/eg/opac/biblio/main_fines/$tokenid"; + $pf_params{SILENTPOSTURL} = "$host/eg/opac/payflow/silent_post"; } - my $server = $settings{testmode} ? $test_server : $live_server; + my $api_server = $live_api_server; + my $forms_server = $live_forms_server; + if ($settings{testmode}) { + $api_server = $test_api_server; + $forms_server = $test_forms_server; + } # Log the request to be sent, minus the user and password values. - $logger->info("PayflowHosted sending to server $server: ". + $logger->info("PayflowHosted sending to server $api_server: ". encode_params(%pf_params)); # Now that we've logged the params, add the user and password $pf_params{USER} = $settings{login}, $pf_params{PWD} = $settings{password}, - my $req = HTTP::Request->new(POST => $server); + my $req = HTTP::Request->new(POST => $api_server); $req->header('content-type' => 'text/namevalue'); $req->content(encode_params(%pf_params)); @@ -96,11 +121,16 @@ sub create_xact_token { return undef; } + # Avoid leaking sensitive data. + delete $pf_params{USER}; + delete $pf_params{PWD}; + return { secure_token => $results{SECURETOKEN}, secure_token_id => $results{SECURETOKENID}, test_mode => $settings{testmode}, - hosted_server => $hosted_server + forms_server => $forms_server, + payflow_params => \%pf_params }; } diff --git a/Open-ILS/src/templates/opac/css/style.css.tt2 b/Open-ILS/src/templates/opac/css/style.css.tt2 index 1d2a912f92..15d752733d 100644 --- a/Open-ILS/src/templates/opac/css/style.css.tt2 +++ b/Open-ILS/src/templates/opac/css/style.css.tt2 @@ -1461,6 +1461,14 @@ div.result_table_utils_cont { padding: 10px; border: 1px solid [% css_colors.accent_medium_dark %]; } +.payflow-error-container { + padding: 10px; border: 1px solid [% css_colors.accent_medium_dark %]; +} + +.payflow-error-text { + font-weight: bold; color: [% css_colors.text_alert %]; +} + .payment-processing { font-weight: bold; color: [% css_colors.text_greatnews %]; diff --git a/Open-ILS/web/css/skin/default/opac/style.css b/Open-ILS/web/css/skin/default/opac/style.css index 76eefb690b..68322eff33 100644 --- a/Open-ILS/web/css/skin/default/opac/style.css +++ b/Open-ILS/web/css/skin/default/opac/style.css @@ -923,6 +923,14 @@ div.select-wrapper:hover { padding: 10px; border: 1px solid #888; } +.payflow-error-container { + padding: 10px; border: 1px solid #888; +} + +.payflow-error-text { + font-weight: bold; color: red; +} + .payment-processing { font-weight: bold; color: green; font-size: 120%; -- 2.11.0