#use IO::Handle;
use IO::Select;
use Storable 2.09 qw(nstore_fd fd_retrieve);
+use Time::HiRes;
$VERSION = '0.03';
'switch_cust' => 'MyAccount/switch_cust',
'customer_info' => 'MyAccount/customer_info',
'customer_info_short' => 'MyAccount/customer_info_short',
+ 'customer_recurring' => 'MyAccount/customer_recurring',
'contact_passwd' => 'MyAccount/contact/contact_passwd',
'list_contacts' => 'MyAccount/contact/list_contacts',
'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
'invoice_logo' => 'MyAccount/invoice_logo',
'list_invoices' => 'MyAccount/list_invoices', #?
+ 'list_payments' => 'MyAccount/list_payments',
+ 'payment_receipt' => 'MyAccount/payment_receipt',
'list_payby' => 'MyAccount/list_payby',
'insert_payby' => 'MyAccount/insert_payby',
+ 'update_payby' => 'MyAccount/update_payby',
'delete_payby' => 'MyAccount/delete_payby',
'cancel' => 'MyAccount/cancel', #add to ss cgi!
'payment_info' => 'MyAccount/payment_info',
'process_prepay' => 'MyAccount/process_prepay',
'realtime_collect' => 'MyAccount/realtime_collect',
'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
+ 'pkg_info' => 'MyAccount/pkg_info',
'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
'list_svc_usage' => 'MyAccount/list_svc_usage',
'svc_status_html' => 'MyAccount/svc_status_html',
my $packet = shift;
warn "sending ". $packet->{_packet}. " to server"
if $DEBUG;
- socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
- connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
- nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
- SOCK->flush;
- #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
+ # Retry socket operation 5 times per second
+ # until successful or $max_retry
+ my $max_retry = 25;
+ my $sock_timeout = 5;
+ my $enable_sock_timeout = 0;
- #block until there is a message on socket
-# my $w = new IO::Select;
-# $w->add(\*SOCK);
-# my @wait = $w->can_read;
+ for my $try ( 1..$max_retry ) {
+ local $@;
- warn "reading message from server"
- if $DEBUG;
+ my $return;
- my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
- die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+ eval {
+ local $SIG{ALRM} = sub{die "socket $socket: timeout ${sock_timeout}s"};
+ alarm $sock_timeout
+ if $enable_sock_timeout;
- warn "returning message to client"
- if $DEBUG;
+ socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+ connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
+ nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
+ SOCK->flush;
- $return;
+ # 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
+ # block until there is a message on socket
+ # my $w = new IO::Select;
+ # $w->add(\*SOCK);
+ # my @wait = $w->can_read;
+
+ warn "reading message from server"
+ if $DEBUG;
+
+ $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
+ die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+
+ warn "returning message to client"
+ if $DEBUG;
+
+ return $return;
+ };
+
+ return $return
+ unless $@;
+
+ die "(Attempt $try) $@"
+ if $try == $max_retry;
+
+ warn "(Attempt $try) $@"
+ if $DEBUG;
+
+ Time::HiRes::sleep(0.2);
+ }
}
=head1 NAME
my $customer_info = customer_info( { 'session_id' => $session_id } );
- #payment_info and process_payment are available in 1.5+ only
my $payment_info = payment_info( { 'session_id' => $session_id } );
#!!! process_payment example
=back
+=item customer_recurring HASHREF
+
+Takes a hash reference as parameter with a single key B<session_id>
+or keys B<agent_session_id> and B<custnum>.
+
+Returns a hash reference with the keys error, custnum and display_recurring.
+
+display_recurring is an arrayref of hashrefs with the following keys:
+
+=over 4
+
+=item freq
+
+frequency of charge, in months unless units are specified
+
+=item freq_pretty
+
+frequency of charge, suitable for display
+
+=item amount
+
+amount charged at this frequency
+
+=back
+
=item edit_info HASHREF
Takes a hash reference as parameter with any of the following keys:
=back
+=item list_payments HASHREF
+
+Returns a list of all customer payments. Takes a hash reference with a single
+key, session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item payments
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item paynum
+
+Payment #
+
+=item _date
+
+Payument date, in UNIX epoch time
+
+=item date
+
+Payment date, in a human-readable format
+
+=item date_short
+
+Payment date, in a shorter human-readable format
+
+=item paid
+
+Amount paid
+
+=item payby
+
+Payment method: CARD, CHEK (electronic check), or BILL (physical check).
+
+=item paycardtype
+
+Payment card type
+
+=item paymask
+
+Payment card mask
+
+=item processor
+
+Processor for cards and electronic checks
+
+=item auth
+
+Authorization number
+
+=item order_number
+
+Order number
+
+=back
+
+=back
+
=item list_payby HASHREF
Returns a list of all stored customer payment information (credit cards and
If there is an error, returns a hash reference with a single key, B<error>,
otherwise returns a hash reference with a single key, B<custpaybynum>.
+=item update_payby HASHREF
+
+Updates stored payment information. Takes a hash reference with the same
+keys as insert_payby, as well as B<custpaybynum> to specify which record
+to update. All keys except B<session_id> and B<custpaybynum> are optional;
+if omitted, the previous values in the record will be preserved.
+
+If there is an error, returns a hash reference with a single key, B<error>,
+otherwise returns a hash reference with a single key, B<custpaybynum>.
+
=item delete_payby HASHREF
Removes stored payment information. Takes a hash reference with two keys,
Returns information that may be useful in displaying a payment page.
-Takes a hash reference as parameter with a single key: B<session_id>.
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Required session ID
+
+=item payment_payby
+
+=item omit_cust_main_county
+
+Optional, pass a true value to omit cust_main_county data for performance.
+
+=back
Returns a hash reference with the following keys:
=back
+=item pkg_info
+
+Returns package information for package.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+Package Number
+
+=back
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+=item pkg_label
+
+Name of this package
+
+=item pkgpart
+
+Part package primary key
+
+=item classnum
+
+Package class number
+
+=item error
+
+error message if errror.
+
+=back
+
=item list_svcs
Returns service information for this customer.
Orders a package for this customer.
+If signup_server-realtime is set, bills the new package, attemps to collect
+payment and (for auto-payment customers) cancels the package if the payment is
+declined.
+
Takes a hash reference as parameter with the following keys:
=over 4
Quantity for this package order (default 1).
+=item run_bill_events
+
+If true, runs billing events for the customer after ordering and billing the
+package (signup_server-realtime must be set).
+
=item locationnum
Optional locationnum for this package order, for existing locations.
Zip or postal code
+=item ship_address1
+
+=item ship_address2
+
+=item ship_city
+
+=item ship_county
+
+=item ship_state
+
+=item ship_zip
+
+Optional shipping address fields. If sending an optional shipping address,
+ship_address1, ship_city, ship_state and ship_zip are required.
+
=item daytime
Daytime phone number
my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
- my $countyflag = 0;
+ my $disabled = $param->{'disabled'};
+
+ my $countyflag = $param->{selected_county} ? 1 : 0;
+ my $cityflag = $param->{selected_city} ? 1 : 0;
my %cust_main_county;
foreach my $c ( @{ $param->{'locales'} } ) {
#$countyflag=1 if $c->county;
$countyflag=1 if $c->{county};
+ $cityflag=1 if ($c->{city} && $cityflag);
#push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
#$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
- $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
+ $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}}{$c->{city}} = 1;
}
# }
- $countyflag=1 if $param->{selected_county};
my $script_html = <<END;
<SCRIPT>
- function opt(what,value,text) {
- var optionName = new Option(text, value, false, false);
+ function opt(what,value,text,selected) {
+ var optionName = new Option(text, value, false, selected);
var length = what.length;
what.options[length] = optionName;
}
#foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
my $text = $county || '(n/a)';
- $script_html .=
- qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+ if (!$county) {
+ if ( $cityflag) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ $script_html .= qq!opt(what.form.${prefix}county, "$county", "$text");\n!
+ #$script_html .= qq!what.form.${prefix}county.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var countySelected = false; if ("$param->{selected_county}" == "$text") { countySelected = true; }\n
+ opt(what.form.${prefix}county, "$county", "$text", countySelected);\n
+ what.form.${prefix}county.style.display='';\n
+ county = what.form.${prefix}county.options[what.form.${prefix}county.selectedIndex].text;\n!;
+ if ( $cityflag) {
+ $script_html .= qq!\nif ( county == \"$county\" ) {\n!;
+ foreach my $city ( sort keys %{$cust_main_county{$country}{$state}{$county}} ) {
+ my $text = $city || '(n/a)';
+ if (!$city) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var citySelected = false; if ("$param->{selected_city}" == "$text") { citySelected = true; }\n
+ opt(what.form.${prefix}city_select, "$city", "$text", citySelected);\n
+ what.form.${prefix}city.style.display='none';\n
+ what.form.${prefix}city_select.style.display='';\n!
+ }
+ }
+ $script_html .= "}\n";
+ }
+ }
}
$script_html .= "}\n";
}
}
}
+ $script_html .= <<END;
+ }
+ function ${prefix}county_changed(what) {
+END
+
+ if ( $cityflag) {
+ $script_html .= <<END;
+ saved_city = "$param->{selected_city}";
+ county = what.options[what.selectedIndex].text;
+ state = what.form.${prefix}state.options[what.form.${prefix}state.selectedIndex].text;
+ country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+ for ( var i = what.form.${prefix}city_select.length; i >= 0; i-- )
+ what.form.${prefix}city_select.options[i] = null;
+END
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ $script_html .= "\nif ( state == \"$state\" ) {\n";
+ #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+ foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+ $script_html .= "\nif ( county == \"$county\" ) {\n";
+ foreach my $city ( sort keys %{$cust_main_county{$country}{$state}{$county}} ) {
+ my $text = $city || '(n/a)';
+ if (!$city) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var citySelected = false; if (saved_city == "$text") { citySelected = true; }\n
+ opt(what.form.${prefix}city_select, "$city", "$text", citySelected);\n
+ what.form.${prefix}city.style.display='none';\n
+ what.form.${prefix}city_select.style.display='';\n!
+ }
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ }
+
+ $script_html .= <<END;
+ }
+ function ${prefix}city_select_changed(what) {
+END
+
+ if ( $cityflag ) {
+ $script_html .= <<END;
+ what.form.${prefix}city.value = what.options[what.selectedIndex].value;
+END
+ }
+
$script_html .= <<END;
}
</SCRIPT>
END
+ my $city_html = '';
+ if ( $cityflag ) {
+ if ( scalar (keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}}{$param->{'selected_county'}} }) > 1 ) {
+ $city_html .= qq!<SELECT NAME="${prefix}city_select" onChange="${prefix}city_select_changed(this); $param->{'onchange'}">!;
+ foreach my $city (
+ sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}}{$param->{'selected_county'}} }
+ ) {
+ my $text = $city || '(n/a)';
+ $city_html .= qq!<OPTION VALUE="$city"!.
+ ($city eq $param->{'selected_city'} ?
+ ' SELECTED>' :
+ '>'
+ ).
+ $text;
+ }
+ $city_html .= qq!</OPTION><INPUT TYPE="text" ID="${prefix}city" NAME="${prefix}city" VALUE="$param->{'selected_city'}" style="display:none">!;
+ } else {
+ $city_html .= qq!<SELECT NAME="${prefix}city_select" onChange="${prefix}city_select_changed(this); $param->{'onchange'}" style="display:none"></SELECT>
+ <INPUT TYPE="text" ID="${prefix}city" NAME="${prefix}city" VALUE="$param->{'selected_city'}" style="display:''">!;
+ }
+ }
+
my $county_html = $script_html;
if ( $countyflag ) {
- $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
+ $county_html .= qq!<SELECT NAME="${prefix}county" !.
+ qq!onChange="${prefix}county_changed(this); $param->{'onchange'}">!;
foreach my $county (
sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
) {
}
- ($county_html, $state_html, $country_html);
+ ($county_html, $state_html, $country_html, $city_html);
}