try again with warning users off buying regular paid time for premium accounts (#3022)

* revert the reversion

* tweaks pt 1

* better error checking on arguments to DW::Pay functions.
  they don't behave nicely when given bogus inputs,
  causing the widget to quietly bomb out from under us.

* also check additional logic in allow_account_conversion
  for permitting paid time to be applied to a premium account.

* note that the 0.7 is expressed as a constant elsewhere.
  possibly this whole code block should be redefined as
  a hook since it assumes dw-nonfree business logic.

* tweaks pt 2

This isn't pretty, but it prevents the scenario where widget
errors disappear into a BML block that was being ignored.
That's why we were seeing the problem with purchase attempts
disappearing instead of telling us what went wrong.

One day, someone will rewrite LJ::Widget not to use BML.
This commit is contained in:
Jen 2023-01-04 17:44:58 -06:00 committed by GitHub
parent 7df3589f23
commit e1a6869015
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 11 deletions

View File

@ -176,14 +176,58 @@ sub handle_post {
# build a new item and try to toss it in the cart. this fails if there's a
# conflict or something
if ( $post->{accttype} ) {
my ( $rv, $err ) = $cart->add_item(
DW::Shop::Item::Account->new(
type => $post->{accttype},
user_confirmed => $post->{alreadyposted},
force_spelling => $post->{force_spelling},
%item_data
)
my $item = DW::Shop::Item::Account->new(
type => $post->{accttype},
user_confirmed => $post->{alreadyposted},
force_spelling => $post->{force_spelling},
%item_data
);
# check for renewing premium as paid
my $u = LJ::load_userid( $item ? $item->t_userid : undef );
my $paid_status = $u ? DW::Pay::get_paid_status($u) : undef;
if ($paid_status) {
my $paid_curtype = DW::Pay::type_shortname( $paid_status->{typeid} );
my $has_premium = $paid_curtype eq 'premium' ? 1 : 0;
my $ok = DW::Shop::Item::Account->allow_account_conversion( $u, $item->class );
if ( $ok && $has_premium && $item->class eq 'paid' && !$post->{prem_convert} ) {
# check account expiration date
my $exptime = DateTime->from_epoch( epoch => $paid_status->{expiretime} );
my $newtime = DateTime->now;
if ( my $future_ymd = $item->deliverydate ) {
my ( $y, $m, $d ) = split /-/, $future_ymd;
$newtime = DateTime->new( year => $y + 0, month => $m + 0, day => $d + 0 );
}
my $to_day = sub { return $_[0]->truncate( to => 'day' ) };
if ( DateTime->compare( $to_day->($exptime), $to_day->($newtime) ) ) {
my $months = $item->months;
my $newexp = $exptime->clone->add( months => $months );
my $paid_d = $exptime->delta_days($newexp)->in_units('days');
# FIXME: this should be DW::BusinessRules::Pay::DWS::CONVERSION_RATE
my $prem_d = int( $paid_d * 0.7 );
my $ml_args =
{ date => $exptime->ymd, premium_num => $prem_d, paid_num => $paid_d };
# but only include date if the logged-in user owns the account
delete $ml_args->{date} unless $remote && $remote->has_same_email_as($u);
# this should be handled as a special case in the caller
return ( error => 'premium_convert', ml_args => $ml_args );
}
}
}
my ( $rv, $err ) = $cart->add_item($item);
return ( error => $err ) unless $rv;
}
elsif ( $post->{item} eq "rename" ) {

View File

@ -69,24 +69,48 @@ body<=
my $post_fields = {};
my $email_checkbox;
my $premium_convert;
if ( LJ::did_post() ) {
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.invalidform'} p?>"
unless LJ::check_form_auth();
my $error;
my %from_post;
$post_fields = LJ::Widget::ShopItemOptions->post_fields( \%POST );
if ( keys %$post_fields ) { # make sure the user selected an account type
# need to do this because all of these form fields are in the BML page instead of in the widget
LJ::Widget->use_specific_form_fields( post => \%POST,
widget => "ShopItemOptions",
fields => [ qw( for username email deliverydate_mm deliverydate_dd deliverydate_yyyy anonymous reason alreadyposted force_spelling ) ] );
my %from_post = LJ::Widget->handle_post( \%POST, 'ShopItemOptions' => { email_checkbox => \$email_checkbox } );
$error = $from_post{error} if $from_post{error};
fields => [ qw( for username email deliverydate_mm deliverydate_dd deliverydate_yyyy anonymous reason alreadyposted force_spelling prem_convert ) ] );
@BMLCodeBlock::errors = (); # LJ::Widget->handle_post uses this global variable
eval { %from_post = LJ::Widget->handle_post( \%POST, 'ShopItemOptions' => { email_checkbox => \$email_checkbox } ); };
my @errs = map { LJ::ehtml($_) } split "\n", $BMLCodeBlock::errors[0] // '';
push @errs, $@ if $@;
if ( $from_post{error} && ( !@errs || $from_post{error} ne 'premium_convert' ) ) {
push @errs, $from_post{error};
}
$error = join "<br>", @errs;
} else {
$error = $ML{'.error.noselection'};
}
if ( $error ) {
if ( $error eq 'premium_convert' ) {
$premium_convert = 1;
my $ml_args = $from_post{ml_args};
$ret .= qq{<div class="error-box">};
$ret .= BML::ml( '.error.premiumconvert', $ml_args );
$ret .= BML::ml( '.error.premiumconvert.postdate', $ml_args ) if $ml_args->{date};
$ret .= qq{</div>};
} elsif ( $error ) {
$ret .= qq{<div class="error-box">$error</div>};
} else {
return BML::redirect( "$LJ::SITEROOT/shop" );
@ -170,6 +194,13 @@ body<=
$ret .= "</table>";
if ( $premium_convert ) {
$ret .= "<p>";
$ret .= LJ::html_check( { name => 'prem_convert', id => 'prem_convert', value => 1 } );
$ret .= "<label for='prem_convert'>$ML{'.premiumconvert.agree'}</label>";
$ret .= "</p>";
}
$ret .= LJ::html_hidden( for => $GET{for} );
$ret .= LJ::html_hidden( alreadyposted => 1 ) if LJ::did_post();
$ret .= "<p>" . LJ::html_submit( $ML{'.btn.addtocart'} ) . "</p>";

View File

@ -9,6 +9,26 @@
.error.noselection=You must select an item to add to your cart.
.error.premiumconvert<<
<p>
The selected account is currently a premium paid account, but you've chosen
to purchase regular paid time. A premium paid account can't be downgraded to a
paid account. If you choose to proceed, the paid time you purchase will be
converted to premium paid time at our standard conversion rate of 70% when it's applied
to the account, granting [[premium_num]] more days of premium paid time instead of
the [[paid_num]] more days you might have expected.
</p>
.
.error.premiumconvert.postdate<<
<p>
If you want to renew the account as a paid account, instead of a premium paid account,
you'll need to adjust your order so that the paid time is scheduled to be applied on
or after the account's expiration date of [[date]]. Doing this may result in up to a
day's wait before the paid time is applied to the account.
</p>
.
.giftfor.anonymous=Do you want to make this an anonymous gift?
.giftfor.deliverydate=Delivery date:
@ -29,5 +49,7 @@
.intro.self=Please choose the type of <a [[aopts]]>Paid Account</a> that you'd like to purchase for your account [[user]].
.premiumconvert.agree=Yes, I want to purchase standard paid time for this premium paid account, which will be converted to a smaller amount of premium paid time if it is applied before the account expires.
.title=Buy a Paid Account