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