Add configurable discount on sunrise domain creates (#2056)

Previously we had a 15% discount applied at invoicing time. We got rid of
that inadvertently in 2022 and we want to add it back, but instead of
being applied at invoicing time we'll just apply it directly to the
creation cost when creating the billing events.

Note: previous behavior didn't care about standard vs premium pricing so
we don't either

https://buganizer.corp.google.com/issues/287070313 is a bug for the
issue, and
https://github.com/google/nomulus/pull/1710/files#diff-5097b0ef57578718444ea6b9d4c6cb32f655686a37e2ca3dd96ad2db86a77f06L151-L170
is the section of the pull request that inadvertently removed it
This commit is contained in:
gbrodman
2023-06-27 18:58:44 -04:00
committed by GitHub
parent fdfbb9572d
commit a4540a847a
12 changed files with 112 additions and 64 deletions
@@ -276,16 +276,24 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
boolean isAnchorTenant = expectedBillingFlags.contains(ANCHOR_TENANT);
// Set up the creation cost.
BigDecimal createCost =
isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())
? BigDecimal.valueOf(200)
: BigDecimal.valueOf(26);
if (isAnchorTenant) {
createCost = BigDecimal.ZERO;
}
if (expectedBillingFlags.contains(SUNRISE)) {
createCost =
createCost.multiply(
BigDecimal.valueOf(1 - RegistryConfig.getSunriseDomainCreateDiscount()));
}
FeesAndCredits feesAndCredits =
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(
Fee.create(
isAnchorTenant
? BigDecimal.valueOf(0)
: isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())
? BigDecimal.valueOf(200)
: BigDecimal.valueOf(26),
createCost,
FeeType.CREATE,
isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())))
.build();
@@ -19,6 +19,7 @@ import static google.registry.model.billing.BillingBase.Flag.AUTO_RENEW;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.domain.fee.BaseFee.FeeType.CREATE;
import static google.registry.model.domain.fee.BaseFee.FeeType.RENEW;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
@@ -28,7 +29,6 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
@@ -49,6 +49,7 @@ import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppinput.EppInput;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.DatabaseHelper;
@@ -72,11 +73,11 @@ public class DomainPricingLogicTest {
final JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
@Inject Clock clock = new FakeClock(DateTime.now(UTC));
@Inject Clock clock = new FakeClock(DateTime.parse("2023-05-13T00:00:00.000Z"));
@Mock EppInput eppInput;
SessionMetadata sessionMetadata;
@Mock FlowMetadata flowMetadata;
Tld registry;
Tld tld;
Domain domain;
@BeforeEach
@@ -86,7 +87,7 @@ public class DomainPricingLogicTest {
domainPricingLogic =
new DomainPricingLogic(
new DomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata));
registry =
tld =
persistResource(
Tld.get("example")
.asBuilder()
@@ -133,12 +134,32 @@ public class DomainPricingLogicTest {
return billingRecurrence;
}
@Test
void testGetDomainCreatePrice_sunrise_appliesDiscount() throws EppException {
ImmutableSortedMap<DateTime, TldState> transitions =
ImmutableSortedMap.<DateTime, TldState>naturalOrder()
.put(START_OF_TIME, TldState.PREDELEGATION)
.put(clock.nowUtc().minusHours(1), TldState.START_DATE_SUNRISE)
.put(clock.nowUtc().plusHours(1), TldState.GENERAL_AVAILABILITY)
.build();
Tld sunriseTld = createTld("sunrise", transitions);
assertThat(
domainPricingLogic.getCreatePrice(
sunriseTld, "domain.sunrise", clock.nowUtc(), 2, false, true, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
// 13 * 2 * 0.85 == 22.1
.addFeeOrCredit(Fee.create(Money.of(USD, 22.1).getAmount(), CREATE, false))
.build());
}
@Test
void testGetDomainRenewPrice_oneYear_standardDomain_noBilling_isStandardPrice()
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
tld, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -151,7 +172,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
tld, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -164,7 +185,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
tld, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -177,7 +198,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
tld, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -189,7 +210,7 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_oneYear_premiumDomain_default_isPremiumPrice() throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -215,7 +236,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -244,7 +265,7 @@ public class DomainPricingLogicTest {
AllocationTokenInvalidForPremiumNameException.class,
() ->
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -256,7 +277,7 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_multiYear_premiumDomain_default_isPremiumCost() throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -283,7 +304,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -313,7 +334,7 @@ public class DomainPricingLogicTest {
AllocationTokenInvalidForPremiumNameException.class,
() ->
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -326,7 +347,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -352,7 +373,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -370,7 +391,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
5,
@@ -397,7 +418,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
5,
@@ -415,7 +436,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -442,7 +463,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -460,7 +481,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -488,7 +509,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -506,7 +527,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -524,7 +545,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
5,
@@ -542,7 +563,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -570,7 +591,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -601,7 +622,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
1,
@@ -626,7 +647,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
5,
@@ -654,7 +675,7 @@ public class DomainPricingLogicTest {
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
5,
@@ -673,7 +694,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
1,
@@ -692,7 +713,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
5,
@@ -713,15 +734,14 @@ public class DomainPricingLogicTest {
IllegalArgumentException.class,
() ->
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
tld, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
assertThat(thrown).hasMessageThat().isEqualTo("Number of years must be positive");
}
@Test
void testGetDomainTransferPrice_standardDomain_default_noBilling_defaultRenewalPrice()
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(registry, "standard.example", clock.nowUtc(), null))
assertThat(domainPricingLogic.getTransferPrice(tld, "standard.example", clock.nowUtc(), null))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -732,8 +752,7 @@ public class DomainPricingLogicTest {
@Test
void testGetDomainTransferPrice_premiumDomain_default_noBilling_premiumRenewalPrice()
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(registry, "premium.example", clock.nowUtc(), null))
assertThat(domainPricingLogic.getTransferPrice(tld, "premium.example", clock.nowUtc(), null))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -745,7 +764,7 @@ public class DomainPricingLogicTest {
void testGetDomainTransferPrice_standardDomain_default_defaultRenewalPrice() throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
persistDomainAndSetRecurrence("standard.example", DEFAULT, Optional.empty())))
@@ -760,7 +779,7 @@ public class DomainPricingLogicTest {
void testGetDomainTransferPrice_premiumDomain_default_premiumRenewalPrice() throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty())))
@@ -776,7 +795,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
persistDomainAndSetRecurrence("standard.example", NONPREMIUM, Optional.empty())))
@@ -792,7 +811,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
persistDomainAndSetRecurrence("premium.example", NONPREMIUM, Optional.empty())))
@@ -808,7 +827,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"standard.example",
clock.nowUtc(),
persistDomainAndSetRecurrence(
@@ -825,7 +844,7 @@ public class DomainPricingLogicTest {
throws EppException {
assertThat(
domainPricingLogic.getTransferPrice(
registry,
tld,
"premium.example",
clock.nowUtc(),
persistDomainAndSetRecurrence(
@@ -55,7 +55,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
@@ -83,7 +83,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
@@ -115,7 +115,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">70.00</fee:fee>
<fee:fee description="create">59.50</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
@@ -59,7 +59,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
@@ -88,7 +88,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
@@ -121,7 +121,7 @@
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">70.00</fee:fee>
<fee:fee description="create">59.50</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
@@ -40,7 +40,7 @@
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
@@ -49,7 +49,7 @@
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd avail="1">
@@ -59,7 +59,7 @@
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">70.00</fee:fee>
<fee:fee description="create">59.50</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
</fee:chkData>
@@ -66,7 +66,7 @@
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
@@ -102,7 +102,7 @@
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="create">11.05</fee:fee>
<fee:class>collision</fee:class>
</fee:command>
</fee:cd>
@@ -142,7 +142,7 @@
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">70.00</fee:fee>
<fee:fee description="create">59.50</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:command>
</fee:cd>