Registrars can use this flow to manually extend the length of a registration, instead of
+ * relying on domain auto-renewal (where the registry performs an automatic one-year renewal at the
+ * instant a domain would expire).
+ *
+ *
ICANN prohibits any registration from being longer than ten years so if the request would
+ * result in a registration greater than ten years long it will fail. In practice this means it's
+ * impossible to request a ten year renewal, since that will always cause the new registration to be
+ * longer than 10 years unless it comes in at the exact millisecond that the domain would have
+ * expired.
+ *
+ * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
- * @error {@link google.registry.flows.ResourceMutateFlow.ResourceDoesNotExistException}
- * @error {@link google.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
+ * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException}
* @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
* @error {@link DomainFlowUtils.FeesMismatchException}
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
+ * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
* @error {@link DomainRenewFlow.DomainHasPendingTransferException}
* @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException}
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
*/
-public class DomainRenewFlow extends OwnedResourceMutateFlow {
+public final class DomainRenewFlow extends LoggedInFlow implements TransactionalFlow {
- private static final Set RENEW_DISALLOWED_STATUSES = ImmutableSet.of(
+ private static final ImmutableSet RENEW_DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.CLIENT_RENEW_PROHIBITED,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_RENEW_PROHIBITED);
- protected FeeTransformCommandExtension feeRenew;
- protected Money renewCost;
-
- protected Optional extraFlowLogic;
-
+ @Inject ResourceCommand resourceCommand;
+ @Inject Optional authInfo;
+ @Inject @ClientId String clientId;
+ @Inject @TargetId String targetId;
+ @Inject HistoryEntry.Builder historyBuilder;
@Inject DomainRenewFlow() {}
@Override
- protected Set getDisallowedStatuses() {
- return RENEW_DISALLOWED_STATUSES;
- }
-
- @Override
- public final void initResourceCreateOrMutateFlow() throws EppException {
+ protected final void initLoggedInFlow() throws EppException {
+ registerExtensions(MetadataExtension.class);
registerExtensions(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
- feeRenew =
+ }
+
+ @Override
+ public final EppOutput run() throws EppException {
+ Renew command = (Renew) resourceCommand;
+ // Loads the target resource if it exists
+ DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
+ verifyRenewAllowed(authInfo, existingDomain, command);
+ int years = command.getPeriod().getValue();
+ Money renewCost = getDomainRenewCost(targetId, now, years);
+ FeeTransformCommandExtension feeRenew =
eppInput.getFirstExtensionOfClasses(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
- extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource);
- }
-
- @Override
- protected void verifyMutationOnOwnedResourceAllowed() throws EppException {
- checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
- // Verify that the resource does not have a pending transfer on it.
- if (existingResource.getTransferData().getTransferStatus() == TransferStatus.PENDING) {
- throw new DomainHasPendingTransferException(targetId);
+ validateFeeChallenge(targetId, existingDomain.getTld(), now, feeRenew, renewCost);
+ HistoryEntry historyEntry = historyBuilder
+ .setType(HistoryEntry.Type.DOMAIN_RENEW)
+ .setPeriod(command.getPeriod())
+ .setModificationTime(now)
+ .setParent(Key.create(existingDomain))
+ .build();
+ DateTime oldExpirationTime = existingDomain.getRegistrationExpirationTime();
+ DateTime newExpirationTime = leapSafeAddYears(oldExpirationTime, years); // Uncapped
+ if (extendRegistrationWithCap(now, oldExpirationTime, years).isBefore(newExpirationTime)) {
+ throw new ExceedsMaxRegistrationYearsException();
}
- verifyUnitIsYears(command.getPeriod());
- // If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
- if (!command.getCurrentExpirationDate().equals(
- existingResource.getRegistrationExpirationTime().toLocalDate())) {
- throw new IncorrectCurrentExpirationDateException();
- }
- renewCost = getDomainRenewCost(targetId, now, command.getPeriod().getValue());
- validateFeeChallenge(
- targetId, existingResource.getTld(), now, feeRenew, renewCost);
- }
-
- @Override
- protected DomainResource createOrMutateResource() throws EppException {
- DateTime newExpirationTime = leapSafeAddYears(
- existingResource.getRegistrationExpirationTime(), command.getPeriod().getValue());
+ String tld = existingDomain.getTld();
// Bill for this explicit renew itself.
- BillingEvent.OneTime explicitRenewEvent = new BillingEvent.OneTime.Builder()
- .setReason(Reason.RENEW)
- .setTargetId(targetId)
- .setClientId(getClientId())
- .setPeriodYears(command.getPeriod().getValue())
- .setCost(checkNotNull(renewCost))
- .setEventTime(now)
- .setBillingTime(
- now.plus(Registry.get(existingResource.getTld()).getRenewGracePeriodLength()))
+ BillingEvent.OneTime explicitRenewEvent =
+ createRenewBillingEvent(tld, renewCost, years, historyEntry);
+ // Create a new autorenew billing event and poll message starting at the new expiration time.
+ BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingDomain)
+ .setEventTime(newExpirationTime)
+ .setParent(historyEntry)
+ .build();
+ PollMessage.Autorenew newAutorenewPollMessage = newAutorenewPollMessage(existingDomain)
+ .setEventTime(newExpirationTime)
.setParent(historyEntry)
.build();
// End the old autorenew billing event and poll message now. This may delete the poll message.
- updateAutorenewRecurrenceEndTime(existingResource, now);
- // Create a new autorenew billing event and poll message starting at the new expiration time.
- BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingResource)
- .setEventTime(newExpirationTime)
- .setParent(historyEntry)
- .build();
- PollMessage.Autorenew newAutorenewPollMessage = newAutorenewPollMessage(existingResource)
- .setEventTime(newExpirationTime)
- .setParent(historyEntry)
- .build();
-
+ updateAutorenewRecurrenceEndTime(existingDomain, now);
// Handle extra flow logic, if any.
+ Optional extraFlowLogic =
+ RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain);
if (extraFlowLogic.isPresent()) {
extraFlowLogic.get().performAdditionalDomainRenewLogic(
- existingResource,
- getClientId(),
- now,
- command.getPeriod().getValue(),
- eppInput,
- historyEntry);
+ existingDomain, clientId, now, years, eppInput, historyEntry);
+ extraFlowLogic.get().commitAdditionalLogicChanges();
}
-
- ofy().save().