mirror of
https://github.com/google/nomulus
synced 2026-02-10 06:50:30 +00:00
Make domain transfers use (and retain) the renewal price/behavior (#1701)
* Use the new renewal price logic in transfer flow * Fix build * Add renewal handling on all transfer flows * Merge branch 'master' into transfer-retain-renewal-price * Merge branch 'master' into transfer-retain-renewal-price * Add more tests
This commit is contained in:
@@ -399,4 +399,128 @@ public class DomainPricingLogicTest {
|
||||
registry, "standard.example", clock.nowUtc(), -1, null));
|
||||
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))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_premiumDomain_default_noBilling_premiumRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(registry, "premium.example", clock.nowUtc(), null))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_standardDomain_default_defaultRenewalPrice() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", DEFAULT, Optional.empty())))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_premiumDomain_default_premiumRenewalPrice() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", DEFAULT, Optional.empty())))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 100).getAmount(), RENEW, true))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_standardDomain_nonPremium_nonPremiumRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", NONPREMIUM, Optional.empty())))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_premiumDomain_nonPremium_nonPremiumRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", NONPREMIUM, Optional.empty())))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 10).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_standardDomain_specified_specifiedRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1.23)))))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1.23).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_premiumDomain_specified_specifiedRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurringBillingEvent(
|
||||
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 1.23)))))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 1.23).getAmount(), RENEW, false))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import static google.registry.testing.DatabaseHelper.deleteTestDomain;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -53,6 +54,7 @@ import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.OneTime;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
@@ -69,10 +71,13 @@ import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.money.Money;
|
||||
@@ -415,6 +420,103 @@ class DomainTransferApproveFlowTest
|
||||
.setRecurringEventKey(domain.getAutorenewBillingEvent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonpremiumPriceRenewalBehavior_carriesOver() throws Exception {
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
setupDomainWithPendingTransfer("example", "tld");
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
setEppInput("domain_transfer_approve_wildcard.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
DateTime now = clock.nowUtc();
|
||||
runFlowAssertResponse(loadFile("domain_transfer_approve_response.xml"));
|
||||
domain = reloadResourceByForeignKey();
|
||||
DomainHistory acceptHistory =
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE, DomainHistory.class);
|
||||
assertBillingEventsForResource(
|
||||
domain,
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setBillingTime(now.plusDays(5))
|
||||
.setEventTime(now)
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setCost(Money.of(USD, new BigDecimal("11.00")))
|
||||
.setDomainHistory(acceptHistory)
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setPeriodYears(1)
|
||||
.setTargetId("example.tld")
|
||||
.build(),
|
||||
getGainingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setDomainHistory(acceptHistory)
|
||||
.build(),
|
||||
getLosingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(now)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_specifiedPriceRenewalBehavior_carriesOver() throws Exception {
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
setupDomainWithPendingTransfer("example", "tld");
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("43.10")))
|
||||
.build());
|
||||
setEppInput("domain_transfer_approve_wildcard.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
DateTime now = clock.nowUtc();
|
||||
runFlowAssertResponse(loadFile("domain_transfer_approve_response.xml"));
|
||||
domain = reloadResourceByForeignKey();
|
||||
DomainHistory acceptHistory =
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE, DomainHistory.class);
|
||||
assertBillingEventsForResource(
|
||||
domain,
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setBillingTime(now.plusDays(5))
|
||||
.setEventTime(now)
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setCost(Money.of(USD, new BigDecimal("43.10")))
|
||||
.setDomainHistory(acceptHistory)
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setPeriodYears(1)
|
||||
.setTargetId("example.tld")
|
||||
.build(),
|
||||
getGainingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("43.10")))
|
||||
.setDomainHistory(acceptHistory)
|
||||
.build(),
|
||||
getLosingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(now)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("43.10")))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badContactPassword() {
|
||||
// Change the contact's password so it does not match the password in the file.
|
||||
|
||||
@@ -28,11 +28,13 @@ import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEventsEqual;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEventsForResource;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessagesEqual;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKeys;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
@@ -83,6 +85,7 @@ import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
|
||||
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainAuthInfo;
|
||||
@@ -101,11 +104,14 @@ import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferResponse;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
@@ -1202,6 +1208,117 @@ class DomainTransferRequestFlowTest
|
||||
runTest("domain_transfer_request_fee.xml", UserPrivileges.SUPERUSER, RICH_DOMAIN_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonPremiumRenewalPrice_isReflectedInTransferCostAndCarriesOver()
|
||||
throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
// This ensures that the transfer has non-premium cost, as otherwise, the fee extension would be
|
||||
// required to ack the premium price.
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
DomainHistory requestHistory =
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST, DomainHistory.class);
|
||||
// Check that the server approve billing recurrence (which will reify after 5 days if the
|
||||
// transfer is not explicitly acked) maintains the non-premium behavior.
|
||||
assertBillingEventsForResource(
|
||||
domain,
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setBillingTime(now.plusDays(10)) // 5 day pending transfer + 5 day billing grace period
|
||||
.setEventTime(now.plusDays(5))
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setCost(Money.of(USD, new BigDecimal("11.00")))
|
||||
.setDomainHistory(requestHistory)
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setPeriodYears(1)
|
||||
.setTargetId("example.tld")
|
||||
.build(),
|
||||
getGainingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.setDomainHistory(requestHistory)
|
||||
.build(),
|
||||
getLosingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(now.plusDays(5))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.NONPREMIUM)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_specifiedRenewalPrice_isReflectedInTransferCostAndCarriesOver()
|
||||
throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("18.79")))
|
||||
.build());
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
|
||||
runFlowAssertResponse(loadFile("domain_transfer_request_response.xml"));
|
||||
domain = loadByEntity(domain);
|
||||
|
||||
DomainHistory requestHistory =
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST, DomainHistory.class);
|
||||
// Check that the server approve billing recurrence (which will reify after 5 days if the
|
||||
// transfer is not explicitly acked) maintains the non-premium behavior.
|
||||
assertBillingEventsForResource(
|
||||
domain,
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setBillingTime(now.plusDays(10)) // 5 day pending transfer + 5 day billing grace period
|
||||
.setEventTime(now.plusDays(5))
|
||||
.setRegistrarId("NewRegistrar")
|
||||
.setCost(Money.of(USD, new BigDecimal("18.79")))
|
||||
.setDomainHistory(requestHistory)
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setPeriodYears(1)
|
||||
.setTargetId("example.tld")
|
||||
.build(),
|
||||
getGainingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("18.79")))
|
||||
.setDomainHistory(requestHistory)
|
||||
.build(),
|
||||
getLosingClientAutorenewEvent()
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(now.plusDays(5))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("18.79")))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void runWrongCurrencyTest(Map<String, String> substitutions) {
|
||||
Map<String, String> fullSubstitutions = Maps.newHashMap();
|
||||
fullSubstitutions.putAll(substitutions);
|
||||
|
||||
@@ -33,10 +33,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>transfer</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<!-- TODO(mcilwain): This should be non-premium once transfer flow
|
||||
changes are made. -->
|
||||
<fee:fee description="renew">100.00</fee:fee>
|
||||
<fee:class>premium</fee:class>
|
||||
<fee:fee description="renew">%RENEWPRICE%</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>rich.example</fee:name>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<transfer op="approve">
|
||||
<domain:transfer
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.extra</domain:name>
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
</domain:transfer>
|
||||
</transfer>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
Reference in New Issue
Block a user