- bring prepaid support into this century (close: Bug#1124)
[freeside.git] / fs_signup / FS-SignupClient / cgi / signup.cgi
1 #!/usr/bin/perl -T
2 #!/usr/bin/perl -Tw
3 #
4 # $Id: signup.cgi,v 1.55 2005-01-29 12:34:11 ivan Exp $
5
6 use strict;
7 use vars qw( @payby $cgi $locales $packages
8              $pops %pop %popnum2pop
9              $init_data $error
10
11              $last $first $ss $company $address1
12              $address2 $city $state $county
13              $country $zip $daytime $night $fax
14
15              $ship_last $ship_first $ship_ss $ship_company $ship_address1
16              $ship_address2 $ship_city $ship_state $ship_county
17              $ship_country $ship_zip $ship_daytime $ship_night $ship_fax
18
19              $invoicing_list $payby $payinfo
20              $paycvv $paydate $payname $referral_custnum $init_popstate
21              $pkgpart $username $password $password2 $sec_phrase $popnum
22              $agentnum $refnum
23              $ieak_file $ieak_template
24              $signup_html $signup_template
25              $success_html $success_template
26              $decline_html $decline_template
27              $ac $exch $loc
28              $email_name $pkg
29              $self_url
30            );
31 use subs qw( print_form print_okay print_decline
32              success_default decline_default
33            );
34 use CGI;
35 #use CGI::Carp qw(fatalsToBrowser);
36 use Text::Template;
37 use Business::CreditCard;
38 use HTTP::BrowserDetect;
39 use FS::SelfService qw( signup_info new_customer expselect );
40
41 #acceptable payment methods
42 #
43 #@payby = qw( CARD BILL COMP );
44 #@payby = qw( CARD BILL );
45 #@payby = qw( CARD );
46 @payby = qw( CARD PREPAY );
47
48 $ieak_file = '/usr/local/freeside/ieak.template';
49 $signup_html = -e 'signup.html'
50                  ? 'signup.html'
51                  : '/usr/local/freeside/signup.html';
52 $success_html = -e 'success.html'
53                   ? 'success.html'
54                   : '/usr/local/freeside/success.html';
55 $decline_html = -e 'decline.html'
56                   ? 'decline.html'
57                   : '/usr/local/freeside/decline.html';
58
59
60 if ( -e $ieak_file ) {
61   my $ieak_txt = Text::Template::_load_text($ieak_file)
62     or die $Text::Template::ERROR;
63   $ieak_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
64   $ieak_txt = $1;
65   $ieak_txt =~ s/\r//g; # don't double \r on old templates
66   $ieak_txt =~ s/\n/\r\n/g;
67   $ieak_template = new Text::Template ( TYPE => 'STRING', SOURCE => $ieak_txt )
68     or die $Text::Template::ERROR;
69 } else {
70   $ieak_template = '';
71 }
72
73 $agentnum = '';
74 if ( -e $signup_html ) {
75   my $signup_txt = Text::Template::_load_text($signup_html)
76     or die $Text::Template::ERROR;
77   $signup_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
78   $signup_txt = $1;
79   $signup_template = new Text::Template ( TYPE => 'STRING',
80                                           SOURCE => $signup_txt,
81                                           DELIMITERS => [ '<%=', '%>' ]
82                                         )
83     or die $Text::Template::ERROR;
84   if ( $signup_txt =~
85          /<\s*INPUT TYPE="?hidden"?\s+NAME="?agentnum"?\s+VALUE="?(\d+)"?\s*>/si
86   ) {
87     $agentnum = $1;
88   }
89 } else {
90   #too much maintenance hassle to keep in this file
91   die "can't find ./signup.html or /usr/local/freeside/signup.html";
92   #$signup_template = new Text::Template ( TYPE => 'STRING',
93   #                                        SOURCE => &signup_default,
94   #                                        DELIMITERS => [ '<%=', '%>' ]
95   #                                      )
96   #  or die $Text::Template::ERROR;
97 }
98
99 if ( -e $success_html ) {
100   my $success_txt = Text::Template::_load_text($success_html)
101     or die $Text::Template::ERROR;
102   $success_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
103   $success_txt = $1;
104   $success_template = new Text::Template ( TYPE => 'STRING',
105                                            SOURCE => $success_txt,
106                                            DELIMITERS => [ '<%=', '%>' ],
107                                          )
108     or die $Text::Template::ERROR;
109 } else {
110   $success_template = new Text::Template ( TYPE => 'STRING',
111                                            SOURCE => &success_default,
112                                            DELIMITERS => [ '<%=', '%>' ],
113                                          )
114     or die $Text::Template::ERROR;
115 }
116
117 if ( -e $decline_html ) {
118   my $decline_txt = Text::Template::_load_text($decline_html)
119     or die $Text::Template::ERROR;
120   $decline_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
121   $decline_txt = $1;
122   $decline_template = new Text::Template ( TYPE => 'STRING',
123                                            SOURCE => $decline_txt,
124                                            DELIMITERS => [ '<%=', '%>' ],
125                                          )
126     or die $Text::Template::ERROR;
127 } else {
128   $decline_template = new Text::Template ( TYPE => 'STRING',
129                                            SOURCE => &decline_default,
130                                            DELIMITERS => [ '<%=', '%>' ],
131                                          )
132     or die $Text::Template::ERROR;
133 }
134
135 $cgi = new CGI;
136
137 $init_data = signup_info( 'agentnum'   => $agentnum,
138                           'promo_code' => scalar($cgi->param('promo_code')),
139                           'reg_code'   => uc(scalar($cgi->param('reg_code'))),
140                         );
141 $error = $init_data->{'error'};
142 $locales = $init_data->{'cust_main_county'};
143 $packages = $init_data->{'part_pkg'};
144 $pops = $init_data->{'svc_acct_pop'};
145 @payby = @{$init_data->{'payby'}} if @{$init_data->{'payby'}};
146 $packages = $init_data->{agentnum2part_pkg}{$agentnum} if $agentnum;
147 %pop = ();
148 %popnum2pop = ();
149 foreach (@$pops) {
150   push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
151   $popnum2pop{$_->{popnum}} = $_;
152 }
153
154 if ( defined $cgi->param('magic') ) {
155   if ( $cgi->param('magic') eq 'process' ) {
156
157     if ( $cgi->param('state') =~ /^(\w*)( \(([\w ]+)\))? ?\/ ?(\w+)$/ ) {
158       $state = $1;
159       $county = $3 || '';
160       $country = $4;
161     } elsif ( $cgi->param('state') =~ /^(\w*)$/ ) {
162       $state = $1;
163       $cgi->param('county') =~ /^([\w ]*)$/
164         or die "illegal county: ". $cgi->param('county');
165       $county = $1;
166       $cgi->param('country') =~ /^(\w+)$/
167         or die "illegal country: ". $cgi->param('country');
168       $country = $1;
169     } else {
170       die "illegal state: ". $cgi->param('state');
171     }
172     if ( $cgi->param('ship_state') =~ /^(\w*)( \(([\w ]+)\))? ?\/ ?(\w+)$/ ) {
173       $ship_state = $1;
174       $ship_county = $3 || '';
175       $ship_country = $4;
176     } elsif ( $cgi->param('ship_state') =~ /^(\w*)$/ ) {
177       $ship_state = $1;
178       $cgi->param('ship_county') =~ /^([\w ]*)$/
179         or die "illegal county: ". $cgi->param('ship_county');
180       $ship_county = $1;
181       #$cgi->param('ship_country') =~ /^(\w+)$/
182       $cgi->param('ship_country') =~ /^(\w*)$/
183         or die "illegal ship_country: ". $cgi->param('ship_country');
184       $ship_country = $1;
185     #} else {
186     #  die "illegal ship_state: ". $cgi->param('ship_state');
187     }
188
189     $payby = $cgi->param('payby');
190     if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
191       #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
192       $payinfo = $cgi->param($payby. '_payinfo1'). '@'. 
193                  $cgi->param($payby. '_payinfo2');
194     } else {
195       $payinfo = $cgi->param( $payby. '_payinfo' );
196     }
197     $paydate =
198       $cgi->param( $payby. '_month' ). '-'. $cgi->param( $payby. '_year' );
199     $payname = $cgi->param( $payby. '_payname' );
200     $paycvv = defined $cgi->param( $payby. '_paycvv' )
201                 ? $cgi->param( $payby. '_paycvv' )
202                 : '';
203
204     if ( $invoicing_list = $cgi->param('invoicing_list') ) {
205       $invoicing_list .= ', POST' if $cgi->param('invoicing_list_POST');
206     } else {
207       $invoicing_list = 'POST';
208     }
209
210     $error = '';
211
212     $last             = $cgi->param('last');
213     $first            = $cgi->param('first');
214     $ss               = $cgi->param('ss');
215     $company          = $cgi->param('company');
216     $address1         = $cgi->param('address1');
217     $address2         = $cgi->param('address2');
218     $city             = $cgi->param('city');
219     #$county,
220     #$state,
221     $zip              = $cgi->param('zip');
222     #$country,
223     $daytime          = $cgi->param('daytime');
224     $night            = $cgi->param('night');
225     $fax              = $cgi->param('fax');
226
227     $ship_last        = $cgi->param('ship_last');
228     $ship_first       = $cgi->param('ship_first');
229     $ship_ss          = $cgi->param('ship_ss');
230     $ship_company     = $cgi->param('ship_company');
231     $ship_address1    = $cgi->param('ship_address1');
232     $ship_address2    = $cgi->param('ship_address2');
233     $ship_city        = $cgi->param('ship_city');
234     #$ship_county,
235     #$ship_state,
236     $ship_zip         = $cgi->param('ship_zip');
237     #$ship_country,
238     $ship_daytime     = $cgi->param('ship_daytime');
239     $ship_night       = $cgi->param('ship_night');
240     $ship_fax         = $cgi->param('ship_fax');
241
242     #$payby,
243     #$payinfo,
244     #$paydate,
245     #$payname,
246     #$invoicing_list,
247     $referral_custnum = $cgi->param('ref');
248     $pkgpart          = $cgi->param('pkgpart');
249     $username         = $cgi->param('username');
250     $sec_phrase       = $cgi->param('sec_phrase');
251     $password         = $cgi->param('_password');
252     $popnum           = $cgi->param('popnum');
253     #$agentnum, #         = $cgi->param('agentnum'),
254     $agentnum         ||= $cgi->param('agentnum');
255     $init_popstate    = $cgi->param('init_popstate');
256     $refnum           = $cgi->param('refnum');
257
258     if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
259       $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
260       $password  = '';
261       $password2 = '';
262     } else {
263       $password2 = $cgi->param('_password2');
264
265       if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
266         $payinfo =~ s/\D//g;
267
268         $payinfo =~ /^(\d{13,16})$/
269           or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
270         $payinfo = $1;
271         validate($payinfo)
272           or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
273         cardtype($payinfo) eq $cgi->param('CARD_type')
274           or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
275       }
276
277       unless ( $error ) {
278
279         my $r = new_customer ( {
280           'last'             => $last,
281           'first'            => $first,
282           'ss'               => $ss,
283           'company'          => $company,
284           'address1'         => $address1,
285           'address2'         => $address2,
286           'city'             => $city,
287           'county'           => $county,
288           'state'            => $state,
289           'zip'              => $zip,
290           'country'          => $country,
291           'daytime'          => $daytime,
292           'night'            => $night,
293           'fax'              => $fax,
294           'ship_last'        => $ship_last,
295           'ship_first'       => $ship_first,
296           'ship_company'     => $ship_company,
297           'ship_address1'    => $ship_address1,
298           'ship_address2'    => $ship_address2,
299           'ship_city'        => $ship_city,
300           'ship_county'      => $ship_county,
301           'ship_state'       => $ship_state,
302           'ship_zip'         => $ship_zip,
303           'ship_country'     => $ship_country,
304           'ship_daytime'     => $ship_daytime,
305           'ship_night'       => $ship_night,
306           'ship_fax'         => $ship_fax,
307           'payby'            => $payby,
308           'payinfo'          => $payinfo,
309           'paycvv'           => $paycvv,
310           'paydate'          => $paydate,
311           'payname'          => $payname,
312           'invoicing_list'   => $invoicing_list,
313           'referral_custnum' => $referral_custnum,
314           'promo_code'       => scalar($cgi->param('promo_code')),
315           'reg_code'         => uc(scalar($cgi->param('reg_code'))),
316           'pkgpart'          => $pkgpart,
317           'username'         => $username,
318           'sec_phrase'       => $sec_phrase,
319           '_password'        => $password,
320           'popnum'           => $popnum,
321           'agentnum'         => $agentnum,
322           'refnum'           => $refnum,
323           map { $_ => $cgi->param($_) } grep { /^snarf_/ } $cgi->param
324         } );
325         $error ||= $r->{'error'};
326
327       }
328
329     }
330     
331     if ( $error eq '_decline' ) {
332       print_decline();
333     } elsif ( $error ) {
334       #fudge the snarf info
335       no strict 'refs';
336       ${$_} = $cgi->param($_) foreach grep { /^snarf_/ } $cgi->param;
337       print_form();
338     } else {
339       print_okay();
340     }
341
342   } else {
343     die "unrecognized magic: ". $cgi->param('magic');
344   }
345 } else {
346   #$error = '';
347   $last = '';
348   $first = '';
349   $ss = '';
350   $company = '';
351   $address1 = '';
352   $address2 = '';
353   $city = '';
354   $state = $init_data->{statedefault};
355   $county = '';
356   $country = $init_data->{countrydefault};
357   $zip = '';
358   $daytime = '';
359   $night = '';
360   $fax = '';
361   $ship_last = '';
362   $ship_first = '';
363   $ship_company = '';
364   $ship_address1 = '';
365   $ship_address2 = '';
366   $ship_city = '';
367   $ship_state = $init_data->{statedefault};
368   $ship_county = '';
369   $ship_country = $init_data->{countrydefault};
370   $ship_zip = '';
371   $ship_daytime = '';
372   $ship_night = '';
373   $ship_fax = '';
374   $invoicing_list = '';
375   $payby = '';
376   $payinfo = '';
377   $paydate = '';
378   $payname = '';
379   $pkgpart = '';
380   $username = '';
381   $password = '';
382   $password2 = '';
383   $sec_phrase = '';
384   $popnum = '';
385   $referral_custnum = $cgi->param('ref') || '';
386   $init_popstate = $cgi->param('init_popstate') || '';
387   $refnum = $init_data->{'refnum'};
388   print_form;
389 }
390
391 sub print_form {
392
393   $cgi->delete('ref');
394   $cgi->delete('init_popstate');
395   $self_url = $cgi->self_url;
396
397   $error = "Error: $error" if $error;
398
399   print $cgi->header( '-expires' => 'now' ),
400         $signup_template->fill_in();
401
402 }
403
404 sub print_decline {
405   print $cgi->header( '-expires' => 'now' ),
406         $decline_template->fill_in();
407 }
408
409 sub print_okay {
410   my $user_agent = new HTTP::BrowserDetect $ENV{HTTP_USER_AGENT};
411
412   $cgi->param('username') =~ /^(.+)$/
413     or die "fatal: invalid username got past FS::SelfService::new_customer";
414   my $username = $1;
415   $cgi->param('_password') =~ /^(.+)$/
416     or die "fatal: invalid password got past FS::SelfService::new_customer";
417   my $password = $1;
418   ( $cgi->param('first'). ' '. $cgi->param('last') ) =~ /^(.*)$/
419     or die "fatal: invalid email_name got past FS::SelfService::new_customer";
420   $email_name = $1; #global for template
421
422   my $pop = $popnum2pop{$cgi->param('popnum')};
423     #or die "fatal: invalid popnum got past FS::SelfService::new_customer";
424   if ( $pop ) {
425     ( $ac, $exch, $loc ) = ( $pop->{'ac'}, $pop->{'exch'}, $pop->{'loc'} );
426   } else {
427     ( $ac, $exch, $loc ) = ( '', '', ''); #presumably you're not using them.
428   }
429
430   #global for template
431   $pkg = ( grep { $_->{'pkgpart'} eq $pkgpart } @$packages )[0]->{'pkg'};
432
433   if ( $ieak_template && $user_agent->windows && $user_agent->ie ) {
434     #send an IEAK config
435     print $cgi->header('application/x-Internet-signup'),
436           $ieak_template->fill_in();
437   } else { #send a simple confirmation
438     print $cgi->header( '-expires' => 'now' ),
439           $success_template->fill_in();
440   }
441 }
442
443 sub success_default { #html to use if you don't specify a success file
444   <<'END';
445 <HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
446 <BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Signup successful</FONT><BR><BR>
447 Thanks for signing up!
448 <BR><BR>
449 Signup information for <%= $email_name %>:
450 <BR><BR>
451 Username: <%= $username %><BR>
452 Password: <%= $password %><BR>
453 Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
454 Package: <%= $pkg %><BR>
455 </BODY></HTML>
456 END
457 }
458
459 sub decline_default { #html to use if there is a decline
460   <<'END';
461 <HTML><HEAD><TITLE>Processing error</TITLE></HEAD>
462 <BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Processing error</FONT><BR><BR>
463 There has been an error processing your account.  Please contact customer
464 support.
465 </BODY></HTML>
466 END
467 }
468
469 # subs for the templates...
470
471 =item regionselector SELECTED_COUNTY, SELECTED_STATE, SELECTED_COUNTRY, PREFIX, ONCHANGE
472
473 =cut
474
475 sub regionselector {
476   my ( $selected_county, $selected_state, $selected_country,
477        $prefix, $onchange ) = @_;
478   signup_info() unless $init_data;
479   FS::SelfService::regionselector({
480     selected_county  => $selected_county,
481     selected_state   => $selected_state,
482     selected_country => $selected_country,
483     prefix           => $prefix,
484     onchange         => $onchange,
485     default_country  => $init_data->{countrydefault},
486     locales          => $init_data->{cust_main_county},
487   });
488     #default_state    => $init_data->{statedefault},
489 }
490
491 =item popselector 
492
493 =cut
494
495 sub popselector {
496   my( $popnum ) = @_;
497   signup_info() unless $init_data;
498   FS::SelfService::popselector({
499     popnum => $popnum,
500     pops   => $init_data->{svc_acct_pop},
501   });
502     #popac =>
503     #acstate =>
504 }
505