]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm
LP#1796942 Treat VISA SIP payments as credit card
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / SIP / Transaction / FeePayment.pm
1 # ---------------------------------------------------------------
2 # Copyright (C) 2011 Merrimack Valley Library Consortium
3 # Jason Stephenson <jstephenson@mvlc.org>
4
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
15 #
16 # An object to handle fee payment
17 #
18
19 package OpenILS::SIP::Transaction::FeePayment;
20
21 use warnings;
22 use strict;
23
24 use POSIX qw(strftime);
25
26 use OpenILS::SIP;
27 use OpenILS::SIP::Transaction;
28 use OpenILS::SIP::Msg qw/:const/;
29 use Sys::Syslog qw(syslog);
30
31 use OpenILS::Application::AppUtils;
32 my $U = 'OpenILS::Application::AppUtils';
33
34
35 our @ISA = qw(OpenILS::SIP::Transaction);
36
37 # Most fields are handled by the Transaction superclass
38 my %fields = (
39               sip_payment_type => undef,
40               fee_id => 0,
41               patron_password => undef,
42              );
43
44 sub new {
45     my $class = shift;
46
47     my $self = $class->SUPER::new(@_);
48
49     foreach my $element (keys %fields) {
50         $self->{_permitted}->{$element} = $fields{$element};
51     }
52
53     @{$self}{keys %fields} = values %fields;
54     return bless $self, $class;
55 }
56
57 sub do_fee_payment {
58     my $self = shift;
59
60     # Just in case something completely unexpected happens, we'll
61     # reject the payment to be 'safe.'
62     $self->ok(0);
63
64     # If the SC sends over a fee id, we try to pay that
65     # fee/transaction on the patron's record.
66     if ($self->fee_id) {
67         my $bill;
68         $bill = $U->simplereq('open-ils.actor', 'open-ils.actor.user.transaction.retrieve', $self->{authtoken}, $self->fee_id);
69         syslog('LOG_DEBUG', 'OILS: open-ils.actor.user.transaction.retrieve returned ' . OpenSRF::Utils::JSON->perl2JSON($bill));
70         # If we got an event or the bill belongs to another patron, set bill to undef.
71         $bill = undef if ($U->event_code($bill) || $bill->usr != $self->patron->internal_id);
72
73         # Attempt the payment here.
74         if ($bill && $bill->balance_owed >= $self->fee_amount) {
75             # We only attempt payment if the balance_owed on the bill
76             # is greater than or equal to the amount paid by the
77             # client.
78             my $payref = [ [$bill->id, $self->fee_amount] ];
79             my $resp = $self->pay_bills($payref);
80             syslog('LOG_INFO', 'OILS: pay_bills returned ' . OpenSRF::Utils::JSON->perl2JSON($resp));
81             if ($U->event_code($resp)) {
82                 $self->ok(0);
83                 $self->screen_msg(($resp->{descr} || $resp->{textcode}));
84             } else {
85                 $self->ok(1);
86             }
87         } else {
88             $self->ok(0);
89             if ($bill) {
90                 # The payment had to be greater than the bill balance
91                 # to end up here. We don't allow credits or
92                 # overpayment.
93                 $self->sreen_msg(OILS_SIP_MSG_OVERPAYMENT);
94             }
95             else {
96                 # In this case, the bill was not found or did not
97                 # belong to the patron.
98                 $self->screen_msg(OILS_SIP_MSG_NO_BILL);
99             }
100         }
101     } else {
102         # We attempt to pay as many of the patron's bills as possible with the payment provided.
103         my $results = $U->simplereq('open-ils.actor', 'open-ils.actor.user.transactions.history.have_balance', $self->{authtoken}, $self->patron->internal_id);
104         if ($results && ref($results) eq 'ARRAY') {
105             syslog('LOG_INFO', 'OILS: ' . scalar @$results . " bills found for " . $self->patron->internal_id);
106
107             # If we get an empty array, we report not bills found and
108             # quit.
109             unless (@$results) {
110                 $self->ok(0);
111                 $self->screen_msg(OILS_SIP_MSG_NO_BILL);
112                 return $self->ok;
113             }
114
115             # We fill an array with the payment information as
116             # open-ils.circ.money.payment expects it, i.e. an arrayref
117             # with the bill_id and payment amount of its members. To
118             # actually pay the bils, we pass the reference to this
119             # array to our pay_bils method.
120             my @payments = ();
121
122             # Pay each bill from the fee_amount provided until we
123             # either run out of bills or the input payment balance
124             # hits zero.
125             my $amount_paid = $self->fee_amount; # If this hits zero, we're done.
126             foreach my $bill (@{$results}) {
127                 my $payment;
128                 syslog('LOG_INFO', 'OILS: bill '. $bill->id . ' amount ' . $bill->balance_owed);
129                 # Skip negative or zero-balance bills. (Not that I've
130                 # ever seen any.)
131                 next if ($bill->balance_owed <= 0);
132                 if ($bill->balance_owed >= $amount_paid) {
133                     # We owe as much as or more than we have money
134                     # left, so pay what we have left.
135                     $payment = $amount_paid;
136                     $amount_paid = 0;
137                 } else {
138                     # This bill is for less than the amount we have
139                     # left, so pay the full bill amount.
140                     $payment = $bill->balance_owed;
141                     $amount_paid -= $bill->balance_owed;
142                 }
143                 # Add the payment to our array.
144                 push(@payments, [$bill->id, $payment]);
145                 # Attempt to round $amount_paid to avoid floating point error.
146                 $amount_paid = sprintf("%.2f", $amount_paid);
147                 syslog('LOG_INFO', "OILS: paid $payment on " . $bill->id . " with balance " . $bill->balance_owed . " and $amount_paid remaining");
148                 # Leave if we ran out of money.
149                 last if ($amount_paid == 0.00);
150             }
151             if (@payments && $amount_paid == 0.00) {
152                 # pay the bills with a reference to our payments
153                 # array.
154                 my $resp = $self->pay_bills(\@payments);
155                 syslog('LOG_INFO', 'OILS: pay_bills returned ' . OpenSRF::Utils::JSON->perl2JSON($resp));
156                 if ($U->event_code($resp)) {
157                     $self->ok(0);
158                     $self->screen_msg(($resp->{descr} || $resp->{textcode}));
159                 } else {
160                     $self->ok(1);
161                 }
162             } else {
163                 $self->ok(0);
164                 if (scalar(@payments) == 0) {
165                     # We didn't find any bills for the patron.
166                     $self->screen_msg(OILS_SIP_MSG_NO_BILL);
167                 } else {
168                     # We have an overpayment
169                     $self->screen_msg(OILS_SIP_MSG_OVERPAYMENT);
170                 }
171             }
172         } else {
173             $self->ok(0);
174             if ($results && $U->event_code($results)) {
175                 syslog('LOG_INFO', 'OILS: open-ils.actor.user.transactions.history.have_balance returned '
176                        . OpenSRF::Utils::JSON->perl2JSON($results));
177                 $self->screen_msg(OILS_SIP_MSG_BILL_ERR);
178             } else {
179                 syslog('LOG_INFO', 'OILS: open-ils.actor.user.transactions.history.have_balance returned nothing');
180                 $self->screen_msg(OILS_SIP_MSG_NO_BILL);
181             }
182         }
183     }
184     return $self->ok;
185 }
186
187 # Takes array ref of array ref of [billid, payment_amount] to pay in
188 # batch.
189 sub pay_bills {
190     my ($self, $paymentref) = @_;
191     my $user = $self->patron->{user};
192     if ($self->sip_payment_type eq '02' || $self->sip_payment_type eq '01') {
193         # '01' is "VISA"
194         # '02' is "credit card"
195         my $transaction_id = $self->transaction_id ? $self->transaction_id : 'Not provided by SIP client';
196         return $U->simplereq('open-ils.circ', 'open-ils.circ.money.payment', $self->{authtoken},
197                              { payment_type => "credit_card_payment", userid => $user->id, note => "via SIP2",
198                                cc_args => { approval_code => $transaction_id, },
199                                payments => $paymentref}, $user->last_xact_id);
200     } else {
201         # record as "cash"
202         return $U->simplereq('open-ils.circ', 'open-ils.circ.money.payment', $self->{authtoken},
203                              { payment_type => "cash_payment", userid => $user->id, note => "via SIP2",
204                                payments => $paymentref}, $user->last_xact_id);
205     }
206 }
207
208
209 1;