From cfd929eb4fb25ac8647dd8e6fb59cce0331c087a 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 --- installer/data/mysql/atomicupdate/bug_11622.sql | 7 + installer/data/mysql/sysprefs.sql | 6 + .../prog/en/modules/admin/preferences/opac.pref | 30 +++++ .../en/modules/opac-account-pay-return.tt | 52 ++++++++ .../opac-tmpl/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 0000000..acc5443 --- /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 8fd6bf5..6bafacd 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -338,6 +338,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 0c7c507..edbd26f 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 @@ -683,3 +683,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 0000000..35422c7 --- /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 e26776c..a1a660f 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 0000000..b7940e1 --- /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 0000000..ca77c68 --- /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 e575650..7c70e69 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; @@ -68,10 +67,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; -- 1.7.2.5