1 # --------------------------------------------------------------------
2 # Copyright (C) 2008 Niles Ingalls
3 # Niles Ingalls <nilesi@zionsville.lib.in.us>
4 # Bill Erickson <erickson@esilibrary.com>
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.
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;
21 use DateTime::Format::ISO8601;
22 use OpenILS::Application::AppUtils;
23 use OpenSRF::Utils qw/:datetime/;
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";
37 __PACKAGE__->register_method(
38 method => 'process_payment',
39 api_name => 'open-ils.credit.process',
41 desc => 'Creates a new provider',
43 { desc => 'Authentication token', type => 'string' },
44 { desc => q/Hash of arguments. Options include:
45 XXX add docs as API stablilizes...
48 return => { desc => 'Hash of status information', type=>'hash' }
58 my $patron = $e->retrieve_actor_user(
60 $argshash->{patron_id},
63 flesh_fields => { au => ["mailing_address"] }
66 ) or return $e->event;
68 return OpenILS::Event->new('BAD_PARAMS')
69 unless $argshash->{login}
70 and $argshash->{password}
71 and $argshash->{action};
73 if ( $argshash->{processor} eq 'PayPal' ) {
74 # XXX not ready for prime time
75 return handle_paypal($e, $argshash, $patron);
77 } elsif ( $argshash->{processor} eq 'AuthorizeNet' ) {
78 return handle_authorizenet($e, $argshash, $patron);
83 my($e, $argshash, $patron) = @_;
85 require Business::PayPal::API;
86 require Business::OnlinePayment::PayPal;
87 my $card = Business::CreditCard::Object->new( $argshash->{cc} );
89 $logger->debug("applying paypal payment");
91 if ( !$card->is_valid ) {
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,
105 my $type = $card->type();
107 if ( substr( $type, -5, 5 ) =~ / card/ ) {
108 $type = substr( $type, 0, -5 );
111 my $transaction = Business::OnlinePayment->new(
112 $argshash->{processor},
113 "Username" => $argshash->{PayPal_Username},
114 "Password" => $argshash->{PayPal_Password},
115 "Signature" => $argshash->{PayPal_Signature}
118 $transaction->content(
119 action => $argshash->{action},
120 amount => $argshash->{amount},
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
132 $transaction->test_transaction(1); # XXX
133 $transaction->submit;
135 if ( $transaction->is_success ) {
137 statusText => "Card approved: ".$transaction->authorization,
139 approvalCode => $transaction->authorization,
140 CorrelationID => $transaction->correlationid
145 statusText => "Card declined: " . $transaction->error_message,
152 sub handle_authorizenet {
153 my($e, $argshash, $patron) = @_;
155 require Business::OnlinePayment::AuthorizeNet;
156 my $card = Business::CreditCard::Object->new( $argshash->{cc} );
158 $logger->debug("applying authorize.net payment");
160 if ( ! $card->is_valid ) {
161 $logger->warn("authorize.net card number is invalid");
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,
176 my $type = $card->type();
178 if ( substr( $type, -5, 5 ) =~ / card/ ) {
179 $type = substr( $type, 0, -5 );
182 my $transaction = new Business::OnlinePayment(
183 $argshash->{processor}, 'test_transaction' => $argshash->{testmode});
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
204 $transaction->submit();
206 if ( $transaction->is_success() ) {
207 $logger->info("authorize.net payment succeeded");
209 statusText => "Card approved: "
210 . $transaction->authorization,
212 approvalCode => $transaction->authorization,
213 server_response => $transaction->server_response
218 $logger->info("authorize.net card declined");
220 statusText => "Card decliined: " . $transaction->error_message,
222 approvalCode => $transaction->error_message,
223 server_response => $transaction->server_response
229 __PACKAGE__->register_method(
230 method => 'retrieve_payable_balance',
231 api_name => 'open-ils.credit.payable_balance.retrieve',
233 desc => q/Returns the total amount of the patron can pay via credit card/,
235 { desc => 'Authentication token', type => 'string' },
236 { desc => 'Authentication token', type => 'string' },
237 { desc => 'User id', type => 'number' }
239 return => { desc => 'The ID of the new provider' }
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;
248 my $user = $e->retrieve_actor_user($user_id)
251 if($e->requestor->id != $user_id) {
252 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou)
255 my $circ_orgs = $e->json_query({
256 "select" => {circ => ["circ_lib"]},
258 "where" => {usr => $user_id, xact_finish => undef},
262 my $groc_orgs = $e->json_query({
263 "select" => {mg => ["billing_location"]},
265 "where" => {usr => $user_id, xact_finish => undef},
271 for my $org ( @$circ_orgs, @$groc_orgs ) {
272 my $o = $org->{billing_location};
273 $o = $org->{circ_lib} unless $o;
275 $hash{$o} = $U->ou_ancestor_setting_value($o, 'global.credit.allow', $e);
278 my @credit_orgs = map { $hash{$_} ? ($_) : () } keys %hash;
279 $logger->debug("credit: relevent orgs that allow credit payments => @credit_orgs");
282 OpenILS::Application::AppUtils->simplereq('open-ils.actor',
283 'open-ils.actor.user.transactions.have_charge', $auth, $user_id);
287 for my $xact (@$xact_summaries) {
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;
295 my $bill = $e->retrieve_money_grocery($xact->id) or return $e->event;
296 next unless grep { $_ == $bill->billing_location } @credit_orgs;
298 $sum += $xact->balance_owed();