eWay self-signup fixes
[freeside.git] / FS / FS / ClientAPI / SGNG.pm
1 #this stuff is SG-specific (i.e. multi-customer company username hack)
2
3 package FS::ClientAPI::SGNG;
4
5 use strict;
6 use vars qw( $cache $DEBUG );
7 use Time::Local qw(timelocal timelocal_nocheck);
8 use Business::CreditCard;
9 use FS::Record qw( qsearch qsearchs );
10 use FS::Conf;
11 use FS::cust_main;
12 use FS::cust_pkg;
13 use FS::ClientAPI::MyAccount; #qw( payment_info process_payment )
14
15 $DEBUG = 0;
16
17 sub _cache {
18   $cache ||= new FS::ClientAPI_SessionCache( {
19                'namespace' => 'FS::ClientAPI::MyAccount', #yes, share session_ids
20              } );
21 }
22
23 sub ping {
24   #my $p = shift;
25
26   return { 'pong' => '1' };
27
28 }
29
30 #this might almost be general-purpose
31 sub decompify_pkgs {
32   my $p = shift;
33
34   my $session = _cache->get($p->{'session_id'})
35     or return { 'error' => "Can't resume session" }; #better error message
36
37   my $custnum = $session->{'custnum'};
38
39   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
40     or return { 'error' => "unknown custnum $custnum" };
41
42   return { 'error' => 'Not a complimentary customer' }
43     unless $cust_main->payby eq 'COMP';
44
45   my $paydate =
46     $cust_main->paydate =~ /^\S+$/ ? $cust_main->paydate : '2037-12-31';
47
48   my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
49
50   my $date = timelocal(0,0,0,$payday,--$paymonth,$payyear);
51
52   foreach my $cust_pkg (
53     qsearch({ 'table'     => 'cust_pkg',
54               'hashref'   => { 'custnum' => $custnum,
55                                'bill'    => '',
56                              },
57               'extra_sql' => ' AND '. FS::cust_pkg->active_sql,
58            })
59   ) {
60     $cust_pkg->set('bill', $date);
61     my $error = $cust_pkg->replace;
62     return { 'error' => $error } if $error;
63   }
64
65   return { 'error' => '' };
66
67 }
68
69 #find old payment info
70 # (should work just like MyAccount::payment_info, except returns previous info
71 #  too)
72 # definitly sg-specific, no one else stores past customer records like this
73 sub previous_payment_info {
74   my $p = shift;
75
76   my $session = _cache->get($p->{'session_id'})
77     or return { 'error' => "Can't resume session" }; #better error message
78
79   my $payment_info = FS::ClientAPI::MyAccount::payment_info($p);
80
81   my $custnum = $session->{'custnum'};
82
83   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
84     or return { 'error' => "unknown custnum $custnum" };
85
86   #?
87   return $payment_info if $cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/;
88
89   foreach my $prev_cust_main (
90     reverse _previous_cust_main( 'custnum'       => $custnum, 
91                                  'username'      => $cust_main->company,
92                                  'with_payments' => 1,
93                                )
94   ) {
95
96     next unless $prev_cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/;
97
98     if ( $prev_cust_main->payby =~ /^(CARD|DCRD)$/ ) {
99
100       #card expired?
101       my ($payyear,$paymonth,$payday) = split (/-/, $cust_main->paydate);
102
103       my $expdate = timelocal_nocheck(0,0,0,1,$paymonth,$payyear);
104
105       next if $expdate < time;
106
107     } elsif ( $prev_cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
108
109       #any check?  or just skip these in favor of cards?
110
111     }
112
113     return { %$payment_info,
114              #$prev_cust_main->payment_info
115              _cust_main_payment_info( $prev_cust_main ),
116              'previous_custnum' => $prev_cust_main->custnum,
117            };
118
119   }
120
121   #still nothing?  return an error?
122   return $payment_info;
123
124 }
125
126 #this is really FS::cust_main::payment_info, but here for now
127 sub _cust_main_payment_info {
128   my $self = shift;
129
130   my %return = ();
131
132   $return{balance} = $self->balance;
133
134   $return{payname} = $self->payname
135                      || ( $self->first. ' '. $self->get('last') );
136
137   $return{$_} = $self->get($_) for qw(address1 address2 city state zip);
138
139   $return{payby} = $self->payby;
140   $return{stateid_state} = $self->stateid_state;
141
142   if ( $self->payby =~ /^(CARD|DCRD)$/ ) {
143     $return{card_type} = cardtype($self->payinfo);
144     $return{payinfo} = $self->paymask;
145
146     @return{'month', 'year'} = $self->paydate_monthyear;
147
148   }
149
150   if ( $self->payby =~ /^(CHEK|DCHK)$/ ) {
151     my ($payinfo1, $payinfo2) = split '@', $self->paymask;
152     $return{payinfo1} = $payinfo1;
153     $return{payinfo2} = $payinfo2;
154     $return{paytype}  = $self->paytype;
155     $return{paystate} = $self->paystate;
156
157   }
158
159   #doubleclick protection
160   my $_date = time;
161   $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
162
163   %return;
164
165 }
166
167 #find old cust_main records (with payments)
168 sub _previous_cust_main {
169
170   #safety check!  return nothing unless we're enabled explicitly
171   return () unless FS::Conf->new->exists('sg-multicustomer_hack');
172
173   my %opt = @_;
174   my $custnum  = $opt{'custnum'};
175   my $username = $opt{'username'};
176   
177   my %search = ();
178   if ( $opt{'with_payments'} ) {
179     $search{'extra_sql'} =
180       ' AND 0 < ( SELECT COUNT(*) FROM cust_pay
181                     WHERE cust_pay.custnum = cust_main.custnum
182                 )
183       ';
184   }
185
186   qsearch( {
187     'table'    => 'cust_main', 
188     'hashref'  => { 'company' => { op => 'ILIKE', value => $opt{'username'} },
189                     'custnum' => { op => '!=',    value => $opt{'custnum'}  },
190                   },
191     'order_by' => 'ORDER BY custnum',
192     %search,
193   } );
194
195 }
196
197 #since we could be passing masked old CC data, need to look that up and
198 #replace it (like regular process_payment does) w/info from old customer record
199 sub previous_process_payment {
200   my $p = shift;
201
202   return FS::ClientAPI::MyAccount::process_payment($p)
203     unless $p->{'previous_custnum'}
204         && (    ( $p->{'payby'} =~ /^(CARD|DCRD)$/ && $p->{'payinfo'}  =~ /x/i )
205              || ( $p->{'payby'} =~ /^(CHEK|DCHK)$/ && $p->{'payinfo1'} =~ /x/i )
206            );
207
208   my $session = _cache->get($p->{'session_id'})
209     or return { 'error' => "Can't resume session" }; #better error message
210
211   my $custnum = $session->{'custnum'};
212
213   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
214     or return { 'error' => "unknown custnum $custnum" };
215
216   #make sure this is really a previous custnum of this customer
217   my @previous_cust_main =
218     grep { $_->custnum == $p->{'previous_custnum'} }
219          _previous_cust_main( 'custnum'       => $custnum, 
220                               'username'      => $cust_main->company,
221                               'with_payments' => 1,
222                             );
223
224   my $previous_cust_main = $previous_cust_main[0];
225
226   #causes problems with old data w/old masking method
227   #if $previous_cust_main->paymask eq $payinfo;
228   
229   if ( $p->{'payby'} =~ /^(CHEK|DCHK)$/ && $p->{'payinfo1'} =~ /x/i ) {
230     ( $p->{'payinfo1'}, $p->{'payinfo2'} ) =
231       split('@', $previous_cust_main->payinfo);
232   } elsif ( $p->{'payby'} =~ /^(CARD|DCRD)$/ && $p->{'payinfo'} =~ /x/i ) {
233     $p->{'payinfo'} = $previous_cust_main->payinfo;
234   }
235
236   FS::ClientAPI::MyAccount::process_payment($p);
237
238 }
239
240 sub previous_payment_info_renew_info {
241   my $p = shift;
242   my $renew_info   = renew_info($p);
243   my $payment_info = previous_payment_info($p);
244   return { %$renew_info,
245            %$payment_info,
246          };
247 }
248
249 sub previous_process_payment_order_pkg {
250   my $p = shift;
251
252   my $hr = previous_process_payment($p);
253   return $hr if $hr->{'error'};
254
255   order_pkg($p);
256 }
257
258 sub previous_process_payment_change_pkg {
259   my $p = shift;
260
261   my $hr = previous_process_payment($p);
262   return $hr if $hr->{'error'};
263
264   change_pkg($p);
265 }
266
267 sub previous_process_payment_order_renew {
268   my $p = shift;
269
270   my $hr = previous_process_payment($p);
271   return $hr if $hr->{'error'};
272
273   order_renew($p);
274 }
275
276 1;
277