=item payinfo - card number, P.O., comp issuer (4-8 lowercase alphanumerics; think username) or prepayment identifier (see L<FS::prepay_credit>)
+=item paycvv - Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
+
=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
=item payname - name on card or billing name
or return gettext('invalid_card'); # . ": ". $self->payinfo;
return gettext('unknown_card_type')
if cardtype($self->payinfo) eq "Unknown";
+ if ( defined $self->dbdef_table->column('paycvv') ) {
+ if ( length($self->paycvv) ) {
+ if ( cardtype($self->payinfo) eq 'American Express card' ) {
+ $self->paycvv =~ /^(\d{4})$/
+ or return "CVV2 (CID) for American Express cards is four digits.";
+ $self->paycvv($1);
+ } else {
+ $self->paycvv =~ /^(\d{3})$/
+ or return "CVV2 (CVC2/CID) is three digits.";
+ $self->paycvv($1);
+ }
+ } else {
+ $self->paycvv('');
+ }
+ }
} elsif ( $self->payby eq 'CHEK' || $self->payby eq 'DCHK' ) {
$payinfo =~ /^(\d+)\@(\d{9})$/ or return 'invalid echeck account@aba';
$payinfo = "$1\@$2";
$self->payinfo($payinfo);
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'LECB' ) {
$payinfo =~ /^1?(\d{10})$/ or return 'invalid btn billing telephone number';
$payinfo = $1;
$self->payinfo($payinfo);
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'BILL' ) {
$error = $self->ut_textn('payinfo');
return "Illegal P.O. number: ". $self->payinfo if $error;
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'COMP' ) {
$error = $self->ut_textn('payinfo');
return "Illegal comp account issuer: ". $self->payinfo if $error;
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'PREPAY' ) {
return "Illegal prepayment identifier: ". $self->payinfo if $error;
return "Unknown prepayment identifier"
unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
}
$self->tax =~ /^(Y?)$/ or return "Illegal tax: ". $self->tax;
$self->tax($1);
- $self->otaker(getotaker);
+ $self->otaker(getotaker) unless $self->otaker;
#warn "AFTER: \n". $self->_dump;
Options are passed as name-value pairs.
-The only currently available option is `time', which bills the customer as if
-it were that time. It is specified as a UNIX timestamp; see
-L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion
-functions. For example:
+Currently available options are:
+
+resetup - if set true, re-charges setup fees.
+
+time - bills the customer as if it were that time. Specified as a UNIX
+timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and
+L<Date::Parse> for conversion functions. For example:
use Date::Parse;
...
$cust_main->bill( 'time' => str2time('April 20th, 2001') );
+
If there is an error, returns the error, otherwise returns false.
=cut
# bill setup
my $setup = 0;
- unless ( $cust_pkg->setup ) {
+ if ( !$cust_pkg->setup || $options{'resetup'} ) {
my $setup_prog = $part_pkg->getfield('setup');
$setup_prog =~ /^(.*)$/ or do {
$dbh->rollback if $oldAutoCommit;
return "Error eval-ing part_pkg->setup pkgpart ". $part_pkg->pkgpart.
"(expression $setup_prog): $@";
}
- $cust_pkg->setfield('setup',$time);
+ $cust_pkg->setfield('setup', $time) unless $cust_pkg->setup;
$cust_pkg_mod_flag=1;
}
#bill recurring fee
my $recur = 0;
my $sdate;
- if ( $part_pkg->getfield('freq') > 0 &&
+ if ( $part_pkg->getfield('freq') ne '0' &&
! $cust_pkg->getfield('susp') &&
( $cust_pkg->getfield('bill') || 0 ) <= $time
) {
$cust_pkg->last_bill($sdate)
if $cust_pkg->dbdef_table->column('last_bill');
- $mon += $part_pkg->freq;
- until ( $mon < 12 ) { $mon -= 12; $year++; }
+ if ( $part_pkg->freq =~ /^\d+$/ ) {
+ $mon += $part_pkg->freq;
+ until ( $mon < 12 ) { $mon -= 12; $year++; }
+ } elsif ( $part_pkg->freq =~ /^(\d+)w$/ ) {
+ my $weeks = $1;
+ $mday += $weeks * 7;
+ } elsif ( $part_pkg->freq =~ /^(\d+)d$/ ) {
+ my $days = $1;
+ $mday += $days;
+ } else {
+ $dbh->rollback if $oldAutoCommit;
+ return "unparsable frequency: ". $part_pkg->freq;
+ }
$cust_pkg->setfield('bill',
timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year));
$cust_pkg_mod_flag = 1;
unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
- my @taxes = qsearch( 'cust_main_county', {
- 'state' => $self->state,
- 'county' => $self->county,
- 'country' => $self->country,
- 'taxclass' => $part_pkg->taxclass,
- } )
- || qsearch( 'cust_main_county', {
+ my @taxes = qsearch( 'cust_main_county', {
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ 'taxclass' => $part_pkg->taxclass,
+ } );
+ unless ( @taxes ) {
+ @taxes = qsearch( 'cust_main_county', {
'state' => $self->state,
'county' => $self->county,
'country' => $self->country,
'taxclass' => '',
} );
+ }
# maybe eliminate this entirely, along with all the 0% records
unless ( @taxes ) {
|| $tax->recurtax =~ /^Y$/i;
next unless $taxable_charged;
- if ( $tax->exempt_amount ) {
+ if ( $tax->exempt_amount > 0 ) {
my ($mon,$year) = (localtime($sdate) )[4,5];
$mon++;
my $freq = $part_pkg->freq || 1;
+ if ( $freq !~ /(\d+)$/ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "daily/weekly package definitions not (yet?)".
+ " compatible with monthly tax exemptions";
+ }
my $taxable_per_month = sprintf("%.2f", $taxable_charged / $freq );
foreach my $which_month ( 1 .. $freq ) {
my %hash = (
my %content;
if ( $method eq 'CC' ) {
+
$content{card_number} = $self->payinfo;
$self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
$content{expiration} = "$2/$1";
+
+ $content{cvv2} = $self->paycvv
+ if defined $self->dbdef_table->column('paycvv')
+ && length($self->paycvv);
+
+ $content{recurring_billing} = 'YES'
+ if qsearch('cust_pay', { 'custnum' => $self->custnum,
+ 'payby' => 'CARD',
+ 'payinfo' => $self->payinfo, } );
+
} elsif ( $method eq 'ECHECK' ) {
my($account_number,$routing_code) = $self->payinfo;
( $content{account_number}, $content{routing_code} ) =
}
+ #remove paycvv after initial transaction
+ #make this disable-able via a config option if anyone insists?
+ # (though that probably violates cardholder agreements)
+ if ( defined $self->dbdef_table->column('paycvv')
+ && length($self->paycvv)
+ && ! grep { $_ eq cardtype($self->payinfo) } $conf->config('cvv-save')
+ ) {
+ my $new = new FS::cust_main { $self->hash };
+ $new->paycvv('');
+ my $error = $new->replace($self);
+ if ( $error ) {
+ warn "error removing cvv: $error\n";
+ }
+ }
+
#result handling
if ( $transaction->is_success() ) {
if ( !$options{'quiet'} && !$realtime_bop_decline_quiet
&& $conf->exists('emaildecline')
&& grep { $_ ne 'POST' } $self->invoicing_list
+ && ! grep { $_ eq $transaction->error_message }
+ $conf->config('emaildecline-exclude')
) {
my @templ = $conf->config('declinetemplate');
my $template = new Text::Template (