Properly handle restore fees on domain checks (#646)

* Properly handle restore command fees for domain checks

* Get tests working and handle fee classes better

* Remove unused ImmutableSet imports

* Fix code review concerns, mostly surrounding immutability

* Rename more targetIds and make them immutable too

* Merge remote-tracking branch 'upstream/master' into domain-check-restore-fees

* Fix Javadoc formatting
This commit is contained in:
Ben McIlwain 2020-06-26 10:59:46 -04:00 committed by GitHub
parent b7b4658d0c
commit 660b2af990
41 changed files with 1290 additions and 185 deletions

View file

@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -33,8 +34,6 @@ import google.registry.model.eppoutput.CheckData.ContactCheckData;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.util.Clock; import google.registry.util.Clock;
import java.util.List;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -59,9 +58,10 @@ public final class ContactCheckFlow implements Flow {
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); ImmutableList<String> targetIds = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(targetIds, maxChecks);
Set<String> existingIds = checkResourcesExist(ContactResource.class, targetIds, clock.nowUtc()); ImmutableSet<String> existingIds =
checkResourcesExist(ContactResource.class, targetIds, clock.nowUtc());
ImmutableList.Builder<ContactCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<ContactCheck> checks = new ImmutableList.Builder<>();
for (String id : targetIds) { for (String id : targetIds) {
boolean unused = !existingIds.contains(id); boolean unused = !existingIds.contains(id);

View file

@ -15,6 +15,8 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
@ -26,7 +28,6 @@ import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName; import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables; import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.EppResourceUtils.checkResourcesExist;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.model.registry.label.ReservationType.getTypeOfHighestSeverity; import static google.registry.model.registry.label.ReservationType.getTypeOfHighestSeverity;
@ -48,11 +49,14 @@ import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponsePar
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData; import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData;
import google.registry.flows.domain.token.AllocationTokenDomainCheckResults; import google.registry.flows.domain.token.AllocationTokenDomainCheckResults;
import google.registry.flows.domain.token.AllocationTokenFlowUtils; import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.model.EppResource;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainCommand.Check;
import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtension;
import google.registry.model.domain.fee.FeeCheckCommandExtensionItem; import google.registry.model.domain.fee.FeeCheckCommandExtensionItem;
import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem;
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06;
import google.registry.model.domain.launch.LaunchCheckExtension; import google.registry.model.domain.launch.LaunchCheckExtension;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.domain.token.AllocationTokenExtension;
@ -62,14 +66,14 @@ import google.registry.model.eppoutput.CheckData.DomainCheck;
import google.registry.model.eppoutput.CheckData.DomainCheckData; import google.registry.model.eppoutput.CheckData.DomainCheckData;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservationType; import google.registry.model.registry.label.ReservationType;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.persistence.VKey;
import google.registry.util.Clock; import google.registry.util.Clock;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -110,14 +114,20 @@ public final class DomainCheckFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput; @Inject EppInput eppInput;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @Config("maxChecks") int maxChecks;
@Inject
@Config("maxChecks")
int maxChecks;
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject Clock clock; @Inject Clock clock;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils; @Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject DomainCheckFlowCustomLogic flowCustomLogic; @Inject DomainCheckFlowCustomLogic flowCustomLogic;
@Inject DomainPricingLogic pricingLogic; @Inject DomainPricingLogic pricingLogic;
@Inject DomainCheckFlow() {}
@Inject
DomainCheckFlow() {}
@Override @Override
public EppResponse run() throws EppException { public EppResponse run() throws EppException {
@ -126,39 +136,41 @@ public final class DomainCheckFlow implements Flow {
flowCustomLogic.beforeValidation(); flowCustomLogic.beforeValidation();
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); ImmutableList<String> domainNames = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(domainNames, maxChecks);
DateTime now = clock.nowUtc(); DateTime now = clock.nowUtc();
ImmutableMap.Builder<String, InternetDomainName> domains = new ImmutableMap.Builder<>(); ImmutableMap.Builder<String, InternetDomainName> parsedDomainsBuilder =
new ImmutableMap.Builder<>();
// Only check that the registrar has access to a TLD the first time it is encountered // Only check that the registrar has access to a TLD the first time it is encountered
Set<String> seenTlds = new HashSet<>(); Set<String> seenTlds = new HashSet<>();
for (String targetId : ImmutableSet.copyOf(targetIds)) { for (String domainName : ImmutableSet.copyOf(domainNames)) {
InternetDomainName domainName = validateDomainName(targetId); InternetDomainName parsedDomain = validateDomainName(domainName);
validateDomainNameWithIdnTables(domainName); validateDomainNameWithIdnTables(parsedDomain);
// This validation is moderately expensive, so cache the results. // This validation is moderately expensive, so cache the results.
domains.put(targetId, domainName); parsedDomainsBuilder.put(domainName, parsedDomain);
String tld = domainName.parent().toString(); String tld = parsedDomain.parent().toString();
boolean tldFirstTimeSeen = seenTlds.add(tld); boolean tldFirstTimeSeen = seenTlds.add(tld);
if (tldFirstTimeSeen && !isSuperuser) { if (tldFirstTimeSeen && !isSuperuser) {
checkAllowedAccessToTld(clientId, tld); checkAllowedAccessToTld(clientId, tld);
verifyNotInPredelegation(Registry.get(tld), now); verifyNotInPredelegation(Registry.get(tld), now);
} }
} }
ImmutableMap<String, InternetDomainName> domainNames = domains.build(); ImmutableMap<String, InternetDomainName> parsedDomains = parsedDomainsBuilder.build();
flowCustomLogic.afterValidation( flowCustomLogic.afterValidation(
DomainCheckFlowCustomLogic.AfterValidationParameters.newBuilder() DomainCheckFlowCustomLogic.AfterValidationParameters.newBuilder()
.setDomainNames(domainNames) .setDomainNames(parsedDomains)
// TODO: Use as of date from fee extension v0.12 instead of now, if specified. // TODO: Use as of date from fee extension v0.12 instead of now, if specified.
.setAsOfDate(now) .setAsOfDate(now)
.build()); .build());
Set<String> existingIds = checkResourcesExist(DomainBase.class, targetIds, now); ImmutableMap<String, ForeignKeyIndex<DomainBase>> existingDomains =
ForeignKeyIndex.load(DomainBase.class, domainNames, now);
Optional<AllocationTokenExtension> allocationTokenExtension = Optional<AllocationTokenExtension> allocationTokenExtension =
eppInput.getSingleExtension(AllocationTokenExtension.class); eppInput.getSingleExtension(AllocationTokenExtension.class);
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults = Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
allocationTokenExtension.map( allocationTokenExtension.map(
tokenExtension -> tokenExtension ->
allocationTokenFlowUtils.checkDomainsWithToken( allocationTokenFlowUtils.checkDomainsWithToken(
ImmutableList.copyOf(domainNames.values()), ImmutableList.copyOf(parsedDomains.values()),
tokenExtension.getAllocationToken(), tokenExtension.getAllocationToken(),
clientId, clientId,
now)); now));
@ -173,18 +185,18 @@ public final class DomainCheckFlow implements Flow {
.orElse(ImmutableMap.of()); .orElse(ImmutableMap.of());
Optional<AllocationToken> allocationToken = Optional<AllocationToken> allocationToken =
tokenDomainCheckResults.flatMap(AllocationTokenDomainCheckResults::token); tokenDomainCheckResults.flatMap(AllocationTokenDomainCheckResults::token);
for (String targetId : targetIds) { for (String domainName : domainNames) {
Optional<String> message = Optional<String> message =
getMessageForCheck( getMessageForCheck(
domainNames.get(targetId), parsedDomains.get(domainName),
existingIds, existingDomains,
domainCheckResults, domainCheckResults,
tldStates, tldStates,
allocationToken); allocationToken);
boolean isAvailable = !message.isPresent(); boolean isAvailable = !message.isPresent();
checksBuilder.add(DomainCheck.create(isAvailable, targetId, message.orElse(null))); checksBuilder.add(DomainCheck.create(isAvailable, domainName, message.orElse(null)));
if (isAvailable) { if (isAvailable) {
availableDomains.add(targetId); availableDomains.add(domainName);
} }
} }
BeforeResponseReturnData responseData = BeforeResponseReturnData responseData =
@ -193,7 +205,11 @@ public final class DomainCheckFlow implements Flow {
.setDomainChecks(checksBuilder.build()) .setDomainChecks(checksBuilder.build())
.setResponseExtensions( .setResponseExtensions(
getResponseExtensions( getResponseExtensions(
domainNames, availableDomains.build(), now, allocationToken)) parsedDomains,
existingDomains,
availableDomains.build(),
now,
allocationToken))
.setAsOfDate(now) .setAsOfDate(now)
.build()); .build());
return responseBuilder return responseBuilder
@ -204,11 +220,11 @@ public final class DomainCheckFlow implements Flow {
private Optional<String> getMessageForCheck( private Optional<String> getMessageForCheck(
InternetDomainName domainName, InternetDomainName domainName,
Set<String> existingIds, ImmutableMap<String, ForeignKeyIndex<DomainBase>> existingDomains,
ImmutableMap<InternetDomainName, String> tokenCheckResults, ImmutableMap<InternetDomainName, String> tokenCheckResults,
Map<String, TldState> tldStates, ImmutableMap<String, TldState> tldStates,
Optional<AllocationToken> allocationToken) { Optional<AllocationToken> allocationToken) {
if (existingIds.contains(domainName.toString())) { if (existingDomains.containsKey(domainName.toString())) {
return Optional.of("In use"); return Optional.of("In use");
} }
TldState tldState = tldStates.get(domainName.parent().toString()); TldState tldState = tldStates.get(domainName.parent().toString());
@ -228,6 +244,7 @@ public final class DomainCheckFlow implements Flow {
/** Handle the fee check extension. */ /** Handle the fee check extension. */
private ImmutableList<? extends ResponseExtension> getResponseExtensions( private ImmutableList<? extends ResponseExtension> getResponseExtensions(
ImmutableMap<String, InternetDomainName> domainNames, ImmutableMap<String, InternetDomainName> domainNames,
ImmutableMap<String, ForeignKeyIndex<DomainBase>> existingDomains,
ImmutableSet<String> availableDomains, ImmutableSet<String> availableDomains,
DateTime now, DateTime now,
Optional<AllocationToken> allocationToken) Optional<AllocationToken> allocationToken)
@ -240,6 +257,9 @@ public final class DomainCheckFlow implements Flow {
FeeCheckCommandExtension<?, ?> feeCheck = feeCheckOpt.get(); FeeCheckCommandExtension<?, ?> feeCheck = feeCheckOpt.get();
ImmutableList.Builder<FeeCheckResponseExtensionItem> responseItems = ImmutableList.Builder<FeeCheckResponseExtensionItem> responseItems =
new ImmutableList.Builder<>(); new ImmutableList.Builder<>();
ImmutableMap<String, EppResource> domainObjs =
loadDomainsForRestoreChecks(feeCheck, domainNames, existingDomains);
for (FeeCheckCommandExtensionItem feeCheckItem : feeCheck.getItems()) { for (FeeCheckCommandExtensionItem feeCheckItem : feeCheck.getItems()) {
for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) { for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) {
FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder(); FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder();
@ -247,7 +267,7 @@ public final class DomainCheckFlow implements Flow {
feeCheckItem, feeCheckItem,
builder, builder,
domainNames.get(domainName), domainNames.get(domainName),
Optional.empty(), Optional.ofNullable((DomainBase) domainObjs.get(domainName)),
feeCheck.getCurrency(), feeCheck.getCurrency(),
now, now,
pricingLogic, pricingLogic,
@ -259,12 +279,57 @@ public final class DomainCheckFlow implements Flow {
return ImmutableList.of(feeCheck.createResponse(responseItems.build())); return ImmutableList.of(feeCheck.createResponse(responseItems.build()));
} }
/**
* Loads and returns all existing domains that are having restore fees checked.
*
* <p>This is necessary so that we can check their expiration dates to determine if a one-year
* renewal is part of the cost of a restore.
*
* <p>This may be resource-intensive for large checks of many restore fees, but those are
* comparatively rare, and we are at least using an in-memory cache. Also this will get a lot
* nicer in Cloud SQL when we can SELECT just the fields we want rather than having to load the
* entire entity.
*/
private ImmutableMap<String, EppResource> loadDomainsForRestoreChecks(
FeeCheckCommandExtension<?, ?> feeCheck,
ImmutableMap<String, InternetDomainName> domainNames,
ImmutableMap<String, ForeignKeyIndex<DomainBase>> existingDomains) {
ImmutableList<String> restoreCheckDomains;
if (feeCheck instanceof FeeCheckCommandExtensionV06) {
// The V06 fee extension supports specifying the command fees to check on a per-domain basis.
restoreCheckDomains =
feeCheck.getItems().stream()
.filter(fc -> fc.getCommandName() == CommandName.RESTORE)
.map(FeeCheckCommandExtensionItem::getDomainName)
.collect(toImmutableList());
} else if (feeCheck.getItems().stream()
.anyMatch(fc -> fc.getCommandName() == CommandName.RESTORE)) {
// The more recent fee extension versions support specifying the command fees to check only on
// the overall domain check, not per-domain.
restoreCheckDomains = ImmutableList.copyOf(domainNames.keySet());
} else {
// Fall-through case for more recent fee extension versions when the restore fee isn't being
// checked.
restoreCheckDomains = ImmutableList.of();
}
// Filter down to just domains we know exist and then use the EppResource cache to load them.
ImmutableMap<String, VKey<DomainBase>> existingDomainsToLoad =
restoreCheckDomains.stream()
.filter(existingDomains::containsKey)
.collect(toImmutableMap(d -> d, d -> existingDomains.get(d).getResourceKey()));
ImmutableMap<VKey<? extends EppResource>, EppResource> loadedDomains =
EppResource.loadCached(ImmutableList.copyOf(existingDomainsToLoad.values()));
return ImmutableMap.copyOf(
Maps.transformEntries(existingDomainsToLoad, (k, v) -> loadedDomains.get(v)));
}
/** /**
* Return the domains to be checked for a particular fee check item. Some versions of the fee * Return the domains to be checked for a particular fee check item. Some versions of the fee
* extension specify the domain name in the extension item, while others use the list of domain * extension specify the domain name in the extension item, while others use the list of domain
* names from the regular check domain availability list. * names from the regular check domain availability list.
*/ */
private Set<String> getDomainNamesToCheckForFee( private ImmutableSet<String> getDomainNamesToCheckForFee(
FeeCheckCommandExtensionItem feeCheckItem, ImmutableSet<String> availabilityCheckDomains) FeeCheckCommandExtensionItem feeCheckItem, ImmutableSet<String> availabilityCheckDomains)
throws OnlyCheckedNamesCanBeFeeCheckedException { throws OnlyCheckedNamesCanBeFeeCheckedException {
if (feeCheckItem.isDomainNameSupported()) { if (feeCheckItem.isDomainNameSupported()) {

View file

@ -48,7 +48,6 @@ import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tmch.ClaimsListShard; import google.registry.model.tmch.ClaimsListShard;
import google.registry.util.Clock; import google.registry.util.Clock;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -87,14 +86,14 @@ public final class DomainClaimsCheckFlow implements Flow {
if (eppInput.getSingleExtension(AllocationTokenExtension.class).isPresent()) { if (eppInput.getSingleExtension(AllocationTokenExtension.class).isPresent()) {
throw new DomainClaimsCheckNotAllowedWithAllocationTokens(); throw new DomainClaimsCheckNotAllowedWithAllocationTokens();
} }
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); ImmutableList<String> domainNames = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(domainNames, maxChecks);
Set<String> seenTlds = new HashSet<>(); Set<String> seenTlds = new HashSet<>();
ImmutableList.Builder<LaunchCheck> launchChecksBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<LaunchCheck> launchChecksBuilder = new ImmutableList.Builder<>();
for (String targetId : ImmutableSet.copyOf(targetIds)) { for (String domainName : ImmutableSet.copyOf(domainNames)) {
InternetDomainName domainName = validateDomainName(targetId); InternetDomainName parsedDomain = validateDomainName(domainName);
validateDomainNameWithIdnTables(domainName); validateDomainNameWithIdnTables(parsedDomain);
String tld = domainName.parent().toString(); String tld = parsedDomain.parent().toString();
// Only validate access to a TLD the first time it is encountered. // Only validate access to a TLD the first time it is encountered.
if (seenTlds.add(tld)) { if (seenTlds.add(tld)) {
if (!isSuperuser) { if (!isSuperuser) {
@ -105,10 +104,10 @@ public final class DomainClaimsCheckFlow implements Flow {
verifyClaimsPeriodNotEnded(registry, now); verifyClaimsPeriodNotEnded(registry, now);
} }
} }
Optional<String> claimKey = ClaimsListShard.get().getClaimKey(domainName.parts().get(0)); Optional<String> claimKey = ClaimsListShard.get().getClaimKey(parsedDomain.parts().get(0));
launchChecksBuilder.add( launchChecksBuilder.add(
LaunchCheck.create( LaunchCheck.create(
LaunchCheckName.create(claimKey.isPresent(), targetId), claimKey.orElse(null))); LaunchCheckName.create(claimKey.isPresent(), domainName), claimKey.orElse(null)));
} }
return responseBuilder return responseBuilder
.setOnlyExtension(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build())) .setOnlyExtension(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build()))

View file

@ -17,6 +17,7 @@ package google.registry.flows.domain;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap; import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
@ -32,7 +33,9 @@ import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABIL
import static google.registry.model.registry.Registry.TldState.PREDELEGATION; import static google.registry.model.registry.Registry.TldState.PREDELEGATION;
import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD; import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED; import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT; import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE; import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@ -87,6 +90,7 @@ import google.registry.model.domain.DomainCommand.InvalidReferencesException;
import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainCommand.Update;
import google.registry.model.domain.ForeignKeyedDesignatedContact; import google.registry.model.domain.ForeignKeyedDesignatedContact;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.fee.BaseFee;
import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.Credit;
import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.Fee;
@ -152,9 +156,7 @@ public class DomainFlowUtils {
/** Reservation types that are only allowed in sunrise by policy. */ /** Reservation types that are only allowed in sunrise by policy. */
public static final ImmutableSet<ReservationType> TYPES_ALLOWED_FOR_CREATE_ONLY_IN_SUNRISE = public static final ImmutableSet<ReservationType> TYPES_ALLOWED_FOR_CREATE_ONLY_IN_SUNRISE =
Sets.immutableEnumSet( Sets.immutableEnumSet(ALLOWED_IN_SUNRISE, NAME_COLLISION);
ReservationType.ALLOWED_IN_SUNRISE,
ReservationType.NAME_COLLISION);
/** Warning message for allocation of collision domains in sunrise. */ /** Warning message for allocation of collision domains in sunrise. */
public static final String COLLISION_MESSAGE = public static final String COLLISION_MESSAGE =
@ -583,15 +585,15 @@ public class DomainFlowUtils {
builder builder
.setCommand(feeRequest.getCommandName(), feeRequest.getPhase(), feeRequest.getSubphase()) .setCommand(feeRequest.getCommandName(), feeRequest.getPhase(), feeRequest.getSubphase())
.setCurrencyIfSupported(registry.getCurrency()) .setCurrencyIfSupported(registry.getCurrency())
.setPeriod(feeRequest.getPeriod()) .setPeriod(feeRequest.getPeriod());
.setClass(pricingLogic.getFeeClass(domainNameString, now).orElse(null));
String feeClass = null;
ImmutableList<Fee> fees = ImmutableList.of(); ImmutableList<Fee> fees = ImmutableList.of();
switch (feeRequest.getCommandName()) { switch (feeRequest.getCommandName()) {
case CREATE: case CREATE:
// Don't return a create price for reserved names. // Don't return a create price for reserved names.
if (isReserved(domainName, isSunrise) && !isAvailable) { if (isReserved(domainName, isSunrise) && !isAvailable) {
builder.setClass("reserved"); // Override whatever class we've set above. feeClass = "reserved";
builder.setAvailIfSupported(false); builder.setAvailIfSupported(false);
builder.setReasonIfSupported("reserved"); builder.setReasonIfSupported("reserved");
} else { } else {
@ -616,15 +618,11 @@ public class DomainFlowUtils {
throw new RestoresAreAlwaysForOneYearException(); throw new RestoresAreAlwaysForOneYearException();
} }
builder.setAvailIfSupported(true); builder.setAvailIfSupported(true);
// The domain object is present only on domain info commands, not on domain check commands, // Domains that never existed, or that used to exist but have completed the entire deletion
// because check commands can query up to 50 domains and it isn't performant to load them // process, don't count as expired for the purposes of requiring an added year of renewal on
// all. So, only on info commands can we actually determine if we should include the renewal // restore because they can't be restored in the first place.
// fee because the domain needs to have been loaded in order to know its expiration time. We
// default to including the renewal fee on domain checks because typically most domains are
// deleted during the autorenew grace period and thus if restored will require a renewal,
// but this is just a best guess.
boolean isExpired = boolean isExpired =
!domain.isPresent() || domain.get().getRegistrationExpirationTime().isBefore(now); domain.isPresent() && domain.get().getRegistrationExpirationTime().isBefore(now);
fees = pricingLogic.getRestorePrice(registry, domainNameString, now, isExpired).getFees(); fees = pricingLogic.getRestorePrice(registry, domainNameString, now, isExpired).getFees();
break; break;
case TRANSFER: case TRANSFER:
@ -642,6 +640,23 @@ public class DomainFlowUtils {
throw new UnknownFeeCommandException(feeRequest.getUnparsedCommandName()); throw new UnknownFeeCommandException(feeRequest.getUnparsedCommandName());
} }
if (feeClass == null) {
// Calculate and set the correct fee class based on whether the name is a collision name or we
// are returning any premium fees, but only if the fee class isn't already set (i.e. because
// the domain is reserved, which overrides any other classes).
boolean isNameCollisionInSunrise =
registry.getTldState(now).equals(START_DATE_SUNRISE)
&& getReservationTypes(domainName).contains(NAME_COLLISION);
boolean isPremium = fees.stream().anyMatch(BaseFee::isPremium);
feeClass =
emptyToNull(
Joiner.on('-')
.skipNulls()
.join(
isPremium ? "premium" : null, isNameCollisionInSunrise ? "collision" : null));
}
builder.setClass(feeClass);
// Set the fees, and based on the validDateRange of the fees, set the notAfterDate. // Set the fees, and based on the validDateRange of the fees, set the notAfterDate.
if (!fees.isEmpty()) { if (!fees.isEmpty()) {
builder.setFees(fees); builder.setFees(fees);

View file

@ -15,7 +15,6 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency; import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency;
import static google.registry.pricing.PricingEngineProxy.getDomainFeeClass;
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
@ -186,11 +185,6 @@ public final class DomainPricingLogic {
.build()); .build());
} }
/** Returns the fee class for a given domain and date. */
public Optional<String> getFeeClass(String domainName, DateTime dateTime) {
return getDomainFeeClass(domainName, dateTime);
}
/** Returns the domain create cost with allocation-token-related discounts applied. */ /** Returns the domain create cost with allocation-token-related discounts applied. */
private Money getDomainCreateCostWithDiscount( private Money getDomainCreateCostWithDiscount(
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken) DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)

View file

@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -33,8 +34,6 @@ import google.registry.model.host.HostCommand.Check;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.util.Clock; import google.registry.util.Clock;
import java.util.List;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -59,13 +58,14 @@ public final class HostCheckFlow implements Flow {
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); ImmutableList<String> hostnames = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(hostnames, maxChecks);
Set<String> existingIds = checkResourcesExist(HostResource.class, targetIds, clock.nowUtc()); ImmutableSet<String> existingIds =
checkResourcesExist(HostResource.class, hostnames, clock.nowUtc());
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
for (String id : targetIds) { for (String hostname : hostnames) {
boolean unused = !existingIds.contains(id); boolean unused = !existingIds.contains(hostname);
checks.add(HostCheck.create(unused, id, unused ? null : "In use")); checks.add(HostCheck.create(unused, hostname, unused ? null : "In use"));
} }
return responseBuilder.setResData(HostCheckData.create(checks.build())).build(); return responseBuilder.setResData(HostCheckData.create(checks.build())).build();
} }

View file

@ -364,9 +364,11 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
/** /**
* A limited size, limited time cache for EPP resource entities. * A limited size, limited time cache for EPP resource entities.
* *
* <p>This is only used to cache contacts and hosts for the purposes of checking whether they are * <p>This is used to cache contacts and hosts for the purposes of checking whether they are
* deleted or in pending delete during a few domain flows. Any operations on contacts and hosts * deleted or in pending delete during a few domain flows, and also to cache domains for the
* directly should of course never use the cache. * purpose of determining restore fees in domain checks. Any mutating operations directly on EPP
* resources should of course never use the cache as they always need perfectly up-to-date
* information.
*/ */
@NonFinalForTesting @NonFinalForTesting
private static LoadingCache<VKey<? extends EppResource>, EppResource> cacheEppResources = private static LoadingCache<VKey<? extends EppResource>, EppResource> cacheEppResources =

View file

@ -23,6 +23,7 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf; import static google.registry.util.DateTimeUtils.latestOf;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result; import com.googlecode.objectify.Result;
@ -45,7 +46,6 @@ import google.registry.model.transfer.TransferStatus;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -169,7 +169,7 @@ public final class EppResourceUtils {
* @param uniqueIds a list of ids to match * @param uniqueIds a list of ids to match
* @param now the logical time of the check * @param now the logical time of the check
*/ */
public static <T extends EppResource> Set<String> checkResourcesExist( public static <T extends EppResource> ImmutableSet<String> checkResourcesExist(
Class<T> clazz, List<String> uniqueIds, final DateTime now) { Class<T> clazz, List<String> uniqueIds, final DateTime now) {
return ForeignKeyIndex.load(clazz, uniqueIds, now).keySet(); return ForeignKeyIndex.load(clazz, uniqueIds, now).keySet();
} }

View file

@ -41,7 +41,6 @@ import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
@ -447,7 +446,8 @@ public class DomainCommand {
private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached( private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached(
final Set<String> foreignKeys, final Class<T> clazz, final DateTime now) final Set<String> foreignKeys, final Class<T> clazz, final DateTime now)
throws InvalidReferencesException { throws InvalidReferencesException {
Map<String, ForeignKeyIndex<T>> fkis = ForeignKeyIndex.loadCached(clazz, foreignKeys, now); ImmutableMap<String, ForeignKeyIndex<T>> fkis =
ForeignKeyIndex.loadCached(clazz, foreignKeys, now);
if (!fkis.keySet().equals(foreignKeys)) { if (!fkis.keySet().equals(foreignKeys)) {
throw new InvalidReferencesException( throw new InvalidReferencesException(
clazz, ImmutableSet.copyOf(difference(foreignKeys, fkis.keySet()))); clazz, ImmutableSet.copyOf(difference(foreignKeys, fkis.keySet())));

View file

@ -15,7 +15,6 @@
package google.registry.model.domain.fee; package google.registry.model.domain.fee;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.eppinput.EppInput.CommandExtension; import google.registry.model.eppinput.EppInput.CommandExtension;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
@ -40,7 +39,7 @@ public interface FeeCheckCommandExtension<
*/ */
CurrencyUnit getCurrency(); CurrencyUnit getCurrency();
ImmutableSet<C> getItems(); ImmutableList<C> getItems();
R createResponse(ImmutableList<? extends FeeCheckResponseExtensionItem> items); R createResponse(ImmutableList<? extends FeeCheckResponseExtensionItem> items);
} }

View file

@ -17,11 +17,10 @@ package google.registry.model.domain.fee06;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtension;
import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem;
import java.util.Set; import java.util.List;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
@ -34,7 +33,7 @@ public class FeeCheckCommandExtensionV06 extends ImmutableObject
FeeCheckResponseExtensionV06> { FeeCheckResponseExtensionV06> {
@XmlElement(name = "domain") @XmlElement(name = "domain")
Set<FeeCheckCommandExtensionItemV06> items; List<FeeCheckCommandExtensionItemV06> items;
@Override @Override
public CurrencyUnit getCurrency() { public CurrencyUnit getCurrency() {
@ -42,7 +41,7 @@ public class FeeCheckCommandExtensionV06 extends ImmutableObject
} }
@Override @Override
public ImmutableSet<FeeCheckCommandExtensionItemV06> getItems() { public ImmutableList<FeeCheckCommandExtensionItemV06> getItems() {
return nullToEmptyImmutableCopy(items); return nullToEmptyImmutableCopy(items);
} }

View file

@ -17,7 +17,6 @@ package google.registry.model.domain.fee11;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtension;
@ -69,8 +68,8 @@ public class FeeCheckCommandExtensionV11 extends ImmutableObject
} }
@Override @Override
public ImmutableSet<FeeCheckCommandExtensionItemV11> getItems() { public ImmutableList<FeeCheckCommandExtensionItemV11> getItems() {
return ImmutableSet.of(new FeeCheckCommandExtensionItemV11()); return ImmutableList.of(new FeeCheckCommandExtensionItemV11());
} }
@Override @Override

View file

@ -17,11 +17,10 @@ package google.registry.model.domain.fee12;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtension;
import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem;
import java.util.Set; import java.util.List;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
@ -43,10 +42,10 @@ public class FeeCheckCommandExtensionV12 extends ImmutableObject
} }
@XmlElement(name = "command") @XmlElement(name = "command")
Set<FeeCheckCommandExtensionItemV12> items; List<FeeCheckCommandExtensionItemV12> items;
@Override @Override
public ImmutableSet<FeeCheckCommandExtensionItemV12> getItems() { public ImmutableList<FeeCheckCommandExtensionItemV12> getItems() {
return nullToEmptyImmutableCopy(items); return nullToEmptyImmutableCopy(items);
} }

View file

@ -15,7 +15,6 @@
package google.registry.model.index; package google.registry.model.index;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Maps.filterValues;
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration; import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries; import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
@ -178,11 +177,11 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
* <p>The returned map will omit any keys for which the {@link ForeignKeyIndex} doesn't exist or * <p>The returned map will omit any keys for which the {@link ForeignKeyIndex} doesn't exist or
* has been soft deleted. * has been soft deleted.
*/ */
public static <E extends EppResource> Map<String, ForeignKeyIndex<E>> load( public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> load(
Class<E> clazz, Iterable<String> foreignKeys, final DateTime now) { Class<E> clazz, Iterable<String> foreignKeys, final DateTime now) {
return filterValues( return ofy().load().type(mapToFkiClass(clazz)).ids(foreignKeys).entrySet().stream()
ofy().load().type(mapToFkiClass(clazz)).ids(foreignKeys), .filter(e -> now.isBefore(e.getValue().deletionTime))
(ForeignKeyIndex<?> fki) -> now.isBefore(fki.deletionTime)); .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
} }
static final CacheLoader<Key<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> CACHE_LOADER = static final CacheLoader<Key<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> CACHE_LOADER =
@ -249,7 +248,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
* <p>Don't use the cached version of this method unless you really need it for performance * <p>Don't use the cached version of this method unless you really need it for performance
* reasons, and are OK with the trade-offs in loss of transactional consistency. * reasons, and are OK with the trade-offs in loss of transactional consistency.
*/ */
public static <E extends EppResource> Map<String, ForeignKeyIndex<E>> loadCached( public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> loadCached(
Class<E> clazz, Iterable<String> foreignKeys, final DateTime now) { Class<E> clazz, Iterable<String> foreignKeys, final DateTime now) {
if (!RegistryConfig.isEppResourceCachingEnabled()) { if (!RegistryConfig.isEppResourceCachingEnabled()) {
return tm().doTransactionless(() -> load(clazz, foreignKeys, now)); return tm().doTransactionless(() -> load(clazz, foreignKeys, now));
@ -262,10 +261,8 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
// This cast is safe because when we loaded ForeignKeyIndexes above we used type clazz, which // This cast is safe because when we loaded ForeignKeyIndexes above we used type clazz, which
// is scoped to E. // is scoped to E.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, ForeignKeyIndex<E>> fkisFromCache = cacheForeignKeyIndexes ImmutableMap<String, ForeignKeyIndex<E>> fkisFromCache =
.getAll(fkiKeys) cacheForeignKeyIndexes.getAll(fkiKeys).entrySet().stream()
.entrySet()
.stream()
.filter(entry -> entry.getValue().isPresent()) .filter(entry -> entry.getValue().isPresent())
.filter(entry -> now.isBefore(entry.getValue().get().getDeletionTime())) .filter(entry -> now.isBefore(entry.getValue().get().getDeletionTime()))
.collect( .collect(

View file

@ -14,7 +14,6 @@
package google.registry.model.pricing; package google.registry.model.pricing;
import java.util.Optional;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -46,18 +45,12 @@ public interface PremiumPricingEngine {
// create, renew, restore, and transfer. // create, renew, restore, and transfer.
private Money createCost; private Money createCost;
private Money renewCost; private Money renewCost;
private Optional<String> feeClass;
static DomainPrices create( static DomainPrices create(boolean isPremium, Money createCost, Money renewCost) {
boolean isPremium,
Money createCost,
Money renewCost,
Optional<String> feeClass) {
DomainPrices instance = new DomainPrices(); DomainPrices instance = new DomainPrices();
instance.isPremium = isPremium; instance.isPremium = isPremium;
instance.createCost = createCost; instance.createCost = createCost;
instance.renewCost = renewCost; instance.renewCost = renewCost;
instance.feeClass = feeClass;
return instance; return instance;
} }
@ -75,10 +68,5 @@ public interface PremiumPricingEngine {
public Money getRenewCost() { public Money getRenewCost() {
return renewCost; return renewCost;
} }
/** Returns the fee class of the cost (used for the Fee extension). */
public Optional<String> getFeeClass() {
return feeClass;
}
} }
} }

View file

@ -15,14 +15,9 @@
package google.registry.model.pricing; package google.registry.model.pricing;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice; import static google.registry.model.registry.label.PremiumListUtils.getPremiumPrice;
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
import static google.registry.model.registry.label.ReservedList.getReservationTypes;
import static google.registry.util.DomainNameUtils.getTldFromDomainName; import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import com.google.common.base.Joiner;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import java.util.Optional; import java.util.Optional;
@ -44,16 +39,9 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
String label = InternetDomainName.from(fullyQualifiedDomainName).parts().get(0); String label = InternetDomainName.from(fullyQualifiedDomainName).parts().get(0);
Registry registry = Registry.get(checkNotNull(tld, "tld")); Registry registry = Registry.get(checkNotNull(tld, "tld"));
Optional<Money> premiumPrice = getPremiumPrice(label, registry); Optional<Money> premiumPrice = getPremiumPrice(label, registry);
boolean isNameCollisionInSunrise =
registry.getTldState(priceTime).equals(START_DATE_SUNRISE)
&& getReservationTypes(label, tld).contains(NAME_COLLISION);
String feeClass = emptyToNull(Joiner.on('-').skipNulls().join(
premiumPrice.isPresent() ? "premium" : null,
isNameCollisionInSunrise ? "collision" : null));
return DomainPrices.create( return DomainPrices.create(
premiumPrice.isPresent(), premiumPrice.isPresent(),
premiumPrice.orElse(registry.getStandardCreateCost()), premiumPrice.orElse(registry.getStandardCreateCost()),
premiumPrice.orElse(registry.getStandardRenewCost(priceTime)), premiumPrice.orElse(registry.getStandardRenewCost(priceTime)));
Optional.ofNullable(feeClass));
} }
} }

View file

@ -587,8 +587,9 @@ public class Registry extends ImmutableObject implements Buildable {
return Fee.create( return Fee.create(
eapFeeSchedule.getValueAtTime(now).getAmount(), eapFeeSchedule.getValueAtTime(now).getAmount(),
FeeType.EAP, FeeType.EAP,
// An EAP fee counts as premium so the domain's overall Fee doesn't show as standard-priced. // An EAP fee does not count as premium -- it's a separate one-time fee, independent of
true, // which the domain is separately considered standard vs premium depending on renewal price.
false,
validPeriod, validPeriod,
validPeriod.upperEndpoint()); validPeriod.upperEndpoint());
} }

View file

@ -22,7 +22,6 @@ import google.registry.model.pricing.PremiumPricingEngine;
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -52,11 +51,6 @@ public final class PricingEngineProxy {
return getPricesForDomainName(domainName, priceTime).isPremium(); return getPricesForDomainName(domainName, priceTime).isPremium();
} }
/** Returns the fee class of the specified domain name. */
public static Optional<String> getDomainFeeClass(String domainName, DateTime priceTime) {
return getPricesForDomainName(domainName, priceTime).getFeeClass();
}
/** /**
* Returns the full {@link DomainPrices} details for the given domain name by dispatching to the * Returns the full {@link DomainPrices} details for the given domain name by dispatching to the
* appropriate {@link PremiumPricingEngine} based on what is configured for the TLD that the * appropriate {@link PremiumPricingEngine} based on what is configured for the TLD that the

View file

@ -22,6 +22,7 @@ import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRIS
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds; import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.loadRegistrar; import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveDomain; import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistDeletedDomain; import static google.registry.testing.DatastoreHelper.persistDeletedDomain;
import static google.registry.testing.DatastoreHelper.persistPremiumList; import static google.registry.testing.DatastoreHelper.persistPremiumList;
@ -65,6 +66,7 @@ import google.registry.flows.exceptions.TooManyResourceChecksException;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservedList; import google.registry.model.registry.label.ReservedList;
@ -656,6 +658,19 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml"));
} }
@Test
public void testSuccess_thirtyDomains_restoreFees() throws Exception {
// Note that 30 is more than 25, which is the maximum # of entity groups you can enlist in a
// single Datastore transaction (each DomainBase entity is in a separate entity group).
// It's also pretty common for registrars to send large domain checks.
setEppInput("domain_check_fee_thirty_domains.xml");
// example-00.tld won't exist and thus will not have a renew fee like the others.
for (int i = 1; i < 30; i++) {
persistPendingDeleteDomain(String.format("example-%02d.tld", i));
}
runFlowAssertResponse(loadFile("domain_check_fee_response_thirty_domains.xml"));
}
/** /**
* Test commands for create, renew, transfer, restore and update with implicit period and * Test commands for create, renew, transfer, restore and update with implicit period and
* currency. * currency.
@ -702,6 +717,27 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06.xml")); runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06.xml"));
} }
@Test
public void testFeeExtension_premium_eap_v06_withRenewalOnRestore() throws Exception {
createTld("example");
setEppInput("domain_check_fee_premium_v06.xml");
clock.setTo(DateTime.parse("2010-01-01T10:00:00Z"));
persistPendingDeleteDomain("rich.example");
persistResource(
Registry.get("example")
.asBuilder()
.setEapFeeSchedule(
new ImmutableSortedMap.Builder<DateTime, Money>(Ordering.natural())
.put(START_OF_TIME, Money.of(USD, 0))
.put(clock.nowUtc().minusDays(1), Money.of(USD, 100))
.put(clock.nowUtc().plusDays(1), Money.of(USD, 50))
.put(clock.nowUtc().plusDays(2), Money.of(USD, 0))
.build())
.build());
runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06_with_renewal.xml"));
}
@Test @Test
public void testFeeExtension_premiumLabels_v11_create() throws Exception { public void testFeeExtension_premiumLabels_v11_create() throws Exception {
createTld("example"); createTld("example");
@ -730,6 +766,15 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_restore.xml")); runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_restore.xml"));
} }
@Test
public void testFeeExtension_premiumLabels_v11_restore_withRenewal() throws Exception {
setEppInput("domain_check_fee_premium_v11_restore.xml");
createTld("example");
persistPendingDeleteDomain("rich.example");
runFlowAssertResponse(
loadFile("domain_check_fee_premium_response_v11_restore_with_renewal.xml"));
}
@Test @Test
public void testFeeExtension_premiumLabels_v11_update() throws Exception { public void testFeeExtension_premiumLabels_v11_update() throws Exception {
createTld("example"); createTld("example");
@ -744,6 +789,14 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml")); runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml"));
} }
@Test
public void testFeeExtension_premiumLabels_v12_withRenewalOnRestore() throws Exception {
createTld("example");
setEppInput("domain_check_fee_premium_v12.xml");
persistPendingDeleteDomain("rich.example");
runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12_with_renewal.xml"));
}
@Test @Test
public void testFeeExtension_fractionalCost() throws Exception { public void testFeeExtension_fractionalCost() throws Exception {
// Note that the response xml expects to see "11.10" with two digits after the decimal point. // Note that the response xml expects to see "11.10" with two digits after the decimal point.
@ -820,6 +873,23 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_restore.xml")); runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_restore.xml"));
} }
@Test
public void testFeeExtension_reservedName_v11_restore_withRenewals() throws Exception {
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(createReservedList())
.setPremiumList(persistPremiumList("tld", "premiumcollision,USD 70"))
.build());
persistPendingDeleteDomain("reserved.tld");
persistPendingDeleteDomain("allowedinsunrise.tld");
persistPendingDeleteDomain("collision.tld");
persistPendingDeleteDomain("premiumcollision.tld");
setEppInput("domain_check_fee_reserved_v11_restore.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_reserved_response_v11_restore_with_renewals.xml"));
}
@Test @Test
public void testFeeExtension_reservedName_v12() throws Exception { public void testFeeExtension_reservedName_v12() throws Exception {
persistResource( persistResource(
@ -845,6 +915,25 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v06.xml")); runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v06.xml"));
} }
@Test
public void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v06_withRestoreRenewals()
throws Exception {
createTld("tld", START_DATE_SUNRISE);
persistResource(
Registry.get("tld")
.asBuilder()
.setReservedLists(createReservedList())
.setPremiumList(persistPremiumList("tld", "premiumcollision,USD 70"))
.build());
persistPendingDeleteDomain("reserved.tld");
persistPendingDeleteDomain("allowedinsunrise.tld");
persistPendingDeleteDomain("collision.tld");
persistPendingDeleteDomain("premiumcollision.tld");
setEppInput("domain_check_fee_reserved_v06.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_reserved_sunrise_response_v06_with_renewals.xml"));
}
@Test @Test
public void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_create() public void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_create()
throws Exception { throws Exception {
@ -1151,4 +1240,14 @@ public class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFl
assertIcannReportingActivityFieldLogged("srs-dom-check"); assertIcannReportingActivityFieldLogged("srs-dom-check");
assertTldsFieldLogged("com", "net", "org"); assertTldsFieldLogged("com", "net", "org");
} }
private void persistPendingDeleteDomain(String domainName) {
persistResource(
newDomainBase(domainName)
.asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(25))
.setRegistrationExpirationTime(clock.nowUtc().minusDays(1))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
}
} }

View file

@ -44,8 +44,8 @@ public class XmlTestUtils {
/** /**
* Asserts that the two XML strings match. * Asserts that the two XML strings match.
* *
* <p>Note that the actual XML must start with a UTF-8 standalone XML header, but the expected * <p>Note that the actual XML must start with a UTF-8 standalone XML header, but the expected XML
* XML has no such restriction (and typically lacks the header entirely). * has no such restriction (and typically lacks the header entirely).
*/ */
public static void assertXmlEquals(String expected, String actual, String... ignoredPaths) public static void assertXmlEquals(String expected, String actual, String... ignoredPaths)
throws Exception { throws Exception {

View file

@ -38,7 +38,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">

View file

@ -46,7 +46,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>

View file

@ -43,9 +43,7 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -0,0 +1,58 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">rich.example</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData 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:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00
</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>rich.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>rich.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>rich.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -41,9 +41,7 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -20,9 +20,7 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -0,0 +1,35 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">rich.example</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.11"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd avail="1">
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command>restore</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -21,7 +21,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="update">0.00</fee:fee> <fee:fee description="update">0.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -1,4 +1,4 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> domain_check_fee_premium_response_v12.xml<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response> <response>
<result code="1000"> <result code="1000">
<msg>Command completed successfully</msg> <msg>Command completed successfully</msg>
@ -49,9 +49,7 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
<fee:cd> <fee:cd>
@ -61,7 +59,6 @@
<fee:command name="update"> <fee:command name="update">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="update">0.00</fee:fee> <fee:fee description="update">0.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>

View file

@ -0,0 +1,74 @@
domain_check_fee_premium_response_v12.xml<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">rich.example</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.12"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="transfer">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="update">
<fee:period unit="y">1</fee:period>
<fee:fee description="update">0.00</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -51,7 +51,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">
@ -80,7 +79,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">
@ -109,7 +107,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">
@ -140,9 +137,7 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -33,7 +33,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
<fee:cd avail="1"> <fee:cd avail="1">
@ -43,7 +42,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
<fee:cd avail="1"> <fee:cd avail="1">
@ -53,7 +51,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
<fee:cd avail="1"> <fee:cd avail="1">
@ -63,8 +60,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:class>premium</fee:class>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>

View file

@ -0,0 +1,77 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">reserved.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">allowedinsunrise.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">collision.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">premiumcollision.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.11"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd avail="1">
<fee:object>
<domain:name>reserved.tld</domain:name>
</fee:object>
<fee:command>restore</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>allowedinsunrise.tld</domain:name>
</fee:object>
<fee:command>restore</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>collision.tld</domain:name>
</fee:object>
<fee:command>restore</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>premiumcollision.tld</domain:name>
</fee:object>
<fee:command>restore</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:class>premium</fee:class>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -60,7 +60,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
@ -97,7 +96,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
@ -134,7 +132,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
@ -173,9 +170,7 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>

View file

@ -48,7 +48,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">
@ -77,7 +76,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</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">
@ -109,7 +107,6 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>collision</fee:class> <fee:class>collision</fee:class>
</fee:cd> </fee:cd>
@ -142,9 +139,8 @@
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium-collision</fee:class> <fee:class>collision</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -0,0 +1,159 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">reserved.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">allowedinsunrise.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">collision.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">premiumcollision.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:class>reserved</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>reserved.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>allowedinsunrise.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>collision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>collision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>collision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>collision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
<fee:class>collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>premiumcollision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">70.00</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>premiumcollision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>premiumcollision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>premiumcollision.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee>
<fee:class>premium-collision</fee:class>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -30,7 +30,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
<fee:cd avail="1"> <fee:cd avail="1">
@ -40,7 +39,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:cd> </fee:cd>
<fee:cd avail="1"> <fee:cd avail="1">
@ -50,7 +48,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>collision</fee:class> <fee:class>collision</fee:class>
</fee:cd> </fee:cd>
@ -61,9 +58,8 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:currency>USD</fee:currency> <fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium-collision</fee:class> <fee:class>collision</fee:class>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>
</extension> </extension>

View file

@ -57,7 +57,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
@ -94,7 +93,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
@ -134,7 +132,6 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>collision</fee:class> <fee:class>collision</fee:class>
</fee:command> </fee:command>
@ -175,9 +172,8 @@
</fee:object> </fee:object>
<fee:command name="restore"> <fee:command name="restore">
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="renew">70.00</fee:fee>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium-collision</fee:class> <fee:class>collision</fee:class>
</fee:command> </fee:command>
</fee:cd> </fee:cd>
</fee:chkData> </fee:chkData>

View file

@ -0,0 +1,378 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns:contact="urn:ietf:params:xml:ns:contact-1.0" xmlns:fee="urn:ietf:params:xml:ns:fee-0.6" xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0" xmlns:fee11="urn:ietf:params:xml:ns:fee-0.11" xmlns:fee12="urn:ietf:params:xml:ns:fee-0.12" xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1" xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="true">example-00.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-01.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-02.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-03.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-04.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-05.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-06.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-07.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-08.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-09.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-10.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-11.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-12.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-13.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-14.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-15.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-16.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-17.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-18.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-19.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-20.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-21.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-22.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-23.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-24.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-25.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-26.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-27.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-28.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-29.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData>
<fee:cd>
<fee:name>example-00.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-01.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-02.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-03.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-04.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-05.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-06.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-07.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-08.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-09.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-10.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-11.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-12.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-13.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-14.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-15.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-16.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-17.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-18.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-19.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-20.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-21.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-22.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-23.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-24.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-25.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-26.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-27.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-28.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd>
<fee:name>example-29.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,226 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-00.tld</domain:name>
<domain:name>example-01.tld</domain:name>
<domain:name>example-02.tld</domain:name>
<domain:name>example-03.tld</domain:name>
<domain:name>example-04.tld</domain:name>
<domain:name>example-05.tld</domain:name>
<domain:name>example-06.tld</domain:name>
<domain:name>example-07.tld</domain:name>
<domain:name>example-08.tld</domain:name>
<domain:name>example-09.tld</domain:name>
<domain:name>example-10.tld</domain:name>
<domain:name>example-11.tld</domain:name>
<domain:name>example-12.tld</domain:name>
<domain:name>example-13.tld</domain:name>
<domain:name>example-14.tld</domain:name>
<domain:name>example-15.tld</domain:name>
<domain:name>example-16.tld</domain:name>
<domain:name>example-17.tld</domain:name>
<domain:name>example-18.tld</domain:name>
<domain:name>example-19.tld</domain:name>
<domain:name>example-20.tld</domain:name>
<domain:name>example-21.tld</domain:name>
<domain:name>example-22.tld</domain:name>
<domain:name>example-23.tld</domain:name>
<domain:name>example-24.tld</domain:name>
<domain:name>example-25.tld</domain:name>
<domain:name>example-26.tld</domain:name>
<domain:name>example-27.tld</domain:name>
<domain:name>example-28.tld</domain:name>
<domain:name>example-29.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:domain>
<fee:name>example-00.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-01.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-02.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-03.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-04.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-05.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-06.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-07.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-08.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-09.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-10.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-11.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-12.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-13.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-14.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-15.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-16.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-17.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-18.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-19.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-20.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-21.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-22.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-23.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-24.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-25.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-26.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-27.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-28.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example-29.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -36,7 +36,6 @@
<fee:command>restore</fee:command> <fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period> <fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee> <fee:fee description="restore">17.00</fee:fee>
<fee:class>premium</fee:class>
</fee:infData> </fee:infData>
</extension> </extension>
<trID> <trID>