LP1993305 Comprise SmartPay support, middle layer bits
authorJason Etheridge <jason@EquinoxOLI.org>
Fri, 4 Feb 2022 14:14:24 +0000 (09:14 -0500)
committerJane Sandberg <sandbergja@gmail.com>
Wed, 3 May 2023 02:52:33 +0000 (19:52 -0700)
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 <jason@EquinoxOLI.org>
Signed-off-by: Jane Sandberg <sandbergja@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2
Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2

index 7cfc168..5ebee1e 100644 (file)
@@ -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};
index f4b0d3e..6dacc2d 100644 (file)
@@ -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');
     }
index 537b266..19bb30f 100644 (file)
@@ -1,4 +1,4 @@
-<p><big>[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages.") %]</big></p>
+<p><big>[% 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)) %]</big></p>
 <a href="[% ctx.smartpay_target %]" class="opac-button">[% l('Submit') %]</a>
 <a href="[% mkurl(ctx.opac_root _ '/myopac/main#selected_fines', {}, 1) %]" class="opac-button">[% l('Cancel') %]</a>
 
index 537b266..19bb30f 100644 (file)
@@ -1,4 +1,4 @@
-<p><big>[% l("Click Submit to temporarily leave this website for the payment processor. After payment, you will be returned to these pages.") %]</big></p>
+<p><big>[% 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)) %]</big></p>
 <a href="[% ctx.smartpay_target %]" class="opac-button">[% l('Submit') %]</a>
 <a href="[% mkurl(ctx.opac_root _ '/myopac/main#selected_fines', {}, 1) %]" class="opac-button">[% l('Cancel') %]</a>