From 85c5efa0c7fc98f30cc5983ba476c7712bb1dcac Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Thu, 23 Jan 2014 15:18:29 -0500 Subject: [PATCH] Bug 11622 - Add ability to pay fees and fines from OPAC via PayPal This patch adds the ability for a logged in user to pay fines and fees from the OPAC via PayPal. Test Plan: 1) Apply this patch 2) Create a paypal developer account 3) Create two test accounts, a Personal account and a Business account 4) Enable PayPal in Sandbox mode via the system preferences. 5) Enter the business account API credentials into the new system preferences. 6) Create a new patron, add some fines/fees 7) Log in as that patron in the OPAC 8) Choose to pay via PayPal, log in as the sandbox Personal account 9) Complete the transaction 10) Note the fee is now paid Signed-off-by: Carol Corrales Signed-off-by: Jonathan Druart --- .../data/mysql/atomicupdate/bug_11622.sql | 7 + installer/data/mysql/sysprefs.sql | 6 + .../en/modules/admin/preferences/opac.pref | 30 ++++ .../en/modules/opac-account-pay-return.tt | 52 +++++++ .../bootstrap/en/modules/opac-account.tt | 69 +++++++++- opac/opac-account-pay-paypal-return.pl | 110 +++++++++++++++ opac/opac-account-pay.pl | 128 ++++++++++++++++++ opac/opac-account.pl | 9 +- 8 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_11622.sql create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account-pay-return.tt create mode 100755 opac/opac-account-pay-paypal-return.pl create mode 100755 opac/opac-account-pay.pl diff --git a/installer/data/mysql/atomicupdate/bug_11622.sql b/installer/data/mysql/atomicupdate/bug_11622.sql new file mode 100644 index 0000000000..acc5443b1d --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_11622.sql @@ -0,0 +1,7 @@ +INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type ) VALUES +( 'EnablePayPalOpacPayments', '0', NULL , 'Enables the ability to pay fees and fines from the OPAC via PayPal', 'YesNo' ), +( 'PayPalChargeDescription', 'Koha fee payment', NULL , 'This preference defines what the user will see the charge listed as in PayPal', 'Free' ), +( 'PayPalPwd', '', NULL , 'Your PayPal API password', 'Free' ), +( 'PayPalSandboxMode', '1', NULL , 'If enabled, the system will use PayPal''s sandbox server for testing, rather than the production server.', 'YesNo' ), +( 'PayPalSignature', '', NULL , 'Your PayPal API signature', 'Free' ), +( 'PayPalUser', '', NULL , 'Your PayPal API username ( email address )', 'Free' ); diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 2c90614f9a..6ff71473d9 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -359,6 +359,12 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('PatronSelfRegistrationExpireTemporaryAccountsDelay','0',NULL,'If PatronSelfRegistrationDefaultCategory is enabled, this system preference controls how long a patron can have a temporary status before the account is deleted automatically. It is an integer value representing a number of days to wait before deleting a temporary patron account. Setting it to 0 disables the deleting of temporary accounts.','Integer'), ('PatronSelfRegistrationVerifyByEmail','0',NULL,'If enabled, any patron attempting to register themselves via the OPAC will be required to verify themselves via email to activate his or her account.','YesNo'), ('PatronsPerPage','20','20','Number of Patrons Per Page displayed by default','Integer'), +('EnablePayPalOpacPayments', '0', NULL , 'Enables the ability to pay fees and fines from the OPAC via PayPal', 'YesNo' ), +('PayPalChargeDescription', 'Koha fee payment', NULL , 'This preference defines what the user will see the charge listed as in PayPal', 'Free'), +('PayPalPwd', '', NULL , 'Your PayPal API password', 'Free'), +('PayPalSandboxMode', '1', NULL , 'If enabled, the system will use PayPal''s sandbox server for testing, rather than the production server.', 'YesNo'), +('PayPalSignature', '', NULL , 'Your PayPal API signature', 'Free'), +('PayPalUser', '', NULL , 'Your PayPal API username ( email address )', 'Free'), ('Persona','0','','Use Mozilla Persona for login','YesNo'), ('PrefillItem','0','','When a new item is added, should it be prefilled with last created item values?','YesNo'), ('previousIssuesDefaultSortOrder','asc','asc|desc','Specify the sort order of Previous Issues on the circulation page','Choice'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref index 252e884d72..e355748d1e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -712,3 +712,33 @@ OPAC: subtype: Subtypes sorting: Sorting location: Location and availability + Payments: + - + - pref: EnablePayPalOpacPayments + default: 1 + choices: + yes: Allow + no: "Don't allow" + - "patrons to make payments from the OPAC via PayPal in" + - pref: PayPalSandboxMode + default: 1 + choices: + yes: "Sandbox" + no: "Production" + - "mode." + - + - "The email address to recieve PayPal payments is " + - pref: PayPalUser + class: long + - + - "The password for the PayPal account to recieve payments is " + - pref: PayPalPwd + class: long + - + - "The signature for the PayPal account to recieve payments is " + - pref: PayPalSignature + class: long + - + - "The patron should see the charge description as " + - pref: PayPalChargeDescription + class: long diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account-pay-return.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account-pay-return.tt new file mode 100644 index 0000000000..35422c753e --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account-pay-return.tt @@ -0,0 +1,52 @@ +[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Your payment +[% INCLUDE 'doc-head-close.inc' %] +[% BLOCK cssinclude %][% END %] + + + +[% INCLUDE 'masthead.inc' %] + +
+ + +
+
+
+ +
+
+
+ [% IF error %] +
+

Error: there was an problem processing your payment

+ + [% IF error == "PAYPAL_UNABLE_TO_CONNECT" %] +

Unable to connect to PayPal.

+

Please try again later.

+ [% ELSIF error == "PAYPAL_ERROR_PROCESSING" %] +

Unable to verify payment.

+

Please contact the library to verify your payment.

+ [% END %] +
+ [% ELSIF credit %] +
+

Payment applied: your payment of [% amount %] has been applied to your account

+
+ [% END %] + + Return to fine details +
+
+
+
+
+ +[% INCLUDE 'opac-bottom.inc' %] + +[% BLOCK jsinclude %][% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account.tt index 316c5bd691..282d990184 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-account.tt @@ -1,5 +1,6 @@ [% USE Koha %] [% USE KohaDates %] +[% SET ENABLE_OPAC_PAYMENTS = Koha.Preference('EnablePayPalOpacPayments') %] [% INCLUDE 'doc-head-open.inc' %] [% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Your fines and charges @@ -26,12 +27,29 @@
+ + [% IF message %] +
+ [% IF message == 'valid_payment' %] +

Your payment of $[% message_value %] has been processed sucessfully!

+ [% ELSIF message == 'duplicate_payment' %] +

A payment with the transaction id [% message_value %] has already been posted to an account.

+

Please contact a librarian for details.

+ [% ELSIF message == 'invalid_payment' %] +

The transaction id [% message_value %] for this payment is invalid.

+

Please contact a librarian for details.

+ [% END %] +
+ [% END %] +

Fines and charges

[% IF ( ACCOUNT_LINES ) %] +
+ [% IF ENABLE_OPAC_PAYMENTS %][% END %] @@ -41,7 +59,12 @@ - + [%- IF ENABLE_OPAC_PAYMENTS -%] + [%- SET COLSPAN = 4 -%] + [%- ELSE -%] + [%- SET COLSPAN = 3 -%] + [%- END -%] + @@ -49,6 +72,13 @@ [% FOREACH ACCOUNT_LINE IN ACCOUNT_LINES %] [% IF ( ACCOUNT_LINE.odd ) %][% ELSE %][% END %] + [% IF ENABLE_OPAC_PAYMENTS %] + + [% END %]
 Date Description Fine amount
Total dueTotal due [% total %]
+ [% IF ACCOUNT_LINE.amountoutstanding > 0 %] + + [% END %] + [% ACCOUNT_LINE.date | $KohaDates %] [% SWITCH ACCOUNT_LINE.accounttype %] @@ -82,6 +112,33 @@
+ + [% IF ENABLE_OPAC_PAYMENTS %] +
+ Pay selected fines and charges +

Payment method

+
+ +
+ + +
+ + +
+
+ [% END %] +
[% ELSE %]

You have no fines or charges

[% END %] @@ -92,4 +149,12 @@
[% INCLUDE 'opac-bottom.inc' %] -[% BLOCK jsinclude %][% END %] +[% BLOCK jsinclude %] + +[% END %] diff --git a/opac/opac-account-pay-paypal-return.pl b/opac/opac-account-pay-paypal-return.pl new file mode 100755 index 0000000000..b7940e13dc --- /dev/null +++ b/opac/opac-account-pay-paypal-return.pl @@ -0,0 +1,110 @@ +#!/usr/bin/perl + +# Copyright ByWater Solutions 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use utf8; + +use CGI; +use HTTP::Request::Common; +use LWP::UserAgent; +use URL::Encode qw(url_params_mixed); + +use C4::Auth; +use C4::Output; +use C4::Accounts; +use C4::Members; +use Koha::Database; + +my $cgi = new CGI; + +unless ( C4::Context->preference('EnablePayPalOpacPayments') ) { + print $cgi->redirect("/cgi-bin/koha/errors/404.pl"); + exit; +} + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => "opac-account-pay-return.tt", + query => $cgi, + type => "opac", + authnotrequired => 0, + flagsrequired => { borrow => 1 }, + debug => 1, + } +); + +my $token = $cgi->param('token'); +my $payer_id = $cgi->param('PayerID'); +my $amount = $cgi->param('amount'); +my @accountlines = $cgi->param('accountlines'); + +my $ua = LWP::UserAgent->new; + +my $url = + C4::Context->preference('PayPalSandboxMode') + ? 'https://api-3t.sandbox.paypal.com/nvp' + : 'https://api-3t.paypal.com/nvp'; + +my $nvp_params = { + 'USER' => C4::Context->preference('PayPalUser'), + 'PWD' => C4::Context->preference('PayPalPwd'), + 'SIGNATURE' => C4::Context->preference('PayPalSignature'), + + # API Version and Operation + 'METHOD' => 'DoExpressCheckoutPayment', + 'VERSION' => '82.0', + + # API specifics for DoExpressCheckout + 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', + 'PAYERID' => $payer_id, + 'TOKEN' => $token, + 'PAYMENTREQUEST_0_AMT' => $amount, +}; + +my $response = $ua->request( POST $url, $nvp_params ); + +if ( $response->is_success ) { + my $params = url_params_mixed( $response->decoded_content ); + + if ( $params->{ACK} eq "Success" ) { + $amount = $params->{PAYMENTINFO_0_AMT}; + $template->param( amount => $amount ); + + my $accountlines_rs = Koha::Database->new()->schema()->resultset('Accountline'); + foreach my $accountlines_id ( @accountlines ) { + my $accountline = $accountlines_rs->find( $accountlines_id ); + makepayment( $accountlines_id, $borrowernumber, undef, $accountline->amountoutstanding, undef, undef, 'PayPal' ); + } + } + else { + $template->param( error => "PAYPAL_ERROR_PROCESSING" ); + } + +} +else { + $template->param( error => "PAYPAL_UNABLE_TO_CONNECT" ); +} + +$template->param( + borrower => GetMemberDetails($borrowernumber), + accountview => 1 +); + +output_html_with_http_headers( $cgi, $cookie, $template->output ); diff --git a/opac/opac-account-pay.pl b/opac/opac-account-pay.pl new file mode 100755 index 0000000000..ca77c68e5a --- /dev/null +++ b/opac/opac-account-pay.pl @@ -0,0 +1,128 @@ +#!/usr/bin/perl + +# Copyright ByWater Solutions 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use utf8; + +use Modern::Perl; + +use CGI; +use HTTP::Request::Common; +use LWP::UserAgent; +use URL::Encode qw(url_encode url_params_mixed); +use URI; + +use C4::Auth; +use C4::Output; +use C4::Context; +use Koha::Database; + +my $cgi = new CGI; + +unless ( C4::Context->preference('EnablePayPalOpacPayments') ) { + print $cgi->redirect("/cgi-bin/koha/errors/404.pl"); + exit; +} + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => "opac-account-pay-return.tt", + query => $cgi, + type => "opac", + authnotrequired => 0, + flagsrequired => { borrow => 1 }, + debug => 1, + } +); + +my $payment_method = $cgi->param('payment_method'); +my @accountlines = $cgi->param('accountline'); + +my $amount_to_pay = + Koha::Database->new()->schema()->resultset('Accountline')->search( { accountlines_id => { -in => \@accountlines } } ) + ->get_column('amountoutstanding')->sum(); +$amount_to_pay = sprintf( "%.2f", $amount_to_pay ); + +my $error = 0; +if ( $payment_method eq 'paypal' ) { + my $ua = LWP::UserAgent->new; + + my $amount = url_encode($amount_to_pay); + + my $url = + C4::Context->preference('PayPalSandboxMode') + ? 'https://api-3t.sandbox.paypal.com/nvp' + : 'https://api-3t.paypal.com/nvp'; + + my $opac_base_url = C4::Context->preference('OPACBaseURL'); + + my $return_url = URI->new( $opac_base_url . "/cgi-bin/koha/opac-account-pay-paypal-return.pl" ); + $return_url->query_form( { amount => $amount, accountlines => \@accountlines } ); + + my $cancel_url = URI->new( $opac_base_url . "/cgi-bin/koha/opac-account.pl" ); + + my $nvp_params = { + 'USER' => C4::Context->preference('PayPalUser'), + 'PWD' => C4::Context->preference('PayPalPwd'), + 'SIGNATURE' => C4::Context->preference('PayPalSignature'), + + # API Version and Operation + 'METHOD' => 'SetExpressCheckout', + 'VERSION' => '82.0', + + # API specifics for SetExpressCheckout + 'NOSHIPPING' => 1, + 'REQCONFIRMSHIPPING' => 0, + 'ALLOWNOTE' => 0, + 'BRANDNAME' => C4::Context->preference('LibraryName'), + 'CANCELURL' => $cancel_url->as_string(), + 'RETURNURL' => $return_url->as_string(), + 'PAYMENTREQUEST_0_AMT' => $amount_to_pay, + 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', + 'PAYMENTREQUEST_0_ALLOWEDPAYMENTMETHOD' => 'InstantPaymentOnly', + 'PAYMENTREQUEST_0_DESC' => C4::Context->preference('PayPalChargeDescription'), + }; + + my $response = $ua->request( POST $url, $nvp_params ); + + if ( $response->is_success ) { + my $params = url_params_mixed( $response->decoded_content ); + + if ( $params->{ACK} eq "Success" ) { + my $token = $params->{TOKEN}; + + my $redirect_url = + C4::Context->preference('PayPalSandboxMode') + ? "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=" + : "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token="; + print $cgi->redirect( $redirect_url . $token ); + + } + else { + $template->param( error => "PAYPAL_ERROR_PROCESSING" ); + $error = 1; + } + + } + else { + $template->param( error => "PAYPAL_UNABLE_TO_CONNECT" ); + $error = 1; + } +} + +output_html_with_http_headers( $cgi, $cookie, $template->output ) if $error; diff --git a/opac/opac-account.pl b/opac/opac-account.pl index d220fed74e..b40924b011 100755 --- a/opac/opac-account.pl +++ b/opac/opac-account.pl @@ -22,7 +22,6 @@ use strict; use CGI qw ( -utf8 ); use C4::Members; -use C4::Circulation; use C4::Auth; use C4::Output; use warnings; @@ -64,10 +63,12 @@ foreach my $row (@$accts) { $num++; } -$template->param ( +$template->param( ACCOUNT_LINES => $accts, - total => sprintf( "%.2f", $total ), - accountview => 1 + total => sprintf( "%.2f", $total ), + accountview => 1, + message => $query->param('message') || q{}, + message_value => $query->param('message_value') || q{}, ); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; -- 2.39.5