]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm
allow circ.reshelving_complete.interval ou setting to override the default reshelving...
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / CreditCard.pm
1 # --------------------------------------------------------------------
2 # Copyright (C) 2008 Niles Ingalls 
3 # Niles Ingalls <nilesi@zionsville.lib.in.us>
4 # Bill Erickson <erickson@esilibrary.com>
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 # --------------------------------------------------------------------
16 package OpenILS::Application::CreditCard;
17 use base qw/OpenSRF::Application/;
18 use strict; use warnings;
19
20 use DateTime;
21 use DateTime::Format::ISO8601;
22 use OpenILS::Application::AppUtils;
23 use OpenSRF::Utils qw/:datetime/;
24 use OpenILS::Event;
25 use OpenSRF::EX qw(:try);
26 use OpenSRF::Utils::Logger qw(:logger);
27 use OpenILS::Utils::Fieldmapper;
28 use OpenILS::Utils::CStoreEditor q/:funcs/;
29 use OpenILS::Const qw/:const/;
30 use OpenSRF::Utils::SettingsClient;
31 use Business::CreditCard;
32 use Business::CreditCard::Object;
33 use Business::OnlinePayment;
34
35
36 __PACKAGE__->register_method(
37     method    => 'process_payment',
38     api_name  => 'open-ils.credit.process',
39     signature => {
40         desc   => 'Creates a new provider',
41         params => [
42             { desc => 'Authentication token', type => 'string' },
43             { desc => q/Hash of arguments.  Options include:
44                 XXX add docs as API stablilizes...
45                 /, type => 'hash' }
46         ],
47         return => { desc => 'Hash of status information', type=>'hash' }
48     }
49 );
50
51 sub process_payment {
52     my $self     = shift;
53     my $client   = shift;
54     my $argshash = shift;
55
56     my $e = new_editor();
57     my $patron = $e->retrieve_actor_user(
58         [
59             $argshash->{patron_id},
60             {
61                 flesh        => 1,
62                 flesh_fields => { au => ["mailing_address"] }
63             }
64         ]
65     ) or return $e->event;
66
67     return OpenILS::Event->new('BAD_PARAMS')
68       unless $argshash->{login}
69           and $argshash->{password}
70           and $argshash->{action};
71
72     if ( $argshash->{processor} eq 'PayPal' ) {    
73         #  XXX not ready for prime time
74         return handle_paypal($e, $argshash, $patron);
75
76     } elsif ( $argshash->{processor} eq 'AuthorizeNet' ) {
77         return handle_authorizenet($e, $argshash, $patron);
78     }
79 }
80
81 sub handle_paypal {
82     my($e, $argshash, $patron) = @_;
83
84     require Business::PayPal::API;
85     require Business::OnlinePayment::PayPal;
86     my $card = Business::CreditCard::Object->new( $argshash->{cc} );
87
88     $logger->debug("applying paypal payment");
89
90     if ( !$card->is_valid ) {
91         return {
92             statusText       => "should return address:(patron_id):",
93             processor        => $argshash->{processor},
94             testmode         => $argshash->{testmode},
95             card             => $card->number(),
96             expiration       => $argshash->{expiration},
97             name             => $patron->first_given_name,
98             patron_id        => $patron->id,
99             patron_patron_id => $patron->mailing_address,
100             statusCode       => 500
101         };
102     }
103
104     my $type = $card->type();
105
106     if ( substr( $type, -5, 5 ) =~ / card/ ) {
107         $type = substr( $type, 0, -5 );
108     }
109
110     my $transaction = Business::OnlinePayment->new(
111         $argshash->{processor},
112         "Username"  => $argshash->{PayPal_Username},
113         "Password"  => $argshash->{PayPal_Password},
114         "Signature" => $argshash->{PayPal_Signature}
115     );
116
117     $transaction->content(
118         action      => $argshash->{action},
119         amount      => $argshash->{amount},
120         type        => "$type",
121         card_number => $card->number(),
122         expiration  => $argshash->{expiration},
123         cvv2        => $argshash->{cvv2},
124         name => $patron->first_given_name . ' ' . $patron->family_name,
125         address => $patron->mailing_address->street1,
126         city    => $patron->mailing_address->city,
127         state   => $patron->mailing_address->state,
128         zip     => $patron->mailing_address->post_code
129     );
130
131     $transaction->test_transaction(1); # XXX
132     $transaction->submit;
133
134     if ( $transaction->is_success ) {
135         return {
136             statusText => "Card approved: ".$transaction->authorization,
137             statusCode    => 200,
138             approvalCode  => $transaction->authorization,
139             CorrelationID => $transaction->correlationid
140         };
141
142     } else {
143         return {
144             statusText => "Card declined: " . $transaction->error_message,
145             statusCode => 500
146
147         };
148     }
149 }
150
151 sub handle_authorizenet {
152     my($e, $argshash, $patron) = @_;
153
154     require Business::OnlinePayment::AuthorizeNet;
155     my $card = Business::CreditCard::Object->new( $argshash->{cc} );
156
157     $logger->debug("applying authorize.net payment");
158
159     if ( ! $card->is_valid ) {
160         $logger->warn("authorize.net card number is invalid");
161
162         return {
163             statusText       => "should return address:(patron_id):",
164             processor        => $argshash->{processor},
165             testmode         => $argshash->{testmode},
166             card             => $card->number(),
167             expiration       => $argshash->{expiration},
168             name             => $patron->first_given_name,
169             patron_id        => $patron->id,
170             patron_patron_id => $patron->mailing_address,
171             statusCode       => 500
172         };
173     }
174
175     my $type = $card->type();
176
177     if ( substr( $type, -5, 5 ) =~ / card/ ) {
178         $type = substr( $type, 0, -5 );
179     }
180
181     my $transaction = new Business::OnlinePayment( 
182         $argshash->{processor}, 'test_transaction' => $argshash->{testmode});
183
184     $transaction->content(
185         type        => "$type", #'American Express', 'VISA', 'MasterCard'
186         login       => $argshash->{login},
187         password    => $argshash->{password},
188         action      => $argshash->{action},
189         description => $argshash->{description},
190         amount      => $argshash->{amount},
191         card_number => $card->number(),
192         expiration  => $argshash->{expiration},
193         cvv2        => $argshash->{cvv2},
194         first_name  => $patron->first_given_name,
195         last_name   => $patron->family_name,
196         address     => $patron->mailing_address->street1,
197         city        => $patron->mailing_address->city,
198         state       => $patron->mailing_address->state,
199         zip         => $patron->mailing_address->post_code,
200         customer_id => $patron->id
201     );
202
203     $transaction->submit();
204
205     if ( $transaction->is_success() ) {
206         $logger->info("authorize.net payment succeeded");
207         return {
208             statusText => "Card approved: "
209               . $transaction->authorization,
210             statusCode      => 200,
211             approvalCode    => $transaction->authorization,
212             server_response => $transaction->server_response
213
214         };
215
216     } else {
217         $logger->info("authorize.net card declined");
218         return {
219             statusText => "Card decliined: " . $transaction->error_message,
220             statusCode      => 500,
221             approvalCode    => $transaction->error_message,
222             server_response => $transaction->server_response
223         };
224     }
225 }
226
227
228 __PACKAGE__->register_method(
229     method    => 'retrieve_payable_balance',
230     api_name  => 'open-ils.credit.payable_balance.retrieve',
231     signature => {
232         desc   => '',
233         params => [
234             { desc => 'Authentication token',      type => 'string' },
235             { desc => 'Authentication token', type => 'string' },
236             { desc => 'User id', type => 'number' }
237         ],
238         return => { desc => 'The ID of the new provider' }
239     }
240 );
241
242 sub retrieve_payable_balance {
243     my ( $self, $conn, $auth, $user_id ) = @_;
244     my $e = new_editor( authtoken => $auth );
245     return $e->event unless $e->checkauth;
246
247     if ( $e->requestor->id != $user_id ) {
248         # XXX check perm
249     }
250
251     my $circ_orgs = $e->json_query({
252         "select" => { "circ" => ["circ_lib"] },
253         from     => "circ",
254         "where"  => { "usr" => $user_id, xact_finish => undef },
255         distinct => 1
256     });
257
258     my $groc_orgs = $e->json_query({
259         "select" => { "mg" => ["billing_location"] },
260         from     => "mg",
261         "where"  => { "usr" => $user_id, xact_finish => undef },
262         distinct => 1
263     });
264
265     my %hash;
266     my @orgs;
267     for my $org ( @$circ_orgs, @$groc_orgs ) {
268         my $o = $org->{billing_location};
269         $o = $org->{circ_lib} unless $o;
270         next if $hash{$org};
271         $hash{$o} =
272           OpenILS::Application::AppUtils->simplereq( 'open-ils.actor',
273             'open-ils.actor.ou_setting.ancestor_default',
274             $o, 'credit.allow' );
275     }
276
277     my @credit_orgs = map { $hash{$_} ? ($_) : () } keys %hash;
278
279     my $xact_summaries =
280       OpenILS::Application::AppUtils->simplereq( 'open-ils.actor',
281         'open-ils.actor.user.transactions.have_charge',
282         $auth, $user_id );
283
284     my $sum = 0.0;
285
286     for my $xact (@$xact_summaries) {
287
288         # make two lists and grab them in batch XXX
289         if ( $xact->xact_type eq 'circulation' ) {
290             my $circ =
291               $e->search_action_circulation( { id => $xact->id } )->[0];
292             next unless grep { $_ == $circ->circ_lib } @credit_orgs;
293         }
294         else {
295             my $bill = $e->search_money_grocery( { id => $xact } )->[0];
296             next unless grep { $_ == $bill->billing_location } @credit_orgs;
297         }
298         $sum += $xact->ballance_owed();
299     }
300
301     return $sum;
302 }
303
304 1;