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;
36 __PACKAGE__->register_method(
37 method => 'process_payment',
38 api_name => 'open-ils.credit.process',
40 desc => 'Creates a new provider',
42 { desc => 'Authentication token', type => 'string' },
43 { desc => q/Hash of arguments. Options include:
44 XXX add docs as API stablilizes...
47 return => { desc => 'Hash of status information', type=>'hash' }
57 my $patron = $e->retrieve_actor_user(
59 $argshash->{patron_id},
62 flesh_fields => { au => ["mailing_address"] }
65 ) or return $e->event;
67 return OpenILS::Event->new('BAD_PARAMS')
68 unless $argshash->{login}
69 and $argshash->{password}
70 and $argshash->{action};
72 if ( $argshash->{processor} eq 'PayPal' ) {
73 # XXX not ready for prime time
74 return handle_paypal($e, $argshash, $patron);
76 } elsif ( $argshash->{processor} eq 'AuthorizeNet' ) {
77 return handle_authorizenet($e, $argshash, $patron);
82 my($e, $argshash, $patron) = @_;
84 require Business::PayPal::API;
85 require Business::OnlinePayment::PayPal;
86 my $card = Business::CreditCard::Object->new( $argshash->{cc} );
88 $logger->debug("applying paypal payment");
90 if ( !$card->is_valid ) {
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,
104 my $type = $card->type();
106 if ( substr( $type, -5, 5 ) =~ / card/ ) {
107 $type = substr( $type, 0, -5 );
110 my $transaction = Business::OnlinePayment->new(
111 $argshash->{processor},
112 "Username" => $argshash->{PayPal_Username},
113 "Password" => $argshash->{PayPal_Password},
114 "Signature" => $argshash->{PayPal_Signature}
117 $transaction->content(
118 action => $argshash->{action},
119 amount => $argshash->{amount},
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
131 $transaction->test_transaction(1); # XXX
132 $transaction->submit;
134 if ( $transaction->is_success ) {
136 statusText => "Card approved: ".$transaction->authorization,
138 approvalCode => $transaction->authorization,
139 CorrelationID => $transaction->correlationid
144 statusText => "Card declined: " . $transaction->error_message,
151 sub handle_authorizenet {
152 my($e, $argshash, $patron) = @_;
154 require Business::OnlinePayment::AuthorizeNet;
155 my $card = Business::CreditCard::Object->new( $argshash->{cc} );
157 $logger->debug("applying authorize.net payment");
159 if ( ! $card->is_valid ) {
160 $logger->warn("authorize.net card number is invalid");
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,
175 my $type = $card->type();
177 if ( substr( $type, -5, 5 ) =~ / card/ ) {
178 $type = substr( $type, 0, -5 );
181 my $transaction = new Business::OnlinePayment(
182 $argshash->{processor}, 'test_transaction' => $argshash->{testmode});
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
203 $transaction->submit();
205 if ( $transaction->is_success() ) {
206 $logger->info("authorize.net payment succeeded");
208 statusText => "Card approved: "
209 . $transaction->authorization,
211 approvalCode => $transaction->authorization,
212 server_response => $transaction->server_response
217 $logger->info("authorize.net card declined");
219 statusText => "Card decliined: " . $transaction->error_message,
221 approvalCode => $transaction->error_message,
222 server_response => $transaction->server_response
228 __PACKAGE__->register_method(
229 method => 'retrieve_payable_balance',
230 api_name => 'open-ils.credit.payable_balance.retrieve',
234 { desc => 'Authentication token', type => 'string' },
235 { desc => 'Authentication token', type => 'string' },
236 { desc => 'User id', type => 'number' }
238 return => { desc => 'The ID of the new provider' }
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;
247 if ( $e->requestor->id != $user_id ) {
251 my $circ_orgs = $e->json_query({
252 "select" => { "circ" => ["circ_lib"] },
254 "where" => { "usr" => $user_id, xact_finish => undef },
258 my $groc_orgs = $e->json_query({
259 "select" => { "mg" => ["billing_location"] },
261 "where" => { "usr" => $user_id, xact_finish => undef },
267 for my $org ( @$circ_orgs, @$groc_orgs ) {
268 my $o = $org->{billing_location};
269 $o = $org->{circ_lib} unless $o;
272 OpenILS::Application::AppUtils->simplereq( 'open-ils.actor',
273 'open-ils.actor.ou_setting.ancestor_default',
274 $o, 'credit.allow' );
277 my @credit_orgs = map { $hash{$_} ? ($_) : () } keys %hash;
280 OpenILS::Application::AppUtils->simplereq( 'open-ils.actor',
281 'open-ils.actor.user.transactions.have_charge',
286 for my $xact (@$xact_summaries) {
288 # make two lists and grab them in batch XXX
289 if ( $xact->xact_type eq 'circulation' ) {
291 $e->search_action_circulation( { id => $xact->id } )->[0];
292 next unless grep { $_ == $circ->circ_lib } @credit_orgs;
295 my $bill = $e->search_money_grocery( { id => $xact } )->[0];
296 next unless grep { $_ == $bill->billing_location } @credit_orgs;
298 $sum += $xact->ballance_owed();