From 6355290266dcb170de0303fee186791b73b4face Mon Sep 17 00:00:00 2001 From: Jason Etheridge Date: Fri, 4 Feb 2022 09:14:24 -0500 Subject: [PATCH] LP1993305 Comprise SmartPay support, middle layer bits Squashed commits: * middle * billing total in smartpay last_chance equivalent * fix xact propagation * remove some logging in middle layer * increase timeout for cached object and hope it exceeds smartpay session expiry Signed-off-by: Jason Etheridge Signed-off-by: Jane Sandberg --- .../perlmods/lib/OpenILS/Application/Circ/Money.pm | 52 ++++++++++++++++++++++ .../lib/OpenILS/WWW/EGCatLoader/Account.pm | 48 +++++++++++--------- .../opac/myopac/smartpay_payment_form.tt2 | 2 +- .../opac/myopac/smartpay_payment_form.tt2 | 2 +- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm index 7cfc16821d..5ebee1ea89 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm @@ -37,6 +37,9 @@ use OpenILS::Utils::DateTime qw/:datetime/; use DateTime::Format::ISO8601; my $parser = DateTime::Format::ISO8601->new; +my $cache; +my $cache_timeout; + sub get_processor_settings { my $e = shift; my $org_unit = shift; @@ -101,6 +104,7 @@ sub process_stripe_or_bop_payment { unless $psettings->{enabled}; # Now we branch. Stripe is one thing, and everything else is another. + # TODO: rename/refactor these methods, we're layering in Smartpay as well if ($cc_args->{processor} eq 'Stripe') { # Stripe my $stripe = Business::Stripe->new(-api_key => $psettings->{secretkey}); @@ -137,6 +141,54 @@ sub process_stripe_or_bop_payment { ); } + } elsif ($cc_args->{processor} eq 'SmartPAY') { # SmartPAY + my $smartpay_secret = $cc_args->{smartpay_secret}; + my $smartpay_session = $cc_args->{smartpay_session}; + if ($smartpay_secret =~ /^smartpay/) { + my $cache = OpenSRF::Utils::Cache->new('global'); + my $secret_data = $cache->get_cache( $smartpay_secret ); + $logger->debug("SmartPAY secret_data: " . Dumper($secret_data)); + my $sessionA = $secret_data->{session_key}; + my $sessionB = $smartpay_session; + if ($sessionA =~ /([A-Za-z0-9]+)/) { + $sessionA = $1; + } + if ($sessionB =~ /([A-Za-z0-9]+)/) { + $sessionB = $1; + } + if ($sessionA ne $sessionB) { + $logger->info("SmartPAY payment failed: session_key mismatch: <$sessionA> vs <$sessionB>"); + return OpenILS::Event->new( + 'CREDIT_PROCESSOR_DECLINED_TRANSACTION', + payload => { 'result' => 'session_key mismatch' } + ); + } + if ($cc_args->{smartpay_result} == 1) { + $logger->info('SmartPAY payment succeeded'); + return OpenILS::Event->new( + 'SUCCESS', payload => { + invoice => 'N/A', + customer => 'N/A', + balance_transaction => 'N/A', + id => 'N/A', + created => 'N/A', + card => 'N/A' + } + ); + } else { + $logger->info('SmartPAY payment failed: ' . $cc_args->{smartpay_result}); + return OpenILS::Event->new( + 'CREDIT_PROCESSOR_DECLINED_TRANSACTION', + payload => { 'result' => $cc_args->{Result} } + ); + } + } else { + $logger->info('SmartPAY payment failed: secret key malformed'); + return OpenILS::Event->new( + 'CREDIT_PROCESSOR_DECLINED_TRANSACTION', + payload => { 'result' => 'secret key malformed' } + ); + } } else { # B::OP style (Paypal/PayflowPro/AuthorizeNet) return OpenILS::Event->new('BAD_PARAMS', note => 'Need CC number') unless $cc_args->{number}; 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 f4b0d3ebc7..6dacc2d20b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -2368,14 +2368,11 @@ sub load_myopac_payment_form { $target .= "&APIKey=$api_key"; my @payment_xacts = ($self->cgi->param('xact'), $self->cgi->param('xact_misc')); - #$logger->error('SmartPAY debug, cache_args = ' . Dumper($cache_args) ); # generate a temporary cache token for our secret my $token = 'smartpay_' . md5_hex($$ . time() . rand()); $target .= "&Secret=$token"; - #$logger->error('SmartPAY debug, target= $target ' . Dumper($target) ); - my $ua = new LWP::UserAgent; # there has been API flux with whether to send API-Key and Secret as HTTP headers or URL params @@ -2384,17 +2381,18 @@ sub load_myopac_payment_form { my $response = $ua->request($req); if ($response->is_success() && $response->content() !~ /HTML/) { - $logger->info('SmartPAY initialized for ' . $self->editor->authtoken); + $logger->info('SmartPAY initialized for ' . $self->editor->authtoken . ' with ' . $token); my $session_key = $response->content(); - # we'll test this secret key again in the create payment method + # we'll test this secret key again in the create payment method and grab the payment_xacts in main_pay_init my $cache_args = { user => $self->ctx->{user}->id, - session_key => $session_key + session_key => $session_key, + xacts => \@payment_xacts }; my $cache = OpenSRF::Utils::Cache->new('global'); - $cache->put_cache($token, $cache_args, 300); + $cache->put_cache($token, $cache_args, 3600); # 1 hour # this will be for our follow-up call client-side @@ -2403,12 +2401,14 @@ sub load_myopac_payment_form { $self->ctx->{smartpay_target} .= "&CustomerID=$customer_id"; $self->ctx->{smartpay_target} .= "&LocationID=$location_id"; $self->ctx->{smartpay_target} .= '&PatronID=' . $self->ctx->{user}->id; # $e->requestor->id; - $self->ctx->{smartpay_target} .= '&InvNum=1234'; - $self->ctx->{smartpay_target} .= '&Amount=' . $self->ctx->{fines}->{balance_owed}; + my $psuedo_invoice_number = $self->cgi->param('xact') . ',' . $self->cgi->param('xact_misc'); + $psuedo_invoice_number =~ s/^,//; + $self->ctx->{smartpay_target} .= "&InvNum=$psuedo_invoice_number"; + $self->ctx->{smartpay_target} .= '&Amount=' . sprintf("%.2f",$self->ctx->{fines}->{balance_owed}); $self->ctx->{smartpay_target} .= '&URLPostBack=' . CGI::escapeHTML( $self->ctx->{hostname} ); #$self->ctx->{smartpay_target} .= '&ScriptPostBack=' . CGI::escapeHTML('/cgi-bin/offline/echo1.pl'); $self->ctx->{smartpay_target} .= '&URLReturn=' . CGI::escapeHTML( $self->ctx->{opac_root} . '/myopac/main_pay_init' ); - $self->ctx->{smartpay_target} .= '&URLCancel=' . CGI::escapeHTML( 'https://' . $self->ctx->{hostname} . $self->ctx->{opac_root} . '/myopac/charges'); + $self->ctx->{smartpay_target} .= '&URLCancel=' . CGI::escapeHTML( 'https://' . $self->ctx->{hostname} . $self->ctx->{opac_root} . '/myopac/main'); $self->ctx->{smartpay_target} .= '&UserName=&Password=&Field1=smartpay&Field2=&Field3=&ItemsData='; } else { @@ -2469,16 +2469,21 @@ sub load_myopac_pay_init { my @payment_xacts = ($self->cgi->param('xact'), $self->cgi->param('xact_misc')); if (!@payment_xacts) { - # for consistency with load_myopac_payment_form() and - # to preserve backwards compatibility, if no xacts are - # selected, assume all (applicable) transactions are wanted. - my $stat = $self->prepare_fines(undef, undef, [$self->cgi->param('xact'), $self->cgi->param('xact_misc')]); - return $stat if $stat; - @payment_xacts = - map { $_->{xact}->id } ( - @{$self->ctx->{fines}->{circulation}}, - @{$self->ctx->{fines}->{grocery}} - ); + if ($self->cgi->param('Secret')) { # we stashed the xacts in the cache for SmartPAY + my $smartpay_cache = $cache->get_cache($self->cgi->param('Secret')); + @payment_xacts = @{$smartpay_cache->{xacts}}; + } else { + # for consistency with load_myopac_payment_form() and + # to preserve backwards compatibility, if no xacts are + # selected, assume all (applicable) transactions are wanted. + my $stat = $self->prepare_fines(undef, undef, [$self->cgi->param('xact'), $self->cgi->param('xact_misc')]); + return $stat if $stat; + @payment_xacts = + map { $_->{xact}->id } ( + @{$self->ctx->{fines}->{circulation}}, + @{$self->ctx->{fines}->{grocery}} + ); + } } return $self->generic_redirect unless @payment_xacts; @@ -2498,6 +2503,9 @@ sub load_myopac_pay_init { if ($self->cgi->param('Secret')) { $cc_args->{smartpay_secret} = $self->cgi->param('Secret'); } + if ($self->cgi->param('SessionKey')) { + $cc_args->{smartpay_session} = $self->cgi->param('SessionKey'); + } if ($self->cgi->param('CCNumber')) { $cc_args->{number} = $self->cgi->param('CCNumber'); } diff --git a/Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2 b/Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2 index 537b266cf2..19bb30f1b2 100644 --- a/Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2 +++ b/Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2 @@ -1,4 +1,4 @@ -

[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages.") %]

+

[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages. The total to be paid is [_1]", money(ctx.fines.balance_owed)) %]

[% l('Submit') %] [% l('Cancel') %] diff --git a/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 b/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 index 537b266cf2..19bb30f1b2 100644 --- a/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 +++ b/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 @@ -1,4 +1,4 @@ -

[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages.") %]

+

[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages. The total to be paid is [_1]", money(ctx.fines.balance_owed)) %]

[% l('Submit') %] [% l('Cancel') %] -- 2.11.0