]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm
adding hold verification support per copy_location
[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 my $U = "OpenILS::Application::AppUtils";
35
36
37 __PACKAGE__->register_method(
38     method    => 'process_payment',
39     api_name  => 'open-ils.credit.process',
40     signature => {
41         desc   => 'Creates a new provider',
42         params => [
43             { desc => 'Authentication token', type => 'string' },
44             { desc => q/Hash of arguments.  Options include:
45                 XXX add docs as API stablilizes...
46                 /, type => 'hash' }
47         ],
48         return => { desc => 'Hash of status information', type=>'hash' }
49     }
50 );
51
52 sub process_payment {
53     my $self     = shift;
54     my $client   = shift;
55     my $argshash = shift;
56
57     my $e = new_editor();
58     my $patron = $e->retrieve_actor_user(
59         [
60             $argshash->{patron_id},
61             {
62                 flesh        => 1,
63                 flesh_fields => { au => ["mailing_address"] }
64             }
65         ]
66     ) or return $e->event;
67
68     return OpenILS::Event->new('BAD_PARAMS')
69       unless $argshash->{login}
70           and $argshash->{password}
71           and $argshash->{action};
72
73     if ( $argshash->{processor} eq 'PayPal' ) {    
74         #  XXX not ready for prime time
75         return handle_paypal($e, $argshash, $patron);
76
77     } elsif ( $argshash->{processor} eq 'AuthorizeNet' ) {
78         return handle_authorizenet($e, $argshash, $patron);
79     }
80 }
81
82 sub handle_paypal {
83     my($e, $argshash, $patron) = @_;
84
85     require Business::PayPal::API;
86     require Business::OnlinePayment::PayPal;
87     my $card = Business::CreditCard::Object->new( $argshash->{cc} );
88
89     $logger->debug("applying paypal payment");
90
91     if ( !$card->is_valid ) {
92         return {
93             statusText       => "should return address:(patron_id):",
94             processor        => $argshash->{processor},
95             testmode         => $argshash->{testmode},
96             card             => $card->number(),
97             expiration       => $argshash->{expiration},
98             name             => $patron->first_given_name,
99             patron_id        => $patron->id,
100             patron_patron_id => $patron->mailing_address,
101             statusCode       => 500
102         };
103     }
104
105     my $type = $card->type();
106
107     if ( substr( $type, -5, 5 ) =~ / card/ ) {
108         $type = substr( $type, 0, -5 );
109     }
110
111     my $transaction = Business::OnlinePayment->new(
112         $argshash->{processor},
113         "Username"  => $argshash->{PayPal_Username},
114         "Password"  => $argshash->{PayPal_Password},
115         "Signature" => $argshash->{PayPal_Signature}
116     );
117
118     $transaction->content(
119         action      => $argshash->{action},
120         amount      => $argshash->{amount},
121         type        => "$type",
122         card_number => $card->number(),
123         expiration  => $argshash->{expiration},
124         cvv2        => $argshash->{cvv2},
125         name => $patron->first_given_name . ' ' . $patron->family_name,
126         address => $patron->mailing_address->street1,
127         city    => $patron->mailing_address->city,
128         state   => $patron->mailing_address->state,
129         zip     => $patron->mailing_address->post_code
130     );
131
132     $transaction->test_transaction(1); # XXX
133     $transaction->submit;
134
135     if ( $transaction->is_success ) {
136         return {
137             statusText => "Card approved: ".$transaction->authorization,
138             statusCode    => 200,
139             approvalCode  => $transaction->authorization,
140             CorrelationID => $transaction->correlationid
141         };
142
143     } else {
144         return {
145             statusText => "Card declined: " . $transaction->error_message,
146             statusCode => 500
147
148         };
149     }
150 }
151
152 sub handle_authorizenet {
153     my($e, $argshash, $patron) = @_;
154
155     require Business::OnlinePayment::AuthorizeNet;
156     my $card = Business::CreditCard::Object->new( $argshash->{cc} );
157
158     $logger->debug("applying authorize.net payment");
159
160     if ( ! $card->is_valid ) {
161         $logger->warn("authorize.net card number is invalid");
162
163         return {
164             statusText       => "should return address:(patron_id):",
165             processor        => $argshash->{processor},
166             testmode         => $argshash->{testmode},
167             card             => $card->number(),
168             expiration       => $argshash->{expiration},
169             name             => $patron->first_given_name,
170             patron_id        => $patron->id,
171             patron_patron_id => $patron->mailing_address,
172             statusCode       => 500
173         };
174     }
175
176     my $type = $card->type();
177
178     if ( substr( $type, -5, 5 ) =~ / card/ ) {
179         $type = substr( $type, 0, -5 );
180     }
181
182     my $transaction = new Business::OnlinePayment( 
183         $argshash->{processor}, 'test_transaction' => $argshash->{testmode});
184
185     $transaction->content(
186         type        => "$type", #'American Express', 'VISA', 'MasterCard'
187         login       => $argshash->{login},
188         password    => $argshash->{password},
189         action      => $argshash->{action},
190         description => $argshash->{description},
191         amount      => $argshash->{amount},
192         card_number => $card->number(),
193         expiration  => $argshash->{expiration},
194         cvv2        => $argshash->{cvv2},
195         first_name  => $patron->first_given_name,
196         last_name   => $patron->family_name,
197         address     => $patron->mailing_address->street1,
198         city        => $patron->mailing_address->city,
199         state       => $patron->mailing_address->state,
200         zip         => $patron->mailing_address->post_code,
201         customer_id => $patron->id
202     );
203
204     $transaction->submit();
205
206     if ( $transaction->is_success() ) {
207         $logger->info("authorize.net payment succeeded");
208         return {
209             statusText => "Card approved: "
210               . $transaction->authorization,
211             statusCode      => 200,
212             approvalCode    => $transaction->authorization,
213             server_response => $transaction->server_response
214
215         };
216
217     } else {
218         $logger->info("authorize.net card declined");
219         return {
220             statusText => "Card decliined: " . $transaction->error_message,
221             statusCode      => 500,
222             approvalCode    => $transaction->error_message,
223             server_response => $transaction->server_response
224         };
225     }
226 }
227
228
229 __PACKAGE__->register_method(
230     method    => 'retrieve_payable_balance',
231     api_name  => 'open-ils.credit.payable_balance.retrieve',
232     signature => {
233         desc   => q/Returns the total amount of the patron can pay via credit card/,
234         params => [
235             { desc => 'Authentication token',      type => 'string' },
236             { desc => 'Authentication token', type => 'string' },
237             { desc => 'User id', type => 'number' }
238         ],
239         return => { desc => 'The ID of the new provider' }
240     }
241 );
242
243 sub retrieve_payable_balance {
244     my ( $self, $conn, $auth, $user_id ) = @_;
245     my $e = new_editor(authtoken => $auth);
246     return $e->event unless $e->checkauth;
247
248     my $user = $e->retrieve_actor_user($user_id) 
249         or return $e->event;
250
251     if($e->requestor->id != $user_id) {
252         return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou)
253     }
254
255     my $circ_orgs = $e->json_query({
256         "select" => {circ => ["circ_lib"]},
257         from     => "circ",
258         "where"  => {usr => $user_id, xact_finish => undef},
259         distinct => 1
260     });
261
262     my $groc_orgs = $e->json_query({
263         "select" => {mg => ["billing_location"]},
264         from     => "mg",
265         "where"  => {usr => $user_id, xact_finish => undef},
266         distinct => 1
267     });
268
269     my %hash;
270     my @orgs;
271     for my $org ( @$circ_orgs, @$groc_orgs ) {
272         my $o = $org->{billing_location};
273         $o = $org->{circ_lib} unless $o;
274         next if $hash{$org};
275         $hash{$o} = $U->ou_ancestor_setting_value($o, 'global.credit.allow', $e);
276     }
277
278     my @credit_orgs = map { $hash{$_} ? ($_) : () } keys %hash;
279     $logger->debug("credit: relevent orgs that allow credit payments => @credit_orgs");
280
281     my $xact_summaries =
282       OpenILS::Application::AppUtils->simplereq('open-ils.actor',
283         'open-ils.actor.user.transactions.have_charge', $auth, $user_id);
284
285     my $sum = 0.0;
286
287     for my $xact (@$xact_summaries) {
288
289         # make two lists and grab them in batch XXX
290         if ( $xact->xact_type eq 'circulation' ) {
291             my $circ = $e->retrieve_action_circulation($xact->id) or return $e->event;
292             next unless grep { $_ == $circ->circ_lib } @credit_orgs;
293
294         } else {
295             my $bill = $e->retrieve_money_grocery($xact->id) or return $e->event;
296             next unless grep { $_ == $bill->billing_location } @credit_orgs;
297         }
298         $sum += $xact->balance_owed();
299     }
300
301     return $sum;
302 }
303
304 1;