mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 12:07:51 +02: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:
parent
6370baf9de
commit
53426a34d1
15 changed files with 409 additions and 30 deletions
|
@ -64,6 +64,7 @@ import google.registry.flows.custom.DomainDeleteFlowCustomLogic.BeforeSaveParame
|
|||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
|
@ -260,8 +261,9 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
|||
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
|
||||
// Close the autorenew billing event and poll message. This may delete the poll message. Store
|
||||
// the updated recurring billing event, we'll need it later and can't reload it.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
BillingEvent.Recurring recurringBillingEvent =
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now);
|
||||
// If there's a pending transfer, the gaining client's autorenew billing
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
|
|
|
@ -583,7 +583,8 @@ public class DomainFlowUtils {
|
|||
*
|
||||
* <p>Returns the new autorenew recurring billing event.
|
||||
*/
|
||||
public static Recurring updateAutorenewRecurrenceEndTime(Domain domain, DateTime newEndTime) {
|
||||
public static Recurring updateAutorenewRecurrenceEndTime(
|
||||
Domain domain, Recurring existingRecurring, DateTime newEndTime) {
|
||||
Optional<PollMessage.Autorenew> autorenewPollMessage =
|
||||
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
|
||||
|
||||
|
@ -611,13 +612,9 @@ public class DomainFlowUtils {
|
|||
tm().put(updatedAutorenewPollMessage);
|
||||
}
|
||||
|
||||
Recurring recurring =
|
||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||
.asBuilder()
|
||||
.setRecurrenceEndTime(newEndTime)
|
||||
.build();
|
||||
tm().put(recurring);
|
||||
return recurring;
|
||||
Recurring newRecurring = existingRecurring.asBuilder().setRecurrenceEndTime(newEndTime).build();
|
||||
tm().put(newRecurring);
|
||||
return newRecurring;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -708,7 +705,10 @@ public class DomainFlowUtils {
|
|||
throw new TransfersAreAlwaysForOneYearException();
|
||||
}
|
||||
builder.setAvailIfSupported(true);
|
||||
fees = pricingLogic.getTransferPrice(registry, domainNameString, now).getFees();
|
||||
fees =
|
||||
pricingLogic
|
||||
.getTransferPrice(registry, domainNameString, now, recurringBillingEvent)
|
||||
.getFees();
|
||||
break;
|
||||
case UPDATE:
|
||||
builder.setAvailIfSupported(true);
|
||||
|
@ -767,7 +767,7 @@ public class DomainFlowUtils {
|
|||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
throws EppException {
|
||||
if (isDomainPremium(domainName, priceTime) && !feeCommand.isPresent()) {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||
throw new FeesRequiredForPremiumNameException();
|
||||
}
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
||||
|
|
|
@ -195,9 +195,14 @@ public final class DomainPricingLogic {
|
|||
}
|
||||
|
||||
/** Returns a new transfer price for the pricer. */
|
||||
FeesAndCredits getTransferPrice(Registry registry, String domainName, DateTime dateTime)
|
||||
FeesAndCredits getTransferPrice(
|
||||
Registry registry,
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
@Nullable Recurring recurringBillingEvent)
|
||||
throws EppException {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
FeesAndCredits renewPrice =
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
|
||||
return customLogic.customizeTransferPrice(
|
||||
TransferPriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
|
@ -205,9 +210,9 @@ public final class DomainPricingLogic {
|
|||
.setCurrency(registry.getCurrency())
|
||||
.addFeeOrCredit(
|
||||
Fee.create(
|
||||
domainPrices.getRenewCost().getAmount(),
|
||||
renewPrice.getRenewCost().getAmount(),
|
||||
FeeType.RENEW,
|
||||
domainPrices.isPremium()))
|
||||
renewPrice.hasAnyPremiumFees()))
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
|
|
|
@ -222,7 +222,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
|||
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||
.build();
|
||||
// End the old autorenew billing event and poll message now. This may delete the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now);
|
||||
Domain newDomain =
|
||||
existingDomain
|
||||
.asBuilder()
|
||||
|
|
|
@ -31,7 +31,6 @@ import static google.registry.model.ResourceTransferUtils.approvePendingTransfer
|
|||
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
|
@ -49,6 +48,7 @@ import google.registry.model.ImmutableObject;
|
|||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
|
@ -96,6 +96,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
|||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
|
||||
@Inject DomainTransferApproveFlow() {}
|
||||
|
||||
/**
|
||||
|
@ -120,6 +122,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
|||
String gainingRegistrarId = transferData.getGainingRegistrarId();
|
||||
// Create a transfer billing event for 1 year, unless the superuser extension was used to set
|
||||
// the transfer period to zero. There is not a transfer cost if the transfer period is zero.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class);
|
||||
historyBuilder.setId(domainHistoryKey.getId());
|
||||
Optional<BillingEvent.OneTime> billingEvent =
|
||||
|
@ -131,7 +134,14 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
|||
.setTargetId(targetId)
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setPeriodYears(1)
|
||||
.setCost(getDomainRenewCost(targetId, transferData.getTransferRequestTime(), 1))
|
||||
.setCost(
|
||||
pricingLogic
|
||||
.getTransferPrice(
|
||||
Registry.get(tld),
|
||||
targetId,
|
||||
transferData.getTransferRequestTime(),
|
||||
existingRecurring)
|
||||
.getRenewCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
||||
.setDomainHistoryId(
|
||||
|
@ -161,7 +171,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
|||
}
|
||||
// Close the old autorenew event and poll message at the transfer time (aka now). This may end
|
||||
// up deleting the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now);
|
||||
DateTime newExpirationTime =
|
||||
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
|
||||
// Create a new autorenew event starting at the expiration time.
|
||||
|
@ -172,6 +182,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
|||
.setTargetId(targetId)
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setDomainHistoryId(
|
||||
new DomainHistoryId(
|
||||
|
|
|
@ -40,6 +40,7 @@ import google.registry.flows.FlowModule.Superuser;
|
|||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
|
@ -114,7 +115,8 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
|||
targetId, newDomain.getTransferData(), null, domainHistoryKey));
|
||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
||||
// may recreate the autorenew poll message if it was deleted when the transfer request was made.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, END_OF_TIME);
|
||||
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||
// been implicitly server approved.
|
||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
||||
|
|
|
@ -42,6 +42,7 @@ import google.registry.flows.FlowModule.Superuser;
|
|||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
|
@ -115,7 +116,8 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
|||
targetId, newDomain.getTransferData(), null, now, domainHistoryKey));
|
||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
||||
// may end up recreating the poll message if it was deleted upon the transfer request.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, END_OF_TIME);
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, END_OF_TIME);
|
||||
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||
// been implicitly server approved.
|
||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
||||
|
|
|
@ -52,6 +52,7 @@ import google.registry.flows.exceptions.InvalidTransferPeriodValueException;
|
|||
import google.registry.flows.exceptions.ObjectAlreadySponsoredException;
|
||||
import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
|
||||
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Transfer;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
|
@ -168,10 +169,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
|||
throw new TransferPeriodZeroAndFeeTransferExtensionException();
|
||||
}
|
||||
// If the period is zero, then there is no fee for the transfer.
|
||||
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
Optional<FeesAndCredits> feesAndCredits =
|
||||
(period.getValue() == 0)
|
||||
? Optional.empty()
|
||||
: Optional.of(pricingLogic.getTransferPrice(registry, targetId, now));
|
||||
: Optional.of(
|
||||
pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
|
||||
if (feesAndCredits.isPresent()) {
|
||||
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get());
|
||||
}
|
||||
|
@ -201,6 +204,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
|||
serverApproveNewExpirationTime,
|
||||
domainHistoryKey,
|
||||
existingDomain,
|
||||
existingRecurring,
|
||||
trid,
|
||||
gainingClientId,
|
||||
feesAndCredits.map(FeesAndCredits::getTotalCost),
|
||||
|
@ -230,7 +234,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
|||
// the poll message if it has no events left. Note that if the automatic transfer succeeds, then
|
||||
// cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
|
||||
// that we've created in this flow and stored in pendingTransferData.
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime);
|
||||
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, automaticTransferTime);
|
||||
Domain newDomain =
|
||||
existingDomain
|
||||
.asBuilder()
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.googlecode.objectify.Key;
|
|||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
|
@ -110,6 +111,7 @@ public final class DomainTransferUtils {
|
|||
DateTime serverApproveNewExpirationTime,
|
||||
Key<DomainHistory> domainHistoryKey,
|
||||
Domain existingDomain,
|
||||
Recurring existingRecurring,
|
||||
Trid trid,
|
||||
String gainingRegistrarId,
|
||||
Optional<Money> transferCost,
|
||||
|
@ -144,7 +146,11 @@ public final class DomainTransferUtils {
|
|||
return builder
|
||||
.add(
|
||||
createGainingClientAutorenewEvent(
|
||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
||||
existingRecurring,
|
||||
serverApproveNewExpirationTime,
|
||||
domainHistoryKey,
|
||||
targetId,
|
||||
gainingRegistrarId))
|
||||
.add(
|
||||
createGainingClientAutorenewPollMessage(
|
||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
||||
|
@ -239,6 +245,7 @@ public final class DomainTransferUtils {
|
|||
}
|
||||
|
||||
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
|
||||
Recurring existingRecurring,
|
||||
DateTime serverApproveNewExpirationTime,
|
||||
Key<DomainHistory> domainHistoryKey,
|
||||
String targetId,
|
||||
|
@ -250,6 +257,8 @@ public final class DomainTransferUtils {
|
|||
.setRegistrarId(gainingRegistrarId)
|
||||
.setEventTime(serverApproveNewExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||
.setDomainHistoryId(
|
||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||
.build();
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableMultimap;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
|
@ -215,7 +216,8 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
|
|||
.setHistoryEntry(domainHistory)
|
||||
.build();
|
||||
// End the old autorenew billing event and poll message now.
|
||||
updateAutorenewRecurrenceEndTime(domain, now);
|
||||
Recurring existingRecurring = tm().loadByKey(domain.getAutorenewBillingEvent());
|
||||
updateAutorenewRecurrenceEndTime(domain, existingRecurring, now);
|
||||
Domain newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Reference in a new issue