X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FClientAPI%2FSignup.pm;h=7fad7b3089e8b6c8ca50c3d68b56e4e48fbc4950;hp=5d719c4901d2384a27eb091da04c7f4ebe742152;hb=da820d8c8837dce295e7cbd61accc22c4c019e14;hpb=a87e91ee19d14a6fc3da62f4b44a9628d32bb6a3 diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm index 5d719c490..7fad7b308 100644 --- a/FS/FS/ClientAPI/Signup.pm +++ b/FS/FS/ClientAPI/Signup.pm @@ -7,7 +7,7 @@ use Data::Dumper; use Tie::RefHash; use Digest::SHA qw(sha512_hex); use FS::Conf; -use FS::Record qw(qsearch qsearchs dbdef); +use FS::Record qw(qsearch qsearchs dbdef dbh); use FS::CGI qw(popurl); use FS::Msgcat qw(gettext); use FS::Misc qw(card_types); @@ -31,6 +31,25 @@ use FS::cust_payby; $DEBUG = 1; $me = '[FS::ClientAPI::Signup]'; +=head1 NAME + +FS::ClientAPI::Signup - Front-end API for signing up customers + +=head1 DESCRIPTION + +This module provides the ClientAPI functions for talking to a signup +server. The signup server is open to the public, i.e. does not require a +login. The back-end Freeside server creates customers, orders packages and +services, and processes initial payments. + +=head1 METHODS + +=over 4 + +=cut + +# document the rest of this as we work on it + sub clear_cache { warn "$me clear_cache called\n" if $DEBUG; my $cache = new FS::ClientAPI_SessionCache( { @@ -145,8 +164,6 @@ sub signup_info { 'security_phrase' => $conf->exists('security_phrase'), - 'nomadix' => $conf->exists('signup_server-nomadix'), - 'payby' => [ $conf->config('signup_server-payby') ], 'payby_longname' => [ map { FS::payby->longname($_) } @@ -173,7 +190,6 @@ sub signup_info { '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_payby->paytypes ], @@ -328,20 +344,11 @@ sub signup_info { 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 || + my $gateway = $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), 'nofatal' => 1, ); @@ -499,21 +506,8 @@ sub new_customer { #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 { - $agentnum = $packet->{agentnum} - || $conf->config('signup_server-default_agentnum'); - } + my $agentnum = get_agentnum($packet); + return $agentnum if ref($agentnum); my ($bill_hash, $ship_hash); foreach my $f (FS::cust_main->location_fields) { @@ -624,17 +618,9 @@ sub new_customer { return { 'error' => "Unknown reseller" } unless $agent; - 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, + my $gw = $agent->payment_gateway( 'method' => FS::payby->payby2bop($payby), + 'nofatal' => 1, ); - } $cust_main->payby('BILL') # MCRD better? no, that's for something else if $gw && $gw->gateway_namespace eq 'Business::OnlineThirdPartyPayment'; @@ -645,9 +631,6 @@ sub new_customer { && ! $cust_main->paycvv && $conf->exists('signup-require_cvv'); - $cust_main->payinfo($cust_main->daytime) - if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo; - my @invoicing_list = $packet->{'invoicing_list'} ? split( /\s*\,\s*/, $packet->{'invoicing_list'} ) : (); @@ -694,6 +677,9 @@ sub new_customer { map { $_ => $packet->{$_} } qw( username _password sec_phrase popnum domsvc ), }; + + my $error = $svc->is_password_allowed($packet->{_password}); + return { error => $error } if $error; my @acct_snarf; my $snarfnum = 1; @@ -924,21 +910,8 @@ sub new_customer_minimal { #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 { - $agentnum = $packet->{agentnum} - || $conf->config('signup_server-default_agentnum'); - } + my $agentnum = get_agentnum($packet); + return $agentnum if ref($agentnum); #shares some stuff with htdocs/edit/process/cust_main.cgi... take any # common that are still here and library them. @@ -1130,36 +1103,28 @@ sub capture_payment { my $conf = new FS::Conf; - 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', + my $url = $packet->{url}; + my $payment_gateway = $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 ) ], - }); - } + 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 - ". @@ -1220,4 +1185,193 @@ sub capture_payment { } +=item get_agentnum PACKET + +Given a PACKET from the signup server, looks up the agentnum to use for signing +up a customer. This will use 'session_id' if the agent is authenticated, +otherwise 'agentnum', otherwise the 'signup_server-default_agentnum' config. If +the agent can't be found, returns an error packet. + +=cut + +sub get_agentnum { + my $packet = shift; + my $conf = new FS::Conf; + 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 { + $agentnum = $packet->{agentnum} + || $conf->config('signup_server-default_agentnum'); + } + if ( $agentnum and FS::agent->count('agentnum = ?', $agentnum) ) { + return $agentnum; + } + return { 'error' => 'Signup is not configured' }; +} + +=item new_prospect PACKET + +Creates a new L entry. PACKET must contain: + +- either agentnum or session_id; if not, signup_server-default_agentnum will +be used and must not be empty + +- either refnum or referral_title; if not, signup_server-default_refnum will +be used and must not be empty + +- last and first (names), and optionally company and title + +- address1, city, state, country, zip, and optionally address2 + +- emailaddress + +and can also contain: + +- one or more of phone_daytime, phone_night, phone_mobile, and phone_fax + +- a 'comment' (will be attached to the contact) + +State and country will be normalized to Freeside state/country codes if +necessary. + +=cut + +sub new_prospect { + + my $packet = shift; + warn "$me new_prospect called\n".Dumper($packet) if $DEBUG; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + my $conf = FS::Conf->new; + + my $error; + + my $agentnum = get_agentnum($packet); + return $agentnum if ref $agentnum; + my $refnum; + if ( my $title = $packet->{referral_title} ) { + my $part_referral = qsearchs('part_referral', { + 'agentnum' => $agentnum, + 'title' => $title, + }); + $part_referral ||= qsearchs('part_referral', { + 'agentnum' => '', + 'title' => $title, + }); + if (!$part_referral) { + $part_referral = FS::part_referral->new({ + 'agentnum' => $agentnum, + 'title' => $title, + 'referral' => $title, + }); + $error = $part_referral->insert; + if ( $error ) { + warn "ERROR: could not create referral type '$title': $error\n"; + } + } + $refnum = $part_referral->refnum; + } elsif ( $packet->{refnum} ) { + $refnum = $packet->{refnum}; + } + $refnum ||= $conf->config('signup_server-default_refnum'); + return { error => "Signup referral type is not configured" } if !$refnum; + + my $prospect = FS::prospect_main->new({ + 'agentnum' => $agentnum, + 'refnum' => $refnum, + 'company' => $packet->{company}, + }); + + my $location = FS::cust_location->new; + foreach ( qw(address1 address2 city county zip ) ) { + $location->set($_, $packet->{$_}); + } + # normalize country and state if they're not already ISO codes + # easier than doing it on the client side--we already have the tables here + my $country = $packet->{country}; + my $state = $packet->{state}; + if (length($country) > 2) { + # it likes title case + $country = join(' ', map ucfirst, split(/\s+/, $country)); + my $lsc = Locale::SubCountry->new($country); + if ($lsc) { + $country = uc($lsc->country_code); + + if ($lsc->has_sub_countries) { + if ( $lsc->full_name($state) eq 'unknown' ) { + # then we were probably given a full name, so resolve it + $state = $lsc->code($state); + if ( $state eq 'unknown' ) { + # doesn't resolve as a full name either, return an error + $error = "Unknown state: ".$packet->{state}; + } else { + $state = uc($state); + } + } + } # else state doesn't matter + } else { + # couldn't find the country in LSC + $error = "Unknown country: $country"; + } + } + $location->set('country', $country); + $location->set('state', $state); + + $error ||= $prospect->insert( cust_location => $location ); + return { error => $error } if $error; + + my $contact = FS::contact->new({ + prospectnum => $prospect->prospectnum, + locationnum => $location->locationnum, + invoice_dest => 'Y', + }); + # use emailaddress pseudo-field behavior here + foreach (qw(last first title emailaddress comment)) { + $contact->set($_, $packet->{$_}); + } + $error = $contact->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return { error => $error }; + } + + foreach my $phone_type (qsearch('phone_type', {})) { + my $key = 'phone_' . lc($phone_type->typename); + my $phonenum = $packet->{$key}; + if ( $phonenum ) { + # just to not have to supply country code from the other end + my $number = Number::Phone->new($location->country, $phonenum); + if (!$number) { + $error = 'invalid phone number'; + } else { + my $phone = FS::contact_phone->new({ + contactnum => $contact->contactnum, + phonenum => $phonenum, + countrycode => $number->country_code, + phonetypenum => $phone_type->phonetypenum, + }); + $error = $phone->insert; + } + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return { error => $phone_type->typename . ' phone: ' . $error }; + } + } + } # foreach $phone_type + + $dbh->commit if $oldAutoCommit; + return { prospectnum => $prospect->prospectnum }; +} + 1;