82705080ce8e7f69f5cef8dcaa76a97c252e59b1
[freeside.git] / fs_selfservice / FS-SelfService / SelfService.pm
1 package FS::SelfService;
2
3 use strict;
4 use vars qw($VERSION @ISA @EXPORT_OK $DEBUG $dir $socket %autoload $tag);
5 use Exporter;
6 use Socket;
7 use FileHandle;
8 #use IO::Handle;
9 use IO::Select;
10 use Storable 2.09 qw(nstore_fd fd_retrieve);
11
12 $VERSION = '0.03';
13
14 @ISA = qw( Exporter );
15
16 $DEBUG = 0;
17
18 $dir = "/usr/local/freeside";
19 $socket =  "$dir/selfservice_socket";
20 $socket .= '.'.$tag if defined $tag && length($tag);
21
22 #maybe should ask ClientAPI for this list
23 %autoload = (
24   'passwd'                    => 'passwd/passwd',
25   'chfn'                      => 'passwd/passwd',
26   'chsh'                      => 'passwd/passwd',
27   'login'                     => 'MyAccount/login',
28   'logout'                    => 'MyAccount/logout',
29   'customer_info'             => 'MyAccount/customer_info',
30   'edit_info'                 => 'MyAccount/edit_info',     #add to ss cgi!
31   'invoice'                   => 'MyAccount/invoice',
32   'invoice_logo'              => 'MyAccount/invoice_logo',
33   'list_invoices'             => 'MyAccount/list_invoices', #?
34   'cancel'                    => 'MyAccount/cancel',        #add to ss cgi!
35   'payment_info'              => 'MyAccount/payment_info',
36   'process_payment'           => 'MyAccount/process_payment',
37   'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
38   'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
39   'process_prepay'            => 'MyAccount/process_prepay',
40   'list_pkgs'                 => 'MyAccount/list_pkgs',     #add to ss (added?)
41   'list_svcs'                 => 'MyAccount/list_svcs',     #add to ss (added?)
42   'list_svc_usage'            => 'MyAccount/list_svc_usage',   
43   'order_pkg'                 => 'MyAccount/order_pkg',     #add to ss cgi!
44   'change_pkg'                => 'MyAccount/change_pkg', 
45   'order_recharge'            => 'MyAccount/order_recharge',
46   'renew_info'                => 'MyAccount/renew_info',
47   'order_renew'               => 'MyAccount/order_renew',
48   'cancel_pkg'                => 'MyAccount/cancel_pkg',    #add to ss cgi!
49   'charge'                    => 'MyAccount/charge',        #?
50   'part_svc_info'             => 'MyAccount/part_svc_info',
51   'provision_acct'            => 'MyAccount/provision_acct',
52   'provision_external'        => 'MyAccount/provision_external',
53   'unprovision_svc'           => 'MyAccount/unprovision_svc',
54   'myaccount_passwd'          => 'MyAccount/myaccount_passwd',
55   'signup_info'               => 'Signup/signup_info',
56   'domain_select_hash'        => 'Signup/domain_select_hash',  # expose?
57   'new_customer'              => 'Signup/new_customer',
58   'agent_login'               => 'Agent/agent_login',
59   'agent_logout'              => 'Agent/agent_logout',
60   'agent_info'                => 'Agent/agent_info',
61   'agent_list_customers'      => 'Agent/agent_list_customers',
62 );
63 @EXPORT_OK = ( keys(%autoload), qw( regionselector expselect popselector domainselector) );
64
65 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
66 $ENV{'SHELL'} = '/bin/sh';
67 $ENV{'IFS'} = " \t\n";
68 $ENV{'CDPATH'} = '';
69 $ENV{'ENV'} = '';
70 $ENV{'BASH_ENV'} = '';
71
72 my $freeside_uid = scalar(getpwnam('freeside'));
73 die "not running as the freeside user\n" if $> != $freeside_uid;
74
75 -e $dir or die "FATAL: $dir doesn't exist!";
76 -d $dir or die "FATAL: $dir isn't a directory!";
77 -r $dir or die "FATAL: Can't read $dir as freeside user!";
78 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
79
80 foreach my $autoload ( keys %autoload ) {
81
82   my $eval =
83   "sub $autoload { ". '
84                    my $param;
85                    if ( ref($_[0]) ) {
86                      $param = shift;
87                    } else {
88                      #warn scalar(@_). ": ". join(" / ", @_);
89                      $param = { @_ };
90                    }
91
92                    $param->{_packet} = \''. $autoload{$autoload}. '\';
93
94                    simple_packet($param);
95                  }';
96
97   eval $eval;
98   die $@ if $@;
99
100 }
101
102 sub simple_packet {
103   my $packet = shift;
104   warn "sending ". $packet->{_packet}. " to server"
105     if $DEBUG;
106   socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
107   connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
108   nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
109   SOCK->flush;
110
111   #shoudl trap: Magic number checking on storable file failed at blib/lib/Storable.pm (autosplit into blib/lib/auto/Storable/fd_retrieve.al) line 337, at /usr/local/share/perl/5.6.1/FS/SelfService.pm line 71
112
113   #block until there is a message on socket
114 #  my $w = new IO::Select;
115 #  $w->add(\*SOCK);
116 #  my @wait = $w->can_read;
117
118   warn "reading message from server"
119     if $DEBUG;
120
121   my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
122   die $return->{'_error'} if defined $return->{_error} && $return->{_error};
123
124   warn "returning message to client"
125     if $DEBUG;
126
127   $return;
128 }
129
130 =head1 NAME
131
132 FS::SelfService - Freeside self-service API
133
134 =head1 SYNOPSIS
135
136   # password and shell account changes
137   use FS::SelfService qw(passwd chfn chsh);
138
139   # "my account" functionality
140   use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
141
142   my $rv = login( { 'username' => $username,
143                     'domain'   => $domain,
144                     'password' => $password,
145                   }
146                 );
147
148   if ( $rv->{'error'} ) {
149     #handle login error...
150   } else {
151     #successful login
152     my $session_id = $rv->{'session_id'};
153   }
154
155   my $customer_info = customer_info( { 'session_id' => $session_id } );
156
157   #payment_info and process_payment are available in 1.5+ only
158   my $payment_info = payment_info( { 'session_id' => $session_id } );
159
160   #!!! process_payment example
161
162   #!!! list_pkgs example
163
164   #!!! order_pkg example
165
166   #!!! cancel_pkg example
167
168   # signup functionality
169   use FS::SelfService qw( signup_info new_customer );
170
171   my $signup_info = signup_info;
172
173   $rv = new_customer( {
174                         'first'            => $first,
175                         'last'             => $last,
176                         'company'          => $company,
177                         'address1'         => $address1,
178                         'address2'         => $address2,
179                         'city'             => $city,
180                         'state'            => $state,
181                         'zip'              => $zip,
182                         'country'          => $country,
183                         'daytime'          => $daytime,
184                         'night'            => $night,
185                         'fax'              => $fax,
186                         'payby'            => $payby,
187                         'payinfo'          => $payinfo,
188                         'paycvv'           => $paycvv,
189                         'paystart_month'   => $paystart_month
190                         'paystart_year'    => $paystart_year,
191                         'payissue'         => $payissue,
192                         'payip'            => $payip
193                         'paydate'          => $paydate,
194                         'payname'          => $payname,
195                         'invoicing_list'   => $invoicing_list,
196                         'referral_custnum' => $referral_custnum,
197                         'pkgpart'          => $pkgpart,
198                         'username'         => $username,
199                         '_password'        => $password,
200                         'popnum'           => $popnum,
201                         'agentnum'         => $agentnum,
202                       }
203                     );
204   
205   my $error = $rv->{'error'};
206   if ( $error eq '_decline' ) {
207     print_decline();
208   } elsif ( $error ) {
209     reprint_signup();
210   } else {
211     print_success();
212   }
213
214 =head1 DESCRIPTION
215
216 Use this API to implement your own client "self-service" module.
217
218 If you just want to customize the look of the existing "self-service" module,
219 see XXXX instead.
220
221 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
222
223 =over 4
224
225 =item passwd
226
227 =item chfn
228
229 =item chsh
230
231 =back
232
233 =head1 "MY ACCOUNT" FUNCTIONS
234
235 =over 4
236
237 =item login HASHREF
238
239 Creates a user session.  Takes a hash reference as parameter with the
240 following keys:
241
242 =over 4
243
244 =item username
245
246 Username
247
248 =item domain
249
250 Domain
251
252 =item password
253
254 Password
255
256 =back
257
258 Returns a hash reference with the following keys:
259
260 =over 4
261
262 =item error
263
264 Empty on success, or an error message on errors.
265
266 =item session_id
267
268 Session identifier for successful logins
269
270 =back
271
272 =item customer_info HASHREF
273
274 Returns general customer information.
275
276 Takes a hash reference as parameter with a single key: B<session_id>
277
278 Returns a hash reference with the following keys:
279
280 =over 4
281
282 =item name
283
284 Customer name
285
286 =item balance
287
288 Balance owed
289
290 =item open
291
292 Array reference of hash references of open inoices.  Each hash reference has
293 the following keys: invnum, date, owed
294
295 =item small_custview
296
297 An HTML fragment containing shipping and billing addresses.
298
299 =item The following fields are also returned
300
301 first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo payname month year invoicing_list postal_invoicing
302
303 =back
304
305 =item edit_info HASHREF
306
307 Takes a hash reference as parameter with any of the following keys:
308
309 first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo paycvv payname month year invoicing_list postal_invoicing
310
311 If a field exists, the customer record is updated with the new value of that
312 field.  If a field does not exist, that field is not changed on the customer
313 record.
314
315 Returns a hash reference with a single key, B<error>, empty on success, or an
316 error message on errors
317
318 =item invoice HASHREF
319
320 Returns an invoice.  Takes a hash reference as parameter with two keys:
321 session_id and invnum
322
323 Returns a hash reference with the following keys:
324
325 =over 4
326
327 =item error
328
329 Empty on success, or an error message on errors
330
331 =item invnum
332
333 Invoice number
334
335 =item invoice_text
336
337 Invoice text
338
339 =back
340
341 =item list_invoices HASHREF
342
343 Returns a list of all customer invoices.  Takes a hash references with a single
344 key, session_id.
345
346 Returns a hash reference with the following keys:
347
348 =over 4
349
350 =item error
351
352 Empty on success, or an error message on errors
353
354 =item invoices
355
356 Reference to array of hash references with the following keys:
357
358 =over 4
359
360 =item invnum
361
362 Invoice ID
363
364 =item _date
365
366 Invoice date, in UNIX epoch time
367
368 =back
369
370 =back
371
372 =item cancel HASHREF
373
374 Cancels this customer.
375
376 Takes a hash reference as parameter with a single key: B<session_id>
377
378 Returns a hash reference with a single key, B<error>, which is empty on
379 success or an error message on errors.
380
381 =item payment_info HASHREF
382
383 Returns information that may be useful in displaying a payment page.
384
385 Takes a hash reference as parameter with a single key: B<session_id>.
386
387 Returns a hash reference with the following keys:
388
389 =over 4
390
391 =item error
392
393 Empty on success, or an error message on errors
394
395 =item balance
396
397 Balance owed
398
399 =item payname
400
401 Exact name on credit card (CARD/DCRD)
402
403 =item address1
404
405 Address line one
406
407 =item address2
408
409 Address line two
410
411 =item city
412
413 City
414
415 =item state
416
417 State
418
419 =item zip
420
421 Zip or postal code
422
423 =item payby
424
425 Customer's current default payment type.
426
427 =item card_type
428
429 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
430
431 =item payinfo
432
433 For CARD/DCRD payment types, the card number
434
435 =item month
436
437 For CARD/DCRD payment types, expiration month
438
439 =item year
440
441 For CARD/DCRD payment types, expiration year
442
443 =item cust_main_county
444
445 County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>).  Note these are not FS::cust_main_county objects, but hash references of columns and values.
446
447 =item states
448
449 Array reference of all states in the current default country.
450
451 =item card_types
452
453 Hash reference of card types; keys are card types, values are the exact strings
454 passed to the process_payment function
455
456 =item paybatch
457
458 Unique transaction identifier (prevents multiple charges), passed to the
459 process_payment function
460
461 =back
462
463 =item process_payment HASHREF
464
465 Processes a payment and possible change of address or payment type.  Takes a
466 hash reference as parameter with the following keys:
467
468 =over 4
469
470 =item session_id
471
472 Session identifier
473
474 =item amount
475
476 Amount
477
478 =item save
479
480 If true, address and card information entered will be saved for subsequent
481 transactions.
482
483 =item auto
484
485 If true, future credit card payments will be done automatically (sets payby to
486 CARD).  If false, future credit card payments will be done on-demand (sets
487 payby to DCRD).  This option only has meaning if B<save> is set true.  
488
489 =item payname
490
491 Name on card
492
493 =item address1
494
495 Address line one
496
497 =item address2
498
499 Address line two
500
501 =item city
502
503 City
504
505 =item state
506
507 State
508
509 =item zip
510
511 Zip or postal code
512
513 =item payinfo
514
515 Card number
516
517 =item month
518
519 Card expiration month
520
521 =item year
522
523 Card expiration year
524
525 =item paybatch
526
527 Unique transaction identifier, returned from the payment_info function.
528 Prevents multiple charges.
529
530 =back
531
532 Returns a hash reference with a single key, B<error>, empty on success, or an
533 error message on errors
534
535 =item process_payment_order_pkg
536
537 Combines the B<process_payment> and B<order_pkg> functions in one step.  If the
538 payment processes sucessfully, the package is ordered.  Takes a hash reference
539 as parameter with the keys of both methods.
540
541 Returns a hash reference with a single key, B<error>, empty on success, or an
542 error message on errors.
543
544 =item process_payment_order_renew
545
546 Combines the B<process_payment> and B<order_renew> functions in one step.  If
547 the payment processes sucessfully, the renewal is processed.  Takes a hash
548 reference as parameter with the keys of both methods.
549
550 Returns a hash reference with a single key, B<error>, empty on success, or an
551 error message on errors.
552
553 =item list_pkgs
554
555 Returns package information for this customer.  For more detail on services,
556 see L</list_svcs>.
557
558 Takes a hash reference as parameter with a single key: B<session_id>
559
560 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
561
562 =over 4
563
564 =item custnum
565
566 Customer number
567
568 =item cust_pkg HASHREF
569
570 Array reference of hash references, each of which has the fields of a cust_pkg
571 record (see L<FS::cust_pkg>) as well as the fields below.  Note these are not
572 the internal FS:: objects, but hash references of columns and values.
573
574 =over 4
575
576 =item part_pkg fields
577
578 All fields of part_pkg for this specific cust_pkg (be careful with this
579 information - it may reveal more about your available packages than you would
580 like users to know in aggregate) 
581
582 =cut
583
584 #XXX pare part_pkg fields down to a more secure subset
585
586 =item part_svc
587
588 An array of hash references indicating information on unprovisioned services
589 available for provisioning for this specific cust_pkg.  Each has the following
590 keys:
591
592 =over 4
593
594 =item part_svc fields
595
596 All fields of part_svc (be careful with this information - it may reveal more
597 about your available packages than you would like users to know in aggregate) 
598
599 =cut
600
601 #XXX pare part_svc fields down to a more secure subset
602
603 =back
604
605 =item cust_svc
606
607 An array of hash references indicating information on the customer services
608 already provisioned for this specific cust_pkg.  Each has the following keys:
609
610 =over 4
611
612 =item label
613
614 Array reference with three elements:
615
616 =over 4
617
618 =item
619
620 Name of this service
621
622 =item
623
624 Meaningful user-specific identifier for the service (i.e. username, domain or mail alias)
625
626 =item
627
628 Table name of this service
629
630 =back
631
632 =item svcnum
633
634 Primary key for this service
635
636 =item svcpart
637
638 Service definition (part_pkg)
639
640 =item pkgnum
641
642 Customer package (cust_pkg)
643
644 =item overlimit
645
646 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
647
648 =back
649
650 =back
651
652 =item error
653
654 Empty on success, or an error message on errors.
655
656 =back
657
658 =item list_svcs
659
660 Returns service information for this customer.
661
662 Takes a hash reference as parameter with a single key: B<session_id>
663
664 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
665
666 =over 4
667
668 =item custnum
669
670 Customer number
671
672 =item svcs
673
674 An array of hash references indicating information on all of this customer's
675 services.  Each has the following keys:
676
677 =over 4
678
679 =item svcnum
680
681 Primary key for this service
682
683 =item label
684
685 Name of this service
686
687 =item value
688
689 Meaningful user-specific identifier for the service (i.e. username, domain, or
690 mail alias).
691
692 =back
693
694 Account (svc_acct) services also have the following keys:
695
696 =item username
697
698 Username
699
700 =item email
701
702 username@domain
703
704 =item seconds
705
706 Seconds remaining
707
708 =item upbytes
709
710 Upload bytes remaining
711
712 =item downbytes
713
714 Download bytes remaining
715
716 =item totalbytes
717
718 Total bytes remaining
719
720 =item recharge_amount
721
722 Cost of a recharge
723
724 =item recharge_seconds
725
726 Number of seconds gained by recharge
727
728 =item recharge_upbytes
729
730 Number of upload bytes gained by recharge
731
732 =item recharge_downbytes
733
734 Number of download bytes gained by recharge
735
736 =item recharge_totalbytes
737
738 Number of total bytes gained by recharge
739
740 =back
741
742 =back
743
744 =item order_pkg
745
746 Orders a package for this customer.
747
748 Takes a hash reference as parameter with the following keys:
749
750 =over 4
751
752 =item session_id
753
754 Session identifier
755
756 =item pkgpart
757
758 pkgpart of package to order
759
760 =item svcpart
761
762 optional svcpart, required only if the package definition does not contain
763 one svc_acct service definition with quantity 1 (it may contain others with
764 quantity >1)
765
766 =item username
767
768 Username
769
770 =item _password
771
772 Password
773
774 =item sec_phrase
775
776 Optional security phrase
777
778 =item popnum
779
780 Optional Access number number
781
782 =back
783
784 Returns a hash reference with a single key, B<error>, empty on success, or an
785 error message on errors.  The special error '_decline' is returned for
786 declined transactions.
787
788 =item renew_info
789
790 Provides useful info for early renewals.
791
792 Takes a hash reference as parameter with the following keys:
793
794 =over 4
795
796 =item session_id
797
798 Session identifier
799
800 =back
801
802 Returns a hash reference.  On errors, it contains a single key, B<error>, with
803 the error message.  Otherwise, contains a single key, B<dates>, pointing to
804 an array refernce of hash references.  Each hash reference contains the
805 following keys:
806
807 =over 4
808
809 =item bill_date
810
811 (Future) Bill date.  Indicates a future date for which billing could be run.
812 Specified as a integer UNIX timestamp.  Pass this value to the B<order_renew>
813 function.
814
815 =item bill_date_pretty
816
817 (Future) Bill date as a human-readable string.  (Convenience for display;
818 subject to change, so best not to parse for the date.)
819
820 =item amount
821
822 Base amount which will be charged if renewed early as of this date.
823
824 =item renew_date
825
826 Renewal date; i.e. even-futher future date at which the customer will be paid
827 through if the early renewal is completed with the given B<bill-date>.
828 Specified as a integer UNIX timestamp.
829
830 =item renew_date_pretty
831
832 Renewal date as a human-readable string.  (Convenience for display;
833 subject to change, so best not to parse for the date.)
834
835 =back
836
837 =item order_renew
838
839 Renews this customer early; i.e. runs billing for this customer in advance.
840
841 Takes a hash reference as parameter with the following keys:
842
843 =over 4
844
845 =item session_id
846
847 Session identifier
848
849 =item date
850
851 Integer date as returned by the B<renew_info> function, indicating the advance
852 date for which to run billing.
853
854 =back
855
856 Returns a hash reference with a single key, B<error>, empty on success, or an
857 error message on errors.
858
859 =item cancel_pkg
860
861 Cancels a package for this customer.
862
863 Takes a hash reference as parameter with the following keys:
864
865 =over 4
866
867 =item session_id
868
869 Session identifier
870
871 =item pkgpart
872
873 pkgpart of package to cancel
874
875 =back
876
877 Returns a hash reference with a single key, B<error>, empty on success, or an
878 error message on errors.
879
880 =back
881
882 =head1 SIGNUP FUNCTIONS
883
884 =over 4
885
886 =item signup_info HASHREF
887
888 Takes a hash reference as parameter with the following keys:
889
890 =over 4
891
892 =item session_id - Optional agent/reseller interface session
893
894 =back
895
896 Returns a hash reference containing information that may be useful in
897 displaying a signup page.  The hash reference contains the following keys:
898
899 =over 4
900
901 =item cust_main_county
902
903 County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>).  Note these are not FS::cust_main_county objects, but hash references of columns and values.
904
905 =item part_pkg
906
907 Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>).  Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>).  Note these are not FS::part_pkg objects, but hash references of columns and values.  Requires the 'signup_server-default_agentnum' configuration value to be set, or
908 an agentnum specified explicitly via reseller interface session_id in the
909 options.
910
911 =item agent
912
913 Array reference of hash references, each of which has the fields of an agent record (see L<FS::agent>).  Note these are not FS::agent objects, but hash references of columns and values.
914
915 =item agentnum2part_pkg
916
917 Hash reference; keys are agentnums, values are array references of available packages for that agent, in the same format as the part_pkg arrayref above.
918
919 =item svc_acct_pop
920
921 Access numbers - array reference of hash references, each of which has the fields of an svc_acct_pop record (see L<FS::svc_acct_pop>).  Note these are not FS::svc_acct_pop objects, but hash references of columns and values.
922
923 =item security_phrase
924
925 True if the "security_phrase" feature is enabled
926
927 =item payby
928
929 Array reference of acceptable payment types for signup
930
931 =over 4
932
933 =item CARD
934
935 credit card - automatic
936
937 =item DCRD
938
939 credit card - on-demand - version 1.5+ only
940
941 =item CHEK
942
943 electronic check - automatic
944
945 =item DCHK
946
947 electronic check - on-demand - version 1.5+ only
948
949 =item LECB
950
951 Phone bill billing
952
953 =item BILL
954
955 billing, not recommended for signups
956
957 =item COMP
958
959 free, definitely not recommended for signups
960
961 =item PREPAY
962
963 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
964
965 =back
966
967 =item cvv_enabled
968
969 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
970
971 =item msgcat
972
973 Hash reference of message catalog values, to support error message customization.  Currently available keys are: passwords_dont_match, invalid_card, unknown_card_type, and not_a (as in "Not a Discover card").  Values are configured in the web interface under "View/Edit message catalog".
974
975 =item statedefault
976
977 Default state
978
979 =item countrydefault
980
981 Default country
982
983 =back
984
985 =item new_customer HASHREF
986
987 Creates a new customer.  Takes a hash reference as parameter with the
988 following keys:
989
990 =over 4
991
992 =item first
993
994 first name (required)
995
996 =item last
997
998 last name (required)
999
1000 =item ss
1001
1002 (not typically collected; mostly used for ACH transactions)
1003
1004 =item company
1005
1006 Company name
1007
1008 =item address1 (required)
1009
1010 Address line one
1011
1012 =item address2
1013
1014 Address line two
1015
1016 =item city (required)
1017
1018 City
1019
1020 =item county
1021
1022 County
1023
1024 =item state (required)
1025
1026 State
1027
1028 =item zip (required)
1029
1030 Zip or postal code
1031
1032 =item daytime
1033
1034 Daytime phone number
1035
1036 =item night
1037
1038 Evening phone number
1039
1040 =item fax
1041
1042 Fax number
1043
1044 =item payby
1045
1046 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1047
1048 =item payinfo
1049
1050 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1051
1052 =item paycvv
1053
1054 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1055
1056 =item paydate
1057
1058 Expiration date for CARD/DCRD
1059
1060 =item payname
1061
1062 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1063
1064 =item invoicing_list
1065
1066 comma-separated list of email addresses for email invoices.  The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
1067
1068 =item referral_custnum
1069
1070 referring customer number
1071
1072 =item pkgpart
1073
1074 pkgpart of initial package
1075
1076 =item username
1077
1078 Username
1079
1080 =item _password
1081
1082 Password
1083
1084 =item sec_phrase
1085
1086 Security phrase
1087
1088 =item popnum
1089
1090 Access number (index, not the literal number)
1091
1092 =item agentnum
1093
1094 Agent number
1095
1096 =back
1097
1098 Returns a hash reference with the following keys:
1099
1100 =over 4
1101
1102 =item error
1103
1104 Empty on success, or an error message on errors.  The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Configuration | View/Edit message catalog)
1105
1106 =back
1107
1108 =item regionselector HASHREF | LIST
1109
1110 Takes as input a hashref or list of key/value pairs with the following keys:
1111
1112 =over 4
1113
1114 =item selected_county
1115
1116 Currently selected county
1117
1118 =item selected_state
1119
1120 Currently selected state
1121
1122 =item selected_country
1123
1124 Currently selected country
1125
1126 =item prefix
1127
1128 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1129
1130 =item onchange
1131
1132 Specify a javascript subroutine to call on changes
1133
1134 =item default_state
1135
1136 Default state
1137
1138 =item default_country
1139
1140 Default country
1141
1142 =item locales
1143
1144 An arrayref of hash references specifying regions.  Normally you can just pass the value of the I<cust_main_county> field returned by B<signup_info>.
1145
1146 =back
1147
1148 Returns a list consisting of three HTML fragments for county selection,
1149 state selection and country selection, respectively.
1150
1151 =cut
1152
1153 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1154 sub regionselector {
1155   my $param;
1156   if ( ref($_[0]) ) {
1157     $param = shift;
1158   } else {
1159     $param = { @_ };
1160   }
1161   $param->{'selected_country'} ||= $param->{'default_country'};
1162   $param->{'selected_state'} ||= $param->{'default_state'};
1163
1164   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1165
1166   my $countyflag = 0;
1167
1168   my %cust_main_county;
1169
1170 #  unless ( @cust_main_county ) { #cache 
1171     #@cust_main_county = qsearch('cust_main_county', {} );
1172     #foreach my $c ( @cust_main_county ) {
1173     foreach my $c ( @{ $param->{'locales'} } ) {
1174       #$countyflag=1 if $c->county;
1175       $countyflag=1 if $c->{county};
1176       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1177       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1178       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1179     }
1180 #  }
1181   $countyflag=1 if $param->{selected_county};
1182
1183   my $script_html = <<END;
1184     <SCRIPT>
1185     function opt(what,value,text) {
1186       var optionName = new Option(text, value, false, false);
1187       var length = what.length;
1188       what.options[length] = optionName;
1189     }
1190     function ${prefix}country_changed(what) {
1191       country = what.options[what.selectedIndex].text;
1192       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1193           what.form.${prefix}state.options[i] = null;
1194 END
1195       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1196
1197   foreach my $country ( sort keys %cust_main_county ) {
1198     $script_html .= "\nif ( country == \"$country\" ) {\n";
1199     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1200       my $text = $state || '(n/a)';
1201       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1202     }
1203     $script_html .= "}\n";
1204   }
1205
1206   $script_html .= <<END;
1207     }
1208     function ${prefix}state_changed(what) {
1209 END
1210
1211   if ( $countyflag ) {
1212     $script_html .= <<END;
1213       state = what.options[what.selectedIndex].text;
1214       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1215       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1216           what.form.${prefix}county.options[i] = null;
1217 END
1218
1219     foreach my $country ( sort keys %cust_main_county ) {
1220       $script_html .= "\nif ( country == \"$country\" ) {\n";
1221       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1222         $script_html .= "\nif ( state == \"$state\" ) {\n";
1223           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1224           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1225             my $text = $county || '(n/a)';
1226             $script_html .=
1227               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1228           }
1229         $script_html .= "}\n";
1230       }
1231       $script_html .= "}\n";
1232     }
1233   }
1234
1235   $script_html .= <<END;
1236     }
1237     </SCRIPT>
1238 END
1239
1240   my $county_html = $script_html;
1241   if ( $countyflag ) {
1242     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1243     $county_html .= '</SELECT>';
1244   } else {
1245     $county_html .=
1246       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1247   }
1248
1249   my $state_html = qq!<SELECT NAME="${prefix}state" !.
1250                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1251   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1252     my $text = $state || '(n/a)';
1253     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1254     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
1255   }
1256   $state_html .= '</SELECT>';
1257
1258   $state_html .= '</SELECT>';
1259
1260   my $country_html = qq!<SELECT NAME="${prefix}country" !.
1261                      qq!onChange="${prefix}country_changed(this); $param->{'onchange'}">!;
1262   my $countrydefault = $param->{default_country} || 'US';
1263   foreach my $country (
1264     sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1265       keys %cust_main_county
1266   ) {
1267     my $selected = $country eq $param->{'selected_country'} ? ' SELECTED' : '';
1268     $country_html .= "\n<OPTION$selected>$country</OPTION>"
1269   }
1270   $country_html .= '</SELECT>';
1271
1272   ($county_html, $state_html, $country_html);
1273
1274 }
1275
1276 #=item expselect HASHREF | LIST
1277 #
1278 #Takes as input a hashref or list of key/value pairs with the following keys:
1279 #
1280 #=over 4
1281 #
1282 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1283 #
1284 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1285 #
1286 #=back
1287
1288 =item expselect PREFIX [ DATE ]
1289
1290 Takes as input a unique prefix string and the current expiration date, in
1291 yyyy-mm-dd or m-d-yyyy format
1292
1293 Returns an HTML fragments for expiration date selection.
1294
1295 =cut
1296
1297 sub expselect {
1298   #my $param;
1299   #if ( ref($_[0]) ) {
1300   #  $param = shift;
1301   #} else {
1302   #  $param = { @_ };
1303   #my $prefix = $param->{'prefix'};
1304   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1305   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
1306   my $prefix = shift;
1307   my $date = scalar(@_) ? shift : '';
1308
1309   my( $m, $y ) = ( 0, 0 );
1310   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1311     ( $m, $y ) = ( $2, $1 );
1312   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1313     ( $m, $y ) = ( $1, $3 );
1314   }
1315   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1316   for ( 1 .. 12 ) {
1317     $return .= qq!<OPTION VALUE="$_"!;
1318     $return .= " SELECTED" if $_ == $m;
1319     $return .= ">$_";
1320   }
1321   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1322   my @t = localtime;
1323   my $thisYear = $t[5] + 1900;
1324   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1325     $return .= qq!<OPTION VALUE="$_"!;
1326     $return .= " SELECTED" if $_ == $y;
1327     $return .= ">$_";
1328   }
1329   $return .= "</SELECT>";
1330
1331   $return;
1332 }
1333
1334 =item popselector HASHREF | LIST
1335
1336 Takes as input a hashref or list of key/value pairs with the following keys:
1337
1338 =over 4
1339
1340 =item popnum
1341
1342 Access number number
1343
1344 =item pops
1345
1346 An arrayref of hash references specifying access numbers.  Normally you can just pass the value of the I<svc_acct_pop> field returned by B<signup_info>.
1347
1348 =back
1349
1350 Returns an HTML fragment for access number selection.
1351
1352 =cut
1353
1354 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1355 sub popselector {
1356   my $param;
1357   if ( ref($_[0]) ) {
1358     $param = shift;
1359   } else {
1360     $param = { @_ };
1361   }
1362   my $popnum = $param->{'popnum'};
1363   my $pops = $param->{'pops'};
1364
1365   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1366   return $pops->[0]{city}. ', '. $pops->[0]{state}.
1367          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1368          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1369     if scalar(@$pops) == 1;
1370
1371   my %pop = ();
1372   my %popnum2pop = ();
1373   foreach (@$pops) {
1374     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1375     $popnum2pop{$_->{popnum}} = $_;
1376   }
1377
1378   my $text = <<END;
1379     <SCRIPT>
1380     function opt(what,href,text) {
1381       var optionName = new Option(text, href, false, false)
1382       var length = what.length;
1383       what.options[length] = optionName;
1384     }
1385 END
1386
1387   my $init_popstate = $param->{'init_popstate'};
1388   if ( $init_popstate ) {
1389     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1390              $init_popstate. '">';
1391   } else {
1392     $text .= <<END;
1393       function acstate_changed(what) {
1394         state = what.options[what.selectedIndex].text;
1395         what.form.popac.options.length = 0
1396         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1397 END
1398   } 
1399
1400   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1401   foreach my $state ( sort { $a cmp $b } @states ) {
1402     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1403
1404     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1405       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1406       if ($ac eq $param->{'popac'}) {
1407         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1408       }
1409     }
1410     $text .= "}\n" unless $init_popstate;
1411   }
1412   $text .= "popac_changed(what.form.popac)}\n";
1413
1414   $text .= <<END;
1415   function popac_changed(what) {
1416     ac = what.options[what.selectedIndex].text;
1417     what.form.popnum.options.length = 0;
1418     what.form.popnum.options[0] = new Option("City", "-1", false, true);
1419
1420 END
1421
1422   foreach my $state ( @states ) {
1423     foreach my $popac ( keys %{ $pop{$state} } ) {
1424       $text .= "\nif ( ac == \"$popac\" ) {\n";
1425
1426       foreach my $pop ( @{$pop{$state}->{$popac}}) {
1427         my $o_popnum = $pop->{popnum};
1428         my $poptext =  $pop->{city}. ', '. $pop->{state}.
1429                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1430
1431         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1432         if ($popnum == $o_popnum) {
1433           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1434         }
1435       }
1436       $text .= "}\n";
1437     }
1438   }
1439
1440
1441   $text .= "}\n</SCRIPT>\n";
1442
1443   $text .=
1444     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1445     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1446   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1447            ">$_" foreach sort { $a cmp $b } @states;
1448   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
1449
1450   $text .=
1451     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1452     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1453
1454   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1455
1456
1457   #comment this block to disable initial list polulation
1458   my @initial_select = ();
1459   if ( scalar( @$pops ) > 100 ) {
1460     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1461   } else {
1462     @initial_select = @$pops;
1463   }
1464   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1465     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1466              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1467              $pop->{city}. ', '. $pop->{state}.
1468                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1469   }
1470
1471   $text .= qq!</SELECT></TD></TR></TABLE>!;
1472
1473   $text;
1474
1475 }
1476
1477 =item domainselector HASHREF | LIST
1478
1479 Takes as input a hashref or list of key/value pairs with the following keys:
1480
1481 =over 4
1482
1483 =item pkgnum
1484
1485 Package number
1486
1487 =item domsvc
1488
1489 Service number of the selected item.
1490
1491 =back
1492
1493 Returns an HTML fragment for domain selection.
1494
1495 =cut
1496
1497 sub domainselector {
1498   my $param;
1499   if ( ref($_[0]) ) {
1500     $param = shift;
1501   } else {
1502     $param = { @_ };
1503   }
1504   my $domsvc= $param->{'domsvc'};
1505   my $rv = 
1506       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1507   my $domains = $rv->{'domains'};
1508   $domsvc = $rv->{'domsvc'} unless $domsvc;
1509
1510   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1511     unless scalar(keys %$domains);
1512     
1513   if (scalar(keys %$domains) == 1) {
1514     my $key;
1515     foreach(keys %$domains) {
1516       $key = $_;
1517     }
1518     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1519            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1520   }
1521
1522   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
1523
1524
1525   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1526     $text .= qq!<OPTION VALUE="!. $domain. '"'.
1527              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1528              $domains->{$domain};
1529   }
1530
1531   $text .= qq!</SELECT></TD></TR>!;
1532
1533   $text;
1534
1535 }
1536
1537 =back
1538
1539 =head1 RESELLER FUNCTIONS
1540
1541 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1542 with their active session, and the B<customer_info> and B<order_pkg> functions
1543 with their active session and an additional I<custnum> parameter.
1544
1545 =over 4
1546
1547 =item agent_login
1548
1549 =item agent_info
1550
1551 =item agent_list_customers
1552
1553 =back
1554
1555 =head1 BUGS
1556
1557 =head1 SEE ALSO
1558
1559 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
1560
1561 =cut
1562
1563 1;
1564