Return the correct renewal fee for anchor tenants in domain checks (#2238)

The code as previously written assumed that creation fees would be the
same as renewal fees -- this is not the case for anchor tenants, where
the renewal fee is always the standard cost for the TLD (instead of any
premium cost). This was already handled properly in the actual billing
implementation, but we didn't tell the user the right renewal cost in
domain checks.

This also removes some warning logs related to nested transactions
This commit is contained in:
gbrodman 2023-12-01 15:37:05 -05:00 committed by GitHub
parent 08551f7bc7
commit dd86c56ddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 9 deletions

View file

@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency; import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency;
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.validateTokenForPossiblePremiumName; import static google.registry.flows.domain.token.AllocationTokenFlowUtils.validateTokenForPossiblePremiumName;
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
@ -31,11 +30,13 @@ import google.registry.flows.custom.DomainPricingCustomLogic.RenewPriceParameter
import google.registry.flows.custom.DomainPricingCustomLogic.RestorePriceParameters; import google.registry.flows.custom.DomainPricingCustomLogic.RestorePriceParameters;
import google.registry.flows.custom.DomainPricingCustomLogic.TransferPriceParameters; import google.registry.flows.custom.DomainPricingCustomLogic.TransferPriceParameters;
import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParameters; import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParameters;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.billing.BillingRecurrence; import google.registry.model.billing.BillingRecurrence;
import google.registry.model.domain.fee.BaseFee; import google.registry.model.domain.fee.BaseFee;
import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
import google.registry.model.domain.token.AllocationToken.TokenBehavior; import google.registry.model.domain.token.AllocationToken.TokenBehavior;
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
import google.registry.model.tld.Tld; import google.registry.model.tld.Tld;
@ -132,12 +133,14 @@ public final class DomainPricingLogic {
// recurrence is null if the domain is still available. Billing events are created // recurrence is null if the domain is still available. Billing events are created
// in the process of domain creation. // in the process of domain creation.
if (billingRecurrence == null) { if (billingRecurrence == null) {
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken); renewCost =
getDomainRenewCostWithDiscount(tld, domainPrices, dateTime, years, allocationToken);
isRenewCostPremiumPrice = domainPrices.isPremium(); isRenewCostPremiumPrice = domainPrices.isPremium();
} else { } else {
switch (billingRecurrence.getRenewalPriceBehavior()) { switch (billingRecurrence.getRenewalPriceBehavior()) {
case DEFAULT: case DEFAULT:
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken); renewCost =
getDomainRenewCostWithDiscount(tld, domainPrices, dateTime, years, allocationToken);
isRenewCostPremiumPrice = domainPrices.isPremium(); isRenewCostPremiumPrice = domainPrices.isPremium();
break; break;
// if the renewal price behavior is specified, then the renewal price should be the same // if the renewal price behavior is specified, then the renewal price should be the same
@ -156,10 +159,7 @@ public final class DomainPricingLogic {
case NONPREMIUM: case NONPREMIUM:
renewCost = renewCost =
getDomainCostWithDiscount( getDomainCostWithDiscount(
false, false, years, allocationToken, tld.getStandardRenewCost(dateTime));
years,
allocationToken,
Tld.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
isRenewCostPremiumPrice = false; isRenewCostPremiumPrice = false;
break; break;
default: default:
@ -257,8 +257,20 @@ public final class DomainPricingLogic {
/** Returns the domain renew cost with allocation-token-related discounts applied. */ /** Returns the domain renew cost with allocation-token-related discounts applied. */
private Money getDomainRenewCostWithDiscount( private Money getDomainRenewCostWithDiscount(
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken) Tld tld,
DomainPrices domainPrices,
DateTime dateTime,
int years,
Optional<AllocationToken> allocationToken)
throws AllocationTokenInvalidForPremiumNameException { throws AllocationTokenInvalidForPremiumNameException {
// Short-circuit if the user sent an anchor-tenant or otherwise NONPREMIUM-renewal token
if (allocationToken.isPresent()) {
AllocationToken token = allocationToken.get();
if (token.getRegistrationBehavior().equals(RegistrationBehavior.ANCHOR_TENANT)
|| token.getRenewalPriceBehavior().equals(RenewalPriceBehavior.NONPREMIUM)) {
return tld.getStandardRenewCost(dateTime).multipliedBy(years);
}
}
return getDomainCostWithDiscount( return getDomainCostWithDiscount(
domainPrices.isPremium(), years, allocationToken, domainPrices.getRenewCost()); domainPrices.isPremium(), years, allocationToken, domainPrices.getRenewCost());
} }

View file

@ -199,7 +199,7 @@ public final class ReservedList
public synchronized ImmutableMap<String, ReservedListEntry> getReservedListEntries() { public synchronized ImmutableMap<String, ReservedListEntry> getReservedListEntries() {
if (reservedListMap == null) { if (reservedListMap == null) {
reservedListMap = reservedListMap =
tm().transact( tm().reTransact(
() -> () ->
tm() tm()
.createQueryComposer(ReservedListEntry.class) .createQueryComposer(ReservedListEntry.class)

View file

@ -340,6 +340,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setDiscountYears(2) .setDiscountYears(2)
.setTokenStatusTransitions( .setTokenStatusTransitions(
@ -364,6 +365,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setDiscountYears(2) .setDiscountYears(2)
.setAllowedEppActions(ImmutableSet.of(CommandName.CREATE))
.setTokenStatusTransitions( .setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder() ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED) .put(START_OF_TIME, TokenStatus.NOT_STARTED)

View file

@ -34,6 +34,24 @@
<fee:command>create</fee:command> <fee:command>create</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
</fee:domain> </fee:domain>
<fee:domain>
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example2.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
</fee:check> </fee:check>
</extension> </extension>
<clTRID>ABC-12345</clTRID> <clTRID>ABC-12345</clTRID>

View file

@ -42,6 +42,28 @@
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class> <fee:class>token-not-supported</fee:class>
</fee:cd> </fee:cd>
<fee:cd>
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd>
<fee:name>example2.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class>
</fee:cd>
<fee:cd>
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class>
</fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>
<trID> <trID>

View file

@ -41,6 +41,27 @@
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:class>reserved</fee:class> <fee:class>reserved</fee:class>
</fee:cd> </fee:cd>
<fee:cd>
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class>
</fee:cd>
<fee:cd>
<fee:name>example2.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class>
</fee:cd>
<fee:cd>
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>token-not-supported</fee:class>
</fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>
<trID> <trID>