LP1993305 Comprise SmartPay support, UI bits
authorJason Etheridge <jason@EquinoxOLI.org>
Tue, 26 Oct 2021 14:52:42 +0000 (10:52 -0400)
committerJane Sandberg <sandbergja@gmail.com>
Wed, 3 May 2023 02:51:53 +0000 (19:51 -0700)
Squashed commits:
  * ui
  * 0.50 minimum across the board

Signed-off-by: Jason Etheridge <jason@EquinoxOLI.org>
Signed-off-by: Jane Sandberg <sandbergja@gmail.com>

Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/templates-bootstrap/opac/myopac/main_payment_form.tt2
Open-ILS/src/templates-bootstrap/opac/myopac/smartpay_payment_form.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac/myopac/main_payment_form.tt2
Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 [new file with mode: 0644]

index 78bba4a..f4b0d3e 100644 (file)
@@ -18,6 +18,9 @@ use DateTime;
 use DateTime::Format::ISO8601;
 my $U = 'OpenILS::Application::AppUtils';
 use List::MoreUtils qw/uniq/;
+use LWP::UserAgent;
+use HTTP::Request::Common qw(POST GET);
+use CGI;
 
 sub prepare_extended_user_info {
     my $self = shift;
@@ -2323,22 +2326,98 @@ sub load_myopac_payment_form {
 
     $r = $self->prepare_fines(undef, undef, [$self->cgi->param('xact'), $self->cgi->param('xact_misc')]);
 
-    if ( ! $self->cgi->param('last_chance') # only do this once
-        && $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.enabled')
-        && $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.default') eq 'Stripe') {
-        my $skey = $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.secretkey');
-        my $currency = $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.currency');
-        my $stripe = Business::Stripe->new(-api_key => $skey);
-        my $intent = $stripe->api('post', 'payment_intents',
-            amount                => $self->ctx->{fines}->{balance_owed} * 100,
-            currency              => $currency || 'usd',
-            description           => 'User Database ID: ' . $self->ctx->{user}->id
-        );
-        if ($stripe->success) {
-            $self->ctx->{stripe_client_secret} = $stripe->success()->{client_secret};
-        } else {
-            $logger->error('Error initializing Stripe: ' . Dumper($stripe->error));
-            $self->ctx->{cc_configuration_error} = 1;
+    if ( ! $self->cgi->param('last_chance')) { # only do this once
+        if ($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.default') eq 'Stripe') {
+            if ($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.enabled')) {
+                my $skey = $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.secretkey');
+                my $currency = $self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.stripe.currency');
+                my $stripe = Business::Stripe->new(-api_key => $skey);
+                my $intent = $stripe->api('post', 'payment_intents',
+                    amount                => $self->ctx->{fines}->{balance_owed} * 100,
+                    currency              => $currency || 'usd',
+                    description           => 'User Database ID: ' . $self->ctx->{user}->id
+                );
+                if ($stripe->success) {
+                    $self->ctx->{stripe_client_secret} = $stripe->success()->{client_secret};
+                } else {
+                    $logger->error('Error initializing Stripe: ' . Dumper($stripe->error));
+                    $self->ctx->{cc_configuration_error} = 1;
+                }
+            }
+        } elsif ($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.default') eq 'SmartPAY') {
+            if ($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.enabled')) {
+                my $location_id =  CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.location_id'));
+                $self->ctx->{smartpay_locationid} = $location_id;
+                my $customer_id =  CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.customer_id'));
+                $self->ctx->{smartpay_customerid} = $customer_id;
+                my $login = CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.login'));
+                my $password = CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.password'));
+                my $api_key = CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.api_key'));
+                my $server = CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.server'));
+                my $port = CGI::escapeHTML($self->ctx->{get_org_setting}->($e->requestor->home_ou, 'credit.processor.smartpay.port'));
+                my $base_target = "https://$server";
+                $base_target .= ":$port" if $port;
+                $base_target .= "/SMARTPAYAPI/WEBSMARTPAY.dll";
+                
+                # first api call
+                my $target = $base_target . "?RequestSessionKey";
+                $target .= "&CustomerID=$customer_id";
+                $target .= "&LocationID=$location_id";
+                $target .= "&UserName=$login";
+                $target .= "&Password=$password";
+                $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
+                #my $req = GET($target, 'API-Key' => "$api_key", 'Secret' => "$token");
+                my $req = GET($target);
+                my $response = $ua->request($req);
+
+                if ($response->is_success() && $response->content() !~ /HTML/) {
+                    $logger->info('SmartPAY initialized for ' . $self->editor->authtoken);
+
+                    my $session_key = $response->content();
+
+                    # we'll test this secret key again in the create payment method
+                    my $cache_args = {
+                        user => $self->ctx->{user}->id,
+                        session_key => $session_key
+                    };
+                    my $cache = OpenSRF::Utils::Cache->new('global');
+                    $cache->put_cache($token, $cache_args, 300);
+
+                    # this will be for our follow-up call client-side
+
+                    $self->ctx->{smartpay_target} = $base_target . '?GetCreditForm';
+                    $self->ctx->{smartpay_target} .= '&SessionKey=' . $session_key;
+                    $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};
+                    $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} .= '&UserName=&Password=&Field1=smartpay&Field2=&Field3=&ItemsData=';
+
+                } else {
+                    my $error = $response->content();
+                    $error =~ s/[\r\n]//g;
+                    $logger->error("Error initializing SmartPAY: $error");
+                    $self->ctx->{cc_configuration_error} = 1;
+                }
+            }
         }
     }
 
@@ -2412,6 +2491,17 @@ sub load_myopac_pay_init {
         billing_zip stripe_payment_intent stripe_client_secret
     /);
 
+    # mapping SmartPAY CGI parameters
+    if ($self->cgi->param('Result')) {
+        $cc_args->{smartpay_result} = $self->cgi->param('Result');
+    }
+    if ($self->cgi->param('Secret')) {
+        $cc_args->{smartpay_secret} = $self->cgi->param('Secret');
+    }
+    if ($self->cgi->param('CCNumber')) {
+        $cc_args->{number} = $self->cgi->param('CCNumber');
+    }
+
     my $cache_args = {
         cc_args => $cc_args,
         user => $self->ctx->{user}->id,
index a774bb7..5234233 100755 (executable)
@@ -6,16 +6,19 @@
 
     last_chance = CGI.param("last_chance");
 
-    IF myopac_main_page == "payment_form" AND
-        ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'Stripe';
-        ctx.use_stripe = 1;
+    IF myopac_main_page == "payment_form";
+        IF ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'Stripe';
+            ctx.use_stripe = 1;
+        ELSIF ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.smartpay.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'SmartPAY';
+            ctx.use_smartpay = 1;
+        END;
     END %]
 
 <a name="payment"> </a>
 <h3 class="sr-only">[% l('Pay Charges') %]</h3>
-[% IF ctx.fines.balance_owed <= 0 %]
+[% IF ctx.fines.balance_owed <= 0.49 %]
 <div>
-    [% l("The minimum amount you can pay is \$0.01.") %]
+    [% l("The minimum amount you can pay is \$0.50.") %]
 </div>
 [% ELSE %]
 <div class="container">
@@ -29,6 +32,8 @@
         [% ELSE %]
             [% IF ctx.use_stripe %]
                 [% PROCESS "opac/myopac/stripe_payment_form.tt2"; %]
+            [% ELSIF ctx.use_smartpay %]
+                [% PROCESS "opac/myopac/smartpay_payment_form.tt2"; %]
             [% ELSE %]
                 [% PROCESS "opac/myopac/generic_payment_form.tt2"; %]
             [% END %]
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
new file mode 100644 (file)
index 0000000..537b266
--- /dev/null
@@ -0,0 +1,39 @@
+<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>
+<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>
+
+ <table title="[% l('List of Transactions') %]" id="acct_fines_confirm_header"
+    class="table_no_border_space table_no_cell_pad" style="padding-top:1em;">
+<thead>
+  <tr>
+    <th>[% l('Charge/Fee') %]</th>
+    <th>[% l('Amount') %]</th>
+ </tr>
+</thead>
+<tbody>
+  [%
+   FOR f IN ctx.fines.circulation;
+     NEXT IF CGI.param('xact').size &&
+        !CGI.param('xact').grep(f.xact.id).size;
+     attrs = {marc_xml => f.marc_xml};
+     IF f.marc_xml;
+         PROCESS get_marc_attrs args=attrs;
+     ELSIF f.xact.reservation;
+          attrs.title = f.xact.reservation.target_resource_type.name;
+     END %]
+     <tr>
+        <td>[% attrs.title | html %]</td>
+        <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+     </tr>
+      [%
+      END;
+      FOR f IN ctx.fines.grocery;
+          NEXT IF CGI.param('xact_misc').size &&
+              !CGI.param('xact_misc').grep(f.xact.id).size %]
+          <tr>
+             <td>[% f.xact.last_billing_type | html %]</td>
+             <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+        </tr>
+    [% END %]
+ </tbody>
+</table> 
index ab83442..3b6a409 100644 (file)
@@ -6,16 +6,19 @@
 
     last_chance = CGI.param("last_chance");
 
-    IF myopac_main_page == "payment_form" AND
-        ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'Stripe';
-        ctx.use_stripe = 1;
+    IF myopac_main_page == "payment_form";
+        IF ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.stripe.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'Stripe';
+            ctx.use_stripe = 1;
+        ELSIF ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.smartpay.enabled') AND ctx.get_org_setting(ctx.user.home_ou.id, 'credit.processor.default') == 'SmartPAY';
+            ctx.use_smartpay = 1;
+        END;
     END %]
 
 <a name="payment"> </a>    
 <h3 class="sr-only">[% l('Pay Charges') %]</h3>
-[% IF ctx.fines.balance_owed <= 0 %]
+[% IF ctx.fines.balance_owed <= 0.49 %]
 <div>
-    [% l("The minimum amount you can pay is \$0.01.") %]
+    [% l("The minimum amount you can pay is \$0.50.") %]
 </div>
 [% ELSE %]
 <div id="pay_fines_now">
         [% ELSE %]
             [% IF ctx.use_stripe %]
                 [% PROCESS "opac/myopac/stripe_payment_form.tt2"; %]
+            [% ELSIF ctx.use_smartpay %]
+                [% PROCESS "opac/myopac/smartpay_payment_form.tt2"; %]
             [% ELSE %]
                 [% PROCESS "opac/myopac/generic_payment_form.tt2"; %]
             [% END %]
         [% END %]
     [% END %]
 </div>
-[% END %] <!-- of IF ctx.fines.balance_owed <= 0 -->
+[% END %] <!-- of IF ctx.fines.balance_owed <= 0.49 -->
 [% END %] <!-- of.. something in one of the PROCESS or WRAPPER blocks? -->
diff --git a/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2 b/Open-ILS/src/templates/opac/myopac/smartpay_payment_form.tt2
new file mode 100644 (file)
index 0000000..537b266
--- /dev/null
@@ -0,0 +1,39 @@
+<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>
+<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>
+
+ <table title="[% l('List of Transactions') %]" id="acct_fines_confirm_header"
+    class="table_no_border_space table_no_cell_pad" style="padding-top:1em;">
+<thead>
+  <tr>
+    <th>[% l('Charge/Fee') %]</th>
+    <th>[% l('Amount') %]</th>
+ </tr>
+</thead>
+<tbody>
+  [%
+   FOR f IN ctx.fines.circulation;
+     NEXT IF CGI.param('xact').size &&
+        !CGI.param('xact').grep(f.xact.id).size;
+     attrs = {marc_xml => f.marc_xml};
+     IF f.marc_xml;
+         PROCESS get_marc_attrs args=attrs;
+     ELSIF f.xact.reservation;
+          attrs.title = f.xact.reservation.target_resource_type.name;
+     END %]
+     <tr>
+        <td>[% attrs.title | html %]</td>
+        <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+     </tr>
+      [%
+      END;
+      FOR f IN ctx.fines.grocery;
+          NEXT IF CGI.param('xact_misc').size &&
+              !CGI.param('xact_misc').grep(f.xact.id).size %]
+          <tr>
+             <td>[% f.xact.last_billing_type | html %]</td>
+             <td class="text-right">[% money(f.xact.balance_owed) %]</td>
+        </tr>
+    [% END %]
+ </tbody>
+</table>