From 54c5a9450dcf910c2217c7a8e56a51f06d32d4f0 Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 26 Jun 2024 16:01:32 -0400 Subject: [PATCH] Add RegistrationBehavior.NONPREMIUM_CREATE (#2481) When using this token (which must be tied to a particular domain), the first year price (and only the first year price, i.e. the creation price) will be the standard price for this TLD. Future years (i.e. renewals) will continue at the normal premium price. --- .../flows/domain/DomainPricingLogic.java | 9 ++ .../model/domain/token/AllocationToken.java | 23 +++- .../model/pricing/PremiumPricingEngine.java | 2 +- .../flows/domain/DomainCreateFlowTest.java | 16 +++ .../flows/domain/DomainPricingLogicTest.java | 123 +++++++++++++----- .../domain/token/AllocationTokenTest.java | 29 +++++ ...omain_create_nonpremium_token_response.xml | 27 ++++ 7 files changed, 193 insertions(+), 36 deletions(-) create mode 100644 core/src/test/resources/google/registry/flows/domain/domain_create_nonpremium_token_response.xml diff --git a/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java b/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java index c1e852710..6478ed152 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java +++ b/core/src/main/java/google/registry/flows/domain/DomainPricingLogic.java @@ -85,6 +85,15 @@ public final class DomainPricingLogic { createFee = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false); } else { DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime); + if (allocationToken.isPresent() + && allocationToken + .get() + .getRegistrationBehavior() + .equals(RegistrationBehavior.NONPREMIUM_CREATE)) { + domainPrices = + DomainPrices.create( + false, tld.getCreateBillingCost(dateTime), domainPrices.getRenewCost()); + } Money domainCreateCost = getDomainCreateCostWithDiscount(domainPrices, years, allocationToken); // Apply a sunrise discount if configured and applicable diff --git a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java index 6b859764f..b3aae9eb0 100644 --- a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java +++ b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java @@ -115,7 +115,17 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda */ BYPASS_TLD_STATE, /** Bypasses most checks and creates the domain as an anchor tenant, with all that implies. */ - ANCHOR_TENANT + ANCHOR_TENANT, + /** + * Bypasses the premium list to use the standard creation price. Does not affect the renewal + * price. + * + *

This cannot be specified along with a discount fraction, and any renewals (automatic or + * otherwise) will use the premium price for the domain if one exists. + * + *

Tokens with this behavior must be tied to a single particular domain. + */ + NONPREMIUM_CREATE } /** Type of the token that indicates how and where it should be used. */ @@ -404,6 +414,17 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda checkArgumentNotNull( getInstance().domainName, "ANCHOR_TENANT tokens must be tied to a domain"); } + if (getInstance().registrationBehavior.equals(RegistrationBehavior.NONPREMIUM_CREATE)) { + checkArgument( + getInstance().discountFraction == 0.0, + "NONPREMIUM_CREATE tokens cannot apply a discount"); + checkArgumentNotNull( + getInstance().domainName, "NONPREMIUM_CREATE tokens must be tied to a domain"); + checkArgument( + getInstance().allowedEppActions == null + || getInstance().allowedEppActions.contains(CommandName.CREATE), + "NONPREMIUM_CREATE tokens must allow for CREATE actions"); + } if (getInstance().domainName != null) { try { DomainFlowUtils.validateDomainName(getInstance().domainName); diff --git a/core/src/main/java/google/registry/model/pricing/PremiumPricingEngine.java b/core/src/main/java/google/registry/model/pricing/PremiumPricingEngine.java index ff62793e4..8e642b64f 100644 --- a/core/src/main/java/google/registry/model/pricing/PremiumPricingEngine.java +++ b/core/src/main/java/google/registry/model/pricing/PremiumPricingEngine.java @@ -46,7 +46,7 @@ public interface PremiumPricingEngine { private Money createCost; private Money renewCost; - static DomainPrices create(boolean isPremium, Money createCost, Money renewCost) { + public static DomainPrices create(boolean isPremium, Money createCost, Money renewCost) { DomainPrices instance = new DomainPrices(); instance.isPremium = isPremium; instance.createCost = createCost; diff --git a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java index a9737666f..d16b71328 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java @@ -3519,6 +3519,22 @@ class DomainCreateFlowTest extends ResourceFlowTestCase validToken.asBuilder().setDomainName(null).build()); + assertThrows( + IllegalArgumentException.class, + () -> validToken.asBuilder().setDiscountFraction(0.5).build()); + assertThrows( + IllegalArgumentException.class, + () -> + validToken + .asBuilder() + .setAllowedEppActions( + ImmutableSet.of( + CommandName.RENEW, + CommandName.TRANSFER, + CommandName.RESTORE, + CommandName.UPDATE)) + .build()); + } + private void assertBadInitialTransition(TokenStatus status) { assertBadTransition( ImmutableSortedMap.naturalOrder() diff --git a/core/src/test/resources/google/registry/flows/domain/domain_create_nonpremium_token_response.xml b/core/src/test/resources/google/registry/flows/domain/domain_create_nonpremium_token_response.xml new file mode 100644 index 000000000..636d2e5f6 --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_create_nonpremium_token_response.xml @@ -0,0 +1,27 @@ + + + + + Command completed successfully + + + + rich.example + 1999-04-03T22:00:00Z + 2000-04-03T22:00:00Z + + + + + USD + 13.00 + + + + ABC-12345 + server-trid + + + \ No newline at end of file