use FS::queue;
use FS::reg_code;
use FS::payby;
+use FS::banned_pay;
$DEBUG = 0;
$me = '[FS::ClientAPI::Signup]';
payinfo paycvv paydate payname paystate paytype
paystart_month paystart_year payissue
payip
+ override_ban_warn
referral_custnum comments
)
$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,
{
'key' => 'signup-recommend_daytime',
'section' => 'self-service',
- 'description' => 'Encourage the entry of a daytime phone number invoicing email address on signup.',
+ 'description' => 'Encourage the entry of a daytime phone number on signup.',
'type' => 'checkbox',
},
{
+ 'key' => 'signup-duplicate_cc-warn_hours',
+ 'section' => 'self-service',
+ 'description' => 'Issue a warning if the same credit card is used for multiple signups within this many hours.',
+ 'type' => 'text',
+ },
+
+ {
'key' => 'svc_phone-radius-default_password',
'section' => 'telephony',
'description' => 'Default password when exporting svc_phone records to RADIUS',
--- /dev/null
+package FS::Cron::expire_banned_pay;
+
+use vars qw( @ISA @EXPORT_OK );
+use Exporter;
+use FS::UID qw(dbh);
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( expire_banned_pay );
+
+sub expire_banned_pay {
+ my $sql = "DELETE FROM banned_pay WHERE end_date IS NOT NULL".
+ " AND end_date < ?";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute(time) or die $sth->errstr;
+
+ dbh->commit or die dbh->errstr if $FS::UID::AutoCommit
+
+}
+
+1;
'payby', 'char', '', 4, '', '',
'payinfo', 'varchar', '', 128, '', '', #say, a 512-big digest _hex encoded
#'paymask', 'varchar', 'NULL', $char_d, '', ''
- '_date', @date_type, '', '',
- 'otaker', 'varchar', 'NULL', 32, '', '',
- 'usernum', 'int', 'NULL', '', '', '',
+ '_date', @date_type, '', '',
+ 'end_date', @date_type, '', '',
+ 'otaker', 'varchar', 'NULL', 32, '', '',
+ 'usernum', 'int', 'NULL', '', '', '',
+ 'bantype', 'varchar', 'NULL', $char_d, '', '',
'reason', 'varchar', 'NULL', $char_d, '', '',
],
'primary_key' => 'bannum',
- 'unique' => [ [ 'payby', 'payinfo' ] ],
- 'index' => [ [ 'usernum' ] ],
+ 'unique' => [],
+ 'index ' => [ [ 'payby', 'payinfo' ], [ 'usernum' ], ],
},
'pkg_category' => {
use strict;
use base qw( FS::otaker_Mixin FS::Record );
+use Digest::MD5 qw(md5_base64);
use FS::Record qw( qsearch qsearchs );
use FS::UID qw( getotaker );
use FS::CurrentUser;
=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
L<Time::Local> and L<Date::Parse> for conversion functions.
+=item end_date - optional end date, also specified as a UNIX timestamp.
+
=item usernum - order taker (assigned automatically, see L<FS::access_user>)
+=item bantype - Ban type: "" or null (regular ban), "warn" (warning)
+
=item reason - reason (text)
=back
|| $self->ut_enum('payby', [ 'CARD', 'CHEK' ] )
|| $self->ut_text('payinfo')
|| $self->ut_numbern('_date')
+ || $self->ut_numbern('end_date')
+ || $self->ut_enum('bantype', [ '', 'warn' ] )
|| $self->ut_textn('reason')
;
return $error if $error;
$self->SUPER::check;
}
+=back
+
+=head1 CLASS METHODS
+
+=item ban_search OPTION => VALUE ...
+
+Takes two parameters: payby and payinfo, and searches for an (un-expired) ban
+matching those items.
+
+Returns the ban, or false if no ban was found.
+
+=cut
+
+sub ban_search {
+ my( $class, %opt ) = @_;
+ qsearchs({
+ 'table' => 'banned_pay',
+ 'hashref' => {
+ 'payby' => $opt{payby},
+ 'payinfo' => md5_base64($opt{payinfo}),
+ },
+ 'extra_sql' => 'AND end_date IS NULL OR end_date >= '. time,
+ });
+}
+
# Used by FS::Upgrade to migrate to a new database.
sub _upgrade_data { # class method
my ($class, %opts) = @_;
$prepay_credit = qsearchs(
'prepay_credit',
- { 'identifier' => $prepay_credit },
+ { 'identifier' => $identifier },
'',
'FOR UPDATE'
);
&& cardtype($self->payinfo) eq "Unknown";
unless ( $ignore_banned_card ) {
- my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+ my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } );
if ( $ban ) {
- return 'Banned credit card: banned on '.
- time2str('%a %h %o at %r', $ban->_date).
- ' by '. $ban->otaker.
- ' (ban# '. $ban->bannum. ')';
+ if ( $ban->bantype eq 'warn' ) {
+ #or others depending on value of $ban->reason ?
+ return '_duplicate_card' unless $self->override_ban_warn;
+ } else {
+ return 'Banned credit card: banned on '.
+ time2str('%a %h %o at %r', $ban->_date).
+ ' by '. $ban->otaker.
+ ' (ban# '. $ban->bannum. ')';
+ }
}
}
$self->paycvv('');
unless ( $ignore_banned_card ) {
- my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+ my $ban = FS::banned_pay->ban_search( %{ $self->_banned_pay_hashref } );
if ( $ban ) {
- return 'Banned ACH account: banned on '.
- time2str('%a %h %o at %r', $ban->_date).
- ' by '. $ban->otaker.
- ' (ban# '. $ban->bannum. ')';
+ if ( $ban->bantype eq 'warn' ) {
+ #or others depending on value of $ban->reason ?
+ return '_duplicate_ach' unless $self->override_ban_warn;
+ } else {
+ return 'Banned ACH account: banned on '.
+ time2str('%a %h %o at %r', $ban->_date).
+ ' by '. $ban->otaker.
+ ' (ban# '. $ban->bannum. ')';
+ }
}
}
return ( "Can't (yet) ban encrypted credit cards" )
if $self->is_encrypted($self->payinfo);
- my $ban = new FS::banned_pay $self->_banned_pay_hashref;
+ my $ban = new FS::banned_pay $self->_new_banned_pay_hashref;
my $error = $ban->insert;
return ( $error ) if $error;
{
'payby' => $payby2ban{$self->payby},
- 'payinfo' => md5_base64($self->payinfo),
+ 'payinfo' => $self->payinfo,
#don't ever *search* on reason! #'reason' =>
};
}
+sub _new_banned_pay_hashref {
+ my $self = shift;
+ my $hr = $self->_banned_pay_hashref;
+ $hr->{payinfo} = md5_base64($hr->{payinfo});
+ $hr;
+}
+
=item notes
Returns all notes (see L<FS::cust_main_note>) for this customer.
use vars qw( $conf $DEBUG $me );
use vars qw( $realtime_bop_decline_quiet ); #ugh
use Data::Dumper;
-use Digest::MD5 qw(md5_base64);
use Business::CreditCard 0.28;
use FS::UID qw( dbh );
use FS::Record qw( qsearch qsearchs );
use FS::cust_pay;
use FS::cust_pay_pending;
use FS::cust_refund;
+use FS::banned_pay;
$realtime_bop_decline_quiet = 0;
# check for banned credit card/ACH
###
- my $ban = qsearchs('banned_pay', {
+ my $ban = FS::banned_pay->ban_search(
'payby' => $bop_method2payby{$options{method}},
- 'payinfo' => md5_base64($options{payinfo}),
- } );
- return "Banned credit card" if $ban;
+ 'payinfo' => $options{payinfo},
+ );
+ return "Banned credit card" if $ban && $ban->bantype ne 'warn';
###
# massage data
use FS::Cron::nms_report qw(nms_report);
nms_report(%opt);
+#no way to skip this yet, but should be harmless/quick
+use FS::Cron::expire_banned_pay qw(expire_banned_pay);
+expire_banned_pay(%opt);
+
#you can skip this by setting the disable_cron_billing config
use FS::Cron::bill qw(bill);
bill(%opt);
payby payinfo paycvv paydate payname paystate paytype
invoicing_list referral_custnum promo_code reg_code
+ override_ban_warn
pkgpart refnum agentnum
username sec_phrase _password popnum
mac_addr
qw( popup_url reference amount );
print_collect($rv);
} elsif ( $error ) {
+
#fudge the snarf info
no strict 'refs';
${$_} = $cgi->param($_) foreach grep { /^snarf_/ } $cgi->param;
+
+ if ( $error =~ /^_duplicate_(card|ach)$/ ) {
+ my $what = ($1 eq 'card') ? 'Credit card' : 'Electronic check';
+ $error = "Warning: $what already used to sign up recently";
+ $init_data->{'override_ban_warn'} = 1;
+ }
+
print_form();
+
} else {
print_okay(
'pkgpart' => scalar($cgi->param('pkgpart')),
sub print_form {
- $error = "Error: $error" if $error;
+ $error = "Error: $error" if $error && $error !~ /^Warning:/i;
my $r = {
$cgi->Vars,
$OUT = join("\n", map { qq|<input type="hidden" name="$_" />| } qw / promo_code reg_code pkgpart username _password _password2 sec_phrase popnum mac_addr countrycode phonenum sip_password pin / );
%>
+<%=
+ if ($override_ban_warn) {
+ $OUT .= 'Are you sure you want to sign up again? <SELECT NAME="override_ban_warn"><OPTION VALUE="0">No<OPTION VALUE="1">Yes</SELECT><BR><BR>';
+ } else {
+ $OUT .= '';
+ }
+%>
+
Where did you hear about our service? <SELECT NAME="refnum">
<%=
$OUT .= '<OPTION VALUE="">' unless $refnum;