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.flows.custom.EntityChanges;
|
||||||
import google.registry.model.ImmutableObject;
|
import google.registry.model.ImmutableObject;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||||
|
@ -260,8 +261,9 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||||
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
|
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
|
||||||
// Close the autorenew billing event and poll message. This may delete the poll message. Store
|
// 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.
|
// the updated recurring billing event, we'll need it later and can't reload it.
|
||||||
|
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||||
BillingEvent.Recurring recurringBillingEvent =
|
BillingEvent.Recurring recurringBillingEvent =
|
||||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now);
|
||||||
// If there's a pending transfer, the gaining client's autorenew billing
|
// If there's a pending transfer, the gaining client's autorenew billing
|
||||||
// event and poll message will already have been deleted in
|
// event and poll message will already have been deleted in
|
||||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||||
|
|
|
@ -583,7 +583,8 @@ public class DomainFlowUtils {
|
||||||
*
|
*
|
||||||
* <p>Returns the new autorenew recurring billing event.
|
* <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 =
|
Optional<PollMessage.Autorenew> autorenewPollMessage =
|
||||||
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
|
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
|
||||||
|
|
||||||
|
@ -611,13 +612,9 @@ public class DomainFlowUtils {
|
||||||
tm().put(updatedAutorenewPollMessage);
|
tm().put(updatedAutorenewPollMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
Recurring recurring =
|
Recurring newRecurring = existingRecurring.asBuilder().setRecurrenceEndTime(newEndTime).build();
|
||||||
tm().loadByKey(domain.getAutorenewBillingEvent())
|
tm().put(newRecurring);
|
||||||
.asBuilder()
|
return newRecurring;
|
||||||
.setRecurrenceEndTime(newEndTime)
|
|
||||||
.build();
|
|
||||||
tm().put(recurring);
|
|
||||||
return recurring;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -708,7 +705,10 @@ public class DomainFlowUtils {
|
||||||
throw new TransfersAreAlwaysForOneYearException();
|
throw new TransfersAreAlwaysForOneYearException();
|
||||||
}
|
}
|
||||||
builder.setAvailIfSupported(true);
|
builder.setAvailIfSupported(true);
|
||||||
fees = pricingLogic.getTransferPrice(registry, domainNameString, now).getFees();
|
fees =
|
||||||
|
pricingLogic
|
||||||
|
.getTransferPrice(registry, domainNameString, now, recurringBillingEvent)
|
||||||
|
.getFees();
|
||||||
break;
|
break;
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
builder.setAvailIfSupported(true);
|
builder.setAvailIfSupported(true);
|
||||||
|
@ -767,7 +767,7 @@ public class DomainFlowUtils {
|
||||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||||
FeesAndCredits feesAndCredits)
|
FeesAndCredits feesAndCredits)
|
||||||
throws EppException {
|
throws EppException {
|
||||||
if (isDomainPremium(domainName, priceTime) && !feeCommand.isPresent()) {
|
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||||
throw new FeesRequiredForPremiumNameException();
|
throw new FeesRequiredForPremiumNameException();
|
||||||
}
|
}
|
||||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
||||||
|
|
|
@ -195,9 +195,14 @@ public final class DomainPricingLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new transfer price for the pricer. */
|
/** 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 {
|
throws EppException {
|
||||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
FeesAndCredits renewPrice =
|
||||||
|
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
|
||||||
return customLogic.customizeTransferPrice(
|
return customLogic.customizeTransferPrice(
|
||||||
TransferPriceParameters.newBuilder()
|
TransferPriceParameters.newBuilder()
|
||||||
.setFeesAndCredits(
|
.setFeesAndCredits(
|
||||||
|
@ -205,9 +210,9 @@ public final class DomainPricingLogic {
|
||||||
.setCurrency(registry.getCurrency())
|
.setCurrency(registry.getCurrency())
|
||||||
.addFeeOrCredit(
|
.addFeeOrCredit(
|
||||||
Fee.create(
|
Fee.create(
|
||||||
domainPrices.getRenewCost().getAmount(),
|
renewPrice.getRenewCost().getAmount(),
|
||||||
FeeType.RENEW,
|
FeeType.RENEW,
|
||||||
domainPrices.isPremium()))
|
renewPrice.hasAnyPremiumFees()))
|
||||||
.build())
|
.build())
|
||||||
.setRegistry(registry)
|
.setRegistry(registry)
|
||||||
.setDomainName(InternetDomainName.from(domainName))
|
.setDomainName(InternetDomainName.from(domainName))
|
||||||
|
|
|
@ -222,7 +222,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||||
.build();
|
.build();
|
||||||
// End the old autorenew billing event and poll message now. This may delete the poll message.
|
// 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 =
|
Domain newDomain =
|
||||||
existingDomain
|
existingDomain
|
||||||
.asBuilder()
|
.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.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
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.CollectionUtils.union;
|
||||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
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;
|
||||||
import google.registry.model.billing.BillingEvent.Flag;
|
import google.registry.model.billing.BillingEvent.Flag;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
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.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||||
|
@ -96,6 +96,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
@Inject @Superuser boolean isSuperuser;
|
@Inject @Superuser boolean isSuperuser;
|
||||||
@Inject DomainHistory.Builder historyBuilder;
|
@Inject DomainHistory.Builder historyBuilder;
|
||||||
@Inject EppResponse.Builder responseBuilder;
|
@Inject EppResponse.Builder responseBuilder;
|
||||||
|
@Inject DomainPricingLogic pricingLogic;
|
||||||
|
|
||||||
@Inject DomainTransferApproveFlow() {}
|
@Inject DomainTransferApproveFlow() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,6 +122,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
String gainingRegistrarId = transferData.getGainingRegistrarId();
|
String gainingRegistrarId = transferData.getGainingRegistrarId();
|
||||||
// Create a transfer billing event for 1 year, unless the superuser extension was used to set
|
// 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.
|
// 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);
|
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class);
|
||||||
historyBuilder.setId(domainHistoryKey.getId());
|
historyBuilder.setId(domainHistoryKey.getId());
|
||||||
Optional<BillingEvent.OneTime> billingEvent =
|
Optional<BillingEvent.OneTime> billingEvent =
|
||||||
|
@ -131,7 +134,14 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
.setTargetId(targetId)
|
.setTargetId(targetId)
|
||||||
.setRegistrarId(gainingRegistrarId)
|
.setRegistrarId(gainingRegistrarId)
|
||||||
.setPeriodYears(1)
|
.setPeriodYears(1)
|
||||||
.setCost(getDomainRenewCost(targetId, transferData.getTransferRequestTime(), 1))
|
.setCost(
|
||||||
|
pricingLogic
|
||||||
|
.getTransferPrice(
|
||||||
|
Registry.get(tld),
|
||||||
|
targetId,
|
||||||
|
transferData.getTransferRequestTime(),
|
||||||
|
existingRecurring)
|
||||||
|
.getRenewCost())
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
||||||
.setDomainHistoryId(
|
.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
|
// Close the old autorenew event and poll message at the transfer time (aka now). This may end
|
||||||
// up deleting the poll message.
|
// up deleting the poll message.
|
||||||
updateAutorenewRecurrenceEndTime(existingDomain, now);
|
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now);
|
||||||
DateTime newExpirationTime =
|
DateTime newExpirationTime =
|
||||||
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
|
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
|
||||||
// Create a new autorenew event starting at the expiration time.
|
// Create a new autorenew event starting at the expiration time.
|
||||||
|
@ -172,6 +182,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||||
.setTargetId(targetId)
|
.setTargetId(targetId)
|
||||||
.setRegistrarId(gainingRegistrarId)
|
.setRegistrarId(gainingRegistrarId)
|
||||||
.setEventTime(newExpirationTime)
|
.setEventTime(newExpirationTime)
|
||||||
|
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||||
|
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||||
.setRecurrenceEndTime(END_OF_TIME)
|
.setRecurrenceEndTime(END_OF_TIME)
|
||||||
.setDomainHistoryId(
|
.setDomainHistoryId(
|
||||||
new DomainHistoryId(
|
new DomainHistoryId(
|
||||||
|
|
|
@ -40,6 +40,7 @@ import google.registry.flows.FlowModule.Superuser;
|
||||||
import google.registry.flows.FlowModule.TargetId;
|
import google.registry.flows.FlowModule.TargetId;
|
||||||
import google.registry.flows.TransactionalFlow;
|
import google.registry.flows.TransactionalFlow;
|
||||||
import google.registry.flows.annotations.ReportingSpec;
|
import google.registry.flows.annotations.ReportingSpec;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
|
@ -114,7 +115,8 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
||||||
targetId, newDomain.getTransferData(), null, domainHistoryKey));
|
targetId, newDomain.getTransferData(), null, domainHistoryKey));
|
||||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
// 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.
|
// 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
|
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||||
// been implicitly server approved.
|
// been implicitly server approved.
|
||||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
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.FlowModule.TargetId;
|
||||||
import google.registry.flows.TransactionalFlow;
|
import google.registry.flows.TransactionalFlow;
|
||||||
import google.registry.flows.annotations.ReportingSpec;
|
import google.registry.flows.annotations.ReportingSpec;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
|
@ -115,7 +116,8 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||||
targetId, newDomain.getTransferData(), null, now, domainHistoryKey));
|
targetId, newDomain.getTransferData(), null, now, domainHistoryKey));
|
||||||
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This
|
// 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.
|
// 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
|
// Delete the billing event and poll messages that were written in case the transfer would have
|
||||||
// been implicitly server approved.
|
// been implicitly server approved.
|
||||||
tm().delete(existingDomain.getTransferData().getServerApproveEntities());
|
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.ObjectAlreadySponsoredException;
|
||||||
import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
|
import google.registry.flows.exceptions.TransferPeriodMustBeOneYearException;
|
||||||
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainCommand.Transfer;
|
import google.registry.model.domain.DomainCommand.Transfer;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
|
@ -168,10 +169,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||||
throw new TransferPeriodZeroAndFeeTransferExtensionException();
|
throw new TransferPeriodZeroAndFeeTransferExtensionException();
|
||||||
}
|
}
|
||||||
// If the period is zero, then there is no fee for the transfer.
|
// If the period is zero, then there is no fee for the transfer.
|
||||||
|
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||||
Optional<FeesAndCredits> feesAndCredits =
|
Optional<FeesAndCredits> feesAndCredits =
|
||||||
(period.getValue() == 0)
|
(period.getValue() == 0)
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: Optional.of(pricingLogic.getTransferPrice(registry, targetId, now));
|
: Optional.of(
|
||||||
|
pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
|
||||||
if (feesAndCredits.isPresent()) {
|
if (feesAndCredits.isPresent()) {
|
||||||
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get());
|
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get());
|
||||||
}
|
}
|
||||||
|
@ -201,6 +204,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||||
serverApproveNewExpirationTime,
|
serverApproveNewExpirationTime,
|
||||||
domainHistoryKey,
|
domainHistoryKey,
|
||||||
existingDomain,
|
existingDomain,
|
||||||
|
existingRecurring,
|
||||||
trid,
|
trid,
|
||||||
gainingClientId,
|
gainingClientId,
|
||||||
feesAndCredits.map(FeesAndCredits::getTotalCost),
|
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
|
// 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
|
// cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
|
||||||
// that we've created in this flow and stored in pendingTransferData.
|
// that we've created in this flow and stored in pendingTransferData.
|
||||||
updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime);
|
updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, automaticTransferTime);
|
||||||
Domain newDomain =
|
Domain newDomain =
|
||||||
existingDomain
|
existingDomain
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.googlecode.objectify.Key;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Flag;
|
import google.registry.model.billing.BillingEvent.Flag;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
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.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||||
|
@ -110,6 +111,7 @@ public final class DomainTransferUtils {
|
||||||
DateTime serverApproveNewExpirationTime,
|
DateTime serverApproveNewExpirationTime,
|
||||||
Key<DomainHistory> domainHistoryKey,
|
Key<DomainHistory> domainHistoryKey,
|
||||||
Domain existingDomain,
|
Domain existingDomain,
|
||||||
|
Recurring existingRecurring,
|
||||||
Trid trid,
|
Trid trid,
|
||||||
String gainingRegistrarId,
|
String gainingRegistrarId,
|
||||||
Optional<Money> transferCost,
|
Optional<Money> transferCost,
|
||||||
|
@ -144,7 +146,11 @@ public final class DomainTransferUtils {
|
||||||
return builder
|
return builder
|
||||||
.add(
|
.add(
|
||||||
createGainingClientAutorenewEvent(
|
createGainingClientAutorenewEvent(
|
||||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
existingRecurring,
|
||||||
|
serverApproveNewExpirationTime,
|
||||||
|
domainHistoryKey,
|
||||||
|
targetId,
|
||||||
|
gainingRegistrarId))
|
||||||
.add(
|
.add(
|
||||||
createGainingClientAutorenewPollMessage(
|
createGainingClientAutorenewPollMessage(
|
||||||
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId))
|
||||||
|
@ -239,6 +245,7 @@ public final class DomainTransferUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
|
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
|
||||||
|
Recurring existingRecurring,
|
||||||
DateTime serverApproveNewExpirationTime,
|
DateTime serverApproveNewExpirationTime,
|
||||||
Key<DomainHistory> domainHistoryKey,
|
Key<DomainHistory> domainHistoryKey,
|
||||||
String targetId,
|
String targetId,
|
||||||
|
@ -250,6 +257,8 @@ public final class DomainTransferUtils {
|
||||||
.setRegistrarId(gainingRegistrarId)
|
.setRegistrarId(gainingRegistrarId)
|
||||||
.setEventTime(serverApproveNewExpirationTime)
|
.setEventTime(serverApproveNewExpirationTime)
|
||||||
.setRecurrenceEndTime(END_OF_TIME)
|
.setRecurrenceEndTime(END_OF_TIME)
|
||||||
|
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
|
||||||
|
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
|
||||||
.setDomainHistoryId(
|
.setDomainHistoryId(
|
||||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainHistory;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.Period;
|
import google.registry.model.domain.Period;
|
||||||
|
@ -215,7 +216,8 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
|
||||||
.setHistoryEntry(domainHistory)
|
.setHistoryEntry(domainHistory)
|
||||||
.build();
|
.build();
|
||||||
// End the old autorenew billing event and poll message now.
|
// 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 newDomain =
|
||||||
domain
|
domain
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
|
|
|
@ -399,4 +399,128 @@ public class DomainPricingLogicTest {
|
||||||
registry, "standard.example", clock.nowUtc(), -1, null));
|
registry, "standard.example", clock.nowUtc(), -1, null));
|
||||||
assertThat(thrown).hasMessageThat().isEqualTo("Number of years must be positive");
|
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.getOnlyHistoryEntryOfType;
|
||||||
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
||||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
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.loadByKey;
|
||||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
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.OneTime;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
import google.registry.model.billing.BillingEvent.Recurring;
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
|
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||||
import google.registry.model.contact.ContactAuthInfo;
|
import google.registry.model.contact.ContactAuthInfo;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainAuthInfo;
|
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.DomainTransactionRecord;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Registry;
|
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.DomainTransferData;
|
||||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.joda.money.Money;
|
import org.joda.money.Money;
|
||||||
|
@ -415,6 +420,103 @@ class DomainTransferApproveFlowTest
|
||||||
.setRecurringEventKey(domain.getAutorenewBillingEvent()));
|
.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
|
@Test
|
||||||
void testFailure_badContactPassword() {
|
void testFailure_badContactPassword() {
|
||||||
// Change the contact's password so it does not match the password in the file.
|
// 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.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||||
import static google.registry.testing.DatabaseHelper.assertBillingEventsEqual;
|
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.assertPollMessagesEqual;
|
||||||
import static google.registry.testing.DatabaseHelper.createTld;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||||
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
|
||||||
import static google.registry.testing.DatabaseHelper.getPollMessages;
|
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.loadByKey;
|
||||||
import static google.registry.testing.DatabaseHelper.loadByKeys;
|
import static google.registry.testing.DatabaseHelper.loadByKeys;
|
||||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
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.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
|
||||||
import google.registry.model.billing.BillingEvent;
|
import google.registry.model.billing.BillingEvent;
|
||||||
import google.registry.model.billing.BillingEvent.Reason;
|
import google.registry.model.billing.BillingEvent.Reason;
|
||||||
|
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||||
import google.registry.model.contact.ContactAuthInfo;
|
import google.registry.model.contact.ContactAuthInfo;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainAuthInfo;
|
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.DomainTransactionRecord;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.tld.Registry;
|
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.DomainTransferData;
|
||||||
import google.registry.model.transfer.TransferResponse;
|
import google.registry.model.transfer.TransferResponse;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -1202,6 +1208,117 @@ class DomainTransferRequestFlowTest
|
||||||
runTest("domain_transfer_request_fee.xml", UserPrivileges.SUPERUSER, RICH_DOMAIN_MAP);
|
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) {
|
private void runWrongCurrencyTest(Map<String, String> substitutions) {
|
||||||
Map<String, String> fullSubstitutions = Maps.newHashMap();
|
Map<String, String> fullSubstitutions = Maps.newHashMap();
|
||||||
fullSubstitutions.putAll(substitutions);
|
fullSubstitutions.putAll(substitutions);
|
||||||
|
|
|
@ -33,10 +33,7 @@
|
||||||
<fee:currency>USD</fee:currency>
|
<fee:currency>USD</fee:currency>
|
||||||
<fee:command>transfer</fee:command>
|
<fee:command>transfer</fee:command>
|
||||||
<fee:period unit="y">1</fee:period>
|
<fee:period unit="y">1</fee:period>
|
||||||
<!-- TODO(mcilwain): This should be non-premium once transfer flow
|
<fee:fee description="renew">%RENEWPRICE%</fee:fee>
|
||||||
changes are made. -->
|
|
||||||
<fee:fee description="renew">100.00</fee:fee>
|
|
||||||
<fee:class>premium</fee:class>
|
|
||||||
</fee:cd>
|
</fee:cd>
|
||||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||||
<fee:name>rich.example</fee:name>
|
<fee:name>rich.example</fee:name>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<transfer op="approve">
|
<transfer op="approve">
|
||||||
<domain:transfer
|
<domain:transfer
|
||||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>example.extra</domain:name>
|
<domain:name>%DOMAIN%</domain:name>
|
||||||
</domain:transfer>
|
</domain:transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>ABC-12345</clTRID>
|
<clTRID>ABC-12345</clTRID>
|
Loading…
Add table
Reference in a new issue