X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FSignup.pm;h=abfcac88280522bcb189ae9032a40d088fb31612;hp=a4032f3b1f00f9caa05820d65acc965824fcde21;hb=697ca3ce9ca4a6a357d6d2b1cfc31525edf252dc;hpb=3706609762d9cec964f337e74829031b895ddbac diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm index a4032f3b1..abfcac882 100644 --- a/FS/FS/ClientAPI/Signup.pm +++ b/FS/FS/ClientAPI/Signup.pm @@ -2,8 +2,10 @@ package FS::ClientAPI::Signup; use strict; use vars qw( $DEBUG $me ); +use subs qw( _myaccount_cache ); use Data::Dumper; use Tie::RefHash; +use Digest::SHA qw(sha512_hex); use FS::Conf; use FS::Record qw(qsearch qsearchs dbdef); use FS::CGI qw(popurl); @@ -22,10 +24,21 @@ use FS::acct_snarf; use FS::queue; use FS::reg_code; use FS::payby; +use FS::banned_pay; +use FS::part_tag; -$DEBUG = 0; +$DEBUG = 1; $me = '[FS::ClientAPI::Signup]'; +sub clear_cache { + warn "$me clear_cache called\n" if $DEBUG; + my $cache = new FS::ClientAPI_SessionCache( { + 'namespace' => 'FS::ClientAPI::Signup', + } ); + $cache->clear(); + return {}; +} + sub signup_info { my $packet = shift; @@ -84,13 +97,37 @@ sub signup_info { my @agent_fields = qw( agentnum agent ); + my @bools = qw( emailinvoiceonly security_phrase ); + + my @signup_bools = qw( no_company recommend_daytime recommend_email ); + + my @signup_server_scalars = qw( default_pkgpart default_svcpart default_domsvc ); + + my @selfservice_textareas = qw( head body_header body_footer ); + + my @selfservice_scalars = qw( + body_bgcolor box_bgcolor + text_color link_color vlink_color hlink_color alink_color + font title_color title_align title_size menu_bgcolor menu_fontsize + ); + + #XXX my @selfservice_bools = qw( + # menu_skipblanks menu_skipheadings menu_nounderline + #); + + #my $selfservice_binaries = qw( + # title_left_image title_right_image + # menu_top_image menu_body_image menu_bottom_image + #); + $signup_info_cache = { + 'cust_main_county' => [ map $_->hashref, qsearch('cust_main_county', {} ) ], 'agent' => [ map { my $agent = $_; - +{ map { $_ => $agent->get($_) } @agent_fields } + +{ map { $_ => $agent->get($_) } @agent_fields } } qsearch('agent', { 'disabled' => '' } ) ], @@ -116,47 +153,45 @@ sub signup_info { 'card_types' => card_types(), - 'paytypes' => [ @FS::cust_main::paytypes ], - - 'cvv_enabled' => 1, - - 'stateid_enabled' => $conf->exists('show_stateid'), - - 'paystate_enabled' => $conf->exists('show_bankstate'), - - 'ship_enabled' => 1, - - 'msgcat' => $msgcat, - - 'label' => $label, - - 'statedefault' => scalar($conf->config('statedefault')) || 'CA', - - 'countrydefault' => scalar($conf->config('countrydefault')) || 'US', - - 'refnum' => scalar($conf->config('signup_server-default_refnum')), - - 'default_pkgpart' => scalar($conf->config('signup_server-default_pkgpart')), - - 'signup_service' => $svc_x, - 'default_svcpart' => scalar($conf->config('signup_server-default_svcpart')), - - 'head' => join("\n", $conf->config('selfservice-head') ), - 'body_header' => join("\n", $conf->config('selfservice-body_header') ), - 'body_footer' => join("\n", $conf->config('selfservice-body_footer') ), - 'body_bgcolor' => scalar( $conf->config('selfservice-body_bgcolor') ), - 'box_bgcolor' => scalar( $conf->config('selfservice-box_bgcolor') ), - - 'company_name' => scalar($conf->config('company_name')), - + ( map { $_ => $conf->exists("signup-$_") } @signup_bools ), + + ( map { $_ => scalar($conf->config("signup_server-$_")) } + @signup_server_scalars + ), + + ( map { $_ => join("\n", $conf->config("selfservice-$_")) } + @selfservice_textareas + ), + ( map { $_ => scalar($conf->config("selfservice-$_")) } + @selfservice_scalars + ), + + #( map { $_ => scalar($conf->config_binary("selfservice-$_")) } + # @selfservice_binaries + #), + + 'agentnum2part_pkg' => $agentnum2part_pkg, + 'svc_acct_pop' => [ map $_->hashref, qsearch('svc_acct_pop',{} ) ], + 'nomadix' => $conf->exists('signup_server-nomadix'), + 'payby' => [ $conf->config('signup_server-payby') ], + 'card_types' => card_types(), + 'paytypes' => [ @FS::cust_main::paytypes ], + 'cvv_enabled' => 1, + 'require_cvv' => $conf->exists('signup-require_cvv'), + 'stateid_enabled' => $conf->exists('show_stateid'), + 'paystate_enabled' => $conf->exists('show_bankstate'), + 'exempt_groups' => [ grep /\S/, $conf->config('tax-cust_exempt-groups') ], + 'ship_enabled' => 1, + 'msgcat' => $msgcat, + 'label' => $label, + 'statedefault' => scalar($conf->config('statedefault')) || 'CA', + 'countrydefault' => scalar($conf->config('countrydefault')) || 'US', + 'refnum' => scalar($conf->config('signup_server-default_refnum')), + 'signup_service' => $svc_x, + 'company_name' => scalar($conf->config('company_name')), #per-agent? - 'agent_ship_address' => scalar($conf->exists('agent-ship_address')), - - 'no_company' => scalar($conf->exists('signup-no_company')), - 'require_phone' => scalar($conf->exists('cust_main-require_phone')), - 'recommend_daytime' => scalar($conf->exists('signup-recommend_daytime')), - 'recommend_email' => scalar($conf->exists('signup-recommend_email')), - + 'logo' => scalar($conf->config_binary('logo.png')), + 'prepaid_template_custnum' => $conf->exists('signup_server-prepaid-template-custnum'), }; $cache->set('signup_info_cache', $signup_info_cache); @@ -286,29 +321,38 @@ sub signup_info { if ( $agentnum ) { warn "$me setting agent-specific payment flag\n" if $DEBUG > 1; - my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); + my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ) + or return { 'error' => "Self-service agent #$agentnum does not exist" }; warn "$me has agent $agent\n" if $DEBUG > 1; - if ( $agent ) { #else complain loudly? - $signup_info->{'hide_payment_fields'} = []; - foreach my $payby (@{$signup_info->{payby}}) { - warn "$me checking $payby payment fields\n" if $DEBUG > 1; - my $hide = 0; - if ( FS::payby->realtime($payby) ) { - my $payment_gateway = - $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), - 'nofatal' => 1, - ); - if ( $payment_gateway - && $payment_gateway->gateway_namespace - eq 'Business::OnlineThirdPartyPayment' - ) { - warn "$me hiding $payby payment fields\n" if $DEBUG > 1; - $hide = 1; - } + my @paybys = @{ $signup_info->{'payby'} }; + $signup_info->{'hide_payment_fields'} = []; + + my $gatewaynum = $conf->config('selfservice-payment_gateway'); + my $force_gateway; + if ( $gatewaynum ) { + $force_gateway = qsearchs('payment_gateway', { gatewaynum => $gatewaynum }); + warn "using forced gateway #$gatewaynum - " . + $force_gateway->gateway_username . '@' . $force_gateway->gateway_module + if $DEBUG > 1; + die "configured gatewaynum $gatewaynum not found!" if !$force_gateway; + } + foreach my $payby (@paybys) { + warn "$me checking $payby payment fields\n" if $DEBUG > 1; + my $hide = 0; + if ( FS::payby->realtime($payby) ) { + my $gateway = $force_gateway || + $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), + 'nofatal' => 1, + ); + if ( $gateway && $gateway->gateway_namespace + eq 'Business::OnlineThirdPartyPayment' + ) { + warn "$me hiding $payby payment fields\n" if $DEBUG > 1; + $hide = 1; } - push @{$signup_info->{'hide_payment_fields'}}, $hide; } - } + push @{$signup_info->{'hide_payment_fields'}}, $hide; + } # foreach $payby warn "$me done setting agent-specific payment flag\n" if $DEBUG > 1; warn "$me setting agent-specific package list\n" if $DEBUG > 1; @@ -334,13 +378,6 @@ sub signup_info { $signup_info->{'company_name'} = $conf->config('company_name', $agentnum); - if ( $signup_info->{'agent_ship_address'} && $agent->agent_custnum ) { - my $cust_main = $agent->agent_cust_main; - my $prefix = length($cust_main->ship_last) ? 'ship_' : ''; - $signup_info->{"ship_$_"} = $cust_main->get("$prefix$_") - foreach qw( address1 city county state zip country ); - } - #some of the above could probably be cached, too my $signup_info_cache_agent = $cache->get("signup_info_cache_agent$agentnum"); @@ -359,11 +396,26 @@ sub signup_info { #( map { $_ => scalar( $conf->config($_, $agentnum) ) } # qw( company_name ) ), ( map { $_ => scalar( $conf->config("selfservice-$_", $agentnum ) ) } - qw( body_bgcolor box_bgcolor) ), + qw( body_bgcolor box_bgcolor menu_bgcolor ) ), ( map { $_ => join("\n", $conf->config("selfservice-$_", $agentnum ) ) } qw( head body_header body_footer ) ), + ( map { $_ => join("\n", $conf->config("signup_server-$_", $agentnum ) ) } + qw( terms_of_service ) ), + + ( map { $_ => scalar($conf->exists($_, $agentnum)) } + qw(cust_main-require_phone agent-ship_address) ), }; + if ( $signup_info_cache_agent->{'agent-ship_address'} + && $agent->agent_cust_main ) { + + my $cust_main = $agent->agent_cust_main; + my $location = $cust_main->ship_location; + $signup_info_cache_agent->{"ship_$_"} = $location->get($_) + foreach qw( address1 city county state zip country ); + + } + $cache->set("signup_info_cache_agent$agentnum", $signup_info_cache_agent); } @@ -386,9 +438,14 @@ sub signup_info { my $agent_signup_info = { %$signup_info }; delete $agent_signup_info->{agentnum2part_pkg}; $agent_signup_info->{'agent'} = $session->{'agent'}; - $agent_signup_info; - } else { - $signup_info; + return $agent_signup_info; + } + elsif ( exists $packet->{'keys'} ) { + my @keys = @{ $packet->{'keys'} }; + return { map { $_ => $signup_info->{$_} } @keys }; + } + else { + return $signup_info; } } @@ -437,6 +494,9 @@ sub new_customer { unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} )); } + elsif ( $svc_x eq 'svc_pbx' ) { + #possibly some validation will be needed + } my $agentnum; if ( exists $packet->{'session_id'} ) { @@ -454,66 +514,127 @@ sub new_customer { || $conf->config('signup_server-default_agentnum'); } + my ($bill_hash, $ship_hash); + foreach my $f (FS::cust_main->location_fields) { + # avoid having to change this in front-end code + $bill_hash->{$f} = $packet->{"bill_$f"} || $packet->{$f}; + $ship_hash->{$f} = $packet->{"ship_$f"}; + } + #shares some stuff with htdocs/edit/process/cust_main.cgi... take any # common that are still here and library them. - my $cust_main = new FS::cust_main ( { - #'custnum' => '', - 'agentnum' => $agentnum, - 'refnum' => $packet->{refnum} - || $conf->config('signup_server-default_refnum'), - map { $_ => $packet->{$_} } qw( + my %cust_main = ( + 'agentnum' => $agentnum, + 'refnum' => $packet->{refnum} + || $conf->config('signup_server-default_refnum'), + 'tagnum' => [ FS::part_tag->default_tags ], + + ( map { $_ => $packet->{$_} } qw( + salesnum + ss stateid stateid_state - last first ss company address1 address2 - city county state zip country - daytime night fax stateid stateid_state + payby + payinfo paycvv paydate payname paystate paytype + paystart_month paystart_year payissue + payip - ship_last ship_first ship_ss ship_company ship_address1 ship_address2 - ship_city ship_county ship_state ship_zip ship_country - ship_daytime ship_night ship_fax + referral_custnum comments + ) + ), - payby - payinfo paycvv paydate payname paystate paytype - paystart_month paystart_year payissue - payip + ); - referral_custnum comments - ) + my $template_custnum = $conf->config('signup_server-prepaid-template-custnum'); + my $cust_main; + if ( $template_custnum && $packet->{prepaid_shortform} ) { + + my $template_cust = qsearchs('cust_main', { 'custnum' => $template_custnum } ); + return { 'error' => 'Configuration error' } unless $template_cust; + $cust_main = new FS::cust_main ( { + %cust_main, + map { $_ => $template_cust->$_ } qw( + last first company daytime night fax mobile + ), + } ); - } ); + $bill_hash = { $template_cust->bill_location->location_hash }; + $ship_hash = { $template_cust->ship_location->location_hash }; + } else { + + $cust_main = new FS::cust_main ( { + %cust_main, + map { $_ => $packet->{$_} } qw( + last first company daytime night fax mobile + override_ban_warn + ), + } ); + } + + my $bill_location = FS::cust_location->new($bill_hash); + my $ship_location; my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); - if ( $conf->exists('agent_ship_address') && $agent->agent_custnum ) { + if ( $conf->exists('agent-ship_address', $agentnum) + && $agent->agent_custnum ) { + my $agent_cust_main = $agent->agent_cust_main; my $prefix = length($agent_cust_main->ship_last) ? 'ship_' : ''; - $cust_main->set("ship_$_", $agent_cust_main->get("$prefix$_") ) - foreach qw( address1 city county state zip country ); - - $cust_main->set("ship_$_", $cust_main->get($_)) - foreach qw( last first ); + $ship_location = FS::cust_location->new({ + $agent_cust_main->ship_location->location_hash + }); } + # we don't have an equivalent of the "same" checkbox in selfservice + # so is there a ship address, and if so, is it different from the billing + # address? + elsif ( length($ship_hash->{address1}) > 0 and + grep { $bill_hash->{$_} ne $ship_hash->{$_} } keys(%$ship_hash) + ) { + + $ship_location = FS::cust_location->new( $ship_hash ); + + } + else { + $ship_location = $bill_location; + } + $cust_main->set('bill_location' => $bill_location); + $cust_main->set('ship_location' => $ship_location); return { 'error' => "Illegal payment type" } unless grep { $_ eq $packet->{'payby'} } $conf->config('signup_server-payby'); - if (FS::payby->realtime($packet->{payby})) { + if (FS::payby->realtime($packet->{payby}) + and not $conf->exists('signup_server-third_party_as_card')) { my $payby = $packet->{payby}; my $agent = qsearchs('agent', { 'agentnum' => $agentnum }); return { 'error' => "Unknown reseller" } unless $agent; - my $gw = $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), - 'nofatal' => 1, + my $gw; + my $gatewaynum = $conf->config('selfservice-payment_gateway'); + if ( $gatewaynum ) { + $gw = qsearchs('payment_gateway', { gatewaynum => $gatewaynum }); + die "configured gatewaynum $gatewaynum not found!" if !$gw; + } + else { + $gw = $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), + 'nofatal' => 1, ); + } - $cust_main->payby('BILL') # MCRD better? + $cust_main->payby('BILL') # MCRD better? no, that's for something else if $gw && $gw->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; } + return { 'error' => "CVV2 is required" } + if $cust_main->payby =~ /^(CARD|DCRD)$/ + && ! $cust_main->paycvv + && $conf->exists('signup-require_cvv'); + $cust_main->payinfo($cust_main->daytime) if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo; @@ -521,6 +642,14 @@ sub new_customer { ? split( /\s*\,\s*/, $packet->{'invoicing_list'} ) : (); + my %insert_options = (); + + my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); + my @tax_exempt = grep { $packet->{"tax_$_"} eq 'Y' } @exempt_groups; + $insert_options{'tax_exemption'} = { + map { $_ => $packet->{"tax_$_".'_num'} } @tax_exempt + }; + $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/; my $pkgpart = $1; return { 'error' => 'Please select a package' } unless $pkgpart; #msgcat @@ -553,7 +682,7 @@ sub new_customer { my $svc = new FS::svc_acct { 'svcpart' => $svcpart, map { $_ => $packet->{$_} } - qw( username _password sec_phrase popnum ), + qw( username _password sec_phrase popnum domsvc ), }; my @acct_snarf; @@ -570,24 +699,27 @@ sub new_customer { push @acct_snarf, $acct_snarf; } $svc->child_objects( \@acct_snarf ); - push @svc, $svc; } elsif ( $svc_x eq 'svc_phone' ) { - my $svc = new FS::svc_phone ( { + push @svc, new FS::svc_phone ( { 'svcpart' => $svcpart, map { $_ => $packet->{$_} } qw( countrycode phonenum sip_password pin ), } ); - push @svc, $svc; + } elsif ( $svc_x eq 'svc_pbx' ) { + push @svc, new FS::svc_pbx ( { + 'svcpart' => $svcpart, + map { $_ => $packet->{$_} } + qw( id title ), + } ); + } else { die "unknown signup service $svc_x"; } - my $y = $svc[0]->setdefault; # arguably should be in new method - return { 'error' => $y } if $y && !ref($y); if ($packet->{'mac_addr'} && $conf->exists('signup_server-mac_addr_svcparts')) { @@ -606,15 +738,16 @@ sub new_customer { '_password' => '', #blank as requested (set passwordmin to 0) }; - my $y = $svc->setdefault; # arguably should be in new method - return { 'error' => $y } if $y && !ref($y); - push @svc, $svc; } - #$error = $svc->check; - #return { 'error' => $error } if $error; + foreach my $svc ( @svc ) { + my $y = $svc->setdefault; # arguably should be in new method + return { 'error' => $y } if $y && !ref($y); + #$error = $svc->check; + #return { 'error' => $error } if $error; + } #setup a job dependancy to delay provisioning my $placeholder = new FS::queue ( { @@ -632,6 +765,7 @@ sub new_customer { \%hash, \@invoicing_list, 'depend_jobnum' => $placeholder->jobnum, + %insert_options, ); if ( $error ) { my $perror = $placeholder->delete; @@ -641,23 +775,26 @@ sub new_customer { if ( $conf->exists('signup_server-realtime') ) { - #warn "[fs_signup_server] Billing customer...\n" if $Debug; + #warn "$me Billing customer...\n" if $Debug; - my $bill_error = $cust_main->bill; - #warn "[fs_signup_server] error billing new customer: $bill_error" + my $bill_error = $cust_main->bill( 'depend_jobnum'=>$placeholder->jobnum ); + #warn "$me error billing new customer: $bill_error" # if $bill_error; $bill_error = $cust_main->apply_payments_and_credits; - #warn "[fs_signup_server] error applying payments and credits for". + #warn "$me error applying payments and credits for". # " new customer: $bill_error" # if $bill_error; - $bill_error = $cust_main->realtime_collect( - method => FS::payby->payby2bop( $packet->{payby} ), - depend_jobnum => $placeholder->jobnum, - ); - #warn "[fs_signup_server] error collecting from new customer: $bill_error" - # if $bill_error; + unless ( $packet->{payby} eq 'PREPAY' ) { + $bill_error = $cust_main->realtime_collect( + method => FS::payby->payby2bop( $packet->{payby} ), + depend_jobnum => $placeholder->jobnum, + selfservice => 1, + ); + #warn "$me error collecting from new customer: $bill_error" + # if $bill_error; + } if ($bill_error && ref($bill_error) eq 'HASH') { return { 'error' => '_collect', @@ -668,6 +805,11 @@ sub new_customer { }; } + $bill_error = $cust_main->apply_payments_and_credits; + #warn "$me error applying payments and credits for". + # " new customer: $bill_error" + # if $bill_error; + if ( $cust_main->balance > 0 ) { #this makes sense. credit is "un-doing" the invoice @@ -701,22 +843,246 @@ sub new_customer { $error = $placeholder->delete; return { 'error' => $error } if $error; + if ( $conf->exists('signup-duplicate_cc-warn_hours') ) { + my $hours = $conf->config('signup-duplicate_cc-warn_hours'); + my $ban = new FS::banned_pay $cust_main->_new_banned_pay_hashref; + $ban->end_date( int( time + $hours*3600 ) ); + $ban->bantype('warn'); + $ban->reason('signup-duplicate_cc-warn_hours'); + $error = $ban->insert; + warn "WARNING: error inserting temporary banned_pay for ". + " signup-duplicate_cc-warn_hours (proceeding anyway): $error" + if $error; + } + my %return = ( 'error' => '', 'signup_service' => $svc_x, + 'custnum' => $cust_main->custnum, ); + if ( $svc[0] ) { + + $return{'svcnum'} = $svc[0]->svcnum; + + if ( $svc_x eq 'svc_acct' ) { + $return{$_} = $svc[0]->$_() for qw( username _password ); + } elsif ( $svc_x eq 'svc_phone' ) { + $return{$_} = $svc[0]->$_() for qw(countrycode phonenum sip_password pin); + } elsif ( $svc_x eq 'svc_pbx' ) { + #$return{$_} = $svc[0]->$_() for qw( ) #nothing yet + } else { + return {'error' => "configuration error: unknown signup service $svc_x"}; + #die "unknown signup service $svc_x"; + # return an error that's visible to someone somewhere + } + + } + + return \%return; + +} + +#false laziness w/ above +# fresh restart to support "free account" portals with 3.x/4.x-style +# addressless accounts +# and a contact (for self-service login) +sub new_customer_minimal { + my $packet = shift; + + my $conf = new FS::Conf; + my $svc_x = $conf->config('signup_server-service') || 'svc_acct'; + if ( $svc_x eq 'svc_acct' ) { - $return{$_} = $svc[0]->$_() for qw( username _password ); - } elsif ( $svc_x eq 'svc_phone' ) { - $return{$_} = $svc[0]->$_() for qw( countrycode phonenum sip_password pin ); + + #things that aren't necessary in base class, but are for signup server + #return "Passwords don't match" + # if $hashref->{'_password'} ne $hashref->{'_password2'} + return { 'error' => gettext('empty_password') } + unless length($packet->{'_password'}); + # a bit inefficient for large numbers of pops + return { 'error' => gettext('no_access_number_selected') } + unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} )); + + } + elsif ( $svc_x eq 'svc_pbx' ) { + #possibly some validation will be needed + } + + my $agentnum; + if ( exists $packet->{'session_id'} ) { + my $cache = new FS::ClientAPI_SessionCache( { + 'namespace' => 'FS::ClientAPI::Agent', + } ); + my $session = $cache->get($packet->{'session_id'}); + if ( $session ) { + $agentnum = $session->{'agentnum'}; + } else { + return { 'error' => "Can't resume session" }; #better error message + } } else { - die "unknown signup service $svc_x"; + $agentnum = $packet->{agentnum} + || $conf->config('signup_server-default_agentnum'); + } + + #shares some stuff with htdocs/edit/process/cust_main.cgi... take any + # common that are still here and library them. + + my $cust_main = new FS::cust_main ( { + 'agentnum' => $agentnum, + 'refnum' => $packet->{refnum} + || $conf->config('signup_server-default_refnum'), + 'tagnum' => [ FS::part_tag->default_tags ], + + map { $_ => $packet->{$_} } qw( + salesnum + last first company daytime night fax mobile + ss + ), + + } ); + + if ( grep length($packet->{$_}), FS::cust_main->location_fields ) { + my $bill_hash; + foreach my $f (FS::cust_main->location_fields) { + $bill_hash->{$f} = $packet->{$f}; + } + my $bill_location = FS::cust_location->new($bill_hash); + $cust_main->set('bill_location' => $bill_location); + $cust_main->set('ship_location' => $bill_location); + } + + my @invoicing_list = $packet->{'invoicing_list'} + ? split( /\s*\,\s*/, $packet->{'invoicing_list'} ) + : (); + + use Tie::RefHash; + tie my %hash, 'Tie::RefHash', (); + my @svc = (); + + $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/; + my $pkgpart = $1; + + if ( $pkgpart ) { + + my $part_pkg = + qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } ) + or return { 'error' => "WARNING: unknown pkgpart: $pkgpart" }; + my $svcpart = $part_pkg->svcpart($svc_x); + + my $cust_pkg = new FS::cust_pkg ( { + #later#'custnum' => $custnum, + 'pkgpart' => $packet->{'pkgpart'}, + } ); + #my $error = $cust_pkg->check; + #return { 'error' => $error } if $error; + + #should be all auto-magic and shit + if ( $svc_x eq 'svc_acct' ) { + + my $svc = new FS::svc_acct { + 'svcpart' => $svcpart, + map { $_ => $packet->{$_} } + qw( username _password sec_phrase popnum domsvc ), + }; + + push @svc, $svc; + + } elsif ( $svc_x eq 'svc_phone' ) { + + push @svc, new FS::svc_phone ( { + 'svcpart' => $svcpart, + map { $_ => $packet->{$_} } + qw( countrycode phonenum sip_password pin ), + } ); + + } elsif ( $svc_x eq 'svc_pbx' ) { + + push @svc, new FS::svc_pbx ( { + 'svcpart' => $svcpart, + map { $_ => $packet->{$_} } + qw( id title ), + } ); + + } else { + die "unknown signup service $svc_x"; + } + + foreach my $svc ( @svc ) { + my $y = $svc->setdefault; # arguably should be in new method + return { 'error' => $y } if $y && !ref($y); + #$error = $svc->check; + #return { 'error' => $error } if $error; + } + + use Tie::RefHash; + tie my %hash, 'Tie::RefHash'; + $hash{ $cust_pkg } = \@svc; + + } + + my %opt = (); + if ( $invoicing_list[0] && $packet->{'_password'} ) { + $opt{'contact'} = [ + new FS::contact { 'first' => $cust_main->first, + 'last' => $cust_main->get('last'), + '_password' => $packet->{'_password'}, + 'emailaddress' => $invoicing_list[0], + 'selfservice_access' => 'Y', + } + ]; + } + + my $error = $cust_main->insert( + \%hash, + \@invoicing_list, + %opt, + ); + return { 'error' => $error } if $error; + + my $session = { 'custnum' => $cust_main->custnum }; + + my $session_id; + do { + $session_id = sha512_hex(time(). {}. rand(). $$) + } until ( ! defined _myaccount_cache->get($session_id) ); #just in case + + _myaccount_cache->set( $session_id, $session, '1 hour' ); # 1 hour? + + my %return = ( 'error' => '', + 'signup_service' => $svc_x, + 'custnum' => $cust_main->custnum, + 'session_id' => $session_id, + ); + + if ( $svc[0] ) { + + $return{'svcnum'} = $svc[0]->svcnum; + + if ( $svc_x eq 'svc_acct' ) { + $return{$_} = $svc[0]->$_() for qw( username _password ); + } elsif ( $svc_x eq 'svc_phone' ) { + $return{$_} = $svc[0]->$_() for qw(countrycode phonenum sip_password pin); + } elsif ( $svc_x eq 'svc_pbx' ) { + #$return{$_} = $svc[0]->$_() for qw( ) #nothing yet + } else { + return {'error' => "configuration error: unknown signup service $svc_x"}; + #die "unknown signup service $svc_x"; + # return an error that's visible to someone somewhere + } + } return \%return; } +use vars qw( $myaccount_cache ); +sub _myaccount_cache { + $myaccount_cache ||= new FS::ClientAPI_SessionCache( { + 'namespace' => 'FS::ClientAPI::MyAccount', + } ); +} + sub capture_payment { my $packet = shift; @@ -728,29 +1094,36 @@ sub capture_payment { my $conf = new FS::Conf; - my $url = $packet->{url}; - my $payment_gateway = - qsearchs('payment_gateway', { 'gateway_callback_url' => popurl(0, $url) } ); - - unless ($payment_gateway) { - - my ( $processor, $login, $password, $action, @bop_options ) = - $conf->config('business-onlinepayment'); - $action ||= 'normal authorization'; - pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/; - die "No real-time processor is enabled - ". - "did you set the business-onlinepayment configuration value?\n" - unless $processor; - - $payment_gateway = new FS::payment_gateway( { - gateway_namespace => $conf->config('business-onlinepayment-namespace'), - gateway_module => $processor, - gateway_username => $login, - gateway_password => $password, - gateway_action => $action, - options => [ ( @bop_options ) ], - }); - + my $payment_gateway; + if ( my $gwnum = $conf->config('selfservice-payment_gateway') ) { + $payment_gateway = qsearchs('payment_gateway', { 'gatewaynum' => $gwnum }) + or die "configured gatewaynum $gwnum not found!"; + } + else { + my $url = $packet->{url}; + + $payment_gateway = qsearchs('payment_gateway', + { 'gateway_callback_url' => popurl(0, $url) } + ); + if (!$payment_gateway) { + + my ( $processor, $login, $password, $action, @bop_options ) = + $conf->config('business-onlinepayment'); + $action ||= 'normal authorization'; + pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/; + die "No real-time processor is enabled - ". + "did you set the business-onlinepayment configuration value?\n" + unless $processor; + + $payment_gateway = new FS::payment_gateway( { + gateway_namespace => $conf->config('business-onlinepayment-namespace'), + gateway_module => $processor, + gateway_username => $login, + gateway_password => $password, + gateway_action => $action, + options => [ ( @bop_options ) ], + }); + } } die "No real-time third party processor is enabled - ". @@ -780,19 +1153,34 @@ sub capture_payment { return { error => '_decline', bill_error => $bill_error }; } - if ($cust_pay_pending->status ne 'pending') { - my $bill_error = "Payment with id $paypendingnum is not pending, but ". + if ($cust_pay_pending->status ne 'thirdparty') { + my $bill_error = "Payment with id $paypendingnum is not thirdparty, but ". $cust_pay_pending->status. "; Transaction aborted."; return { error => '_decline', bill_error => $bill_error }; } my $cust_main = $cust_pay_pending->cust_main; - my $bill_error = - $cust_main->realtime_botpp_capture( $cust_pay_pending, %{$packet->{data}} ); + if ( $packet->{cancel} ) { + # the user has chosen not to make this payment + # (probably should be a separate API call, but I don't want to duplicate + # all of the above...which should eventually go away) + my $error = $cust_pay_pending->delete; + # don't show any errors related to this; they're not meaningful + warn "error canceling pending payment $paypendingnum: $error\n" if $error; + return { 'error' => '_cancel', + 'session_id' => $cust_pay_pending->session_id }; + } else { + # create the payment + my $bill_error = + $cust_main->realtime_botpp_capture( $cust_pay_pending, + %{$packet->{data}}, + apply => 1, + ); - return { 'error' => ( $bill_error->{bill_error} ? '_decline' : '' ), - %$bill_error, - }; + return { 'error' => ( $bill_error->{bill_error} ? '_decline' : '' ), + %$bill_error, + }; + } }